@buildersgarden/siwa 0.0.2 → 0.0.4
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 +1 -1
- package/dist/keystore.d.ts +23 -10
- package/dist/keystore.js +230 -63
- package/dist/proxy-auth.js +2 -2
- package/dist/registry.d.ts +6 -6
- package/dist/registry.js +69 -17
- package/dist/siwa.d.ts +4 -4
- package/dist/siwa.js +34 -19
- package/package.json +44 -11
package/README.md
CHANGED
|
@@ -64,7 +64,7 @@ See [`references/security-model.md`](references/security-model.md) for the full
|
|
|
64
64
|
## Tech Stack
|
|
65
65
|
|
|
66
66
|
- **TypeScript** (ES modules, strict mode)
|
|
67
|
-
- **
|
|
67
|
+
- **viem** — wallet management and contract interaction
|
|
68
68
|
- **pnpm** — package manager
|
|
69
69
|
|
|
70
70
|
## References
|
package/dist/keystore.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Three backends, in order of preference:
|
|
7
7
|
* 0. Keyring Proxy (via HMAC-authenticated HTTP) — key never enters agent process
|
|
8
|
-
* 1. Ethereum V3 Encrypted JSON Keystore (via
|
|
8
|
+
* 1. Ethereum V3 Encrypted JSON Keystore (via @noble/ciphers) — password-encrypted file on disk
|
|
9
9
|
* 2. Environment variable fallback (AGENT_PRIVATE_KEY) — least secure, for CI/testing only
|
|
10
10
|
*
|
|
11
11
|
* The private key NEVER leaves this module as a return value.
|
|
@@ -18,15 +18,15 @@
|
|
|
18
18
|
* - getAddress() → returns the public address
|
|
19
19
|
* - hasWallet() → returns boolean
|
|
20
20
|
*
|
|
21
|
-
* EIP-7702 Support
|
|
22
|
-
* Wallets are standard EOAs created via
|
|
21
|
+
* EIP-7702 Support:
|
|
22
|
+
* Wallets are standard EOAs created via viem's generatePrivateKey().
|
|
23
23
|
* EIP-7702 allows these EOAs to temporarily delegate to smart contract
|
|
24
24
|
* implementations via authorization lists in type 4 transactions.
|
|
25
25
|
* Use signAuthorization() to sign delegation authorizations without
|
|
26
26
|
* exposing the private key.
|
|
27
27
|
*
|
|
28
28
|
* Dependencies:
|
|
29
|
-
* npm install
|
|
29
|
+
* npm install viem
|
|
30
30
|
*
|
|
31
31
|
* Configuration (via env vars or passed options):
|
|
32
32
|
* KEYSTORE_BACKEND — "encrypted-file" | "env" | "proxy" (auto-detected if omitted)
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
* KEYSTORE_PATH — Path to encrypted keystore file (default: ./agent-keystore.json)
|
|
35
35
|
* AGENT_PRIVATE_KEY — Fallback for env backend only
|
|
36
36
|
*/
|
|
37
|
-
import {
|
|
37
|
+
import { type WalletClient, type Account, type Chain, type Transport } from 'viem';
|
|
38
38
|
export type KeystoreBackend = 'encrypted-file' | 'env' | 'proxy';
|
|
39
39
|
export interface KeystoreConfig {
|
|
40
40
|
backend?: KeystoreBackend;
|
|
@@ -65,6 +65,20 @@ export interface SignedAuthorization {
|
|
|
65
65
|
r: string;
|
|
66
66
|
s: string;
|
|
67
67
|
}
|
|
68
|
+
export interface TransactionLike {
|
|
69
|
+
to?: string;
|
|
70
|
+
data?: string;
|
|
71
|
+
value?: bigint;
|
|
72
|
+
nonce?: number;
|
|
73
|
+
chainId?: number;
|
|
74
|
+
type?: number;
|
|
75
|
+
maxFeePerGas?: bigint | null;
|
|
76
|
+
maxPriorityFeePerGas?: bigint | null;
|
|
77
|
+
gasLimit?: bigint;
|
|
78
|
+
gas?: bigint;
|
|
79
|
+
gasPrice?: bigint | null;
|
|
80
|
+
accessList?: any[];
|
|
81
|
+
}
|
|
68
82
|
export declare function detectBackend(): Promise<KeystoreBackend>;
|
|
69
83
|
/**
|
|
70
84
|
* Create a new random wallet and store it securely.
|
|
@@ -96,13 +110,12 @@ export declare function signMessage(message: string, config?: KeystoreConfig): P
|
|
|
96
110
|
* The private key is loaded, used, and immediately discarded.
|
|
97
111
|
* Only the signed transaction is returned.
|
|
98
112
|
*/
|
|
99
|
-
export declare function signTransaction(tx:
|
|
113
|
+
export declare function signTransaction(tx: TransactionLike, config?: KeystoreConfig): Promise<{
|
|
100
114
|
signedTx: string;
|
|
101
115
|
address: string;
|
|
102
116
|
}>;
|
|
103
117
|
/**
|
|
104
118
|
* Sign an EIP-7702 authorization for delegating the EOA to a contract.
|
|
105
|
-
* Requires ethers >= 6.14.3.
|
|
106
119
|
*
|
|
107
120
|
* This allows the agent's EOA to temporarily act as a smart contract
|
|
108
121
|
* during a type 4 transaction. The private key is loaded, used, and
|
|
@@ -113,12 +126,12 @@ export declare function signTransaction(tx: ethers.TransactionRequest, config?:
|
|
|
113
126
|
*/
|
|
114
127
|
export declare function signAuthorization(auth: AuthorizationRequest, config?: KeystoreConfig): Promise<SignedAuthorization>;
|
|
115
128
|
/**
|
|
116
|
-
* Get a
|
|
117
|
-
* NOTE: This
|
|
129
|
+
* Get a wallet client for contract interactions.
|
|
130
|
+
* NOTE: This creates a client with the private key in memory.
|
|
118
131
|
* Use only within a narrow scope and discard immediately.
|
|
119
132
|
* Prefer signMessage() / signTransaction() when possible.
|
|
120
133
|
*/
|
|
121
|
-
export declare function
|
|
134
|
+
export declare function getWalletClient(rpcUrl: string, config?: KeystoreConfig): Promise<WalletClient<Transport, Chain | undefined, Account>>;
|
|
122
135
|
/**
|
|
123
136
|
* Delete the stored wallet from the active backend.
|
|
124
137
|
* DESTRUCTIVE — the identity is lost if no backup exists.
|
package/dist/keystore.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Three backends, in order of preference:
|
|
7
7
|
* 0. Keyring Proxy (via HMAC-authenticated HTTP) — key never enters agent process
|
|
8
|
-
* 1. Ethereum V3 Encrypted JSON Keystore (via
|
|
8
|
+
* 1. Ethereum V3 Encrypted JSON Keystore (via @noble/ciphers) — password-encrypted file on disk
|
|
9
9
|
* 2. Environment variable fallback (AGENT_PRIVATE_KEY) — least secure, for CI/testing only
|
|
10
10
|
*
|
|
11
11
|
* The private key NEVER leaves this module as a return value.
|
|
@@ -18,15 +18,15 @@
|
|
|
18
18
|
* - getAddress() → returns the public address
|
|
19
19
|
* - hasWallet() → returns boolean
|
|
20
20
|
*
|
|
21
|
-
* EIP-7702 Support
|
|
22
|
-
* Wallets are standard EOAs created via
|
|
21
|
+
* EIP-7702 Support:
|
|
22
|
+
* Wallets are standard EOAs created via viem's generatePrivateKey().
|
|
23
23
|
* EIP-7702 allows these EOAs to temporarily delegate to smart contract
|
|
24
24
|
* implementations via authorization lists in type 4 transactions.
|
|
25
25
|
* Use signAuthorization() to sign delegation authorizations without
|
|
26
26
|
* exposing the private key.
|
|
27
27
|
*
|
|
28
28
|
* Dependencies:
|
|
29
|
-
* npm install
|
|
29
|
+
* npm install viem
|
|
30
30
|
*
|
|
31
31
|
* Configuration (via env vars or passed options):
|
|
32
32
|
* KEYSTORE_BACKEND — "encrypted-file" | "env" | "proxy" (auto-detected if omitted)
|
|
@@ -34,7 +34,12 @@
|
|
|
34
34
|
* KEYSTORE_PATH — Path to encrypted keystore file (default: ./agent-keystore.json)
|
|
35
35
|
* AGENT_PRIVATE_KEY — Fallback for env backend only
|
|
36
36
|
*/
|
|
37
|
-
import {
|
|
37
|
+
import { createWalletClient, http, keccak256, toBytes, toHex, concat, } from 'viem';
|
|
38
|
+
import { privateKeyToAccount, generatePrivateKey } from 'viem/accounts';
|
|
39
|
+
import { hashAuthorization } from 'viem/experimental';
|
|
40
|
+
import { scrypt } from '@noble/hashes/scrypt';
|
|
41
|
+
import { randomBytes } from '@noble/hashes/utils';
|
|
42
|
+
import { ctr } from '@noble/ciphers/aes';
|
|
38
43
|
import * as fs from 'fs';
|
|
39
44
|
import * as crypto from 'crypto';
|
|
40
45
|
import * as os from 'os';
|
|
@@ -43,6 +48,84 @@ import { computeHmac } from './proxy-auth.js';
|
|
|
43
48
|
// Constants
|
|
44
49
|
// ---------------------------------------------------------------------------
|
|
45
50
|
const DEFAULT_KEYSTORE_PATH = './agent-keystore.json';
|
|
51
|
+
function generateUUID() {
|
|
52
|
+
const bytes = randomBytes(16);
|
|
53
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4
|
|
54
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant
|
|
55
|
+
const hex = toHex(bytes).slice(2);
|
|
56
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
57
|
+
}
|
|
58
|
+
async function encryptKeystore(privateKey, password) {
|
|
59
|
+
const privateKeyBytes = toBytes(privateKey);
|
|
60
|
+
const salt = randomBytes(32);
|
|
61
|
+
const iv = randomBytes(16);
|
|
62
|
+
// Scrypt parameters (standard for V3 keystores)
|
|
63
|
+
const n = 262144; // 2^18
|
|
64
|
+
const r = 8;
|
|
65
|
+
const p = 1;
|
|
66
|
+
const dklen = 32;
|
|
67
|
+
// Derive key using scrypt
|
|
68
|
+
const derivedKey = scrypt(new TextEncoder().encode(password), salt, { N: n, r, p, dkLen: dklen });
|
|
69
|
+
// Encrypt private key with AES-128-CTR
|
|
70
|
+
const encryptionKey = derivedKey.slice(0, 16);
|
|
71
|
+
const cipher = ctr(encryptionKey, iv);
|
|
72
|
+
const ciphertext = cipher.encrypt(privateKeyBytes);
|
|
73
|
+
// Calculate MAC: keccak256(derivedKey[16:32] + ciphertext)
|
|
74
|
+
const macData = concat([toHex(derivedKey.slice(16, 32)), toHex(ciphertext)]);
|
|
75
|
+
const mac = keccak256(macData);
|
|
76
|
+
// Get address from private key
|
|
77
|
+
const account = privateKeyToAccount(privateKey);
|
|
78
|
+
const keystore = {
|
|
79
|
+
version: 3,
|
|
80
|
+
id: generateUUID(),
|
|
81
|
+
address: account.address.toLowerCase().slice(2),
|
|
82
|
+
crypto: {
|
|
83
|
+
ciphertext: toHex(ciphertext).slice(2),
|
|
84
|
+
cipherparams: { iv: toHex(iv).slice(2) },
|
|
85
|
+
cipher: 'aes-128-ctr',
|
|
86
|
+
kdf: 'scrypt',
|
|
87
|
+
kdfparams: {
|
|
88
|
+
dklen,
|
|
89
|
+
salt: toHex(salt).slice(2),
|
|
90
|
+
n,
|
|
91
|
+
r,
|
|
92
|
+
p,
|
|
93
|
+
},
|
|
94
|
+
mac: mac.slice(2),
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
return JSON.stringify(keystore);
|
|
98
|
+
}
|
|
99
|
+
async function decryptKeystore(json, password) {
|
|
100
|
+
const keystore = JSON.parse(json);
|
|
101
|
+
if (keystore.version !== 3) {
|
|
102
|
+
throw new Error(`Unsupported keystore version: ${keystore.version}`);
|
|
103
|
+
}
|
|
104
|
+
const { crypto: cryptoData } = keystore;
|
|
105
|
+
if (cryptoData.kdf !== 'scrypt') {
|
|
106
|
+
throw new Error(`Unsupported KDF: ${cryptoData.kdf}`);
|
|
107
|
+
}
|
|
108
|
+
if (cryptoData.cipher !== 'aes-128-ctr') {
|
|
109
|
+
throw new Error(`Unsupported cipher: ${cryptoData.cipher}`);
|
|
110
|
+
}
|
|
111
|
+
const { kdfparams } = cryptoData;
|
|
112
|
+
const salt = toBytes(`0x${kdfparams.salt}`);
|
|
113
|
+
const iv = toBytes(`0x${cryptoData.cipherparams.iv}`);
|
|
114
|
+
const ciphertext = toBytes(`0x${cryptoData.ciphertext}`);
|
|
115
|
+
// Derive key using scrypt
|
|
116
|
+
const derivedKey = scrypt(new TextEncoder().encode(password), salt, { N: kdfparams.n, r: kdfparams.r, p: kdfparams.p, dkLen: kdfparams.dklen });
|
|
117
|
+
// Verify MAC
|
|
118
|
+
const macData = concat([toHex(derivedKey.slice(16, 32)), toHex(ciphertext)]);
|
|
119
|
+
const calculatedMac = keccak256(macData).slice(2);
|
|
120
|
+
if (calculatedMac.toLowerCase() !== cryptoData.mac.toLowerCase()) {
|
|
121
|
+
throw new Error('Invalid password or corrupted keystore');
|
|
122
|
+
}
|
|
123
|
+
// Decrypt private key with AES-128-CTR
|
|
124
|
+
const encryptionKey = derivedKey.slice(0, 16);
|
|
125
|
+
const cipher = ctr(encryptionKey, iv);
|
|
126
|
+
const privateKeyBytes = cipher.decrypt(ciphertext);
|
|
127
|
+
return toHex(privateKeyBytes);
|
|
128
|
+
}
|
|
46
129
|
// ---------------------------------------------------------------------------
|
|
47
130
|
// Proxy backend — HMAC-authenticated HTTP to a keyring proxy server
|
|
48
131
|
// ---------------------------------------------------------------------------
|
|
@@ -87,20 +170,17 @@ export async function detectBackend() {
|
|
|
87
170
|
return 'encrypted-file';
|
|
88
171
|
}
|
|
89
172
|
// ---------------------------------------------------------------------------
|
|
90
|
-
// Encrypted V3 JSON Keystore backend
|
|
173
|
+
// Encrypted V3 JSON Keystore backend
|
|
91
174
|
// ---------------------------------------------------------------------------
|
|
92
|
-
async function encryptedFileStore(privateKey,
|
|
93
|
-
const
|
|
94
|
-
// ethers v6: encryptKeystoreJsonSync or encryptKeystoreJson
|
|
95
|
-
const json = await ethers.encryptKeystoreJson(account, password);
|
|
175
|
+
async function encryptedFileStore(privateKey, password, filePath) {
|
|
176
|
+
const json = await encryptKeystore(privateKey, password);
|
|
96
177
|
fs.writeFileSync(filePath, json, { mode: 0o600 }); // Owner-only read/write
|
|
97
178
|
}
|
|
98
179
|
async function encryptedFileLoad(password, filePath) {
|
|
99
180
|
if (!fs.existsSync(filePath))
|
|
100
181
|
return null;
|
|
101
182
|
const json = fs.readFileSync(filePath, 'utf-8');
|
|
102
|
-
|
|
103
|
-
return wallet.privateKey;
|
|
183
|
+
return decryptKeystore(json, password);
|
|
104
184
|
}
|
|
105
185
|
function encryptedFileExists(filePath) {
|
|
106
186
|
return fs.existsSync(filePath);
|
|
@@ -139,13 +219,13 @@ export async function createWallet(config = {}) {
|
|
|
139
219
|
const data = await proxyRequest(config, '/create-wallet');
|
|
140
220
|
return { address: data.address, backend, keystorePath: undefined };
|
|
141
221
|
}
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
const address =
|
|
222
|
+
const privateKey = generatePrivateKey();
|
|
223
|
+
const account = privateKeyToAccount(privateKey);
|
|
224
|
+
const address = account.address;
|
|
145
225
|
switch (backend) {
|
|
146
226
|
case 'encrypted-file': {
|
|
147
227
|
const password = config.password || process.env.KEYSTORE_PASSWORD || deriveMachinePassword();
|
|
148
|
-
await encryptedFileStore(privateKey,
|
|
228
|
+
await encryptedFileStore(privateKey, password, keystorePath);
|
|
149
229
|
break;
|
|
150
230
|
}
|
|
151
231
|
case 'env':
|
|
@@ -174,12 +254,13 @@ export async function importWallet(privateKey, config = {}) {
|
|
|
174
254
|
throw new Error('importWallet() is not supported via proxy. Import the wallet on the proxy server directly.');
|
|
175
255
|
}
|
|
176
256
|
const keystorePath = config.keystorePath || process.env.KEYSTORE_PATH || DEFAULT_KEYSTORE_PATH;
|
|
177
|
-
const
|
|
178
|
-
const
|
|
257
|
+
const hexKey = (privateKey.startsWith('0x') ? privateKey : `0x${privateKey}`);
|
|
258
|
+
const account = privateKeyToAccount(hexKey);
|
|
259
|
+
const address = account.address;
|
|
179
260
|
switch (backend) {
|
|
180
261
|
case 'encrypted-file': {
|
|
181
262
|
const password = config.password || process.env.KEYSTORE_PASSWORD || deriveMachinePassword();
|
|
182
|
-
await encryptedFileStore(
|
|
263
|
+
await encryptedFileStore(hexKey, password, keystorePath);
|
|
183
264
|
break;
|
|
184
265
|
}
|
|
185
266
|
case 'env':
|
|
@@ -218,12 +299,11 @@ export async function getAddress(config = {}) {
|
|
|
218
299
|
const data = await proxyRequest(config, '/get-address');
|
|
219
300
|
return data.address;
|
|
220
301
|
}
|
|
221
|
-
const
|
|
222
|
-
if (!
|
|
302
|
+
const privateKey = await _loadPrivateKeyInternal(config);
|
|
303
|
+
if (!privateKey)
|
|
223
304
|
return null;
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
return address;
|
|
305
|
+
const account = privateKeyToAccount(privateKey);
|
|
306
|
+
return account.address;
|
|
227
307
|
}
|
|
228
308
|
/**
|
|
229
309
|
* Sign a message (EIP-191 personal_sign).
|
|
@@ -236,13 +316,52 @@ export async function signMessage(message, config = {}) {
|
|
|
236
316
|
const data = await proxyRequest(config, '/sign-message', { message });
|
|
237
317
|
return { signature: data.signature, address: data.address };
|
|
238
318
|
}
|
|
239
|
-
const
|
|
240
|
-
if (!
|
|
319
|
+
const privateKey = await _loadPrivateKeyInternal(config);
|
|
320
|
+
if (!privateKey)
|
|
241
321
|
throw new Error('No wallet found. Run createWallet() first.');
|
|
242
|
-
const
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
322
|
+
const account = privateKeyToAccount(privateKey);
|
|
323
|
+
const signature = await account.signMessage({ message });
|
|
324
|
+
return { signature, address: account.address };
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Parse a numeric value from JSON (string/number) to bigint.
|
|
328
|
+
* Returns undefined for null, undefined, or zero values.
|
|
329
|
+
* Zero is returned as undefined so viem encodes it as empty (0x80 in RLP).
|
|
330
|
+
*/
|
|
331
|
+
function parseBigIntFromJson(value) {
|
|
332
|
+
if (value === null || value === undefined)
|
|
333
|
+
return undefined;
|
|
334
|
+
let result;
|
|
335
|
+
if (typeof value === 'bigint') {
|
|
336
|
+
result = value;
|
|
337
|
+
}
|
|
338
|
+
else if (typeof value === 'number') {
|
|
339
|
+
result = BigInt(value);
|
|
340
|
+
}
|
|
341
|
+
else if (typeof value === 'string') {
|
|
342
|
+
// Handle hex strings (0x...) and decimal strings
|
|
343
|
+
result = BigInt(value);
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
return undefined;
|
|
347
|
+
}
|
|
348
|
+
// Return undefined for zero so viem encodes it as empty (0x80)
|
|
349
|
+
// instead of 0x00 which is non-canonical RLP
|
|
350
|
+
return result === 0n ? undefined : result;
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Parse a numeric value, keeping zero as 0n (for fields like nonce where 0 is valid).
|
|
354
|
+
*/
|
|
355
|
+
function parseBigIntKeepZero(value) {
|
|
356
|
+
if (value === null || value === undefined)
|
|
357
|
+
return undefined;
|
|
358
|
+
if (typeof value === 'bigint')
|
|
359
|
+
return value;
|
|
360
|
+
if (typeof value === 'number')
|
|
361
|
+
return BigInt(value);
|
|
362
|
+
if (typeof value === 'string')
|
|
363
|
+
return BigInt(value);
|
|
364
|
+
return undefined;
|
|
246
365
|
}
|
|
247
366
|
/**
|
|
248
367
|
* Sign a transaction.
|
|
@@ -255,16 +374,45 @@ export async function signTransaction(tx, config = {}) {
|
|
|
255
374
|
const data = await proxyRequest(config, '/sign-transaction', { tx: tx });
|
|
256
375
|
return { signedTx: data.signedTx, address: data.address };
|
|
257
376
|
}
|
|
258
|
-
const
|
|
259
|
-
if (!
|
|
377
|
+
const privateKey = await _loadPrivateKeyInternal(config);
|
|
378
|
+
if (!privateKey)
|
|
260
379
|
throw new Error('No wallet found. Run createWallet() first.');
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
380
|
+
const account = privateKeyToAccount(privateKey);
|
|
381
|
+
// Parse numeric fields from JSON representation (strings) to bigints.
|
|
382
|
+
// For 'value', zero is converted to undefined so viem encodes it as 0x80 (empty)
|
|
383
|
+
// instead of 0x00, which is non-canonical RLP and rejected by nodes.
|
|
384
|
+
const value = parseBigIntFromJson(tx.value);
|
|
385
|
+
const gas = parseBigIntKeepZero(tx.gasLimit ?? tx.gas);
|
|
386
|
+
const maxFeePerGas = parseBigIntKeepZero(tx.maxFeePerGas);
|
|
387
|
+
const maxPriorityFeePerGas = parseBigIntKeepZero(tx.maxPriorityFeePerGas);
|
|
388
|
+
const gasPrice = parseBigIntKeepZero(tx.gasPrice);
|
|
389
|
+
// Build transaction request for viem
|
|
390
|
+
const viemTx = {
|
|
391
|
+
to: tx.to,
|
|
392
|
+
data: tx.data,
|
|
393
|
+
value,
|
|
394
|
+
nonce: tx.nonce,
|
|
395
|
+
chainId: tx.chainId,
|
|
396
|
+
gas,
|
|
397
|
+
};
|
|
398
|
+
// Handle EIP-1559 vs legacy transactions
|
|
399
|
+
if (tx.type === 2 || tx.maxFeePerGas !== undefined) {
|
|
400
|
+
viemTx.type = 'eip1559';
|
|
401
|
+
viemTx.maxFeePerGas = maxFeePerGas;
|
|
402
|
+
viemTx.maxPriorityFeePerGas = maxPriorityFeePerGas;
|
|
403
|
+
}
|
|
404
|
+
else if (tx.gasPrice !== undefined) {
|
|
405
|
+
viemTx.type = 'legacy';
|
|
406
|
+
viemTx.gasPrice = gasPrice;
|
|
407
|
+
}
|
|
408
|
+
if (tx.accessList) {
|
|
409
|
+
viemTx.accessList = tx.accessList;
|
|
410
|
+
}
|
|
411
|
+
const signedTx = await account.signTransaction(viemTx);
|
|
412
|
+
return { signedTx, address: account.address };
|
|
264
413
|
}
|
|
265
414
|
/**
|
|
266
415
|
* Sign an EIP-7702 authorization for delegating the EOA to a contract.
|
|
267
|
-
* Requires ethers >= 6.14.3.
|
|
268
416
|
*
|
|
269
417
|
* This allows the agent's EOA to temporarily act as a smart contract
|
|
270
418
|
* during a type 4 transaction. The private key is loaded, used, and
|
|
@@ -279,37 +427,54 @@ export async function signAuthorization(auth, config = {}) {
|
|
|
279
427
|
const data = await proxyRequest(config, '/sign-authorization', { auth });
|
|
280
428
|
return data;
|
|
281
429
|
}
|
|
282
|
-
const
|
|
283
|
-
if (!
|
|
430
|
+
const privateKey = await _loadPrivateKeyInternal(config);
|
|
431
|
+
if (!privateKey)
|
|
284
432
|
throw new Error('No wallet found. Run createWallet() first.');
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
433
|
+
const account = privateKeyToAccount(privateKey);
|
|
434
|
+
// EIP-7702 authorization signing using viem experimental
|
|
435
|
+
const chainId = auth.chainId ?? 1;
|
|
436
|
+
const nonce = auth.nonce ?? 0;
|
|
437
|
+
// Hash the authorization struct according to EIP-7702
|
|
438
|
+
const authHash = hashAuthorization({
|
|
439
|
+
contractAddress: auth.address,
|
|
440
|
+
chainId,
|
|
441
|
+
nonce,
|
|
294
442
|
});
|
|
295
|
-
//
|
|
296
|
-
|
|
443
|
+
// Sign the authorization hash
|
|
444
|
+
const signature = await account.sign({ hash: authHash });
|
|
445
|
+
// Parse signature into r, s, yParity
|
|
446
|
+
const r = signature.slice(0, 66);
|
|
447
|
+
const s = `0x${signature.slice(66, 130)}`;
|
|
448
|
+
const v = parseInt(signature.slice(130, 132), 16);
|
|
449
|
+
const yParity = v - 27; // Convert v to yParity (0 or 1)
|
|
450
|
+
return {
|
|
451
|
+
address: auth.address,
|
|
452
|
+
nonce,
|
|
453
|
+
chainId,
|
|
454
|
+
yParity,
|
|
455
|
+
r,
|
|
456
|
+
s,
|
|
457
|
+
};
|
|
297
458
|
}
|
|
298
459
|
/**
|
|
299
|
-
* Get a
|
|
300
|
-
* NOTE: This
|
|
460
|
+
* Get a wallet client for contract interactions.
|
|
461
|
+
* NOTE: This creates a client with the private key in memory.
|
|
301
462
|
* Use only within a narrow scope and discard immediately.
|
|
302
463
|
* Prefer signMessage() / signTransaction() when possible.
|
|
303
464
|
*/
|
|
304
|
-
export async function
|
|
465
|
+
export async function getWalletClient(rpcUrl, config = {}) {
|
|
305
466
|
const backend = config.backend || await detectBackend();
|
|
306
467
|
if (backend === 'proxy') {
|
|
307
|
-
throw new Error('
|
|
468
|
+
throw new Error('getWalletClient() is not supported via proxy. The private key cannot be serialized over HTTP. Use signMessage() or signTransaction() instead.');
|
|
308
469
|
}
|
|
309
|
-
const
|
|
310
|
-
if (!
|
|
470
|
+
const privateKey = await _loadPrivateKeyInternal(config);
|
|
471
|
+
if (!privateKey)
|
|
311
472
|
throw new Error('No wallet found. Run createWallet() first.');
|
|
312
|
-
|
|
473
|
+
const account = privateKeyToAccount(privateKey);
|
|
474
|
+
return createWalletClient({
|
|
475
|
+
account,
|
|
476
|
+
transport: http(rpcUrl),
|
|
477
|
+
});
|
|
313
478
|
}
|
|
314
479
|
/**
|
|
315
480
|
* Delete the stored wallet from the active backend.
|
|
@@ -334,9 +499,9 @@ export async function deleteWallet(config = {}) {
|
|
|
334
499
|
}
|
|
335
500
|
}
|
|
336
501
|
// ---------------------------------------------------------------------------
|
|
337
|
-
// Internal — loads the
|
|
502
|
+
// Internal — loads the private key. NEVER exposed publicly.
|
|
338
503
|
// ---------------------------------------------------------------------------
|
|
339
|
-
async function
|
|
504
|
+
async function _loadPrivateKeyInternal(config = {}) {
|
|
340
505
|
const backend = config.backend || await detectBackend();
|
|
341
506
|
const keystorePath = config.keystorePath || process.env.KEYSTORE_PATH || DEFAULT_KEYSTORE_PATH;
|
|
342
507
|
let privateKey = null;
|
|
@@ -346,11 +511,13 @@ async function _loadWalletInternal(config = {}) {
|
|
|
346
511
|
privateKey = await encryptedFileLoad(password, keystorePath);
|
|
347
512
|
break;
|
|
348
513
|
}
|
|
349
|
-
case 'env':
|
|
350
|
-
|
|
514
|
+
case 'env': {
|
|
515
|
+
const envKey = process.env.AGENT_PRIVATE_KEY || null;
|
|
516
|
+
if (envKey) {
|
|
517
|
+
privateKey = (envKey.startsWith('0x') ? envKey : `0x${envKey}`);
|
|
518
|
+
}
|
|
351
519
|
break;
|
|
520
|
+
}
|
|
352
521
|
}
|
|
353
|
-
|
|
354
|
-
return null;
|
|
355
|
-
return new ethers.Wallet(privateKey);
|
|
522
|
+
return privateKey;
|
|
356
523
|
}
|
package/dist/proxy-auth.js
CHANGED
|
@@ -46,8 +46,8 @@ export function verifyHmac(secret, method, path, body, timestamp, signature) {
|
|
|
46
46
|
.update(payload)
|
|
47
47
|
.digest('hex');
|
|
48
48
|
// Constant-time comparison
|
|
49
|
-
const sigBuf = Buffer.from(signature, 'utf-8');
|
|
50
|
-
const expBuf = Buffer.from(expected, 'utf-8');
|
|
49
|
+
const sigBuf = new Uint8Array(Buffer.from(signature, 'utf-8'));
|
|
50
|
+
const expBuf = new Uint8Array(Buffer.from(expected, 'utf-8'));
|
|
51
51
|
if (sigBuf.length !== expBuf.length) {
|
|
52
52
|
return { valid: false, error: 'Signature mismatch' };
|
|
53
53
|
}
|
package/dist/registry.d.ts
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* Provides functions to read agent profiles and reputation from on-chain registries.
|
|
6
6
|
*
|
|
7
7
|
* Dependencies:
|
|
8
|
-
* npm install
|
|
8
|
+
* npm install viem
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
10
|
+
import { type PublicClient } from 'viem';
|
|
11
11
|
/** Service endpoint types defined in ERC-8004 */
|
|
12
12
|
export type ServiceType = 'web' | 'A2A' | 'MCP' | 'OASF' | 'ENS' | 'DID' | 'email';
|
|
13
13
|
/** Trust models defined in ERC-8004 */
|
|
@@ -42,7 +42,7 @@ export interface AgentProfile {
|
|
|
42
42
|
}
|
|
43
43
|
export interface GetAgentOptions {
|
|
44
44
|
registryAddress: string;
|
|
45
|
-
|
|
45
|
+
client: PublicClient;
|
|
46
46
|
fetchMetadata?: boolean;
|
|
47
47
|
}
|
|
48
48
|
export interface ReputationSummary {
|
|
@@ -53,7 +53,7 @@ export interface ReputationSummary {
|
|
|
53
53
|
}
|
|
54
54
|
export interface GetReputationOptions {
|
|
55
55
|
reputationRegistryAddress: string;
|
|
56
|
-
|
|
56
|
+
client: PublicClient;
|
|
57
57
|
clients?: string[];
|
|
58
58
|
tag1?: ReputationTag | (string & {});
|
|
59
59
|
tag2?: string;
|
|
@@ -62,13 +62,13 @@ export interface GetReputationOptions {
|
|
|
62
62
|
* Read an agent from the Identity Registry and parse its profile.
|
|
63
63
|
*
|
|
64
64
|
* @param agentId The on-chain agent token ID
|
|
65
|
-
* @param options Registry address,
|
|
65
|
+
* @param options Registry address, client, and optional fetchMetadata flag
|
|
66
66
|
*/
|
|
67
67
|
export declare function getAgent(agentId: number, options: GetAgentOptions): Promise<AgentProfile>;
|
|
68
68
|
/**
|
|
69
69
|
* Read an agent's reputation summary from the Reputation Registry.
|
|
70
70
|
*
|
|
71
71
|
* @param agentId The on-chain agent token ID
|
|
72
|
-
* @param options Reputation registry address,
|
|
72
|
+
* @param options Reputation registry address, client, and optional filters
|
|
73
73
|
*/
|
|
74
74
|
export declare function getReputation(agentId: number, options: GetReputationOptions): Promise<ReputationSummary>;
|
package/dist/registry.js
CHANGED
|
@@ -5,17 +5,50 @@
|
|
|
5
5
|
* Provides functions to read agent profiles and reputation from on-chain registries.
|
|
6
6
|
*
|
|
7
7
|
* Dependencies:
|
|
8
|
-
* npm install
|
|
8
|
+
* npm install viem
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
10
|
+
import { zeroAddress, } from 'viem';
|
|
11
11
|
// ─── ABI Fragments ──────────────────────────────────────────────────
|
|
12
12
|
const IDENTITY_REGISTRY_ABI = [
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
{
|
|
14
|
+
name: 'ownerOf',
|
|
15
|
+
type: 'function',
|
|
16
|
+
stateMutability: 'view',
|
|
17
|
+
inputs: [{ name: 'tokenId', type: 'uint256' }],
|
|
18
|
+
outputs: [{ name: '', type: 'address' }],
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: 'tokenURI',
|
|
22
|
+
type: 'function',
|
|
23
|
+
stateMutability: 'view',
|
|
24
|
+
inputs: [{ name: 'tokenId', type: 'uint256' }],
|
|
25
|
+
outputs: [{ name: '', type: 'string' }],
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: 'getAgentWallet',
|
|
29
|
+
type: 'function',
|
|
30
|
+
stateMutability: 'view',
|
|
31
|
+
inputs: [{ name: 'agentId', type: 'uint256' }],
|
|
32
|
+
outputs: [{ name: '', type: 'address' }],
|
|
33
|
+
},
|
|
16
34
|
];
|
|
17
35
|
const REPUTATION_REGISTRY_ABI = [
|
|
18
|
-
|
|
36
|
+
{
|
|
37
|
+
name: 'getSummary',
|
|
38
|
+
type: 'function',
|
|
39
|
+
stateMutability: 'view',
|
|
40
|
+
inputs: [
|
|
41
|
+
{ name: 'agentId', type: 'uint256' },
|
|
42
|
+
{ name: 'clients', type: 'address[]' },
|
|
43
|
+
{ name: 'tag1', type: 'string' },
|
|
44
|
+
{ name: 'tag2', type: 'string' },
|
|
45
|
+
],
|
|
46
|
+
outputs: [
|
|
47
|
+
{ name: 'count', type: 'uint64' },
|
|
48
|
+
{ name: 'summaryValue', type: 'int128' },
|
|
49
|
+
{ name: 'valueDecimals', type: 'uint8' },
|
|
50
|
+
],
|
|
51
|
+
},
|
|
19
52
|
];
|
|
20
53
|
// ─── Internal Helpers ───────────────────────────────────────────────
|
|
21
54
|
const IPFS_GATEWAY = 'https://gateway.pinata.cloud/ipfs/';
|
|
@@ -49,17 +82,31 @@ async function resolveURI(uri) {
|
|
|
49
82
|
* Read an agent from the Identity Registry and parse its profile.
|
|
50
83
|
*
|
|
51
84
|
* @param agentId The on-chain agent token ID
|
|
52
|
-
* @param options Registry address,
|
|
85
|
+
* @param options Registry address, client, and optional fetchMetadata flag
|
|
53
86
|
*/
|
|
54
87
|
export async function getAgent(agentId, options) {
|
|
55
|
-
const { registryAddress,
|
|
56
|
-
const registry = new ethers.Contract(registryAddress, IDENTITY_REGISTRY_ABI, provider);
|
|
88
|
+
const { registryAddress, client, fetchMetadata = true } = options;
|
|
57
89
|
const [owner, uri, walletAddr] = await Promise.all([
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
90
|
+
client.readContract({
|
|
91
|
+
address: registryAddress,
|
|
92
|
+
abi: IDENTITY_REGISTRY_ABI,
|
|
93
|
+
functionName: 'ownerOf',
|
|
94
|
+
args: [BigInt(agentId)],
|
|
95
|
+
}),
|
|
96
|
+
client.readContract({
|
|
97
|
+
address: registryAddress,
|
|
98
|
+
abi: IDENTITY_REGISTRY_ABI,
|
|
99
|
+
functionName: 'tokenURI',
|
|
100
|
+
args: [BigInt(agentId)],
|
|
101
|
+
}),
|
|
102
|
+
client.readContract({
|
|
103
|
+
address: registryAddress,
|
|
104
|
+
abi: IDENTITY_REGISTRY_ABI,
|
|
105
|
+
functionName: 'getAgentWallet',
|
|
106
|
+
args: [BigInt(agentId)],
|
|
107
|
+
}),
|
|
61
108
|
]);
|
|
62
|
-
const agentWallet = walletAddr ===
|
|
109
|
+
const agentWallet = walletAddr === zeroAddress ? null : walletAddr;
|
|
63
110
|
let metadata = null;
|
|
64
111
|
if (fetchMetadata) {
|
|
65
112
|
try {
|
|
@@ -76,12 +123,17 @@ export async function getAgent(agentId, options) {
|
|
|
76
123
|
* Read an agent's reputation summary from the Reputation Registry.
|
|
77
124
|
*
|
|
78
125
|
* @param agentId The on-chain agent token ID
|
|
79
|
-
* @param options Reputation registry address,
|
|
126
|
+
* @param options Reputation registry address, client, and optional filters
|
|
80
127
|
*/
|
|
81
128
|
export async function getReputation(agentId, options) {
|
|
82
|
-
const { reputationRegistryAddress,
|
|
83
|
-
const
|
|
84
|
-
|
|
129
|
+
const { reputationRegistryAddress, client, clients = [], tag1 = '', tag2 = '', } = options;
|
|
130
|
+
const result = await client.readContract({
|
|
131
|
+
address: reputationRegistryAddress,
|
|
132
|
+
abi: REPUTATION_REGISTRY_ABI,
|
|
133
|
+
functionName: 'getSummary',
|
|
134
|
+
args: [BigInt(agentId), clients, tag1, tag2],
|
|
135
|
+
});
|
|
136
|
+
const [count, summaryValue, valueDecimals] = result;
|
|
85
137
|
const decimals = Number(valueDecimals);
|
|
86
138
|
const rawValue = BigInt(summaryValue);
|
|
87
139
|
const score = Number(rawValue) / 10 ** decimals;
|
package/dist/siwa.d.ts
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* Provides message building, signing (agent-side), and verification (server-side).
|
|
6
6
|
*
|
|
7
7
|
* Dependencies:
|
|
8
|
-
* npm install
|
|
8
|
+
* npm install viem
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
10
|
+
import { type PublicClient } from 'viem';
|
|
11
11
|
import { AgentProfile, ServiceType, TrustModel } from './registry.js';
|
|
12
12
|
export interface SIWAMessageFields {
|
|
13
13
|
domain: string;
|
|
@@ -93,7 +93,7 @@ export declare function signSIWAMessageUnsafe(privateKey: string, fields: SIWAMe
|
|
|
93
93
|
* @param signature EIP-191 signature hex string
|
|
94
94
|
* @param expectedDomain The server's domain (for domain binding)
|
|
95
95
|
* @param nonceValid Callback that returns true if the nonce is valid and unconsumed
|
|
96
|
-
* @param
|
|
96
|
+
* @param client viem PublicClient for onchain verification
|
|
97
97
|
* @param criteria Optional criteria to validate agent profile/reputation after ownership check
|
|
98
98
|
*/
|
|
99
|
-
export declare function verifySIWA(message: string, signature: string, expectedDomain: string, nonceValid: (nonce: string) => boolean | Promise<boolean>,
|
|
99
|
+
export declare function verifySIWA(message: string, signature: string, expectedDomain: string, nonceValid: (nonce: string) => boolean | Promise<boolean>, client: PublicClient, criteria?: SIWAVerifyCriteria): Promise<SIWAVerificationResult>;
|
package/dist/siwa.js
CHANGED
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
* Provides message building, signing (agent-side), and verification (server-side).
|
|
6
6
|
*
|
|
7
7
|
* Dependencies:
|
|
8
|
-
* npm install
|
|
8
|
+
* npm install viem
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
10
|
+
import { verifyMessage, hashMessage, } from 'viem';
|
|
11
|
+
import { privateKeyToAccount } from 'viem/accounts';
|
|
11
12
|
import * as crypto from 'crypto';
|
|
12
13
|
import { getAgent, getReputation } from './registry.js';
|
|
13
14
|
// ─── Message Construction ────────────────────────────────────────────
|
|
@@ -136,12 +137,13 @@ export async function signSIWAMessage(fields, keystoreConfig) {
|
|
|
136
137
|
* Kept only for server-side testing or environments without keystore.
|
|
137
138
|
*/
|
|
138
139
|
export async function signSIWAMessageUnsafe(privateKey, fields) {
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
140
|
+
const hexKey = (privateKey.startsWith('0x') ? privateKey : `0x${privateKey}`);
|
|
141
|
+
const account = privateKeyToAccount(hexKey);
|
|
142
|
+
if (account.address.toLowerCase() !== fields.address.toLowerCase()) {
|
|
143
|
+
throw new Error(`Address mismatch: wallet is ${account.address}, message says ${fields.address}`);
|
|
142
144
|
}
|
|
143
145
|
const message = buildSIWAMessage(fields);
|
|
144
|
-
const signature = await
|
|
146
|
+
const signature = await account.signMessage({ message });
|
|
145
147
|
return { message, signature };
|
|
146
148
|
}
|
|
147
149
|
// ─── Server-Side Verification ────────────────────────────────────────
|
|
@@ -161,19 +163,24 @@ export async function signSIWAMessageUnsafe(privateKey, fields) {
|
|
|
161
163
|
* @param signature EIP-191 signature hex string
|
|
162
164
|
* @param expectedDomain The server's domain (for domain binding)
|
|
163
165
|
* @param nonceValid Callback that returns true if the nonce is valid and unconsumed
|
|
164
|
-
* @param
|
|
166
|
+
* @param client viem PublicClient for onchain verification
|
|
165
167
|
* @param criteria Optional criteria to validate agent profile/reputation after ownership check
|
|
166
168
|
*/
|
|
167
|
-
export async function verifySIWA(message, signature, expectedDomain, nonceValid,
|
|
169
|
+
export async function verifySIWA(message, signature, expectedDomain, nonceValid, client, criteria) {
|
|
168
170
|
try {
|
|
169
171
|
// 1. Parse
|
|
170
172
|
const fields = parseSIWAMessage(message);
|
|
171
173
|
// 2. Recover signer
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
174
|
+
const isValid = await verifyMessage({
|
|
175
|
+
address: fields.address,
|
|
176
|
+
message,
|
|
177
|
+
signature: signature,
|
|
178
|
+
});
|
|
179
|
+
if (!isValid) {
|
|
180
|
+
return { valid: false, address: fields.address, agentId: fields.agentId, agentRegistry: fields.agentRegistry, chainId: fields.chainId, error: 'Invalid signature' };
|
|
176
181
|
}
|
|
182
|
+
const recovered = fields.address;
|
|
183
|
+
// 3. Address match is implicit in verifyMessage (it checks against the address)
|
|
177
184
|
// 4. Domain binding
|
|
178
185
|
if (fields.domain !== expectedDomain) {
|
|
179
186
|
return { valid: false, address: recovered, agentId: fields.agentId, agentRegistry: fields.agentRegistry, chainId: fields.chainId, error: `Domain mismatch: expected ${expectedDomain}, got ${fields.domain}` };
|
|
@@ -197,16 +204,24 @@ export async function verifySIWA(message, signature, expectedDomain, nonceValid,
|
|
|
197
204
|
return { valid: false, address: recovered, agentId: fields.agentId, agentRegistry: fields.agentRegistry, chainId: fields.chainId, error: 'Invalid agentRegistry format' };
|
|
198
205
|
}
|
|
199
206
|
const registryAddress = registryParts[2];
|
|
200
|
-
const
|
|
201
|
-
|
|
207
|
+
const owner = await client.readContract({
|
|
208
|
+
address: registryAddress,
|
|
209
|
+
abi: [{ name: 'ownerOf', type: 'function', stateMutability: 'view', inputs: [{ name: 'tokenId', type: 'uint256' }], outputs: [{ name: '', type: 'address' }] }],
|
|
210
|
+
functionName: 'ownerOf',
|
|
211
|
+
args: [BigInt(fields.agentId)],
|
|
212
|
+
});
|
|
202
213
|
if (owner.toLowerCase() !== recovered.toLowerCase()) {
|
|
203
214
|
// 7b. ERC-1271 fallback for smart contract wallets / EIP-7702 delegated accounts.
|
|
204
215
|
// If ecrecover doesn't match the NFT owner, the owner may be a contract
|
|
205
216
|
// that validates signatures via isValidSignature (ERC-1271).
|
|
206
|
-
const messageHash =
|
|
217
|
+
const messageHash = hashMessage(message);
|
|
207
218
|
try {
|
|
208
|
-
const
|
|
209
|
-
|
|
219
|
+
const magicValue = await client.readContract({
|
|
220
|
+
address: owner,
|
|
221
|
+
abi: [{ name: 'isValidSignature', type: 'function', stateMutability: 'view', inputs: [{ name: 'hash', type: 'bytes32' }, { name: 'signature', type: 'bytes' }], outputs: [{ name: '', type: 'bytes4' }] }],
|
|
222
|
+
functionName: 'isValidSignature',
|
|
223
|
+
args: [messageHash, signature],
|
|
224
|
+
});
|
|
210
225
|
// ERC-1271 magic value: 0x1626ba7e
|
|
211
226
|
if (magicValue !== '0x1626ba7e') {
|
|
212
227
|
return { valid: false, address: recovered, agentId: fields.agentId, agentRegistry: fields.agentRegistry, chainId: fields.chainId, error: 'Signer is not the owner of this agent NFT (ERC-1271 check also failed)' };
|
|
@@ -230,7 +245,7 @@ export async function verifySIWA(message, signature, expectedDomain, nonceValid,
|
|
|
230
245
|
return baseResult;
|
|
231
246
|
const agent = await getAgent(fields.agentId, {
|
|
232
247
|
registryAddress: registryAddress,
|
|
233
|
-
|
|
248
|
+
client,
|
|
234
249
|
fetchMetadata: true,
|
|
235
250
|
});
|
|
236
251
|
baseResult.agent = agent;
|
|
@@ -261,7 +276,7 @@ export async function verifySIWA(message, signature, expectedDomain, nonceValid,
|
|
|
261
276
|
}
|
|
262
277
|
const rep = await getReputation(fields.agentId, {
|
|
263
278
|
reputationRegistryAddress: criteria.reputationRegistryAddress,
|
|
264
|
-
|
|
279
|
+
client,
|
|
265
280
|
});
|
|
266
281
|
if (criteria.minFeedbackCount !== undefined && rep.count < criteria.minFeedbackCount) {
|
|
267
282
|
return { ...baseResult, valid: false, error: `Agent feedback count ${rep.count} below minimum ${criteria.minFeedbackCount}` };
|
package/package.json
CHANGED
|
@@ -1,28 +1,61 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buildersgarden/siwa",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
|
-
".": {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"./
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
".": {
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"default": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"./keystore": {
|
|
11
|
+
"types": "./dist/keystore.d.ts",
|
|
12
|
+
"default": "./dist/keystore.js"
|
|
13
|
+
},
|
|
14
|
+
"./siwa": {
|
|
15
|
+
"types": "./dist/siwa.d.ts",
|
|
16
|
+
"default": "./dist/siwa.js"
|
|
17
|
+
},
|
|
18
|
+
"./memory": {
|
|
19
|
+
"types": "./dist/memory.d.ts",
|
|
20
|
+
"default": "./dist/memory.js"
|
|
21
|
+
},
|
|
22
|
+
"./proxy-auth": {
|
|
23
|
+
"types": "./dist/proxy-auth.d.ts",
|
|
24
|
+
"default": "./dist/proxy-auth.js"
|
|
25
|
+
},
|
|
26
|
+
"./registry": {
|
|
27
|
+
"types": "./dist/registry.d.ts",
|
|
28
|
+
"default": "./dist/registry.js"
|
|
29
|
+
},
|
|
30
|
+
"./addresses": {
|
|
31
|
+
"types": "./dist/addresses.d.ts",
|
|
32
|
+
"default": "./dist/addresses.js"
|
|
33
|
+
}
|
|
13
34
|
},
|
|
14
35
|
"main": "./dist/index.js",
|
|
15
36
|
"types": "./dist/index.d.ts",
|
|
16
|
-
"files": [
|
|
17
|
-
|
|
37
|
+
"files": [
|
|
38
|
+
"dist"
|
|
39
|
+
],
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "https://github.com/builders-garden/siwa",
|
|
43
|
+
"directory": "packages/siwa"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
18
48
|
"scripts": {
|
|
19
49
|
"build": "tsc",
|
|
20
50
|
"clean": "rm -rf dist"
|
|
21
51
|
},
|
|
22
52
|
"dependencies": {
|
|
23
|
-
"
|
|
53
|
+
"@noble/ciphers": "^0.5.0",
|
|
54
|
+
"@noble/hashes": "^1.4.0",
|
|
55
|
+
"viem": "^2.21.0"
|
|
24
56
|
},
|
|
25
57
|
"devDependencies": {
|
|
58
|
+
"@types/node": "^25.2.1",
|
|
26
59
|
"typescript": "^5.5.0"
|
|
27
60
|
}
|
|
28
61
|
}
|