clawntenna 0.8.3 → 0.8.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/dist/cli/index.js +122 -13
- package/dist/index.cjs +24 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +24 -6
- package/dist/index.js.map +1 -1
- package/package.json +9 -5
package/dist/cli/index.js
CHANGED
|
@@ -3374,14 +3374,23 @@ var Clawntenna = class {
|
|
|
3374
3374
|
*/
|
|
3375
3375
|
async readMessages(topicId, options) {
|
|
3376
3376
|
const limit = options?.limit ?? 50;
|
|
3377
|
-
const fromBlock = options?.fromBlock ?? -1e5;
|
|
3378
3377
|
const key = await this.getEncryptionKey(topicId);
|
|
3379
3378
|
const filter = this.registry.filters.MessageSent(topicId);
|
|
3380
|
-
const
|
|
3379
|
+
const CHUNK_SIZE = 2e3;
|
|
3380
|
+
const currentBlock = await this.provider.getBlockNumber();
|
|
3381
|
+
const maxRange = options?.fromBlock != null ? currentBlock - options.fromBlock : 1e5;
|
|
3382
|
+
const startBlock = currentBlock - maxRange;
|
|
3383
|
+
const allEvents = [];
|
|
3384
|
+
let toBlock = currentBlock;
|
|
3385
|
+
while (toBlock > startBlock && allEvents.length < limit) {
|
|
3386
|
+
const chunkFrom = Math.max(toBlock - CHUNK_SIZE + 1, startBlock);
|
|
3387
|
+
const events = await this.registry.queryFilter(filter, chunkFrom, toBlock);
|
|
3388
|
+
allEvents.unshift(...events);
|
|
3389
|
+
toBlock = chunkFrom - 1;
|
|
3390
|
+
}
|
|
3391
|
+
const recent = allEvents.slice(-limit);
|
|
3381
3392
|
const messages = [];
|
|
3382
|
-
const
|
|
3383
|
-
for (const event of recent) {
|
|
3384
|
-
const log = event;
|
|
3393
|
+
for (const log of recent) {
|
|
3385
3394
|
const payloadStr = ethers.toUtf8String(log.args.payload);
|
|
3386
3395
|
const parsed = decryptMessage(payloadStr, key);
|
|
3387
3396
|
messages.push({
|
|
@@ -3620,6 +3629,12 @@ var Clawntenna = class {
|
|
|
3620
3629
|
async grantKeyAccess(topicId, userAddress, topicKey) {
|
|
3621
3630
|
if (!this.wallet) throw new Error("Wallet required");
|
|
3622
3631
|
if (!this.ecdhPrivateKey) throw new Error("ECDH key not derived yet");
|
|
3632
|
+
const hasKey = await this.keyManager.hasPublicKey(userAddress);
|
|
3633
|
+
if (!hasKey) {
|
|
3634
|
+
throw new Error(
|
|
3635
|
+
`User ${userAddress} has no ECDH public key registered. They must run 'keys register' first.`
|
|
3636
|
+
);
|
|
3637
|
+
}
|
|
3623
3638
|
const userPubKeyBytes = ethers.getBytes(await this.keyManager.getPublicKey(userAddress));
|
|
3624
3639
|
const encrypted = encryptTopicKeyForUser(topicKey, this.ecdhPrivateKey, userPubKeyBytes);
|
|
3625
3640
|
return this.keyManager.grantKeyAccess(topicId, userAddress, encrypted);
|
|
@@ -3993,8 +4008,11 @@ var Clawntenna = class {
|
|
|
3993
4008
|
if (storedKey) return storedKey;
|
|
3994
4009
|
const topic = await this.getTopic(topicId);
|
|
3995
4010
|
if (topic.accessLevel === 2 /* PRIVATE */) {
|
|
4011
|
+
if (this.ecdhPrivateKey) {
|
|
4012
|
+
return this.fetchAndDecryptTopicKey(topicId);
|
|
4013
|
+
}
|
|
3996
4014
|
throw new Error(
|
|
3997
|
-
`Topic ${topicId} is PRIVATE.
|
|
4015
|
+
`Topic ${topicId} is PRIVATE. Load ECDH keys first (loadECDHKeypair or deriveECDHFromWallet), then call fetchAndDecryptTopicKey() or setTopicKey().`
|
|
3998
4016
|
);
|
|
3999
4017
|
}
|
|
4000
4018
|
return derivePublicTopicKey(topicId);
|
|
@@ -4168,6 +4186,12 @@ async function send(topicId, message, flags) {
|
|
|
4168
4186
|
const client = loadClient(flags);
|
|
4169
4187
|
const json = flags.json ?? false;
|
|
4170
4188
|
const noWait = flags.noWait ?? false;
|
|
4189
|
+
const creds = loadCredentials();
|
|
4190
|
+
const chainId = flags.chain === "base" ? "8453" : "43114";
|
|
4191
|
+
const ecdhCreds = creds?.chains[chainId]?.ecdh;
|
|
4192
|
+
if (ecdhCreds?.privateKey) {
|
|
4193
|
+
client.loadECDHKeypair(ecdhCreds.privateKey);
|
|
4194
|
+
}
|
|
4171
4195
|
if (!json) console.log(`Sending to topic ${topicId} on ${flags.chain}...`);
|
|
4172
4196
|
const sendOptions = {
|
|
4173
4197
|
replyTo: flags.replyTo,
|
|
@@ -4208,6 +4232,12 @@ async function send(topicId, message, flags) {
|
|
|
4208
4232
|
async function read(topicId, flags) {
|
|
4209
4233
|
const client = loadClient(flags, false);
|
|
4210
4234
|
const json = flags.json ?? false;
|
|
4235
|
+
const creds = loadCredentials();
|
|
4236
|
+
const chainId = flags.chain === "base" ? "8453" : "43114";
|
|
4237
|
+
const ecdhCreds = creds?.chains[chainId]?.ecdh;
|
|
4238
|
+
if (ecdhCreds?.privateKey) {
|
|
4239
|
+
client.loadECDHKeypair(ecdhCreds.privateKey);
|
|
4240
|
+
}
|
|
4211
4241
|
if (!json) console.log(`Reading topic ${topicId} on ${flags.chain} (last ${flags.limit} messages)...
|
|
4212
4242
|
`);
|
|
4213
4243
|
const messages = await client.readMessages(topicId, { limit: flags.limit });
|
|
@@ -4305,6 +4335,7 @@ async function whoami(appId, flags) {
|
|
|
4305
4335
|
}
|
|
4306
4336
|
|
|
4307
4337
|
// src/cli/app.ts
|
|
4338
|
+
import { ethers as ethers4 } from "ethers";
|
|
4308
4339
|
async function appInfo(appId, flags) {
|
|
4309
4340
|
const client = loadClient(flags, false);
|
|
4310
4341
|
const json = flags.json ?? false;
|
|
@@ -4341,9 +4372,22 @@ async function appCreate(name, description, url, isPublic, flags) {
|
|
|
4341
4372
|
const tx = await client.createApplication(name, description, url, isPublic);
|
|
4342
4373
|
if (!json) console.log(`TX submitted: ${tx.hash}`);
|
|
4343
4374
|
const receipt = await tx.wait();
|
|
4375
|
+
let appId = null;
|
|
4376
|
+
if (receipt) {
|
|
4377
|
+
const iface = new ethers4.Interface(REGISTRY_ABI);
|
|
4378
|
+
const parsed = receipt.logs.map((l) => {
|
|
4379
|
+
try {
|
|
4380
|
+
return iface.parseLog(l);
|
|
4381
|
+
} catch {
|
|
4382
|
+
return null;
|
|
4383
|
+
}
|
|
4384
|
+
}).find((l) => l?.name === "ApplicationCreated");
|
|
4385
|
+
appId = parsed?.args?.applicationId?.toString() ?? null;
|
|
4386
|
+
}
|
|
4344
4387
|
if (json) {
|
|
4345
|
-
output({ txHash: tx.hash, blockNumber: receipt?.blockNumber, chain: flags.chain }, true);
|
|
4388
|
+
output({ txHash: tx.hash, blockNumber: receipt?.blockNumber, appId, chain: flags.chain }, true);
|
|
4346
4389
|
} else {
|
|
4390
|
+
if (appId) console.log(`Application created with ID: ${appId}`);
|
|
4347
4391
|
console.log(`Confirmed in block ${receipt?.blockNumber}`);
|
|
4348
4392
|
}
|
|
4349
4393
|
}
|
|
@@ -4362,6 +4406,7 @@ async function appUpdateUrl(appId, url, flags) {
|
|
|
4362
4406
|
}
|
|
4363
4407
|
|
|
4364
4408
|
// src/cli/topics.ts
|
|
4409
|
+
import { ethers as ethers5 } from "ethers";
|
|
4365
4410
|
var ACCESS_NAMES = ["public", "limited", "private"];
|
|
4366
4411
|
async function topicsList(appId, flags) {
|
|
4367
4412
|
const client = loadClient(flags, false);
|
|
@@ -4441,9 +4486,22 @@ async function topicCreate(appId, name, description, access, flags) {
|
|
|
4441
4486
|
if (!json) console.log(`Creating topic "${name}" in app ${appId} (${access})...`);
|
|
4442
4487
|
const tx = await client.createTopic(appId, name, description, level);
|
|
4443
4488
|
const receipt = await tx.wait();
|
|
4489
|
+
let topicId = null;
|
|
4490
|
+
if (receipt) {
|
|
4491
|
+
const iface = new ethers5.Interface(REGISTRY_ABI);
|
|
4492
|
+
const parsed = receipt.logs.map((l) => {
|
|
4493
|
+
try {
|
|
4494
|
+
return iface.parseLog(l);
|
|
4495
|
+
} catch {
|
|
4496
|
+
return null;
|
|
4497
|
+
}
|
|
4498
|
+
}).find((l) => l?.name === "TopicCreated");
|
|
4499
|
+
topicId = parsed?.args?.topicId?.toString() ?? null;
|
|
4500
|
+
}
|
|
4444
4501
|
if (json) {
|
|
4445
|
-
output({ txHash: tx.hash, blockNumber: receipt?.blockNumber, appId, access }, true);
|
|
4502
|
+
output({ txHash: tx.hash, blockNumber: receipt?.blockNumber, topicId, appId, access }, true);
|
|
4446
4503
|
} else {
|
|
4504
|
+
if (topicId) console.log(`Topic created with ID: ${topicId}`);
|
|
4447
4505
|
console.log(`TX: ${tx.hash}`);
|
|
4448
4506
|
console.log(`Confirmed in block ${receipt?.blockNumber}`);
|
|
4449
4507
|
}
|
|
@@ -4488,10 +4546,12 @@ async function nicknameClear(appId, flags) {
|
|
|
4488
4546
|
}
|
|
4489
4547
|
|
|
4490
4548
|
// src/cli/members.ts
|
|
4549
|
+
import { ethers as ethers6 } from "ethers";
|
|
4491
4550
|
async function membersList(appId, flags) {
|
|
4492
4551
|
const client = loadClient(flags, false);
|
|
4493
4552
|
const json = flags.json ?? false;
|
|
4494
|
-
const
|
|
4553
|
+
const raw = await client.getApplicationMembers(appId);
|
|
4554
|
+
const addresses = [...new Set(raw)].filter((a) => a !== ethers6.ZeroAddress);
|
|
4495
4555
|
const members = await Promise.all(
|
|
4496
4556
|
addresses.map(async (addr) => {
|
|
4497
4557
|
const m = await client.getMember(appId, addr);
|
|
@@ -4940,7 +5000,7 @@ async function subscribe(topicId, flags) {
|
|
|
4940
5000
|
}
|
|
4941
5001
|
|
|
4942
5002
|
// src/cli/fees.ts
|
|
4943
|
-
import { ethers as
|
|
5003
|
+
import { ethers as ethers7 } from "ethers";
|
|
4944
5004
|
async function feeTopicCreationSet(appId, token, amount, flags) {
|
|
4945
5005
|
const client = loadClient(flags);
|
|
4946
5006
|
const json = flags.json ?? false;
|
|
@@ -4971,7 +5031,7 @@ async function feeMessageGet(topicId, flags) {
|
|
|
4971
5031
|
const client = loadClient(flags, false);
|
|
4972
5032
|
const json = flags.json ?? false;
|
|
4973
5033
|
const fee = await client.getTopicMessageFee(topicId);
|
|
4974
|
-
const isZero = fee.token ===
|
|
5034
|
+
const isZero = fee.token === ethers7.ZeroAddress && fee.amount === 0n;
|
|
4975
5035
|
if (json) {
|
|
4976
5036
|
output({ topicId, token: fee.token, amount: fee.amount.toString() }, true);
|
|
4977
5037
|
} else {
|
|
@@ -4985,8 +5045,57 @@ async function feeMessageGet(topicId, flags) {
|
|
|
4985
5045
|
}
|
|
4986
5046
|
}
|
|
4987
5047
|
|
|
5048
|
+
// src/cli/errors.ts
|
|
5049
|
+
var ERROR_MAP = {
|
|
5050
|
+
"0xea8e4eb5": "NotAuthorized \u2014 you lack permission for this action",
|
|
5051
|
+
"0x291fc442": "NotMember \u2014 address is not a member of this app",
|
|
5052
|
+
"0x810074be": "AlreadyMember \u2014 address is already a member",
|
|
5053
|
+
"0x5e03d55f": "CannotRemoveSelf \u2014 owner cannot remove themselves",
|
|
5054
|
+
"0x17b29d2e": "ApplicationNotFound \u2014 app ID does not exist",
|
|
5055
|
+
"0x04a29d55": "TopicNotFound \u2014 topic ID does not exist",
|
|
5056
|
+
"0x430f13b3": "InvalidName \u2014 name is empty or invalid",
|
|
5057
|
+
"0x9e4b2685": "NameTaken \u2014 that name is already in use",
|
|
5058
|
+
"0xa2d0fee8": "InvalidPublicKey \u2014 must be 33-byte compressed secp256k1 key",
|
|
5059
|
+
"0x16ea6d54": "PublicKeyNotRegistered \u2014 user has no ECDH key (run: keys register)",
|
|
5060
|
+
"0x5303c506": "InvalidEncryptedKey \u2014 encrypted key too short or malformed",
|
|
5061
|
+
"0xf4d678b8": "InsufficientBalance \u2014 not enough tokens",
|
|
5062
|
+
"0x13be252b": "InsufficientAllowance \u2014 token allowance too low",
|
|
5063
|
+
"0x0c79a8da": "InvalidAccessLevel \u2014 use public, limited, or private",
|
|
5064
|
+
"0x15b3521e": "NicknameCooldownActive \u2014 wait before changing nickname again",
|
|
5065
|
+
"0xae0ca2dd": "SchemaNotFound \u2014 schema ID does not exist",
|
|
5066
|
+
"0x03230700": "AppNameTaken \u2014 schema name already used in this app"
|
|
5067
|
+
};
|
|
5068
|
+
function decodeContractError(err) {
|
|
5069
|
+
if (!(err instanceof Error)) return String(err);
|
|
5070
|
+
const message = err.message;
|
|
5071
|
+
const dataMatch = message.match(/data="(0x[0-9a-fA-F]+)"/) ?? message.match(/error=\{[^}]*"data":"(0x[0-9a-fA-F]+)"/) ?? message.match(/(0x[0-9a-fA-F]{8})/);
|
|
5072
|
+
if (dataMatch) {
|
|
5073
|
+
const selector = dataMatch[1].slice(0, 10).toLowerCase();
|
|
5074
|
+
const decoded = ERROR_MAP[selector];
|
|
5075
|
+
if (decoded) return decoded;
|
|
5076
|
+
}
|
|
5077
|
+
const anyErr = err;
|
|
5078
|
+
if (typeof anyErr.data === "string" && anyErr.data.startsWith("0x")) {
|
|
5079
|
+
const selector = anyErr.data.slice(0, 10).toLowerCase();
|
|
5080
|
+
const decoded = ERROR_MAP[selector];
|
|
5081
|
+
if (decoded) return decoded;
|
|
5082
|
+
}
|
|
5083
|
+
if (anyErr.info && typeof anyErr.info === "object") {
|
|
5084
|
+
const info = anyErr.info;
|
|
5085
|
+
if (info.error && typeof info.error === "object") {
|
|
5086
|
+
const innerErr = info.error;
|
|
5087
|
+
if (typeof innerErr.data === "string" && innerErr.data.startsWith("0x")) {
|
|
5088
|
+
const selector = innerErr.data.slice(0, 10).toLowerCase();
|
|
5089
|
+
const decoded = ERROR_MAP[selector];
|
|
5090
|
+
if (decoded) return decoded;
|
|
5091
|
+
}
|
|
5092
|
+
}
|
|
5093
|
+
}
|
|
5094
|
+
return message;
|
|
5095
|
+
}
|
|
5096
|
+
|
|
4988
5097
|
// src/cli/index.ts
|
|
4989
|
-
var VERSION = "0.8.
|
|
5098
|
+
var VERSION = "0.8.4";
|
|
4990
5099
|
var HELP = `
|
|
4991
5100
|
clawntenna v${VERSION}
|
|
4992
5101
|
On-chain encrypted messaging for AI agents
|
|
@@ -5433,7 +5542,7 @@ async function main() {
|
|
|
5433
5542
|
outputError(`Unknown command: ${command}. Run 'clawntenna --help' for usage.`, json);
|
|
5434
5543
|
}
|
|
5435
5544
|
} catch (err) {
|
|
5436
|
-
const message =
|
|
5545
|
+
const message = decodeContractError(err);
|
|
5437
5546
|
if (json) {
|
|
5438
5547
|
console.error(JSON.stringify({ error: message }));
|
|
5439
5548
|
} else {
|
package/dist/index.cjs
CHANGED
|
@@ -531,14 +531,23 @@ var Clawntenna = class {
|
|
|
531
531
|
*/
|
|
532
532
|
async readMessages(topicId, options) {
|
|
533
533
|
const limit = options?.limit ?? 50;
|
|
534
|
-
const fromBlock = options?.fromBlock ?? -1e5;
|
|
535
534
|
const key = await this.getEncryptionKey(topicId);
|
|
536
535
|
const filter = this.registry.filters.MessageSent(topicId);
|
|
537
|
-
const
|
|
536
|
+
const CHUNK_SIZE = 2e3;
|
|
537
|
+
const currentBlock = await this.provider.getBlockNumber();
|
|
538
|
+
const maxRange = options?.fromBlock != null ? currentBlock - options.fromBlock : 1e5;
|
|
539
|
+
const startBlock = currentBlock - maxRange;
|
|
540
|
+
const allEvents = [];
|
|
541
|
+
let toBlock = currentBlock;
|
|
542
|
+
while (toBlock > startBlock && allEvents.length < limit) {
|
|
543
|
+
const chunkFrom = Math.max(toBlock - CHUNK_SIZE + 1, startBlock);
|
|
544
|
+
const events = await this.registry.queryFilter(filter, chunkFrom, toBlock);
|
|
545
|
+
allEvents.unshift(...events);
|
|
546
|
+
toBlock = chunkFrom - 1;
|
|
547
|
+
}
|
|
548
|
+
const recent = allEvents.slice(-limit);
|
|
538
549
|
const messages = [];
|
|
539
|
-
const
|
|
540
|
-
for (const event of recent) {
|
|
541
|
-
const log = event;
|
|
550
|
+
for (const log of recent) {
|
|
542
551
|
const payloadStr = import_ethers.ethers.toUtf8String(log.args.payload);
|
|
543
552
|
const parsed = decryptMessage(payloadStr, key);
|
|
544
553
|
messages.push({
|
|
@@ -777,6 +786,12 @@ var Clawntenna = class {
|
|
|
777
786
|
async grantKeyAccess(topicId, userAddress, topicKey) {
|
|
778
787
|
if (!this.wallet) throw new Error("Wallet required");
|
|
779
788
|
if (!this.ecdhPrivateKey) throw new Error("ECDH key not derived yet");
|
|
789
|
+
const hasKey = await this.keyManager.hasPublicKey(userAddress);
|
|
790
|
+
if (!hasKey) {
|
|
791
|
+
throw new Error(
|
|
792
|
+
`User ${userAddress} has no ECDH public key registered. They must run 'keys register' first.`
|
|
793
|
+
);
|
|
794
|
+
}
|
|
780
795
|
const userPubKeyBytes = import_ethers.ethers.getBytes(await this.keyManager.getPublicKey(userAddress));
|
|
781
796
|
const encrypted = encryptTopicKeyForUser(topicKey, this.ecdhPrivateKey, userPubKeyBytes);
|
|
782
797
|
return this.keyManager.grantKeyAccess(topicId, userAddress, encrypted);
|
|
@@ -1150,8 +1165,11 @@ var Clawntenna = class {
|
|
|
1150
1165
|
if (storedKey) return storedKey;
|
|
1151
1166
|
const topic = await this.getTopic(topicId);
|
|
1152
1167
|
if (topic.accessLevel === 2 /* PRIVATE */) {
|
|
1168
|
+
if (this.ecdhPrivateKey) {
|
|
1169
|
+
return this.fetchAndDecryptTopicKey(topicId);
|
|
1170
|
+
}
|
|
1153
1171
|
throw new Error(
|
|
1154
|
-
`Topic ${topicId} is PRIVATE.
|
|
1172
|
+
`Topic ${topicId} is PRIVATE. Load ECDH keys first (loadECDHKeypair or deriveECDHFromWallet), then call fetchAndDecryptTopicKey() or setTopicKey().`
|
|
1155
1173
|
);
|
|
1156
1174
|
}
|
|
1157
1175
|
return derivePublicTopicKey(topicId);
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/chains.ts","../src/contracts.ts","../src/crypto/encrypt.ts","../src/types.ts","../src/constants.ts","../src/crypto/ecdh.ts"],"sourcesContent":["// Main client\nexport { Clawntenna } from './client.js';\n\n// Types\nexport {\n AccessLevel,\n Permission,\n Role,\n} from './types.js';\nexport type {\n ChainConfig,\n ChainName,\n ClawtennaOptions,\n ReadOptions,\n SendOptions,\n Message,\n MessageContent,\n EncryptedPayload,\n Topic,\n Application,\n Member,\n KeyGrant,\n TopicMessageFee,\n SchemaInfo,\n TopicSchemaBinding,\n Credentials,\n CredentialChain,\n CredentialApp,\n CredentialsV1,\n} from './types.js';\n\n// Chain configs\nexport { CHAINS, CHAIN_IDS, getChain } from './chains.js';\n\n// Constants\nexport {\n ACCESS_PUBLIC,\n ACCESS_PUBLIC_LIMITED,\n ACCESS_PRIVATE,\n PERMISSION_NONE,\n PERMISSION_READ,\n PERMISSION_WRITE,\n PERMISSION_READ_WRITE,\n PERMISSION_ADMIN,\n ROLE_MEMBER,\n ROLE_SUPPORT_MANAGER,\n ROLE_TOPIC_MANAGER,\n ROLE_ADMIN,\n ROLE_OWNER_DELEGATE,\n} from './constants.js';\n\n// Contract ABIs\nexport { REGISTRY_ABI, KEY_MANAGER_ABI, SCHEMA_REGISTRY_ABI, IDENTITY_REGISTRY_ABI } from './contracts.js';\n\n// Crypto utilities\nexport {\n derivePublicTopicKey,\n deriveKeyFromPassphrase,\n encrypt,\n decrypt,\n encryptMessage,\n decryptMessage,\n deriveKeypairFromSignature,\n keypairFromPrivateKey,\n computeSharedSecret,\n deriveAESKeyFromSecret,\n encryptTopicKeyForUser,\n decryptTopicKey,\n bytesToHex,\n hexToBytes,\n} from './crypto/index.js';\n","import { ethers } from 'ethers';\nimport { CHAINS } from './chains.js';\nimport { REGISTRY_ABI, KEY_MANAGER_ABI, SCHEMA_REGISTRY_ABI, IDENTITY_REGISTRY_ABI } from './contracts.js';\nimport {\n derivePublicTopicKey,\n deriveKeyFromPassphrase,\n encryptMessage,\n decryptMessage,\n} from './crypto/encrypt.js';\nimport {\n deriveKeypairFromSignature,\n keypairFromPrivateKey,\n computeSharedSecret,\n deriveAESKeyFromSecret,\n encryptTopicKeyForUser,\n decryptTopicKey,\n bytesToHex,\n hexToBytes,\n} from './crypto/ecdh.js';\nimport { AccessLevel } from './types.js';\nimport type {\n ClawtennaOptions,\n ReadOptions,\n SendOptions,\n Message,\n Topic,\n Application,\n Member,\n TopicMessageFee,\n SchemaInfo,\n TopicSchemaBinding,\n ChainName,\n} from './types.js';\n\nexport class Clawntenna {\n readonly provider: ethers.JsonRpcProvider;\n readonly wallet: ethers.Wallet | null;\n readonly registry: ethers.Contract;\n readonly keyManager: ethers.Contract;\n readonly schemaRegistry: ethers.Contract;\n readonly identityRegistry: ethers.Contract | null;\n readonly chainName: ChainName;\n\n // In-memory ECDH state\n private ecdhPrivateKey: Uint8Array | null = null;\n private ecdhPublicKey: Uint8Array | null = null;\n private topicKeys: Map<number, Uint8Array> = new Map();\n\n constructor(options: ClawtennaOptions = {}) {\n const chainName = options.chain ?? 'base';\n const chain = CHAINS[chainName];\n if (!chain) throw new Error(`Unsupported chain: ${chainName}`);\n this.chainName = chainName;\n\n const rpcUrl = options.rpcUrl ?? chain.rpc;\n this.provider = new ethers.JsonRpcProvider(rpcUrl);\n\n const registryAddr = options.registryAddress ?? chain.registry;\n const keyManagerAddr = options.keyManagerAddress ?? chain.keyManager;\n const schemaRegistryAddr = options.schemaRegistryAddress ?? chain.schemaRegistry;\n\n if (options.privateKey) {\n this.wallet = new ethers.Wallet(options.privateKey, this.provider);\n this.registry = new ethers.Contract(registryAddr, REGISTRY_ABI, this.wallet);\n this.keyManager = new ethers.Contract(keyManagerAddr, KEY_MANAGER_ABI, this.wallet);\n this.schemaRegistry = new ethers.Contract(schemaRegistryAddr, SCHEMA_REGISTRY_ABI, this.wallet);\n this.identityRegistry = chain.identityRegistry\n ? new ethers.Contract(chain.identityRegistry, IDENTITY_REGISTRY_ABI, this.wallet)\n : null;\n } else {\n this.wallet = null;\n this.registry = new ethers.Contract(registryAddr, REGISTRY_ABI, this.provider);\n this.keyManager = new ethers.Contract(keyManagerAddr, KEY_MANAGER_ABI, this.provider);\n this.schemaRegistry = new ethers.Contract(schemaRegistryAddr, SCHEMA_REGISTRY_ABI, this.provider);\n this.identityRegistry = chain.identityRegistry\n ? new ethers.Contract(chain.identityRegistry, IDENTITY_REGISTRY_ABI, this.provider)\n : null;\n }\n }\n\n get address(): string | null {\n return this.wallet?.address ?? null;\n }\n\n // ===== MESSAGING =====\n\n /**\n * Send an encrypted message to a topic.\n * Automatically determines encryption key based on topic access level.\n */\n async sendMessage(topicId: number, text: string, options?: SendOptions): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required to send messages');\n\n let replyText = options?.replyText;\n let replyAuthor = options?.replyAuthor;\n\n // Auto-resolve reply metadata if replyTo is set but text/author aren't\n if (options?.replyTo && (!replyText || !replyAuthor)) {\n try {\n const messages = await this.readMessages(topicId, { limit: 50 });\n const original = messages.find(m => m.txHash === options.replyTo);\n if (original) {\n replyText = replyText || original.text.slice(0, 100);\n replyAuthor = replyAuthor || original.sender;\n }\n } catch {\n // Non-fatal: reply will still work, just without cached text/author\n }\n }\n\n const key = await this.getEncryptionKey(topicId);\n const encrypted = encryptMessage(text, key, {\n replyTo: options?.replyTo,\n replyText,\n replyAuthor,\n mentions: options?.mentions,\n });\n\n return this.registry.sendMessage(topicId, ethers.toUtf8Bytes(encrypted));\n }\n\n /**\n * Read and decrypt recent messages from a topic.\n */\n async readMessages(topicId: number, options?: ReadOptions): Promise<Message[]> {\n const limit = options?.limit ?? 50;\n const fromBlock = options?.fromBlock ?? -100_000;\n\n const key = await this.getEncryptionKey(topicId);\n const filter = this.registry.filters.MessageSent(topicId);\n const events = await this.registry.queryFilter(filter, fromBlock);\n\n const messages: Message[] = [];\n const recent = events.slice(-limit);\n\n for (const event of recent) {\n const log = event as ethers.EventLog;\n const payloadStr = ethers.toUtf8String(log.args.payload);\n const parsed = decryptMessage(payloadStr, key);\n\n messages.push({\n topicId: BigInt(topicId),\n sender: log.args.sender,\n text: parsed?.text ?? '[decryption failed]',\n replyTo: parsed?.replyTo ?? null,\n mentions: parsed?.mentions ?? null,\n timestamp: log.args.timestamp,\n txHash: log.transactionHash,\n blockNumber: log.blockNumber,\n });\n }\n\n return messages;\n }\n\n /**\n * Subscribe to real-time messages on a topic.\n * Returns an unsubscribe function.\n */\n onMessage(\n topicId: number,\n callback: (msg: Message) => void\n ): () => void {\n let key: Uint8Array | null = null;\n\n // Pre-derive key, then start listening\n this.getEncryptionKey(topicId).then((k) => {\n key = k;\n });\n\n const handler = (\n tId: bigint,\n sender: string,\n payload: string,\n timestamp: bigint,\n event: ethers.EventLog\n ) => {\n if (!key) return;\n const payloadStr = ethers.toUtf8String(payload);\n const parsed = decryptMessage(payloadStr, key);\n\n callback({\n topicId: tId,\n sender,\n text: parsed?.text ?? '[decryption failed]',\n replyTo: parsed?.replyTo ?? null,\n mentions: parsed?.mentions ?? null,\n timestamp,\n txHash: event.transactionHash,\n blockNumber: event.blockNumber,\n });\n };\n\n this.registry.on(this.registry.filters.MessageSent(topicId), handler);\n return () => {\n this.registry.off(this.registry.filters.MessageSent(topicId), handler);\n };\n }\n\n // ===== NICKNAMES =====\n\n async setNickname(appId: number, nickname: string): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.setNickname(appId, nickname);\n }\n\n async getNickname(appId: number, address: string): Promise<string> {\n return this.registry.getNickname(appId, address);\n }\n\n async hasNickname(appId: number, address: string): Promise<boolean> {\n return this.registry.hasNickname(appId, address);\n }\n\n async canChangeNickname(appId: number, address: string): Promise<{ canChange: boolean; timeRemaining: bigint }> {\n const [canChange, timeRemaining] = await this.registry.canChangeNickname(appId, address);\n return { canChange, timeRemaining };\n }\n\n async clearNickname(appId: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.clearNickname(appId);\n }\n\n async setNicknameCooldown(appId: number, cooldownSeconds: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.setNicknameCooldown(appId, cooldownSeconds);\n }\n\n async getNicknameCooldown(appId: number): Promise<bigint> {\n return this.registry.appNicknameCooldown(appId);\n }\n\n // ===== TOPICS =====\n\n async createTopic(\n appId: number,\n name: string,\n description: string,\n accessLevel: AccessLevel\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.createTopic(appId, name, description, accessLevel);\n }\n\n async getTopic(topicId: number): Promise<Topic> {\n const t = await this.registry.getTopic(topicId);\n return {\n id: t.id,\n applicationId: t.applicationId,\n name: t.name,\n description: t.description,\n owner: t.owner,\n creator: t.creator,\n createdAt: t.createdAt,\n lastMessageAt: t.lastMessageAt,\n messageCount: t.messageCount,\n accessLevel: Number(t.accessLevel),\n active: t.active,\n };\n }\n\n async getApplicationTopics(appId: number): Promise<bigint[]> {\n return this.registry.getApplicationTopics(appId);\n }\n\n async getTopicCount(): Promise<number> {\n const count = await this.registry.topicCount();\n return Number(count);\n }\n\n async setTopicPermission(\n topicId: number,\n user: string,\n permission: number\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.setTopicPermission(topicId, user, permission);\n }\n\n async getTopicPermission(topicId: number, user: string): Promise<number> {\n const perm = await this.registry.getTopicPermission(topicId, user);\n return Number(perm);\n }\n\n // ===== MEMBERS =====\n\n async addMember(\n appId: number,\n address: string,\n nickname: string,\n roles: number\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.addMember(appId, address, nickname, roles);\n }\n\n async removeMember(appId: number, address: string): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.removeMember(appId, address);\n }\n\n async updateMemberRoles(\n appId: number,\n address: string,\n roles: number\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.updateMemberRoles(appId, address, roles);\n }\n\n async getMember(appId: number, address: string): Promise<Member> {\n const m = await this.registry.getMember(appId, address);\n return {\n account: m.account,\n nickname: m.nickname,\n roles: Number(m.roles),\n joinedAt: m.joinedAt,\n };\n }\n\n async isMember(appId: number, address: string): Promise<boolean> {\n return this.registry.isMember(appId, address);\n }\n\n async getApplicationMembers(appId: number): Promise<string[]> {\n return this.registry.getApplicationMembers(appId);\n }\n\n // ===== ACCESS CHECKS =====\n\n async canRead(topicId: number, address: string): Promise<boolean> {\n return this.registry.canReadTopic(topicId, address);\n }\n\n async canWrite(topicId: number, address: string): Promise<boolean> {\n return this.registry.canWriteToTopic(topicId, address);\n }\n\n // ===== APPLICATIONS =====\n\n async createApplication(\n name: string,\n description: string,\n frontendUrl: string,\n allowPublicTopicCreation: boolean\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.createApplication(name, description, frontendUrl, allowPublicTopicCreation);\n }\n\n async getApplicationCount(): Promise<number> {\n const count = await this.registry.applicationCount();\n return Number(count);\n }\n\n async updateFrontendUrl(appId: number, frontendUrl: string): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.updateApplicationFrontendUrl(appId, frontendUrl);\n }\n\n async getApplication(appId: number): Promise<Application> {\n const a = await this.registry.getApplication(appId);\n return {\n id: a.id,\n name: a.name,\n description: a.description,\n frontendUrl: a.frontendUrl,\n owner: a.owner,\n createdAt: a.createdAt,\n memberCount: Number(a.memberCount),\n topicCount: Number(a.topicCount),\n active: a.active,\n allowPublicTopicCreation: a.allowPublicTopicCreation,\n topicCreationFeeToken: a.topicCreationFeeToken,\n topicCreationFeeAmount: a.topicCreationFeeAmount,\n };\n }\n\n // ===== FEES =====\n\n async getTopicMessageFee(topicId: number): Promise<TopicMessageFee> {\n const [token, amount] = await this.registry.getTopicMessageFee(topicId);\n return { token, amount };\n }\n\n async setTopicCreationFee(\n appId: number,\n feeToken: string,\n feeAmount: bigint\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.setTopicCreationFee(appId, feeToken, feeAmount);\n }\n\n async setTopicMessageFee(\n topicId: number,\n feeToken: string,\n feeAmount: bigint\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.setTopicMessageFee(topicId, feeToken, feeAmount);\n }\n\n // ===== ECDH (Private Topics) =====\n\n /**\n * Derive ECDH keypair from wallet signature (deterministic).\n * Requires a signer capable of signing messages.\n */\n async deriveECDHFromWallet(appId: number = 1): Promise<{ publicKey: Uint8Array }> {\n if (!this.wallet) throw new Error('Wallet required');\n\n const { privateKey, publicKey } = await deriveKeypairFromSignature(\n this.wallet.address,\n (msg) => this.wallet!.signMessage(msg),\n appId\n );\n\n this.ecdhPrivateKey = privateKey;\n this.ecdhPublicKey = publicKey;\n return { publicKey };\n }\n\n /**\n * Load ECDH keypair from a hex private key (e.g. from credentials file).\n */\n loadECDHKeypair(privateKeyHex: string): void {\n const { privateKey, publicKey } = keypairFromPrivateKey(privateKeyHex);\n this.ecdhPrivateKey = privateKey;\n this.ecdhPublicKey = publicKey;\n }\n\n /**\n * Register ECDH public key on-chain.\n */\n async registerPublicKey(): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n if (!this.ecdhPublicKey) throw new Error('ECDH key not derived yet');\n\n const hasKey = await this.keyManager.hasPublicKey(this.wallet.address);\n if (hasKey) {\n throw new Error('Public key already registered on-chain');\n }\n\n return this.keyManager.registerPublicKey(this.ecdhPublicKey);\n }\n\n /**\n * Fetch and decrypt the topic symmetric key from an on-chain ECDH grant.\n */\n async fetchAndDecryptTopicKey(topicId: number): Promise<Uint8Array> {\n if (!this.ecdhPrivateKey) throw new Error('ECDH key not derived yet');\n\n const grant = await this.keyManager.getMyKey(topicId);\n const encryptedKey = ethers.getBytes(grant.encryptedKey);\n const granterPubKey = ethers.getBytes(grant.granterPublicKey);\n\n const topicKey = decryptTopicKey(encryptedKey, this.ecdhPrivateKey, granterPubKey);\n this.topicKeys.set(topicId, topicKey);\n return topicKey;\n }\n\n /**\n * Grant a user access to a private topic's symmetric key.\n */\n async grantKeyAccess(\n topicId: number,\n userAddress: string,\n topicKey: Uint8Array\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n if (!this.ecdhPrivateKey) throw new Error('ECDH key not derived yet');\n\n const userPubKeyBytes = ethers.getBytes(await this.keyManager.getPublicKey(userAddress));\n const encrypted = encryptTopicKeyForUser(topicKey, this.ecdhPrivateKey, userPubKeyBytes);\n return this.keyManager.grantKeyAccess(topicId, userAddress, encrypted);\n }\n\n /**\n * Store a pre-known topic key (e.g. loaded from credentials).\n */\n setTopicKey(topicId: number, key: Uint8Array): void {\n this.topicKeys.set(topicId, key);\n }\n\n /**\n * Check if an address has an ECDH public key registered on-chain.\n */\n async hasPublicKey(address: string): Promise<boolean> {\n return this.keyManager.hasPublicKey(address);\n }\n\n /**\n * Get an address's ECDH public key from chain.\n */\n async getPublicKey(address: string): Promise<Uint8Array> {\n const key = await this.keyManager.getPublicKey(address);\n return ethers.getBytes(key);\n }\n\n /**\n * Check if a user has key access for a topic.\n */\n async hasKeyAccess(topicId: number, address: string): Promise<boolean> {\n return this.keyManager.hasKeyAccess(topicId, address);\n }\n\n /**\n * Get the key grant details for a user on a topic.\n */\n async getKeyGrant(topicId: number, address: string): Promise<{\n encryptedKey: Uint8Array;\n granterPublicKey: Uint8Array;\n granter: string;\n keyVersion: bigint;\n grantedAt: bigint;\n }> {\n const g = await this.keyManager.getKeyGrant(topicId, address);\n return {\n encryptedKey: ethers.getBytes(g.encryptedKey),\n granterPublicKey: ethers.getBytes(g.granterPublicKey),\n granter: g.granter,\n keyVersion: g.keyVersion,\n grantedAt: g.grantedAt,\n };\n }\n\n /**\n * Get the current key version for a topic.\n */\n async getKeyVersion(topicId: number): Promise<bigint> {\n return this.keyManager.keyVersions(topicId);\n }\n\n /**\n * Batch grant key access to multiple users at once (max 50).\n */\n async batchGrantKeyAccess(\n topicId: number,\n users: string[],\n topicKey: Uint8Array\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n if (!this.ecdhPrivateKey) throw new Error('ECDH key not derived yet');\n\n const encryptedKeys: Uint8Array[] = [];\n for (const user of users) {\n const userPubKeyBytes = ethers.getBytes(await this.keyManager.getPublicKey(user));\n const encrypted = encryptTopicKeyForUser(topicKey, this.ecdhPrivateKey, userPubKeyBytes);\n encryptedKeys.push(encrypted);\n }\n\n return this.keyManager.batchGrantKeyAccess(topicId, users, encryptedKeys);\n }\n\n /**\n * Revoke a user's key access for a topic.\n */\n async revokeKeyAccess(topicId: number, address: string): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.keyManager.revokeKeyAccess(topicId, address);\n }\n\n /**\n * Rotate the key version for a topic. Existing grants become stale.\n */\n async rotateKey(topicId: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.keyManager.rotateKey(topicId);\n }\n\n // ===== SCHEMAS =====\n\n /**\n * Create a schema scoped to an application. Requires app admin role.\n */\n async createAppSchema(\n appId: number,\n name: string,\n description: string,\n body: string\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.schemaRegistry.createAppSchema(appId, name, description, body);\n }\n\n /**\n * Publish a new version of an existing schema.\n */\n async publishSchemaVersion(\n schemaId: number,\n body: string\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.schemaRegistry.publishSchemaVersion(schemaId, body);\n }\n\n /**\n * Deactivate a schema.\n */\n async deactivateSchema(schemaId: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.schemaRegistry.deactivateSchema(schemaId);\n }\n\n /**\n * Get schema info including application scope.\n */\n async getSchema(schemaId: number): Promise<SchemaInfo> {\n const s = await this.schemaRegistry.getSchemaWithApp(schemaId);\n return {\n id: Number(s.id),\n name: s.name,\n description: s.description,\n creator: s.creator,\n createdAt: Number(s.createdAt),\n versionCount: Number(s.versionCount),\n active: s.active,\n applicationId: Number(s.applicationId),\n };\n }\n\n /**\n * Get all schemas scoped to an application.\n */\n async getApplicationSchemas(appId: number): Promise<SchemaInfo[]> {\n const ids: bigint[] = await this.schemaRegistry.getApplicationSchemas(appId);\n const schemas: SchemaInfo[] = [];\n for (const id of ids) {\n const s = await this.getSchema(Number(id));\n schemas.push(s);\n }\n return schemas;\n }\n\n /**\n * Get schema body for a specific version.\n */\n async getSchemaBody(schemaId: number, version: number): Promise<string> {\n return this.schemaRegistry.getSchemaBody(schemaId, version);\n }\n\n /**\n * Get the schema binding for a topic.\n */\n async getTopicSchema(topicId: number): Promise<TopicSchemaBinding> {\n const s = await this.schemaRegistry.getTopicSchema(topicId);\n return {\n schemaId: Number(s.schemaId),\n version: Number(s.version),\n body: s.body,\n };\n }\n\n /**\n * Bind a schema version to a topic. Requires topic admin.\n */\n async setTopicSchema(\n topicId: number,\n schemaId: number,\n version: number\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.schemaRegistry.setTopicSchema(topicId, schemaId, version);\n }\n\n /**\n * Remove schema binding from a topic.\n */\n async clearTopicSchema(topicId: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.schemaRegistry.clearTopicSchema(topicId);\n }\n\n // ===== AGENT IDENTITY (V5) =====\n\n /**\n * Register your ERC-8004 agent identity for an application (V5).\n * Verifies ownership via ownerOf on the identity registry.\n */\n async registerAgentIdentity(appId: number, tokenId: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.registerAgentIdentity(appId, tokenId);\n }\n\n /**\n * Clear your agent identity registration for an application (V5).\n */\n async clearAgentIdentity(appId: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.clearAgentIdentity(appId);\n }\n\n /**\n * Get the registered agent token ID for a user in an application (V5/V6).\n * Returns 0 if not registered. V6 validates ownership live via ownerOf.\n */\n async getAgentTokenId(appId: number, address: string): Promise<bigint> {\n return this.registry.getAgentTokenId(appId, address);\n }\n\n /**\n * Check if a user has a registered agent identity for an application (V5/V6).\n * V6 validates ownership live — returns false if the token was transferred.\n */\n async hasAgentIdentity(appId: number, address: string): Promise<boolean> {\n return this.registry.hasAgentIdentity(appId, address);\n }\n\n // ===== ERC-8004 IDENTITY REGISTRY =====\n\n private requireIdentityRegistry(): ethers.Contract {\n if (!this.identityRegistry) {\n throw new Error('ERC-8004 Identity Registry not available on this chain. Use Base.');\n }\n return this.identityRegistry;\n }\n\n /**\n * Register as an agent on the ERC-8004 Identity Registry.\n * Optionally provide a URI for the agent's metadata.\n */\n async registerAgent(agentURI?: string): Promise<{ agentId: bigint; tx: ethers.TransactionResponse }> {\n if (!this.wallet) throw new Error('Wallet required');\n const registry = this.requireIdentityRegistry();\n\n const tx: ethers.TransactionResponse = agentURI\n ? await registry['register(string)'](agentURI)\n : await registry['register()']();\n\n const receipt = await tx.wait();\n let agentId = 0n;\n if (receipt) {\n for (const log of receipt.logs) {\n try {\n const parsed = registry.interface.parseLog(log);\n if (parsed?.name === 'Registered') {\n agentId = parsed.args.agentId;\n break;\n }\n } catch { /* skip non-matching logs */ }\n }\n }\n\n return { agentId, tx };\n }\n\n /**\n * Register as an agent with metadata entries.\n */\n async registerAgentWithMetadata(\n agentURI: string,\n metadata: Array<{ metadataKey: string; metadataValue: Uint8Array }>\n ): Promise<{ agentId: bigint; tx: ethers.TransactionResponse }> {\n if (!this.wallet) throw new Error('Wallet required');\n const registry = this.requireIdentityRegistry();\n\n const tx: ethers.TransactionResponse = await registry['register(string,(string,bytes)[])'](agentURI, metadata);\n\n const receipt = await tx.wait();\n let agentId = 0n;\n if (receipt) {\n for (const log of receipt.logs) {\n try {\n const parsed = registry.interface.parseLog(log);\n if (parsed?.name === 'Registered') {\n agentId = parsed.args.agentId;\n break;\n }\n } catch { /* skip non-matching logs */ }\n }\n }\n\n return { agentId, tx };\n }\n\n /**\n * Check if an address has an ERC-8004 agent identity NFT.\n * Defaults to the connected wallet address.\n */\n async isRegisteredAgent(address?: string): Promise<boolean> {\n const registry = this.requireIdentityRegistry();\n const addr = address ?? this.wallet?.address;\n if (!addr) throw new Error('Address required');\n\n const balance: bigint = await registry.balanceOf(addr);\n return balance > 0n;\n }\n\n /**\n * Get agent info by agent ID.\n */\n async getAgentInfo(agentId: number): Promise<{ owner: string; uri: string; wallet: string }> {\n const registry = this.requireIdentityRegistry();\n\n const [owner, uri, wallet] = await Promise.all([\n registry.ownerOf(agentId) as Promise<string>,\n registry.tokenURI(agentId) as Promise<string>,\n registry.getAgentWallet(agentId) as Promise<string>,\n ]);\n\n return { owner, uri, wallet };\n }\n\n /**\n * Set metadata for an agent.\n */\n async setAgentMetadata(\n agentId: number,\n key: string,\n value: Uint8Array\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n const registry = this.requireIdentityRegistry();\n return registry.setMetadata(agentId, key, value);\n }\n\n /**\n * Get metadata for an agent by key.\n */\n async getAgentMetadata(agentId: number, key: string): Promise<string> {\n const registry = this.requireIdentityRegistry();\n const data: string = await registry.getMetadata(agentId, key);\n return ethers.toUtf8String(data);\n }\n\n /**\n * Update the URI for an agent registration.\n */\n async setAgentURI(agentId: number, newURI: string): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n const registry = this.requireIdentityRegistry();\n return registry.setAgentURI(agentId, newURI);\n }\n\n /**\n * Look up an agent by its wallet address using the V5/V6 on-chain registry mapping.\n * V6 validates ownership live — stale registrations (transferred tokens) return 0.\n * Returns registration status, token ID, owner, URI, wallet, and parsed metadata.\n */\n async getAgentByAddress(address: string, appId: number): Promise<\n | { registered: false }\n | { registered: true; agentId: number; owner: string; uri: string; wallet: string; metadata: Record<string, unknown> | null }\n > {\n const onChainTokenId: bigint = await this.registry.getAgentTokenId(appId, address);\n if (onChainTokenId === 0n) return { registered: false };\n\n const agentId = Number(onChainTokenId);\n const { owner, uri, wallet } = await this.getAgentInfo(agentId);\n let metadata: Record<string, unknown> | null = null;\n if (uri) {\n metadata = await this.parseTokenURI(uri);\n }\n return { registered: true, agentId, owner, uri, wallet, metadata };\n }\n\n /**\n * Parse a tokenURI into JSON metadata.\n * Handles data:application/json;base64, data:application/json, HTTP(S), and ipfs:// URIs.\n */\n private async parseTokenURI(uri: string): Promise<Record<string, unknown> | null> {\n try {\n if (uri.startsWith('data:application/json;base64,')) {\n const json = atob(uri.slice('data:application/json;base64,'.length));\n return JSON.parse(json);\n }\n if (uri.startsWith('data:application/json,')) {\n return JSON.parse(decodeURIComponent(uri.slice('data:application/json,'.length)));\n }\n let url = uri;\n if (url.startsWith('ipfs://')) {\n url = 'https://ipfs.io/ipfs/' + url.slice('ipfs://'.length);\n }\n const resp = await fetch(url);\n if (!resp.ok) return null;\n return await resp.json();\n } catch {\n return null;\n }\n }\n\n // ===== DATA EXPORT =====\n\n /**\n * Look up an application ID by its name.\n */\n async getApplicationByName(name: string): Promise<number> {\n const id = await this.registry.applicationNames(name);\n return Number(id);\n }\n\n /**\n * Get schema version details including body and publish timestamp.\n */\n async getSchemaVersion(schemaId: number, version: number): Promise<{ body: string; publishedAt: bigint }> {\n const [body, publishedAt] = await this.schemaRegistry.getSchemaVersion(schemaId, version);\n return { body, publishedAt };\n }\n\n /**\n * Export member data for a user in an application.\n */\n async exportMemberData(appId: number, user: string): Promise<string> {\n const data = await this.registry.exportMemberData(appId, user);\n return ethers.hexlify(data);\n }\n\n /**\n * Export all application data.\n */\n async exportApplicationData(appId: number): Promise<string> {\n const data = await this.registry.exportApplicationData(appId);\n return ethers.hexlify(data);\n }\n\n /**\n * Export user data from the key manager for specified topics.\n */\n async exportUserData(user: string, topicIds: number[]): Promise<string> {\n const data = await this.keyManager.exportUserData(user, topicIds);\n return ethers.hexlify(data);\n }\n\n // ===== INTERNAL =====\n\n /**\n * Get the encryption key for a topic, determining the type automatically.\n */\n private async getEncryptionKey(topicId: number): Promise<Uint8Array> {\n // Check for a stored private topic key first\n const storedKey = this.topicKeys.get(topicId);\n if (storedKey) return storedKey;\n\n // Fetch topic metadata to determine access level\n const topic = await this.getTopic(topicId);\n\n if (topic.accessLevel === AccessLevel.PRIVATE) {\n throw new Error(\n `Topic ${topicId} is PRIVATE. Call fetchAndDecryptTopicKey() or setTopicKey() first.`\n );\n }\n\n // Public or public_limited: derive deterministic key\n return derivePublicTopicKey(topicId);\n }\n}\n","import type { ChainConfig, ChainName } from './types.js';\n\nexport const CHAINS: Record<ChainName, ChainConfig> = {\n baseSepolia: {\n chainId: 84532,\n name: 'Base Sepolia',\n shortName: 'Sepolia',\n rpc: 'https://sepolia.base.org',\n explorer: 'https://sepolia.basescan.org',\n registry: '0xf39b193aedC1Ec9FD6C5ccc24fBAe58ba9f52413',\n keyManager: '0x5562B553a876CBdc8AA4B3fb0687f22760F4759e',\n schemaRegistry: '0xB7eB50e9058198b99b5b2589E6D70b2d99d5440a',\n identityRegistry: '0x8004A169FB4a3325136EB29fA0ceB6D2e539a432',\n },\n base: {\n chainId: 8453,\n name: 'Base',\n shortName: 'Base',\n rpc: 'https://mainnet.base.org',\n explorer: 'https://basescan.org',\n registry: '0x5fF6BF04F1B5A78ae884D977a3C80A0D8E2072bF',\n keyManager: '0xdc302ff43a34F6aEa19426D60C9D150e0661E4f4',\n schemaRegistry: '0x5c11d2eA4470eD9025D810A21a885FE16dC987Bd',\n identityRegistry: '0x8004A169FB4a3325136EB29fA0ceB6D2e539a432',\n },\n avalanche: {\n chainId: 43114,\n name: 'Avalanche C-Chain',\n shortName: 'Avalanche',\n rpc: 'https://api.avax.network/ext/bc/C/rpc',\n explorer: 'https://snowtrace.io',\n registry: '0x3Ca2FF0bD1b3633513299EB5d3e2d63e058b0713',\n keyManager: '0x5a5ea9D408FBA984fFf6e243Dcc71ff6E00C73E4',\n schemaRegistry: '0x23D96e610E8E3DA5341a75B77F1BFF7EA9c3A62B',\n identityRegistry: '0x8004A169FB4a3325136EB29fA0ceB6D2e539a432',\n },\n};\n\nexport const CHAIN_IDS: Record<number, ChainName> = {\n 84532: 'baseSepolia',\n 8453: 'base',\n 43114: 'avalanche',\n};\n\nexport function getChain(nameOrId: ChainName | number): ChainConfig {\n if (typeof nameOrId === 'number') {\n const name = CHAIN_IDS[nameOrId];\n if (!name) throw new Error(`Unsupported chain ID: ${nameOrId}`);\n return CHAINS[name];\n }\n const chain = CHAINS[nameOrId];\n if (!chain) throw new Error(`Unsupported chain: ${nameOrId}`);\n return chain;\n}\n","export const REGISTRY_ABI = [\n // ===== READ FUNCTIONS =====\n\n // Applications\n 'function applications(uint256) view returns (uint256 id, string name, string description, string frontendUrl, address owner, uint64 createdAt, uint32 memberCount, uint32 topicCount, bool active, bool allowPublicTopicCreation)',\n 'function getApplication(uint256 appId) view returns (tuple(uint256 id, string name, string description, string frontendUrl, address owner, uint64 createdAt, uint32 memberCount, uint32 topicCount, bool active, bool allowPublicTopicCreation, address topicCreationFeeToken, uint256 topicCreationFeeAmount))',\n 'function applicationCount() view returns (uint256)',\n 'function applicationNames(string) view returns (uint256)',\n\n // Topics\n 'function topics(uint256) view returns (uint256 id, uint256 applicationId, string name, string description, address owner, address creator, uint64 createdAt, uint64 lastMessageAt, uint256 messageCount, uint8 accessLevel, bool active)',\n 'function getTopic(uint256 topicId) view returns (tuple(uint256 id, uint256 applicationId, string name, string description, address owner, address creator, uint64 createdAt, uint64 lastMessageAt, uint256 messageCount, uint8 accessLevel, bool active))',\n 'function topicCount() view returns (uint256)',\n 'function getApplicationTopics(uint256 appId) view returns (uint256[])',\n\n // Members\n 'function members(uint256 appId, address user) view returns (address account, string nickname, uint8 roles, uint64 joinedAt)',\n 'function getMember(uint256 appId, address account) view returns (tuple(address account, string nickname, uint8 roles, uint64 joinedAt))',\n 'function isMember(uint256 appId, address account) view returns (bool)',\n 'function getApplicationMembers(uint256 appId) view returns (address[])',\n\n // Permissions\n 'function canReadTopic(uint256 topicId, address user) view returns (bool)',\n 'function canWriteToTopic(uint256 topicId, address user) view returns (bool)',\n 'function getTopicPermission(uint256 topicId, address user) view returns (uint8)',\n 'function topicPermissions(uint256, address) view returns (uint8)',\n\n // Nicknames\n 'function getNickname(uint256 appId, address user) view returns (string)',\n 'function hasNickname(uint256 appId, address user) view returns (bool)',\n 'function canChangeNickname(uint256 appId, address user) view returns (bool canChange, uint256 timeRemaining)',\n 'function appNicknameCooldown(uint256 appId) view returns (uint256)',\n\n // Fees\n 'function getTopicMessageFee(uint256 topicId) view returns (address token, uint256 amount)',\n\n // ===== WRITE FUNCTIONS =====\n\n // Applications\n 'function createApplication(string name, string description, string frontendUrl, bool allowPublicTopicCreation) returns (uint256)',\n 'function updateApplicationFrontendUrl(uint256 appId, string frontendUrl)',\n\n // Topics\n 'function createTopic(uint256 appId, string name, string description, uint8 accessLevel) returns (uint256)',\n 'function setTopicPermission(uint256 topicId, address user, uint8 permission)',\n\n // Members\n 'function addMember(uint256 appId, address member, string nickname, uint8 roles)',\n 'function removeMember(uint256 appId, address member)',\n 'function updateMemberRoles(uint256 appId, address member, uint8 roles)',\n 'function updateMemberNickname(uint256 appId, string nickname)',\n\n // Nicknames (V3)\n 'function setNickname(uint256 appId, string nickname)',\n 'function clearNickname(uint256 appId)',\n 'function setNicknameCooldown(uint256 appId, uint256 cooldownSeconds)',\n\n // Messaging\n 'function sendMessage(uint256 topicId, bytes payload)',\n\n // Fees\n 'function setTopicCreationFee(uint256 appId, address feeTokenAddr, uint256 feeAmount)',\n 'function setTopicMessageFee(uint256 topicId, address feeTokenAddr, uint256 feeAmount)',\n\n // ===== EVENTS =====\n 'event ApplicationCreated(uint256 indexed applicationId, string name, address indexed owner)',\n 'event TopicCreated(uint256 indexed topicId, uint256 indexed applicationId, string name, address indexed creator, uint8 accessLevel)',\n 'event MemberAdded(uint256 indexed applicationId, address indexed member, string nickname, uint8 roles)',\n 'event MemberRemoved(uint256 indexed applicationId, address indexed member)',\n 'event MemberRolesUpdated(uint256 indexed applicationId, address indexed member, uint8 roles)',\n 'event NicknameUpdated(uint256 indexed applicationId, address indexed member, string nickname)',\n 'event UserNicknameSet(uint256 indexed applicationId, address indexed user, string nickname)',\n 'event TopicPermissionSet(uint256 indexed topicId, address indexed user, uint8 permission)',\n 'event MessageSent(uint256 indexed topicId, address indexed sender, bytes payload, uint256 timestamp)',\n 'event TopicMessageFeeUpdated(uint256 indexed topicId, address token, uint256 amount)',\n\n // Agent identity (V5)\n 'function registerAgentIdentity(uint256 appId, uint256 tokenId)',\n 'function clearAgentIdentity(uint256 appId)',\n 'function getAgentTokenId(uint256 appId, address user) view returns (uint256)',\n 'function hasAgentIdentity(uint256 appId, address user) view returns (bool)',\n 'event AgentIdentityRegistered(uint256 indexed applicationId, address indexed user, uint256 tokenId)',\n 'event AgentIdentityCleared(uint256 indexed applicationId, address indexed user)',\n\n // Data export\n 'function exportMemberData(uint256 appId, address user) view returns (bytes)',\n 'function exportApplicationData(uint256 appId) view returns (bytes)',\n] as const;\n\nexport const SCHEMA_REGISTRY_ABI = [\n // ===== READ FUNCTIONS =====\n\n // Schema queries\n 'function schemaCount() view returns (uint256)',\n 'function getSchema(uint256 schemaId) view returns (uint256 id, string name, string description, address creator, uint64 createdAt, uint256 versionCount, bool active)',\n 'function getSchemaWithApp(uint256 schemaId) view returns (uint256 id, string name, string description, address creator, uint64 createdAt, uint256 versionCount, bool active, uint256 applicationId)',\n 'function getSchemaBody(uint256 schemaId, uint256 version) view returns (string)',\n 'function getSchemaVersion(uint256 schemaId, uint256 version) view returns (string body, uint64 publishedAt)',\n 'function schemaApplicationId(uint256 schemaId) view returns (uint256)',\n\n // App-scoped queries (V2)\n 'function getApplicationSchemas(uint256 applicationId) view returns (uint256[])',\n 'function getApplicationSchemaCount(uint256 applicationId) view returns (uint256)',\n\n // Topic binding\n 'function getTopicSchema(uint256 topicId) view returns (uint256 schemaId, uint256 version, string body)',\n\n // Version\n 'function contractVersion() view returns (string)',\n\n // ===== WRITE FUNCTIONS =====\n\n // Schema creation (V2 app-scoped)\n 'function createAppSchema(uint256 applicationId, string name, string description, string body) returns (uint256)',\n 'function publishSchemaVersion(uint256 schemaId, string body) returns (uint256)',\n 'function deactivateSchema(uint256 schemaId)',\n\n // Topic binding\n 'function setTopicSchema(uint256 topicId, uint256 schemaId, uint256 version)',\n 'function clearTopicSchema(uint256 topicId)',\n\n // ===== EVENTS =====\n 'event AppSchemaCreated(uint256 indexed schemaId, uint256 indexed applicationId, string name, address indexed creator)',\n 'event SchemaVersionPublished(uint256 indexed schemaId, uint256 version)',\n 'event SchemaDeactivated(uint256 indexed schemaId)',\n 'event TopicSchemaSet(uint256 indexed topicId, uint256 indexed schemaId, uint256 version)',\n 'event TopicSchemaCleared(uint256 indexed topicId)',\n 'event SchemaAssignedToApp(uint256 indexed schemaId, uint256 indexed applicationId)',\n] as const;\n\nexport const IDENTITY_REGISTRY_ABI = [\n 'function register() returns (uint256)',\n 'function register(string agentURI) returns (uint256)',\n 'function register(string agentURI, tuple(string metadataKey, bytes metadataValue)[] metadata) returns (uint256)',\n 'function getMetadata(uint256 agentId, string metadataKey) view returns (bytes)',\n 'function setMetadata(uint256 agentId, string metadataKey, bytes metadataValue)',\n 'function setAgentURI(uint256 agentId, string newURI)',\n 'function getAgentWallet(uint256 agentId) view returns (address)',\n 'function setAgentWallet(uint256 agentId, address newWallet, uint256 deadline, bytes signature)',\n 'function unsetAgentWallet(uint256 agentId)',\n 'function ownerOf(uint256 tokenId) view returns (address)',\n 'function balanceOf(address owner) view returns (uint256)',\n 'function tokenOfOwnerByIndex(address owner, uint256 index) view returns (uint256)',\n 'function tokenURI(uint256 tokenId) view returns (string)',\n 'function isAuthorizedOrOwner(address spender, uint256 agentId) view returns (bool)',\n 'function getVersion() pure returns (string)',\n 'event Registered(uint256 indexed agentId, string agentURI, address indexed owner)',\n] as const;\n\nexport const KEY_MANAGER_ABI = [\n // ===== READ FUNCTIONS =====\n 'function hasPublicKey(address user) view returns (bool)',\n 'function getPublicKey(address user) view returns (bytes)',\n 'function publicKeys(address) view returns (bytes)',\n 'function hasKeyAccess(uint256 topicId, address user) view returns (bool)',\n 'function getMyKey(uint256 topicId) view returns (bytes encryptedKey, bytes granterPublicKey, address granter, uint256 keyVersion, uint256 currentVersion)',\n 'function getKeyGrant(uint256 topicId, address user) view returns (tuple(bytes encryptedKey, bytes granterPublicKey, address granter, uint256 keyVersion, uint64 grantedAt))',\n 'function keyVersions(uint256 topicId) view returns (uint256)',\n\n // ===== WRITE FUNCTIONS =====\n 'function registerPublicKey(bytes publicKey)',\n 'function grantKeyAccess(uint256 topicId, address user, bytes encryptedKey)',\n 'function batchGrantKeyAccess(uint256 topicId, address[] users, bytes[] encryptedKeys)',\n 'function revokeKeyAccess(uint256 topicId, address user)',\n 'function rotateKey(uint256 topicId)',\n\n // Data export\n 'function exportUserData(address user, uint256[] topicIds) view returns (bytes)',\n\n // ===== EVENTS =====\n 'event PublicKeyRegistered(address indexed user, bytes publicKey)',\n 'event PublicKeyUpdated(address indexed user, bytes publicKey)',\n 'event KeyAccessGranted(uint256 indexed topicId, address indexed user, address indexed granter, uint256 version)',\n 'event KeyAccessRevoked(uint256 indexed topicId, address indexed user)',\n 'event KeyRotated(uint256 indexed topicId, uint256 newVersion)',\n] as const;\n","import { pbkdf2 } from '@noble/hashes/pbkdf2';\nimport { sha256 } from '@noble/hashes/sha256';\nimport { gcm } from '@noble/ciphers/aes';\nimport { randomBytes } from '@noble/hashes/utils';\nimport {\n PUBLIC_KEY_MATERIAL_PREFIX,\n SALT_PREFIX,\n PBKDF2_ITERATIONS,\n} from '../constants.js';\nimport type { EncryptedPayload, MessageContent } from '../types.js';\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\n/**\n * Derive AES-256 key for a public/public_limited topic.\n * Uses PBKDF2 with SHA-256, matching the web frontend exactly.\n */\nexport function derivePublicTopicKey(topicId: number | bigint): Uint8Array {\n const keyMaterial = PUBLIC_KEY_MATERIAL_PREFIX + topicId;\n const salt = encoder.encode(SALT_PREFIX + topicId);\n return pbkdf2(sha256, keyMaterial, salt, { c: PBKDF2_ITERATIONS, dkLen: 32 });\n}\n\n/**\n * Derive AES-256 key from arbitrary passphrase (for private topics with manual passphrase).\n */\nexport function deriveKeyFromPassphrase(passphrase: string, topicId: number | bigint): Uint8Array {\n const salt = encoder.encode(SALT_PREFIX + topicId);\n return pbkdf2(sha256, passphrase, salt, { c: PBKDF2_ITERATIONS, dkLen: 32 });\n}\n\n/**\n * Encrypt a message payload with AES-256-GCM.\n * Output format: `{ e: true, v: 2, iv: base64, ct: base64 }`\n * The ciphertext includes the GCM auth tag (last 16 bytes).\n */\nexport function encrypt(plaintext: string, key: Uint8Array): string {\n const iv = randomBytes(12);\n const aes = gcm(key, iv);\n const ciphertext = aes.encrypt(encoder.encode(plaintext));\n\n const payload: EncryptedPayload = {\n e: true,\n v: 2,\n iv: toBase64(iv),\n ct: toBase64(ciphertext),\n };\n return JSON.stringify(payload);\n}\n\n/**\n * Decrypt a message payload. Handles both v1 and v2 formats.\n * Returns the decrypted string or null on failure.\n */\nexport function decrypt(jsonStr: string, key: Uint8Array): string | null {\n try {\n const data = JSON.parse(jsonStr) as EncryptedPayload;\n if (!data.e) {\n // Not encrypted — return raw for caller to parse\n return jsonStr;\n }\n\n const iv = fromBase64(data.iv);\n const ct = fromBase64(data.ct);\n\n const aes = gcm(key, iv);\n const decrypted = aes.decrypt(ct);\n return decoder.decode(decrypted);\n } catch {\n return null;\n }\n}\n\n/**\n * Encrypt a structured message (text + optional replyTo/mentions).\n */\nexport function encryptMessage(\n text: string,\n key: Uint8Array,\n options?: { replyTo?: string; replyText?: string; replyAuthor?: string; mentions?: string[] }\n): string {\n const content: MessageContent = { text };\n if (options?.replyTo) content.replyTo = options.replyTo;\n if (options?.replyText) content.replyText = options.replyText;\n if (options?.replyAuthor) content.replyAuthor = options.replyAuthor;\n if (options?.mentions) content.mentions = options.mentions;\n return encrypt(JSON.stringify(content), key);\n}\n\n/**\n * Decrypt and parse a message payload into structured content.\n */\nexport function decryptMessage(\n jsonStr: string,\n key: Uint8Array\n): { text: string; replyTo: string | null; replyText: string | null; replyAuthor: string | null; mentions: string[] | null } | null {\n const decrypted = decrypt(jsonStr, key);\n if (!decrypted) return null;\n\n try {\n const content = JSON.parse(decrypted);\n if (typeof content === 'object' && content.text) {\n return {\n text: content.text,\n replyTo: content.replyTo || null,\n replyText: content.replyText || null,\n replyAuthor: content.replyAuthor || null,\n mentions: content.mentions || null,\n };\n }\n // Plain text string was JSON-stringified\n return { text: decrypted, replyTo: null, replyText: null, replyAuthor: null, mentions: null };\n } catch {\n // Raw string, not JSON\n return { text: decrypted, replyTo: null, replyText: null, replyAuthor: null, mentions: null };\n }\n}\n\n// ===== Base64 helpers (isomorphic) =====\n\nfunction toBase64(bytes: Uint8Array): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(bytes).toString('base64');\n }\n return btoa(String.fromCharCode(...bytes));\n}\n\nfunction fromBase64(str: string): Uint8Array {\n if (typeof Buffer !== 'undefined') {\n return new Uint8Array(Buffer.from(str, 'base64'));\n }\n return Uint8Array.from(atob(str), (c) => c.charCodeAt(0));\n}\n","// ===== Enums =====\n\nexport enum AccessLevel {\n PUBLIC = 0,\n PUBLIC_LIMITED = 1,\n PRIVATE = 2,\n}\n\nexport enum Permission {\n NONE = 0,\n READ = 1,\n WRITE = 2,\n READ_WRITE = 3,\n ADMIN = 4,\n}\n\nexport enum Role {\n MEMBER = 1,\n SUPPORT_MANAGER = 2,\n TOPIC_MANAGER = 4,\n ADMIN = 8,\n OWNER_DELEGATE = 16,\n}\n\n// ===== Chain types =====\n\nexport interface ChainConfig {\n chainId: number;\n name: string;\n shortName: string;\n rpc: string;\n explorer: string;\n registry: string;\n keyManager: string;\n schemaRegistry: string;\n identityRegistry?: string;\n}\n\nexport type ChainName = 'base' | 'avalanche' | 'baseSepolia';\n\n// ===== Contract return types =====\n\nexport interface Application {\n id: bigint;\n name: string;\n description: string;\n frontendUrl: string;\n owner: string;\n createdAt: bigint;\n memberCount: number;\n topicCount: number;\n active: boolean;\n allowPublicTopicCreation: boolean;\n topicCreationFeeToken?: string;\n topicCreationFeeAmount?: bigint;\n}\n\nexport interface Topic {\n id: bigint;\n applicationId: bigint;\n name: string;\n description: string;\n owner: string;\n creator: string;\n createdAt: bigint;\n lastMessageAt: bigint;\n messageCount: bigint;\n accessLevel: number;\n active: boolean;\n}\n\nexport interface Member {\n account: string;\n nickname: string;\n roles: number;\n joinedAt: bigint;\n}\n\nexport interface KeyGrant {\n encryptedKey: Uint8Array;\n granterPublicKey: Uint8Array;\n granter: string;\n keyVersion: bigint;\n grantedAt?: bigint;\n currentVersion?: bigint;\n}\n\n// ===== Message types =====\n\nexport interface MessageContent {\n text: string;\n replyTo?: string;\n replyText?: string;\n replyAuthor?: string;\n mentions?: string[];\n}\n\nexport interface EncryptedPayload {\n e: boolean;\n v: number;\n iv: string;\n ct: string;\n}\n\nexport interface Message {\n topicId: bigint;\n sender: string;\n text: string;\n replyTo: string | null;\n mentions: string[] | null;\n timestamp: bigint;\n txHash: string;\n blockNumber: number;\n}\n\nexport interface TopicMessageFee {\n token: string;\n amount: bigint;\n}\n\nexport interface SchemaInfo {\n id: number;\n name: string;\n description: string;\n creator: string;\n createdAt: number;\n versionCount: number;\n active: boolean;\n applicationId: number;\n}\n\nexport interface TopicSchemaBinding {\n schemaId: number;\n version: number;\n body: string;\n}\n\n// ===== Client options =====\n\nexport interface ClawtennaOptions {\n chain?: ChainName;\n chainId?: number;\n rpcUrl?: string;\n privateKey?: string;\n registryAddress?: string;\n keyManagerAddress?: string;\n schemaRegistryAddress?: string;\n}\n\nexport interface ReadOptions {\n limit?: number;\n fromBlock?: number;\n}\n\nexport interface SendOptions {\n replyTo?: string;\n replyText?: string;\n replyAuthor?: string;\n mentions?: string[];\n}\n\n// ===== Credentials =====\n\nexport interface Credentials {\n version: 2;\n wallet: {\n address: string;\n privateKey: string;\n };\n chains: Record<string, CredentialChain>;\n}\n\nexport interface CredentialChain {\n name: string;\n ecdh: {\n privateKey: string;\n publicKey: string;\n registered: boolean;\n } | null;\n apps: Record<string, CredentialApp>;\n}\n\nexport interface CredentialApp {\n name: string;\n nickname: string;\n agentTokenId: number | null;\n topicKeys: Record<string, string>;\n}\n\n// Legacy v1 format for migration\nexport interface CredentialsV1 {\n wallet: {\n address: string;\n privateKey: string;\n };\n apps: Record<string, {\n name: string;\n nickname: string;\n ecdh: {\n privateKey: string;\n publicKey: string;\n registeredOnChain: boolean;\n topicKeys: Record<string, string>;\n } | null;\n }>;\n}\n","import { AccessLevel, Permission, Role } from './types.js';\n\n// Re-export enums as individual constants for convenience\nexport const ACCESS_PUBLIC = AccessLevel.PUBLIC;\nexport const ACCESS_PUBLIC_LIMITED = AccessLevel.PUBLIC_LIMITED;\nexport const ACCESS_PRIVATE = AccessLevel.PRIVATE;\n\nexport const PERMISSION_NONE = Permission.NONE;\nexport const PERMISSION_READ = Permission.READ;\nexport const PERMISSION_WRITE = Permission.WRITE;\nexport const PERMISSION_READ_WRITE = Permission.READ_WRITE;\nexport const PERMISSION_ADMIN = Permission.ADMIN;\n\nexport const ROLE_MEMBER = Role.MEMBER;\nexport const ROLE_SUPPORT_MANAGER = Role.SUPPORT_MANAGER;\nexport const ROLE_TOPIC_MANAGER = Role.TOPIC_MANAGER;\nexport const ROLE_ADMIN = Role.ADMIN;\nexport const ROLE_OWNER_DELEGATE = Role.OWNER_DELEGATE;\n\n// Encryption constants\nexport const PUBLIC_KEY_MATERIAL_PREFIX = 'antenna-public-topic-';\nexport const SALT_PREFIX = 'antenna-v2-salt-';\nexport const PBKDF2_ITERATIONS = 100_000;\nexport const ECDH_HKDF_SALT = 'antenna-ecdh-v1';\nexport const ECDH_HKDF_INFO = 'topic-key-encryption';\nexport const ECDH_DERIVATION_MESSAGE = (address: string, appId: number) =>\n `Clawntenna ECDH Key Derivation\\n\\nThis signature generates your encryption key.\\nIt never leaves your device.\\n\\nWallet: ${address}\\nApp: ${appId}\\nChain: Base (8453)`;\n","import { secp256k1 } from '@noble/curves/secp256k1';\nimport { hkdf } from '@noble/hashes/hkdf';\nimport { sha256 } from '@noble/hashes/sha256';\nimport { gcm } from '@noble/ciphers/aes';\nimport { randomBytes } from '@noble/hashes/utils';\nimport { ECDH_HKDF_SALT, ECDH_HKDF_INFO, ECDH_DERIVATION_MESSAGE } from '../constants.js';\n\nconst encoder = new TextEncoder();\n\n/**\n * Derive an ECDH keypair deterministically from a wallet signature.\n * This produces the same keypair as the web frontend for the same wallet + app.\n */\nexport async function deriveKeypairFromSignature(\n walletAddress: string,\n signMessage: (message: string) => Promise<string>,\n appId: number = 1\n): Promise<{ privateKey: Uint8Array; publicKey: Uint8Array }> {\n const message = ECDH_DERIVATION_MESSAGE(walletAddress, appId);\n const signature = await signMessage(message);\n\n // Hash the signature string (as UTF-8 bytes) to get private key\n const sigBytes = encoder.encode(signature);\n const hashBuffer = sha256(sigBytes);\n const privateKey = new Uint8Array(hashBuffer);\n\n // Derive compressed public key\n const publicKey = secp256k1.getPublicKey(privateKey, true);\n\n return { privateKey, publicKey };\n}\n\n/**\n * Derive ECDH keypair from a raw private key (e.g. from stored credentials).\n */\nexport function keypairFromPrivateKey(privateKeyHex: string): {\n privateKey: Uint8Array;\n publicKey: Uint8Array;\n} {\n const cleaned = privateKeyHex.startsWith('0x') ? privateKeyHex.slice(2) : privateKeyHex;\n const privateKey = hexToBytes(cleaned);\n const publicKey = secp256k1.getPublicKey(privateKey, true);\n return { privateKey, publicKey };\n}\n\n/**\n * Compute ECDH shared secret (x-coordinate of shared point).\n */\nexport function computeSharedSecret(\n ourPrivateKey: Uint8Array,\n theirPublicKey: Uint8Array\n): Uint8Array {\n const sharedPoint = secp256k1.getSharedSecret(ourPrivateKey, theirPublicKey);\n // Return x-coordinate only (skip the 0x04 prefix byte)\n return sharedPoint.slice(1, 33);\n}\n\n/**\n * Derive AES-256 key from an ECDH shared secret using HKDF.\n * Matches the web frontend: salt='antenna-ecdh-v1', info='topic-key-encryption'.\n */\nexport function deriveAESKeyFromSecret(\n sharedSecret: Uint8Array,\n info: string = ECDH_HKDF_INFO\n): Uint8Array {\n return hkdf(sha256, sharedSecret, encoder.encode(ECDH_HKDF_SALT), info, 32);\n}\n\n/**\n * Encrypt a topic symmetric key for a recipient using ECDH.\n * Returns IV (12 bytes) + ciphertext (includes GCM auth tag).\n */\nexport function encryptTopicKeyForUser(\n topicKey: Uint8Array,\n ourPrivateKey: Uint8Array,\n recipientPublicKey: Uint8Array\n): Uint8Array {\n const shared = computeSharedSecret(ourPrivateKey, recipientPublicKey);\n const aesKey = deriveAESKeyFromSecret(shared);\n const iv = randomBytes(12);\n const aes = gcm(aesKey, iv);\n const ciphertext = aes.encrypt(topicKey);\n\n // Combine: IV + ciphertext\n const result = new Uint8Array(iv.length + ciphertext.length);\n result.set(iv);\n result.set(ciphertext, iv.length);\n return result;\n}\n\n/**\n * Decrypt a topic symmetric key received via ECDH grant.\n * Input format: IV (12 bytes) + ciphertext (includes GCM auth tag).\n */\nexport function decryptTopicKey(\n encryptedKey: Uint8Array,\n ourPrivateKey: Uint8Array,\n granterPublicKey: Uint8Array\n): Uint8Array {\n const shared = computeSharedSecret(ourPrivateKey, granterPublicKey);\n const aesKey = deriveAESKeyFromSecret(shared);\n const iv = encryptedKey.slice(0, 12);\n const ciphertext = encryptedKey.slice(12);\n const aes = gcm(aesKey, iv);\n return aes.decrypt(ciphertext);\n}\n\n// ===== Hex helpers =====\n\nexport function bytesToHex(bytes: Uint8Array): string {\n return '0x' + Array.from(bytes).map((b) => b.toString(16).padStart(2, '0')).join('');\n}\n\nexport function hexToBytes(hex: string): Uint8Array {\n const cleaned = hex.startsWith('0x') ? hex.slice(2) : hex;\n return new Uint8Array(cleaned.match(/.{1,2}/g)!.map((b) => parseInt(b, 16)));\n}\n"],"mappings":"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","names":["AccessLevel","Permission","Role","import_sha256","import_aes","import_utils","encoder"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/chains.ts","../src/contracts.ts","../src/crypto/encrypt.ts","../src/types.ts","../src/constants.ts","../src/crypto/ecdh.ts"],"sourcesContent":["// Main client\nexport { Clawntenna } from './client.js';\n\n// Types\nexport {\n AccessLevel,\n Permission,\n Role,\n} from './types.js';\nexport type {\n ChainConfig,\n ChainName,\n ClawtennaOptions,\n ReadOptions,\n SendOptions,\n Message,\n MessageContent,\n EncryptedPayload,\n Topic,\n Application,\n Member,\n KeyGrant,\n TopicMessageFee,\n SchemaInfo,\n TopicSchemaBinding,\n Credentials,\n CredentialChain,\n CredentialApp,\n CredentialsV1,\n} from './types.js';\n\n// Chain configs\nexport { CHAINS, CHAIN_IDS, getChain } from './chains.js';\n\n// Constants\nexport {\n ACCESS_PUBLIC,\n ACCESS_PUBLIC_LIMITED,\n ACCESS_PRIVATE,\n PERMISSION_NONE,\n PERMISSION_READ,\n PERMISSION_WRITE,\n PERMISSION_READ_WRITE,\n PERMISSION_ADMIN,\n ROLE_MEMBER,\n ROLE_SUPPORT_MANAGER,\n ROLE_TOPIC_MANAGER,\n ROLE_ADMIN,\n ROLE_OWNER_DELEGATE,\n} from './constants.js';\n\n// Contract ABIs\nexport { REGISTRY_ABI, KEY_MANAGER_ABI, SCHEMA_REGISTRY_ABI, IDENTITY_REGISTRY_ABI } from './contracts.js';\n\n// Crypto utilities\nexport {\n derivePublicTopicKey,\n deriveKeyFromPassphrase,\n encrypt,\n decrypt,\n encryptMessage,\n decryptMessage,\n deriveKeypairFromSignature,\n keypairFromPrivateKey,\n computeSharedSecret,\n deriveAESKeyFromSecret,\n encryptTopicKeyForUser,\n decryptTopicKey,\n bytesToHex,\n hexToBytes,\n} from './crypto/index.js';\n","import { ethers } from 'ethers';\nimport { CHAINS } from './chains.js';\nimport { REGISTRY_ABI, KEY_MANAGER_ABI, SCHEMA_REGISTRY_ABI, IDENTITY_REGISTRY_ABI } from './contracts.js';\nimport {\n derivePublicTopicKey,\n deriveKeyFromPassphrase,\n encryptMessage,\n decryptMessage,\n} from './crypto/encrypt.js';\nimport {\n deriveKeypairFromSignature,\n keypairFromPrivateKey,\n computeSharedSecret,\n deriveAESKeyFromSecret,\n encryptTopicKeyForUser,\n decryptTopicKey,\n bytesToHex,\n hexToBytes,\n} from './crypto/ecdh.js';\nimport { AccessLevel } from './types.js';\nimport type {\n ClawtennaOptions,\n ReadOptions,\n SendOptions,\n Message,\n Topic,\n Application,\n Member,\n TopicMessageFee,\n SchemaInfo,\n TopicSchemaBinding,\n ChainName,\n} from './types.js';\n\nexport class Clawntenna {\n readonly provider: ethers.JsonRpcProvider;\n readonly wallet: ethers.Wallet | null;\n readonly registry: ethers.Contract;\n readonly keyManager: ethers.Contract;\n readonly schemaRegistry: ethers.Contract;\n readonly identityRegistry: ethers.Contract | null;\n readonly chainName: ChainName;\n\n // In-memory ECDH state\n private ecdhPrivateKey: Uint8Array | null = null;\n private ecdhPublicKey: Uint8Array | null = null;\n private topicKeys: Map<number, Uint8Array> = new Map();\n\n constructor(options: ClawtennaOptions = {}) {\n const chainName = options.chain ?? 'base';\n const chain = CHAINS[chainName];\n if (!chain) throw new Error(`Unsupported chain: ${chainName}`);\n this.chainName = chainName;\n\n const rpcUrl = options.rpcUrl ?? chain.rpc;\n this.provider = new ethers.JsonRpcProvider(rpcUrl);\n\n const registryAddr = options.registryAddress ?? chain.registry;\n const keyManagerAddr = options.keyManagerAddress ?? chain.keyManager;\n const schemaRegistryAddr = options.schemaRegistryAddress ?? chain.schemaRegistry;\n\n if (options.privateKey) {\n this.wallet = new ethers.Wallet(options.privateKey, this.provider);\n this.registry = new ethers.Contract(registryAddr, REGISTRY_ABI, this.wallet);\n this.keyManager = new ethers.Contract(keyManagerAddr, KEY_MANAGER_ABI, this.wallet);\n this.schemaRegistry = new ethers.Contract(schemaRegistryAddr, SCHEMA_REGISTRY_ABI, this.wallet);\n this.identityRegistry = chain.identityRegistry\n ? new ethers.Contract(chain.identityRegistry, IDENTITY_REGISTRY_ABI, this.wallet)\n : null;\n } else {\n this.wallet = null;\n this.registry = new ethers.Contract(registryAddr, REGISTRY_ABI, this.provider);\n this.keyManager = new ethers.Contract(keyManagerAddr, KEY_MANAGER_ABI, this.provider);\n this.schemaRegistry = new ethers.Contract(schemaRegistryAddr, SCHEMA_REGISTRY_ABI, this.provider);\n this.identityRegistry = chain.identityRegistry\n ? new ethers.Contract(chain.identityRegistry, IDENTITY_REGISTRY_ABI, this.provider)\n : null;\n }\n }\n\n get address(): string | null {\n return this.wallet?.address ?? null;\n }\n\n // ===== MESSAGING =====\n\n /**\n * Send an encrypted message to a topic.\n * Automatically determines encryption key based on topic access level.\n */\n async sendMessage(topicId: number, text: string, options?: SendOptions): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required to send messages');\n\n let replyText = options?.replyText;\n let replyAuthor = options?.replyAuthor;\n\n // Auto-resolve reply metadata if replyTo is set but text/author aren't\n if (options?.replyTo && (!replyText || !replyAuthor)) {\n try {\n const messages = await this.readMessages(topicId, { limit: 50 });\n const original = messages.find(m => m.txHash === options.replyTo);\n if (original) {\n replyText = replyText || original.text.slice(0, 100);\n replyAuthor = replyAuthor || original.sender;\n }\n } catch {\n // Non-fatal: reply will still work, just without cached text/author\n }\n }\n\n const key = await this.getEncryptionKey(topicId);\n const encrypted = encryptMessage(text, key, {\n replyTo: options?.replyTo,\n replyText,\n replyAuthor,\n mentions: options?.mentions,\n });\n\n return this.registry.sendMessage(topicId, ethers.toUtf8Bytes(encrypted));\n }\n\n /**\n * Read and decrypt recent messages from a topic.\n */\n async readMessages(topicId: number, options?: ReadOptions): Promise<Message[]> {\n const limit = options?.limit ?? 50;\n const key = await this.getEncryptionKey(topicId);\n const filter = this.registry.filters.MessageSent(topicId);\n\n // Chunked log fetching to stay within RPC limits (e.g. Avalanche 2048 block cap)\n const CHUNK_SIZE = 2000;\n const currentBlock = await this.provider.getBlockNumber();\n const maxRange = options?.fromBlock != null ? currentBlock - options.fromBlock : 100_000;\n const startBlock = currentBlock - maxRange;\n\n const allEvents: ethers.EventLog[] = [];\n let toBlock = currentBlock;\n\n while (toBlock > startBlock && allEvents.length < limit) {\n const chunkFrom = Math.max(toBlock - CHUNK_SIZE + 1, startBlock);\n const events = await this.registry.queryFilter(filter, chunkFrom, toBlock);\n // Prepend since we're walking backwards\n allEvents.unshift(...(events as ethers.EventLog[]));\n toBlock = chunkFrom - 1;\n }\n\n const recent = allEvents.slice(-limit);\n const messages: Message[] = [];\n\n for (const log of recent) {\n const payloadStr = ethers.toUtf8String(log.args.payload);\n const parsed = decryptMessage(payloadStr, key);\n\n messages.push({\n topicId: BigInt(topicId),\n sender: log.args.sender,\n text: parsed?.text ?? '[decryption failed]',\n replyTo: parsed?.replyTo ?? null,\n mentions: parsed?.mentions ?? null,\n timestamp: log.args.timestamp,\n txHash: log.transactionHash,\n blockNumber: log.blockNumber,\n });\n }\n\n return messages;\n }\n\n /**\n * Subscribe to real-time messages on a topic.\n * Returns an unsubscribe function.\n */\n onMessage(\n topicId: number,\n callback: (msg: Message) => void\n ): () => void {\n let key: Uint8Array | null = null;\n\n // Pre-derive key, then start listening\n this.getEncryptionKey(topicId).then((k) => {\n key = k;\n });\n\n const handler = (\n tId: bigint,\n sender: string,\n payload: string,\n timestamp: bigint,\n event: ethers.EventLog\n ) => {\n if (!key) return;\n const payloadStr = ethers.toUtf8String(payload);\n const parsed = decryptMessage(payloadStr, key);\n\n callback({\n topicId: tId,\n sender,\n text: parsed?.text ?? '[decryption failed]',\n replyTo: parsed?.replyTo ?? null,\n mentions: parsed?.mentions ?? null,\n timestamp,\n txHash: event.transactionHash,\n blockNumber: event.blockNumber,\n });\n };\n\n this.registry.on(this.registry.filters.MessageSent(topicId), handler);\n return () => {\n this.registry.off(this.registry.filters.MessageSent(topicId), handler);\n };\n }\n\n // ===== NICKNAMES =====\n\n async setNickname(appId: number, nickname: string): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.setNickname(appId, nickname);\n }\n\n async getNickname(appId: number, address: string): Promise<string> {\n return this.registry.getNickname(appId, address);\n }\n\n async hasNickname(appId: number, address: string): Promise<boolean> {\n return this.registry.hasNickname(appId, address);\n }\n\n async canChangeNickname(appId: number, address: string): Promise<{ canChange: boolean; timeRemaining: bigint }> {\n const [canChange, timeRemaining] = await this.registry.canChangeNickname(appId, address);\n return { canChange, timeRemaining };\n }\n\n async clearNickname(appId: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.clearNickname(appId);\n }\n\n async setNicknameCooldown(appId: number, cooldownSeconds: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.setNicknameCooldown(appId, cooldownSeconds);\n }\n\n async getNicknameCooldown(appId: number): Promise<bigint> {\n return this.registry.appNicknameCooldown(appId);\n }\n\n // ===== TOPICS =====\n\n async createTopic(\n appId: number,\n name: string,\n description: string,\n accessLevel: AccessLevel\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.createTopic(appId, name, description, accessLevel);\n }\n\n async getTopic(topicId: number): Promise<Topic> {\n const t = await this.registry.getTopic(topicId);\n return {\n id: t.id,\n applicationId: t.applicationId,\n name: t.name,\n description: t.description,\n owner: t.owner,\n creator: t.creator,\n createdAt: t.createdAt,\n lastMessageAt: t.lastMessageAt,\n messageCount: t.messageCount,\n accessLevel: Number(t.accessLevel),\n active: t.active,\n };\n }\n\n async getApplicationTopics(appId: number): Promise<bigint[]> {\n return this.registry.getApplicationTopics(appId);\n }\n\n async getTopicCount(): Promise<number> {\n const count = await this.registry.topicCount();\n return Number(count);\n }\n\n async setTopicPermission(\n topicId: number,\n user: string,\n permission: number\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.setTopicPermission(topicId, user, permission);\n }\n\n async getTopicPermission(topicId: number, user: string): Promise<number> {\n const perm = await this.registry.getTopicPermission(topicId, user);\n return Number(perm);\n }\n\n // ===== MEMBERS =====\n\n async addMember(\n appId: number,\n address: string,\n nickname: string,\n roles: number\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.addMember(appId, address, nickname, roles);\n }\n\n async removeMember(appId: number, address: string): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.removeMember(appId, address);\n }\n\n async updateMemberRoles(\n appId: number,\n address: string,\n roles: number\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.updateMemberRoles(appId, address, roles);\n }\n\n async getMember(appId: number, address: string): Promise<Member> {\n const m = await this.registry.getMember(appId, address);\n return {\n account: m.account,\n nickname: m.nickname,\n roles: Number(m.roles),\n joinedAt: m.joinedAt,\n };\n }\n\n async isMember(appId: number, address: string): Promise<boolean> {\n return this.registry.isMember(appId, address);\n }\n\n async getApplicationMembers(appId: number): Promise<string[]> {\n return this.registry.getApplicationMembers(appId);\n }\n\n // ===== ACCESS CHECKS =====\n\n async canRead(topicId: number, address: string): Promise<boolean> {\n return this.registry.canReadTopic(topicId, address);\n }\n\n async canWrite(topicId: number, address: string): Promise<boolean> {\n return this.registry.canWriteToTopic(topicId, address);\n }\n\n // ===== APPLICATIONS =====\n\n async createApplication(\n name: string,\n description: string,\n frontendUrl: string,\n allowPublicTopicCreation: boolean\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.createApplication(name, description, frontendUrl, allowPublicTopicCreation);\n }\n\n async getApplicationCount(): Promise<number> {\n const count = await this.registry.applicationCount();\n return Number(count);\n }\n\n async updateFrontendUrl(appId: number, frontendUrl: string): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.updateApplicationFrontendUrl(appId, frontendUrl);\n }\n\n async getApplication(appId: number): Promise<Application> {\n const a = await this.registry.getApplication(appId);\n return {\n id: a.id,\n name: a.name,\n description: a.description,\n frontendUrl: a.frontendUrl,\n owner: a.owner,\n createdAt: a.createdAt,\n memberCount: Number(a.memberCount),\n topicCount: Number(a.topicCount),\n active: a.active,\n allowPublicTopicCreation: a.allowPublicTopicCreation,\n topicCreationFeeToken: a.topicCreationFeeToken,\n topicCreationFeeAmount: a.topicCreationFeeAmount,\n };\n }\n\n // ===== FEES =====\n\n async getTopicMessageFee(topicId: number): Promise<TopicMessageFee> {\n const [token, amount] = await this.registry.getTopicMessageFee(topicId);\n return { token, amount };\n }\n\n async setTopicCreationFee(\n appId: number,\n feeToken: string,\n feeAmount: bigint\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.setTopicCreationFee(appId, feeToken, feeAmount);\n }\n\n async setTopicMessageFee(\n topicId: number,\n feeToken: string,\n feeAmount: bigint\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.setTopicMessageFee(topicId, feeToken, feeAmount);\n }\n\n // ===== ECDH (Private Topics) =====\n\n /**\n * Derive ECDH keypair from wallet signature (deterministic).\n * Requires a signer capable of signing messages.\n */\n async deriveECDHFromWallet(appId: number = 1): Promise<{ publicKey: Uint8Array }> {\n if (!this.wallet) throw new Error('Wallet required');\n\n const { privateKey, publicKey } = await deriveKeypairFromSignature(\n this.wallet.address,\n (msg) => this.wallet!.signMessage(msg),\n appId\n );\n\n this.ecdhPrivateKey = privateKey;\n this.ecdhPublicKey = publicKey;\n return { publicKey };\n }\n\n /**\n * Load ECDH keypair from a hex private key (e.g. from credentials file).\n */\n loadECDHKeypair(privateKeyHex: string): void {\n const { privateKey, publicKey } = keypairFromPrivateKey(privateKeyHex);\n this.ecdhPrivateKey = privateKey;\n this.ecdhPublicKey = publicKey;\n }\n\n /**\n * Register ECDH public key on-chain.\n */\n async registerPublicKey(): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n if (!this.ecdhPublicKey) throw new Error('ECDH key not derived yet');\n\n const hasKey = await this.keyManager.hasPublicKey(this.wallet.address);\n if (hasKey) {\n throw new Error('Public key already registered on-chain');\n }\n\n return this.keyManager.registerPublicKey(this.ecdhPublicKey);\n }\n\n /**\n * Fetch and decrypt the topic symmetric key from an on-chain ECDH grant.\n */\n async fetchAndDecryptTopicKey(topicId: number): Promise<Uint8Array> {\n if (!this.ecdhPrivateKey) throw new Error('ECDH key not derived yet');\n\n const grant = await this.keyManager.getMyKey(topicId);\n const encryptedKey = ethers.getBytes(grant.encryptedKey);\n const granterPubKey = ethers.getBytes(grant.granterPublicKey);\n\n const topicKey = decryptTopicKey(encryptedKey, this.ecdhPrivateKey, granterPubKey);\n this.topicKeys.set(topicId, topicKey);\n return topicKey;\n }\n\n /**\n * Grant a user access to a private topic's symmetric key.\n */\n async grantKeyAccess(\n topicId: number,\n userAddress: string,\n topicKey: Uint8Array\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n if (!this.ecdhPrivateKey) throw new Error('ECDH key not derived yet');\n\n const hasKey = await this.keyManager.hasPublicKey(userAddress);\n if (!hasKey) {\n throw new Error(\n `User ${userAddress} has no ECDH public key registered. They must run 'keys register' first.`\n );\n }\n\n const userPubKeyBytes = ethers.getBytes(await this.keyManager.getPublicKey(userAddress));\n const encrypted = encryptTopicKeyForUser(topicKey, this.ecdhPrivateKey, userPubKeyBytes);\n return this.keyManager.grantKeyAccess(topicId, userAddress, encrypted);\n }\n\n /**\n * Store a pre-known topic key (e.g. loaded from credentials).\n */\n setTopicKey(topicId: number, key: Uint8Array): void {\n this.topicKeys.set(topicId, key);\n }\n\n /**\n * Check if an address has an ECDH public key registered on-chain.\n */\n async hasPublicKey(address: string): Promise<boolean> {\n return this.keyManager.hasPublicKey(address);\n }\n\n /**\n * Get an address's ECDH public key from chain.\n */\n async getPublicKey(address: string): Promise<Uint8Array> {\n const key = await this.keyManager.getPublicKey(address);\n return ethers.getBytes(key);\n }\n\n /**\n * Check if a user has key access for a topic.\n */\n async hasKeyAccess(topicId: number, address: string): Promise<boolean> {\n return this.keyManager.hasKeyAccess(topicId, address);\n }\n\n /**\n * Get the key grant details for a user on a topic.\n */\n async getKeyGrant(topicId: number, address: string): Promise<{\n encryptedKey: Uint8Array;\n granterPublicKey: Uint8Array;\n granter: string;\n keyVersion: bigint;\n grantedAt: bigint;\n }> {\n const g = await this.keyManager.getKeyGrant(topicId, address);\n return {\n encryptedKey: ethers.getBytes(g.encryptedKey),\n granterPublicKey: ethers.getBytes(g.granterPublicKey),\n granter: g.granter,\n keyVersion: g.keyVersion,\n grantedAt: g.grantedAt,\n };\n }\n\n /**\n * Get the current key version for a topic.\n */\n async getKeyVersion(topicId: number): Promise<bigint> {\n return this.keyManager.keyVersions(topicId);\n }\n\n /**\n * Batch grant key access to multiple users at once (max 50).\n */\n async batchGrantKeyAccess(\n topicId: number,\n users: string[],\n topicKey: Uint8Array\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n if (!this.ecdhPrivateKey) throw new Error('ECDH key not derived yet');\n\n const encryptedKeys: Uint8Array[] = [];\n for (const user of users) {\n const userPubKeyBytes = ethers.getBytes(await this.keyManager.getPublicKey(user));\n const encrypted = encryptTopicKeyForUser(topicKey, this.ecdhPrivateKey, userPubKeyBytes);\n encryptedKeys.push(encrypted);\n }\n\n return this.keyManager.batchGrantKeyAccess(topicId, users, encryptedKeys);\n }\n\n /**\n * Revoke a user's key access for a topic.\n */\n async revokeKeyAccess(topicId: number, address: string): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.keyManager.revokeKeyAccess(topicId, address);\n }\n\n /**\n * Rotate the key version for a topic. Existing grants become stale.\n */\n async rotateKey(topicId: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.keyManager.rotateKey(topicId);\n }\n\n // ===== SCHEMAS =====\n\n /**\n * Create a schema scoped to an application. Requires app admin role.\n */\n async createAppSchema(\n appId: number,\n name: string,\n description: string,\n body: string\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.schemaRegistry.createAppSchema(appId, name, description, body);\n }\n\n /**\n * Publish a new version of an existing schema.\n */\n async publishSchemaVersion(\n schemaId: number,\n body: string\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.schemaRegistry.publishSchemaVersion(schemaId, body);\n }\n\n /**\n * Deactivate a schema.\n */\n async deactivateSchema(schemaId: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.schemaRegistry.deactivateSchema(schemaId);\n }\n\n /**\n * Get schema info including application scope.\n */\n async getSchema(schemaId: number): Promise<SchemaInfo> {\n const s = await this.schemaRegistry.getSchemaWithApp(schemaId);\n return {\n id: Number(s.id),\n name: s.name,\n description: s.description,\n creator: s.creator,\n createdAt: Number(s.createdAt),\n versionCount: Number(s.versionCount),\n active: s.active,\n applicationId: Number(s.applicationId),\n };\n }\n\n /**\n * Get all schemas scoped to an application.\n */\n async getApplicationSchemas(appId: number): Promise<SchemaInfo[]> {\n const ids: bigint[] = await this.schemaRegistry.getApplicationSchemas(appId);\n const schemas: SchemaInfo[] = [];\n for (const id of ids) {\n const s = await this.getSchema(Number(id));\n schemas.push(s);\n }\n return schemas;\n }\n\n /**\n * Get schema body for a specific version.\n */\n async getSchemaBody(schemaId: number, version: number): Promise<string> {\n return this.schemaRegistry.getSchemaBody(schemaId, version);\n }\n\n /**\n * Get the schema binding for a topic.\n */\n async getTopicSchema(topicId: number): Promise<TopicSchemaBinding> {\n const s = await this.schemaRegistry.getTopicSchema(topicId);\n return {\n schemaId: Number(s.schemaId),\n version: Number(s.version),\n body: s.body,\n };\n }\n\n /**\n * Bind a schema version to a topic. Requires topic admin.\n */\n async setTopicSchema(\n topicId: number,\n schemaId: number,\n version: number\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.schemaRegistry.setTopicSchema(topicId, schemaId, version);\n }\n\n /**\n * Remove schema binding from a topic.\n */\n async clearTopicSchema(topicId: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.schemaRegistry.clearTopicSchema(topicId);\n }\n\n // ===== AGENT IDENTITY (V5) =====\n\n /**\n * Register your ERC-8004 agent identity for an application (V5).\n * Verifies ownership via ownerOf on the identity registry.\n */\n async registerAgentIdentity(appId: number, tokenId: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.registerAgentIdentity(appId, tokenId);\n }\n\n /**\n * Clear your agent identity registration for an application (V5).\n */\n async clearAgentIdentity(appId: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.clearAgentIdentity(appId);\n }\n\n /**\n * Get the registered agent token ID for a user in an application (V5/V6).\n * Returns 0 if not registered. V6 validates ownership live via ownerOf.\n */\n async getAgentTokenId(appId: number, address: string): Promise<bigint> {\n return this.registry.getAgentTokenId(appId, address);\n }\n\n /**\n * Check if a user has a registered agent identity for an application (V5/V6).\n * V6 validates ownership live — returns false if the token was transferred.\n */\n async hasAgentIdentity(appId: number, address: string): Promise<boolean> {\n return this.registry.hasAgentIdentity(appId, address);\n }\n\n // ===== ERC-8004 IDENTITY REGISTRY =====\n\n private requireIdentityRegistry(): ethers.Contract {\n if (!this.identityRegistry) {\n throw new Error('ERC-8004 Identity Registry not available on this chain. Use Base.');\n }\n return this.identityRegistry;\n }\n\n /**\n * Register as an agent on the ERC-8004 Identity Registry.\n * Optionally provide a URI for the agent's metadata.\n */\n async registerAgent(agentURI?: string): Promise<{ agentId: bigint; tx: ethers.TransactionResponse }> {\n if (!this.wallet) throw new Error('Wallet required');\n const registry = this.requireIdentityRegistry();\n\n const tx: ethers.TransactionResponse = agentURI\n ? await registry['register(string)'](agentURI)\n : await registry['register()']();\n\n const receipt = await tx.wait();\n let agentId = 0n;\n if (receipt) {\n for (const log of receipt.logs) {\n try {\n const parsed = registry.interface.parseLog(log);\n if (parsed?.name === 'Registered') {\n agentId = parsed.args.agentId;\n break;\n }\n } catch { /* skip non-matching logs */ }\n }\n }\n\n return { agentId, tx };\n }\n\n /**\n * Register as an agent with metadata entries.\n */\n async registerAgentWithMetadata(\n agentURI: string,\n metadata: Array<{ metadataKey: string; metadataValue: Uint8Array }>\n ): Promise<{ agentId: bigint; tx: ethers.TransactionResponse }> {\n if (!this.wallet) throw new Error('Wallet required');\n const registry = this.requireIdentityRegistry();\n\n const tx: ethers.TransactionResponse = await registry['register(string,(string,bytes)[])'](agentURI, metadata);\n\n const receipt = await tx.wait();\n let agentId = 0n;\n if (receipt) {\n for (const log of receipt.logs) {\n try {\n const parsed = registry.interface.parseLog(log);\n if (parsed?.name === 'Registered') {\n agentId = parsed.args.agentId;\n break;\n }\n } catch { /* skip non-matching logs */ }\n }\n }\n\n return { agentId, tx };\n }\n\n /**\n * Check if an address has an ERC-8004 agent identity NFT.\n * Defaults to the connected wallet address.\n */\n async isRegisteredAgent(address?: string): Promise<boolean> {\n const registry = this.requireIdentityRegistry();\n const addr = address ?? this.wallet?.address;\n if (!addr) throw new Error('Address required');\n\n const balance: bigint = await registry.balanceOf(addr);\n return balance > 0n;\n }\n\n /**\n * Get agent info by agent ID.\n */\n async getAgentInfo(agentId: number): Promise<{ owner: string; uri: string; wallet: string }> {\n const registry = this.requireIdentityRegistry();\n\n const [owner, uri, wallet] = await Promise.all([\n registry.ownerOf(agentId) as Promise<string>,\n registry.tokenURI(agentId) as Promise<string>,\n registry.getAgentWallet(agentId) as Promise<string>,\n ]);\n\n return { owner, uri, wallet };\n }\n\n /**\n * Set metadata for an agent.\n */\n async setAgentMetadata(\n agentId: number,\n key: string,\n value: Uint8Array\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n const registry = this.requireIdentityRegistry();\n return registry.setMetadata(agentId, key, value);\n }\n\n /**\n * Get metadata for an agent by key.\n */\n async getAgentMetadata(agentId: number, key: string): Promise<string> {\n const registry = this.requireIdentityRegistry();\n const data: string = await registry.getMetadata(agentId, key);\n return ethers.toUtf8String(data);\n }\n\n /**\n * Update the URI for an agent registration.\n */\n async setAgentURI(agentId: number, newURI: string): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n const registry = this.requireIdentityRegistry();\n return registry.setAgentURI(agentId, newURI);\n }\n\n /**\n * Look up an agent by its wallet address using the V5/V6 on-chain registry mapping.\n * V6 validates ownership live — stale registrations (transferred tokens) return 0.\n * Returns registration status, token ID, owner, URI, wallet, and parsed metadata.\n */\n async getAgentByAddress(address: string, appId: number): Promise<\n | { registered: false }\n | { registered: true; agentId: number; owner: string; uri: string; wallet: string; metadata: Record<string, unknown> | null }\n > {\n const onChainTokenId: bigint = await this.registry.getAgentTokenId(appId, address);\n if (onChainTokenId === 0n) return { registered: false };\n\n const agentId = Number(onChainTokenId);\n const { owner, uri, wallet } = await this.getAgentInfo(agentId);\n let metadata: Record<string, unknown> | null = null;\n if (uri) {\n metadata = await this.parseTokenURI(uri);\n }\n return { registered: true, agentId, owner, uri, wallet, metadata };\n }\n\n /**\n * Parse a tokenURI into JSON metadata.\n * Handles data:application/json;base64, data:application/json, HTTP(S), and ipfs:// URIs.\n */\n private async parseTokenURI(uri: string): Promise<Record<string, unknown> | null> {\n try {\n if (uri.startsWith('data:application/json;base64,')) {\n const json = atob(uri.slice('data:application/json;base64,'.length));\n return JSON.parse(json);\n }\n if (uri.startsWith('data:application/json,')) {\n return JSON.parse(decodeURIComponent(uri.slice('data:application/json,'.length)));\n }\n let url = uri;\n if (url.startsWith('ipfs://')) {\n url = 'https://ipfs.io/ipfs/' + url.slice('ipfs://'.length);\n }\n const resp = await fetch(url);\n if (!resp.ok) return null;\n return await resp.json();\n } catch {\n return null;\n }\n }\n\n // ===== DATA EXPORT =====\n\n /**\n * Look up an application ID by its name.\n */\n async getApplicationByName(name: string): Promise<number> {\n const id = await this.registry.applicationNames(name);\n return Number(id);\n }\n\n /**\n * Get schema version details including body and publish timestamp.\n */\n async getSchemaVersion(schemaId: number, version: number): Promise<{ body: string; publishedAt: bigint }> {\n const [body, publishedAt] = await this.schemaRegistry.getSchemaVersion(schemaId, version);\n return { body, publishedAt };\n }\n\n /**\n * Export member data for a user in an application.\n */\n async exportMemberData(appId: number, user: string): Promise<string> {\n const data = await this.registry.exportMemberData(appId, user);\n return ethers.hexlify(data);\n }\n\n /**\n * Export all application data.\n */\n async exportApplicationData(appId: number): Promise<string> {\n const data = await this.registry.exportApplicationData(appId);\n return ethers.hexlify(data);\n }\n\n /**\n * Export user data from the key manager for specified topics.\n */\n async exportUserData(user: string, topicIds: number[]): Promise<string> {\n const data = await this.keyManager.exportUserData(user, topicIds);\n return ethers.hexlify(data);\n }\n\n // ===== INTERNAL =====\n\n /**\n * Get the encryption key for a topic, determining the type automatically.\n */\n private async getEncryptionKey(topicId: number): Promise<Uint8Array> {\n // Check for a stored private topic key first\n const storedKey = this.topicKeys.get(topicId);\n if (storedKey) return storedKey;\n\n // Fetch topic metadata to determine access level\n const topic = await this.getTopic(topicId);\n\n if (topic.accessLevel === AccessLevel.PRIVATE) {\n // Auto-fetch if ECDH keys are loaded\n if (this.ecdhPrivateKey) {\n return this.fetchAndDecryptTopicKey(topicId);\n }\n throw new Error(\n `Topic ${topicId} is PRIVATE. Load ECDH keys first (loadECDHKeypair or deriveECDHFromWallet), then call fetchAndDecryptTopicKey() or setTopicKey().`\n );\n }\n\n // Public or public_limited: derive deterministic key\n return derivePublicTopicKey(topicId);\n }\n}\n","import type { ChainConfig, ChainName } from './types.js';\n\nexport const CHAINS: Record<ChainName, ChainConfig> = {\n baseSepolia: {\n chainId: 84532,\n name: 'Base Sepolia',\n shortName: 'Sepolia',\n rpc: 'https://sepolia.base.org',\n explorer: 'https://sepolia.basescan.org',\n registry: '0xf39b193aedC1Ec9FD6C5ccc24fBAe58ba9f52413',\n keyManager: '0x5562B553a876CBdc8AA4B3fb0687f22760F4759e',\n schemaRegistry: '0xB7eB50e9058198b99b5b2589E6D70b2d99d5440a',\n identityRegistry: '0x8004A169FB4a3325136EB29fA0ceB6D2e539a432',\n },\n base: {\n chainId: 8453,\n name: 'Base',\n shortName: 'Base',\n rpc: 'https://mainnet.base.org',\n explorer: 'https://basescan.org',\n registry: '0x5fF6BF04F1B5A78ae884D977a3C80A0D8E2072bF',\n keyManager: '0xdc302ff43a34F6aEa19426D60C9D150e0661E4f4',\n schemaRegistry: '0x5c11d2eA4470eD9025D810A21a885FE16dC987Bd',\n identityRegistry: '0x8004A169FB4a3325136EB29fA0ceB6D2e539a432',\n },\n avalanche: {\n chainId: 43114,\n name: 'Avalanche C-Chain',\n shortName: 'Avalanche',\n rpc: 'https://api.avax.network/ext/bc/C/rpc',\n explorer: 'https://snowtrace.io',\n registry: '0x3Ca2FF0bD1b3633513299EB5d3e2d63e058b0713',\n keyManager: '0x5a5ea9D408FBA984fFf6e243Dcc71ff6E00C73E4',\n schemaRegistry: '0x23D96e610E8E3DA5341a75B77F1BFF7EA9c3A62B',\n identityRegistry: '0x8004A169FB4a3325136EB29fA0ceB6D2e539a432',\n },\n};\n\nexport const CHAIN_IDS: Record<number, ChainName> = {\n 84532: 'baseSepolia',\n 8453: 'base',\n 43114: 'avalanche',\n};\n\nexport function getChain(nameOrId: ChainName | number): ChainConfig {\n if (typeof nameOrId === 'number') {\n const name = CHAIN_IDS[nameOrId];\n if (!name) throw new Error(`Unsupported chain ID: ${nameOrId}`);\n return CHAINS[name];\n }\n const chain = CHAINS[nameOrId];\n if (!chain) throw new Error(`Unsupported chain: ${nameOrId}`);\n return chain;\n}\n","export const REGISTRY_ABI = [\n // ===== READ FUNCTIONS =====\n\n // Applications\n 'function applications(uint256) view returns (uint256 id, string name, string description, string frontendUrl, address owner, uint64 createdAt, uint32 memberCount, uint32 topicCount, bool active, bool allowPublicTopicCreation)',\n 'function getApplication(uint256 appId) view returns (tuple(uint256 id, string name, string description, string frontendUrl, address owner, uint64 createdAt, uint32 memberCount, uint32 topicCount, bool active, bool allowPublicTopicCreation, address topicCreationFeeToken, uint256 topicCreationFeeAmount))',\n 'function applicationCount() view returns (uint256)',\n 'function applicationNames(string) view returns (uint256)',\n\n // Topics\n 'function topics(uint256) view returns (uint256 id, uint256 applicationId, string name, string description, address owner, address creator, uint64 createdAt, uint64 lastMessageAt, uint256 messageCount, uint8 accessLevel, bool active)',\n 'function getTopic(uint256 topicId) view returns (tuple(uint256 id, uint256 applicationId, string name, string description, address owner, address creator, uint64 createdAt, uint64 lastMessageAt, uint256 messageCount, uint8 accessLevel, bool active))',\n 'function topicCount() view returns (uint256)',\n 'function getApplicationTopics(uint256 appId) view returns (uint256[])',\n\n // Members\n 'function members(uint256 appId, address user) view returns (address account, string nickname, uint8 roles, uint64 joinedAt)',\n 'function getMember(uint256 appId, address account) view returns (tuple(address account, string nickname, uint8 roles, uint64 joinedAt))',\n 'function isMember(uint256 appId, address account) view returns (bool)',\n 'function getApplicationMembers(uint256 appId) view returns (address[])',\n\n // Permissions\n 'function canReadTopic(uint256 topicId, address user) view returns (bool)',\n 'function canWriteToTopic(uint256 topicId, address user) view returns (bool)',\n 'function getTopicPermission(uint256 topicId, address user) view returns (uint8)',\n 'function topicPermissions(uint256, address) view returns (uint8)',\n\n // Nicknames\n 'function getNickname(uint256 appId, address user) view returns (string)',\n 'function hasNickname(uint256 appId, address user) view returns (bool)',\n 'function canChangeNickname(uint256 appId, address user) view returns (bool canChange, uint256 timeRemaining)',\n 'function appNicknameCooldown(uint256 appId) view returns (uint256)',\n\n // Fees\n 'function getTopicMessageFee(uint256 topicId) view returns (address token, uint256 amount)',\n\n // ===== WRITE FUNCTIONS =====\n\n // Applications\n 'function createApplication(string name, string description, string frontendUrl, bool allowPublicTopicCreation) returns (uint256)',\n 'function updateApplicationFrontendUrl(uint256 appId, string frontendUrl)',\n\n // Topics\n 'function createTopic(uint256 appId, string name, string description, uint8 accessLevel) returns (uint256)',\n 'function setTopicPermission(uint256 topicId, address user, uint8 permission)',\n\n // Members\n 'function addMember(uint256 appId, address member, string nickname, uint8 roles)',\n 'function removeMember(uint256 appId, address member)',\n 'function updateMemberRoles(uint256 appId, address member, uint8 roles)',\n 'function updateMemberNickname(uint256 appId, string nickname)',\n\n // Nicknames (V3)\n 'function setNickname(uint256 appId, string nickname)',\n 'function clearNickname(uint256 appId)',\n 'function setNicknameCooldown(uint256 appId, uint256 cooldownSeconds)',\n\n // Messaging\n 'function sendMessage(uint256 topicId, bytes payload)',\n\n // Fees\n 'function setTopicCreationFee(uint256 appId, address feeTokenAddr, uint256 feeAmount)',\n 'function setTopicMessageFee(uint256 topicId, address feeTokenAddr, uint256 feeAmount)',\n\n // ===== EVENTS =====\n 'event ApplicationCreated(uint256 indexed applicationId, string name, address indexed owner)',\n 'event TopicCreated(uint256 indexed topicId, uint256 indexed applicationId, string name, address indexed creator, uint8 accessLevel)',\n 'event MemberAdded(uint256 indexed applicationId, address indexed member, string nickname, uint8 roles)',\n 'event MemberRemoved(uint256 indexed applicationId, address indexed member)',\n 'event MemberRolesUpdated(uint256 indexed applicationId, address indexed member, uint8 roles)',\n 'event NicknameUpdated(uint256 indexed applicationId, address indexed member, string nickname)',\n 'event UserNicknameSet(uint256 indexed applicationId, address indexed user, string nickname)',\n 'event TopicPermissionSet(uint256 indexed topicId, address indexed user, uint8 permission)',\n 'event MessageSent(uint256 indexed topicId, address indexed sender, bytes payload, uint256 timestamp)',\n 'event TopicMessageFeeUpdated(uint256 indexed topicId, address token, uint256 amount)',\n\n // Agent identity (V5)\n 'function registerAgentIdentity(uint256 appId, uint256 tokenId)',\n 'function clearAgentIdentity(uint256 appId)',\n 'function getAgentTokenId(uint256 appId, address user) view returns (uint256)',\n 'function hasAgentIdentity(uint256 appId, address user) view returns (bool)',\n 'event AgentIdentityRegistered(uint256 indexed applicationId, address indexed user, uint256 tokenId)',\n 'event AgentIdentityCleared(uint256 indexed applicationId, address indexed user)',\n\n // Data export\n 'function exportMemberData(uint256 appId, address user) view returns (bytes)',\n 'function exportApplicationData(uint256 appId) view returns (bytes)',\n] as const;\n\nexport const SCHEMA_REGISTRY_ABI = [\n // ===== READ FUNCTIONS =====\n\n // Schema queries\n 'function schemaCount() view returns (uint256)',\n 'function getSchema(uint256 schemaId) view returns (uint256 id, string name, string description, address creator, uint64 createdAt, uint256 versionCount, bool active)',\n 'function getSchemaWithApp(uint256 schemaId) view returns (uint256 id, string name, string description, address creator, uint64 createdAt, uint256 versionCount, bool active, uint256 applicationId)',\n 'function getSchemaBody(uint256 schemaId, uint256 version) view returns (string)',\n 'function getSchemaVersion(uint256 schemaId, uint256 version) view returns (string body, uint64 publishedAt)',\n 'function schemaApplicationId(uint256 schemaId) view returns (uint256)',\n\n // App-scoped queries (V2)\n 'function getApplicationSchemas(uint256 applicationId) view returns (uint256[])',\n 'function getApplicationSchemaCount(uint256 applicationId) view returns (uint256)',\n\n // Topic binding\n 'function getTopicSchema(uint256 topicId) view returns (uint256 schemaId, uint256 version, string body)',\n\n // Version\n 'function contractVersion() view returns (string)',\n\n // ===== WRITE FUNCTIONS =====\n\n // Schema creation (V2 app-scoped)\n 'function createAppSchema(uint256 applicationId, string name, string description, string body) returns (uint256)',\n 'function publishSchemaVersion(uint256 schemaId, string body) returns (uint256)',\n 'function deactivateSchema(uint256 schemaId)',\n\n // Topic binding\n 'function setTopicSchema(uint256 topicId, uint256 schemaId, uint256 version)',\n 'function clearTopicSchema(uint256 topicId)',\n\n // ===== EVENTS =====\n 'event AppSchemaCreated(uint256 indexed schemaId, uint256 indexed applicationId, string name, address indexed creator)',\n 'event SchemaVersionPublished(uint256 indexed schemaId, uint256 version)',\n 'event SchemaDeactivated(uint256 indexed schemaId)',\n 'event TopicSchemaSet(uint256 indexed topicId, uint256 indexed schemaId, uint256 version)',\n 'event TopicSchemaCleared(uint256 indexed topicId)',\n 'event SchemaAssignedToApp(uint256 indexed schemaId, uint256 indexed applicationId)',\n] as const;\n\nexport const IDENTITY_REGISTRY_ABI = [\n 'function register() returns (uint256)',\n 'function register(string agentURI) returns (uint256)',\n 'function register(string agentURI, tuple(string metadataKey, bytes metadataValue)[] metadata) returns (uint256)',\n 'function getMetadata(uint256 agentId, string metadataKey) view returns (bytes)',\n 'function setMetadata(uint256 agentId, string metadataKey, bytes metadataValue)',\n 'function setAgentURI(uint256 agentId, string newURI)',\n 'function getAgentWallet(uint256 agentId) view returns (address)',\n 'function setAgentWallet(uint256 agentId, address newWallet, uint256 deadline, bytes signature)',\n 'function unsetAgentWallet(uint256 agentId)',\n 'function ownerOf(uint256 tokenId) view returns (address)',\n 'function balanceOf(address owner) view returns (uint256)',\n 'function tokenOfOwnerByIndex(address owner, uint256 index) view returns (uint256)',\n 'function tokenURI(uint256 tokenId) view returns (string)',\n 'function isAuthorizedOrOwner(address spender, uint256 agentId) view returns (bool)',\n 'function getVersion() pure returns (string)',\n 'event Registered(uint256 indexed agentId, string agentURI, address indexed owner)',\n] as const;\n\nexport const KEY_MANAGER_ABI = [\n // ===== READ FUNCTIONS =====\n 'function hasPublicKey(address user) view returns (bool)',\n 'function getPublicKey(address user) view returns (bytes)',\n 'function publicKeys(address) view returns (bytes)',\n 'function hasKeyAccess(uint256 topicId, address user) view returns (bool)',\n 'function getMyKey(uint256 topicId) view returns (bytes encryptedKey, bytes granterPublicKey, address granter, uint256 keyVersion, uint256 currentVersion)',\n 'function getKeyGrant(uint256 topicId, address user) view returns (tuple(bytes encryptedKey, bytes granterPublicKey, address granter, uint256 keyVersion, uint64 grantedAt))',\n 'function keyVersions(uint256 topicId) view returns (uint256)',\n\n // ===== WRITE FUNCTIONS =====\n 'function registerPublicKey(bytes publicKey)',\n 'function grantKeyAccess(uint256 topicId, address user, bytes encryptedKey)',\n 'function batchGrantKeyAccess(uint256 topicId, address[] users, bytes[] encryptedKeys)',\n 'function revokeKeyAccess(uint256 topicId, address user)',\n 'function rotateKey(uint256 topicId)',\n\n // Data export\n 'function exportUserData(address user, uint256[] topicIds) view returns (bytes)',\n\n // ===== EVENTS =====\n 'event PublicKeyRegistered(address indexed user, bytes publicKey)',\n 'event PublicKeyUpdated(address indexed user, bytes publicKey)',\n 'event KeyAccessGranted(uint256 indexed topicId, address indexed user, address indexed granter, uint256 version)',\n 'event KeyAccessRevoked(uint256 indexed topicId, address indexed user)',\n 'event KeyRotated(uint256 indexed topicId, uint256 newVersion)',\n] as const;\n","import { pbkdf2 } from '@noble/hashes/pbkdf2';\nimport { sha256 } from '@noble/hashes/sha256';\nimport { gcm } from '@noble/ciphers/aes';\nimport { randomBytes } from '@noble/hashes/utils';\nimport {\n PUBLIC_KEY_MATERIAL_PREFIX,\n SALT_PREFIX,\n PBKDF2_ITERATIONS,\n} from '../constants.js';\nimport type { EncryptedPayload, MessageContent } from '../types.js';\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\n/**\n * Derive AES-256 key for a public/public_limited topic.\n * Uses PBKDF2 with SHA-256, matching the web frontend exactly.\n */\nexport function derivePublicTopicKey(topicId: number | bigint): Uint8Array {\n const keyMaterial = PUBLIC_KEY_MATERIAL_PREFIX + topicId;\n const salt = encoder.encode(SALT_PREFIX + topicId);\n return pbkdf2(sha256, keyMaterial, salt, { c: PBKDF2_ITERATIONS, dkLen: 32 });\n}\n\n/**\n * Derive AES-256 key from arbitrary passphrase (for private topics with manual passphrase).\n */\nexport function deriveKeyFromPassphrase(passphrase: string, topicId: number | bigint): Uint8Array {\n const salt = encoder.encode(SALT_PREFIX + topicId);\n return pbkdf2(sha256, passphrase, salt, { c: PBKDF2_ITERATIONS, dkLen: 32 });\n}\n\n/**\n * Encrypt a message payload with AES-256-GCM.\n * Output format: `{ e: true, v: 2, iv: base64, ct: base64 }`\n * The ciphertext includes the GCM auth tag (last 16 bytes).\n */\nexport function encrypt(plaintext: string, key: Uint8Array): string {\n const iv = randomBytes(12);\n const aes = gcm(key, iv);\n const ciphertext = aes.encrypt(encoder.encode(plaintext));\n\n const payload: EncryptedPayload = {\n e: true,\n v: 2,\n iv: toBase64(iv),\n ct: toBase64(ciphertext),\n };\n return JSON.stringify(payload);\n}\n\n/**\n * Decrypt a message payload. Handles both v1 and v2 formats.\n * Returns the decrypted string or null on failure.\n */\nexport function decrypt(jsonStr: string, key: Uint8Array): string | null {\n try {\n const data = JSON.parse(jsonStr) as EncryptedPayload;\n if (!data.e) {\n // Not encrypted — return raw for caller to parse\n return jsonStr;\n }\n\n const iv = fromBase64(data.iv);\n const ct = fromBase64(data.ct);\n\n const aes = gcm(key, iv);\n const decrypted = aes.decrypt(ct);\n return decoder.decode(decrypted);\n } catch {\n return null;\n }\n}\n\n/**\n * Encrypt a structured message (text + optional replyTo/mentions).\n */\nexport function encryptMessage(\n text: string,\n key: Uint8Array,\n options?: { replyTo?: string; replyText?: string; replyAuthor?: string; mentions?: string[] }\n): string {\n const content: MessageContent = { text };\n if (options?.replyTo) content.replyTo = options.replyTo;\n if (options?.replyText) content.replyText = options.replyText;\n if (options?.replyAuthor) content.replyAuthor = options.replyAuthor;\n if (options?.mentions) content.mentions = options.mentions;\n return encrypt(JSON.stringify(content), key);\n}\n\n/**\n * Decrypt and parse a message payload into structured content.\n */\nexport function decryptMessage(\n jsonStr: string,\n key: Uint8Array\n): { text: string; replyTo: string | null; replyText: string | null; replyAuthor: string | null; mentions: string[] | null } | null {\n const decrypted = decrypt(jsonStr, key);\n if (!decrypted) return null;\n\n try {\n const content = JSON.parse(decrypted);\n if (typeof content === 'object' && content.text) {\n return {\n text: content.text,\n replyTo: content.replyTo || null,\n replyText: content.replyText || null,\n replyAuthor: content.replyAuthor || null,\n mentions: content.mentions || null,\n };\n }\n // Plain text string was JSON-stringified\n return { text: decrypted, replyTo: null, replyText: null, replyAuthor: null, mentions: null };\n } catch {\n // Raw string, not JSON\n return { text: decrypted, replyTo: null, replyText: null, replyAuthor: null, mentions: null };\n }\n}\n\n// ===== Base64 helpers (isomorphic) =====\n\nfunction toBase64(bytes: Uint8Array): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(bytes).toString('base64');\n }\n return btoa(String.fromCharCode(...bytes));\n}\n\nfunction fromBase64(str: string): Uint8Array {\n if (typeof Buffer !== 'undefined') {\n return new Uint8Array(Buffer.from(str, 'base64'));\n }\n return Uint8Array.from(atob(str), (c) => c.charCodeAt(0));\n}\n","// ===== Enums =====\n\nexport enum AccessLevel {\n PUBLIC = 0,\n PUBLIC_LIMITED = 1,\n PRIVATE = 2,\n}\n\nexport enum Permission {\n NONE = 0,\n READ = 1,\n WRITE = 2,\n READ_WRITE = 3,\n ADMIN = 4,\n}\n\nexport enum Role {\n MEMBER = 1,\n SUPPORT_MANAGER = 2,\n TOPIC_MANAGER = 4,\n ADMIN = 8,\n OWNER_DELEGATE = 16,\n}\n\n// ===== Chain types =====\n\nexport interface ChainConfig {\n chainId: number;\n name: string;\n shortName: string;\n rpc: string;\n explorer: string;\n registry: string;\n keyManager: string;\n schemaRegistry: string;\n identityRegistry?: string;\n}\n\nexport type ChainName = 'base' | 'avalanche' | 'baseSepolia';\n\n// ===== Contract return types =====\n\nexport interface Application {\n id: bigint;\n name: string;\n description: string;\n frontendUrl: string;\n owner: string;\n createdAt: bigint;\n memberCount: number;\n topicCount: number;\n active: boolean;\n allowPublicTopicCreation: boolean;\n topicCreationFeeToken?: string;\n topicCreationFeeAmount?: bigint;\n}\n\nexport interface Topic {\n id: bigint;\n applicationId: bigint;\n name: string;\n description: string;\n owner: string;\n creator: string;\n createdAt: bigint;\n lastMessageAt: bigint;\n messageCount: bigint;\n accessLevel: number;\n active: boolean;\n}\n\nexport interface Member {\n account: string;\n nickname: string;\n roles: number;\n joinedAt: bigint;\n}\n\nexport interface KeyGrant {\n encryptedKey: Uint8Array;\n granterPublicKey: Uint8Array;\n granter: string;\n keyVersion: bigint;\n grantedAt?: bigint;\n currentVersion?: bigint;\n}\n\n// ===== Message types =====\n\nexport interface MessageContent {\n text: string;\n replyTo?: string;\n replyText?: string;\n replyAuthor?: string;\n mentions?: string[];\n}\n\nexport interface EncryptedPayload {\n e: boolean;\n v: number;\n iv: string;\n ct: string;\n}\n\nexport interface Message {\n topicId: bigint;\n sender: string;\n text: string;\n replyTo: string | null;\n mentions: string[] | null;\n timestamp: bigint;\n txHash: string;\n blockNumber: number;\n}\n\nexport interface TopicMessageFee {\n token: string;\n amount: bigint;\n}\n\nexport interface SchemaInfo {\n id: number;\n name: string;\n description: string;\n creator: string;\n createdAt: number;\n versionCount: number;\n active: boolean;\n applicationId: number;\n}\n\nexport interface TopicSchemaBinding {\n schemaId: number;\n version: number;\n body: string;\n}\n\n// ===== Client options =====\n\nexport interface ClawtennaOptions {\n chain?: ChainName;\n chainId?: number;\n rpcUrl?: string;\n privateKey?: string;\n registryAddress?: string;\n keyManagerAddress?: string;\n schemaRegistryAddress?: string;\n}\n\nexport interface ReadOptions {\n limit?: number;\n fromBlock?: number;\n}\n\nexport interface SendOptions {\n replyTo?: string;\n replyText?: string;\n replyAuthor?: string;\n mentions?: string[];\n}\n\n// ===== Credentials =====\n\nexport interface Credentials {\n version: 2;\n wallet: {\n address: string;\n privateKey: string;\n };\n chains: Record<string, CredentialChain>;\n}\n\nexport interface CredentialChain {\n name: string;\n ecdh: {\n privateKey: string;\n publicKey: string;\n registered: boolean;\n } | null;\n apps: Record<string, CredentialApp>;\n}\n\nexport interface CredentialApp {\n name: string;\n nickname: string;\n agentTokenId: number | null;\n topicKeys: Record<string, string>;\n}\n\n// Legacy v1 format for migration\nexport interface CredentialsV1 {\n wallet: {\n address: string;\n privateKey: string;\n };\n apps: Record<string, {\n name: string;\n nickname: string;\n ecdh: {\n privateKey: string;\n publicKey: string;\n registeredOnChain: boolean;\n topicKeys: Record<string, string>;\n } | null;\n }>;\n}\n","import { AccessLevel, Permission, Role } from './types.js';\n\n// Re-export enums as individual constants for convenience\nexport const ACCESS_PUBLIC = AccessLevel.PUBLIC;\nexport const ACCESS_PUBLIC_LIMITED = AccessLevel.PUBLIC_LIMITED;\nexport const ACCESS_PRIVATE = AccessLevel.PRIVATE;\n\nexport const PERMISSION_NONE = Permission.NONE;\nexport const PERMISSION_READ = Permission.READ;\nexport const PERMISSION_WRITE = Permission.WRITE;\nexport const PERMISSION_READ_WRITE = Permission.READ_WRITE;\nexport const PERMISSION_ADMIN = Permission.ADMIN;\n\nexport const ROLE_MEMBER = Role.MEMBER;\nexport const ROLE_SUPPORT_MANAGER = Role.SUPPORT_MANAGER;\nexport const ROLE_TOPIC_MANAGER = Role.TOPIC_MANAGER;\nexport const ROLE_ADMIN = Role.ADMIN;\nexport const ROLE_OWNER_DELEGATE = Role.OWNER_DELEGATE;\n\n// Encryption constants\nexport const PUBLIC_KEY_MATERIAL_PREFIX = 'antenna-public-topic-';\nexport const SALT_PREFIX = 'antenna-v2-salt-';\nexport const PBKDF2_ITERATIONS = 100_000;\nexport const ECDH_HKDF_SALT = 'antenna-ecdh-v1';\nexport const ECDH_HKDF_INFO = 'topic-key-encryption';\nexport const ECDH_DERIVATION_MESSAGE = (address: string, appId: number) =>\n `Clawntenna ECDH Key Derivation\\n\\nThis signature generates your encryption key.\\nIt never leaves your device.\\n\\nWallet: ${address}\\nApp: ${appId}\\nChain: Base (8453)`;\n","import { secp256k1 } from '@noble/curves/secp256k1';\nimport { hkdf } from '@noble/hashes/hkdf';\nimport { sha256 } from '@noble/hashes/sha256';\nimport { gcm } from '@noble/ciphers/aes';\nimport { randomBytes } from '@noble/hashes/utils';\nimport { ECDH_HKDF_SALT, ECDH_HKDF_INFO, ECDH_DERIVATION_MESSAGE } from '../constants.js';\n\nconst encoder = new TextEncoder();\n\n/**\n * Derive an ECDH keypair deterministically from a wallet signature.\n * This produces the same keypair as the web frontend for the same wallet + app.\n */\nexport async function deriveKeypairFromSignature(\n walletAddress: string,\n signMessage: (message: string) => Promise<string>,\n appId: number = 1\n): Promise<{ privateKey: Uint8Array; publicKey: Uint8Array }> {\n const message = ECDH_DERIVATION_MESSAGE(walletAddress, appId);\n const signature = await signMessage(message);\n\n // Hash the signature string (as UTF-8 bytes) to get private key\n const sigBytes = encoder.encode(signature);\n const hashBuffer = sha256(sigBytes);\n const privateKey = new Uint8Array(hashBuffer);\n\n // Derive compressed public key\n const publicKey = secp256k1.getPublicKey(privateKey, true);\n\n return { privateKey, publicKey };\n}\n\n/**\n * Derive ECDH keypair from a raw private key (e.g. from stored credentials).\n */\nexport function keypairFromPrivateKey(privateKeyHex: string): {\n privateKey: Uint8Array;\n publicKey: Uint8Array;\n} {\n const cleaned = privateKeyHex.startsWith('0x') ? privateKeyHex.slice(2) : privateKeyHex;\n const privateKey = hexToBytes(cleaned);\n const publicKey = secp256k1.getPublicKey(privateKey, true);\n return { privateKey, publicKey };\n}\n\n/**\n * Compute ECDH shared secret (x-coordinate of shared point).\n */\nexport function computeSharedSecret(\n ourPrivateKey: Uint8Array,\n theirPublicKey: Uint8Array\n): Uint8Array {\n const sharedPoint = secp256k1.getSharedSecret(ourPrivateKey, theirPublicKey);\n // Return x-coordinate only (skip the 0x04 prefix byte)\n return sharedPoint.slice(1, 33);\n}\n\n/**\n * Derive AES-256 key from an ECDH shared secret using HKDF.\n * Matches the web frontend: salt='antenna-ecdh-v1', info='topic-key-encryption'.\n */\nexport function deriveAESKeyFromSecret(\n sharedSecret: Uint8Array,\n info: string = ECDH_HKDF_INFO\n): Uint8Array {\n return hkdf(sha256, sharedSecret, encoder.encode(ECDH_HKDF_SALT), info, 32);\n}\n\n/**\n * Encrypt a topic symmetric key for a recipient using ECDH.\n * Returns IV (12 bytes) + ciphertext (includes GCM auth tag).\n */\nexport function encryptTopicKeyForUser(\n topicKey: Uint8Array,\n ourPrivateKey: Uint8Array,\n recipientPublicKey: Uint8Array\n): Uint8Array {\n const shared = computeSharedSecret(ourPrivateKey, recipientPublicKey);\n const aesKey = deriveAESKeyFromSecret(shared);\n const iv = randomBytes(12);\n const aes = gcm(aesKey, iv);\n const ciphertext = aes.encrypt(topicKey);\n\n // Combine: IV + ciphertext\n const result = new Uint8Array(iv.length + ciphertext.length);\n result.set(iv);\n result.set(ciphertext, iv.length);\n return result;\n}\n\n/**\n * Decrypt a topic symmetric key received via ECDH grant.\n * Input format: IV (12 bytes) + ciphertext (includes GCM auth tag).\n */\nexport function decryptTopicKey(\n encryptedKey: Uint8Array,\n ourPrivateKey: Uint8Array,\n granterPublicKey: Uint8Array\n): Uint8Array {\n const shared = computeSharedSecret(ourPrivateKey, granterPublicKey);\n const aesKey = deriveAESKeyFromSecret(shared);\n const iv = encryptedKey.slice(0, 12);\n const ciphertext = encryptedKey.slice(12);\n const aes = gcm(aesKey, iv);\n return aes.decrypt(ciphertext);\n}\n\n// ===== Hex helpers =====\n\nexport function bytesToHex(bytes: Uint8Array): string {\n return '0x' + Array.from(bytes).map((b) => b.toString(16).padStart(2, '0')).join('');\n}\n\nexport function hexToBytes(hex: string): Uint8Array {\n const cleaned = hex.startsWith('0x') ? hex.slice(2) : hex;\n return new Uint8Array(cleaned.match(/.{1,2}/g)!.map((b) => parseInt(b, 16)));\n}\n"],"mappings":"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","names":["AccessLevel","Permission","Role","import_sha256","import_aes","import_utils","encoder"]}
|
package/dist/index.js
CHANGED
|
@@ -468,14 +468,23 @@ var Clawntenna = class {
|
|
|
468
468
|
*/
|
|
469
469
|
async readMessages(topicId, options) {
|
|
470
470
|
const limit = options?.limit ?? 50;
|
|
471
|
-
const fromBlock = options?.fromBlock ?? -1e5;
|
|
472
471
|
const key = await this.getEncryptionKey(topicId);
|
|
473
472
|
const filter = this.registry.filters.MessageSent(topicId);
|
|
474
|
-
const
|
|
473
|
+
const CHUNK_SIZE = 2e3;
|
|
474
|
+
const currentBlock = await this.provider.getBlockNumber();
|
|
475
|
+
const maxRange = options?.fromBlock != null ? currentBlock - options.fromBlock : 1e5;
|
|
476
|
+
const startBlock = currentBlock - maxRange;
|
|
477
|
+
const allEvents = [];
|
|
478
|
+
let toBlock = currentBlock;
|
|
479
|
+
while (toBlock > startBlock && allEvents.length < limit) {
|
|
480
|
+
const chunkFrom = Math.max(toBlock - CHUNK_SIZE + 1, startBlock);
|
|
481
|
+
const events = await this.registry.queryFilter(filter, chunkFrom, toBlock);
|
|
482
|
+
allEvents.unshift(...events);
|
|
483
|
+
toBlock = chunkFrom - 1;
|
|
484
|
+
}
|
|
485
|
+
const recent = allEvents.slice(-limit);
|
|
475
486
|
const messages = [];
|
|
476
|
-
const
|
|
477
|
-
for (const event of recent) {
|
|
478
|
-
const log = event;
|
|
487
|
+
for (const log of recent) {
|
|
479
488
|
const payloadStr = ethers.toUtf8String(log.args.payload);
|
|
480
489
|
const parsed = decryptMessage(payloadStr, key);
|
|
481
490
|
messages.push({
|
|
@@ -714,6 +723,12 @@ var Clawntenna = class {
|
|
|
714
723
|
async grantKeyAccess(topicId, userAddress, topicKey) {
|
|
715
724
|
if (!this.wallet) throw new Error("Wallet required");
|
|
716
725
|
if (!this.ecdhPrivateKey) throw new Error("ECDH key not derived yet");
|
|
726
|
+
const hasKey = await this.keyManager.hasPublicKey(userAddress);
|
|
727
|
+
if (!hasKey) {
|
|
728
|
+
throw new Error(
|
|
729
|
+
`User ${userAddress} has no ECDH public key registered. They must run 'keys register' first.`
|
|
730
|
+
);
|
|
731
|
+
}
|
|
717
732
|
const userPubKeyBytes = ethers.getBytes(await this.keyManager.getPublicKey(userAddress));
|
|
718
733
|
const encrypted = encryptTopicKeyForUser(topicKey, this.ecdhPrivateKey, userPubKeyBytes);
|
|
719
734
|
return this.keyManager.grantKeyAccess(topicId, userAddress, encrypted);
|
|
@@ -1087,8 +1102,11 @@ var Clawntenna = class {
|
|
|
1087
1102
|
if (storedKey) return storedKey;
|
|
1088
1103
|
const topic = await this.getTopic(topicId);
|
|
1089
1104
|
if (topic.accessLevel === 2 /* PRIVATE */) {
|
|
1105
|
+
if (this.ecdhPrivateKey) {
|
|
1106
|
+
return this.fetchAndDecryptTopicKey(topicId);
|
|
1107
|
+
}
|
|
1090
1108
|
throw new Error(
|
|
1091
|
-
`Topic ${topicId} is PRIVATE.
|
|
1109
|
+
`Topic ${topicId} is PRIVATE. Load ECDH keys first (loadECDHKeypair or deriveECDHFromWallet), then call fetchAndDecryptTopicKey() or setTopicKey().`
|
|
1092
1110
|
);
|
|
1093
1111
|
}
|
|
1094
1112
|
return derivePublicTopicKey(topicId);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts","../src/chains.ts","../src/contracts.ts","../src/crypto/encrypt.ts","../src/types.ts","../src/constants.ts","../src/crypto/ecdh.ts"],"sourcesContent":["import { ethers } from 'ethers';\nimport { CHAINS } from './chains.js';\nimport { REGISTRY_ABI, KEY_MANAGER_ABI, SCHEMA_REGISTRY_ABI, IDENTITY_REGISTRY_ABI } from './contracts.js';\nimport {\n derivePublicTopicKey,\n deriveKeyFromPassphrase,\n encryptMessage,\n decryptMessage,\n} from './crypto/encrypt.js';\nimport {\n deriveKeypairFromSignature,\n keypairFromPrivateKey,\n computeSharedSecret,\n deriveAESKeyFromSecret,\n encryptTopicKeyForUser,\n decryptTopicKey,\n bytesToHex,\n hexToBytes,\n} from './crypto/ecdh.js';\nimport { AccessLevel } from './types.js';\nimport type {\n ClawtennaOptions,\n ReadOptions,\n SendOptions,\n Message,\n Topic,\n Application,\n Member,\n TopicMessageFee,\n SchemaInfo,\n TopicSchemaBinding,\n ChainName,\n} from './types.js';\n\nexport class Clawntenna {\n readonly provider: ethers.JsonRpcProvider;\n readonly wallet: ethers.Wallet | null;\n readonly registry: ethers.Contract;\n readonly keyManager: ethers.Contract;\n readonly schemaRegistry: ethers.Contract;\n readonly identityRegistry: ethers.Contract | null;\n readonly chainName: ChainName;\n\n // In-memory ECDH state\n private ecdhPrivateKey: Uint8Array | null = null;\n private ecdhPublicKey: Uint8Array | null = null;\n private topicKeys: Map<number, Uint8Array> = new Map();\n\n constructor(options: ClawtennaOptions = {}) {\n const chainName = options.chain ?? 'base';\n const chain = CHAINS[chainName];\n if (!chain) throw new Error(`Unsupported chain: ${chainName}`);\n this.chainName = chainName;\n\n const rpcUrl = options.rpcUrl ?? chain.rpc;\n this.provider = new ethers.JsonRpcProvider(rpcUrl);\n\n const registryAddr = options.registryAddress ?? chain.registry;\n const keyManagerAddr = options.keyManagerAddress ?? chain.keyManager;\n const schemaRegistryAddr = options.schemaRegistryAddress ?? chain.schemaRegistry;\n\n if (options.privateKey) {\n this.wallet = new ethers.Wallet(options.privateKey, this.provider);\n this.registry = new ethers.Contract(registryAddr, REGISTRY_ABI, this.wallet);\n this.keyManager = new ethers.Contract(keyManagerAddr, KEY_MANAGER_ABI, this.wallet);\n this.schemaRegistry = new ethers.Contract(schemaRegistryAddr, SCHEMA_REGISTRY_ABI, this.wallet);\n this.identityRegistry = chain.identityRegistry\n ? new ethers.Contract(chain.identityRegistry, IDENTITY_REGISTRY_ABI, this.wallet)\n : null;\n } else {\n this.wallet = null;\n this.registry = new ethers.Contract(registryAddr, REGISTRY_ABI, this.provider);\n this.keyManager = new ethers.Contract(keyManagerAddr, KEY_MANAGER_ABI, this.provider);\n this.schemaRegistry = new ethers.Contract(schemaRegistryAddr, SCHEMA_REGISTRY_ABI, this.provider);\n this.identityRegistry = chain.identityRegistry\n ? new ethers.Contract(chain.identityRegistry, IDENTITY_REGISTRY_ABI, this.provider)\n : null;\n }\n }\n\n get address(): string | null {\n return this.wallet?.address ?? null;\n }\n\n // ===== MESSAGING =====\n\n /**\n * Send an encrypted message to a topic.\n * Automatically determines encryption key based on topic access level.\n */\n async sendMessage(topicId: number, text: string, options?: SendOptions): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required to send messages');\n\n let replyText = options?.replyText;\n let replyAuthor = options?.replyAuthor;\n\n // Auto-resolve reply metadata if replyTo is set but text/author aren't\n if (options?.replyTo && (!replyText || !replyAuthor)) {\n try {\n const messages = await this.readMessages(topicId, { limit: 50 });\n const original = messages.find(m => m.txHash === options.replyTo);\n if (original) {\n replyText = replyText || original.text.slice(0, 100);\n replyAuthor = replyAuthor || original.sender;\n }\n } catch {\n // Non-fatal: reply will still work, just without cached text/author\n }\n }\n\n const key = await this.getEncryptionKey(topicId);\n const encrypted = encryptMessage(text, key, {\n replyTo: options?.replyTo,\n replyText,\n replyAuthor,\n mentions: options?.mentions,\n });\n\n return this.registry.sendMessage(topicId, ethers.toUtf8Bytes(encrypted));\n }\n\n /**\n * Read and decrypt recent messages from a topic.\n */\n async readMessages(topicId: number, options?: ReadOptions): Promise<Message[]> {\n const limit = options?.limit ?? 50;\n const fromBlock = options?.fromBlock ?? -100_000;\n\n const key = await this.getEncryptionKey(topicId);\n const filter = this.registry.filters.MessageSent(topicId);\n const events = await this.registry.queryFilter(filter, fromBlock);\n\n const messages: Message[] = [];\n const recent = events.slice(-limit);\n\n for (const event of recent) {\n const log = event as ethers.EventLog;\n const payloadStr = ethers.toUtf8String(log.args.payload);\n const parsed = decryptMessage(payloadStr, key);\n\n messages.push({\n topicId: BigInt(topicId),\n sender: log.args.sender,\n text: parsed?.text ?? '[decryption failed]',\n replyTo: parsed?.replyTo ?? null,\n mentions: parsed?.mentions ?? null,\n timestamp: log.args.timestamp,\n txHash: log.transactionHash,\n blockNumber: log.blockNumber,\n });\n }\n\n return messages;\n }\n\n /**\n * Subscribe to real-time messages on a topic.\n * Returns an unsubscribe function.\n */\n onMessage(\n topicId: number,\n callback: (msg: Message) => void\n ): () => void {\n let key: Uint8Array | null = null;\n\n // Pre-derive key, then start listening\n this.getEncryptionKey(topicId).then((k) => {\n key = k;\n });\n\n const handler = (\n tId: bigint,\n sender: string,\n payload: string,\n timestamp: bigint,\n event: ethers.EventLog\n ) => {\n if (!key) return;\n const payloadStr = ethers.toUtf8String(payload);\n const parsed = decryptMessage(payloadStr, key);\n\n callback({\n topicId: tId,\n sender,\n text: parsed?.text ?? '[decryption failed]',\n replyTo: parsed?.replyTo ?? null,\n mentions: parsed?.mentions ?? null,\n timestamp,\n txHash: event.transactionHash,\n blockNumber: event.blockNumber,\n });\n };\n\n this.registry.on(this.registry.filters.MessageSent(topicId), handler);\n return () => {\n this.registry.off(this.registry.filters.MessageSent(topicId), handler);\n };\n }\n\n // ===== NICKNAMES =====\n\n async setNickname(appId: number, nickname: string): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.setNickname(appId, nickname);\n }\n\n async getNickname(appId: number, address: string): Promise<string> {\n return this.registry.getNickname(appId, address);\n }\n\n async hasNickname(appId: number, address: string): Promise<boolean> {\n return this.registry.hasNickname(appId, address);\n }\n\n async canChangeNickname(appId: number, address: string): Promise<{ canChange: boolean; timeRemaining: bigint }> {\n const [canChange, timeRemaining] = await this.registry.canChangeNickname(appId, address);\n return { canChange, timeRemaining };\n }\n\n async clearNickname(appId: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.clearNickname(appId);\n }\n\n async setNicknameCooldown(appId: number, cooldownSeconds: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.setNicknameCooldown(appId, cooldownSeconds);\n }\n\n async getNicknameCooldown(appId: number): Promise<bigint> {\n return this.registry.appNicknameCooldown(appId);\n }\n\n // ===== TOPICS =====\n\n async createTopic(\n appId: number,\n name: string,\n description: string,\n accessLevel: AccessLevel\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.createTopic(appId, name, description, accessLevel);\n }\n\n async getTopic(topicId: number): Promise<Topic> {\n const t = await this.registry.getTopic(topicId);\n return {\n id: t.id,\n applicationId: t.applicationId,\n name: t.name,\n description: t.description,\n owner: t.owner,\n creator: t.creator,\n createdAt: t.createdAt,\n lastMessageAt: t.lastMessageAt,\n messageCount: t.messageCount,\n accessLevel: Number(t.accessLevel),\n active: t.active,\n };\n }\n\n async getApplicationTopics(appId: number): Promise<bigint[]> {\n return this.registry.getApplicationTopics(appId);\n }\n\n async getTopicCount(): Promise<number> {\n const count = await this.registry.topicCount();\n return Number(count);\n }\n\n async setTopicPermission(\n topicId: number,\n user: string,\n permission: number\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.setTopicPermission(topicId, user, permission);\n }\n\n async getTopicPermission(topicId: number, user: string): Promise<number> {\n const perm = await this.registry.getTopicPermission(topicId, user);\n return Number(perm);\n }\n\n // ===== MEMBERS =====\n\n async addMember(\n appId: number,\n address: string,\n nickname: string,\n roles: number\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.addMember(appId, address, nickname, roles);\n }\n\n async removeMember(appId: number, address: string): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.removeMember(appId, address);\n }\n\n async updateMemberRoles(\n appId: number,\n address: string,\n roles: number\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.updateMemberRoles(appId, address, roles);\n }\n\n async getMember(appId: number, address: string): Promise<Member> {\n const m = await this.registry.getMember(appId, address);\n return {\n account: m.account,\n nickname: m.nickname,\n roles: Number(m.roles),\n joinedAt: m.joinedAt,\n };\n }\n\n async isMember(appId: number, address: string): Promise<boolean> {\n return this.registry.isMember(appId, address);\n }\n\n async getApplicationMembers(appId: number): Promise<string[]> {\n return this.registry.getApplicationMembers(appId);\n }\n\n // ===== ACCESS CHECKS =====\n\n async canRead(topicId: number, address: string): Promise<boolean> {\n return this.registry.canReadTopic(topicId, address);\n }\n\n async canWrite(topicId: number, address: string): Promise<boolean> {\n return this.registry.canWriteToTopic(topicId, address);\n }\n\n // ===== APPLICATIONS =====\n\n async createApplication(\n name: string,\n description: string,\n frontendUrl: string,\n allowPublicTopicCreation: boolean\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.createApplication(name, description, frontendUrl, allowPublicTopicCreation);\n }\n\n async getApplicationCount(): Promise<number> {\n const count = await this.registry.applicationCount();\n return Number(count);\n }\n\n async updateFrontendUrl(appId: number, frontendUrl: string): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.updateApplicationFrontendUrl(appId, frontendUrl);\n }\n\n async getApplication(appId: number): Promise<Application> {\n const a = await this.registry.getApplication(appId);\n return {\n id: a.id,\n name: a.name,\n description: a.description,\n frontendUrl: a.frontendUrl,\n owner: a.owner,\n createdAt: a.createdAt,\n memberCount: Number(a.memberCount),\n topicCount: Number(a.topicCount),\n active: a.active,\n allowPublicTopicCreation: a.allowPublicTopicCreation,\n topicCreationFeeToken: a.topicCreationFeeToken,\n topicCreationFeeAmount: a.topicCreationFeeAmount,\n };\n }\n\n // ===== FEES =====\n\n async getTopicMessageFee(topicId: number): Promise<TopicMessageFee> {\n const [token, amount] = await this.registry.getTopicMessageFee(topicId);\n return { token, amount };\n }\n\n async setTopicCreationFee(\n appId: number,\n feeToken: string,\n feeAmount: bigint\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.setTopicCreationFee(appId, feeToken, feeAmount);\n }\n\n async setTopicMessageFee(\n topicId: number,\n feeToken: string,\n feeAmount: bigint\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.setTopicMessageFee(topicId, feeToken, feeAmount);\n }\n\n // ===== ECDH (Private Topics) =====\n\n /**\n * Derive ECDH keypair from wallet signature (deterministic).\n * Requires a signer capable of signing messages.\n */\n async deriveECDHFromWallet(appId: number = 1): Promise<{ publicKey: Uint8Array }> {\n if (!this.wallet) throw new Error('Wallet required');\n\n const { privateKey, publicKey } = await deriveKeypairFromSignature(\n this.wallet.address,\n (msg) => this.wallet!.signMessage(msg),\n appId\n );\n\n this.ecdhPrivateKey = privateKey;\n this.ecdhPublicKey = publicKey;\n return { publicKey };\n }\n\n /**\n * Load ECDH keypair from a hex private key (e.g. from credentials file).\n */\n loadECDHKeypair(privateKeyHex: string): void {\n const { privateKey, publicKey } = keypairFromPrivateKey(privateKeyHex);\n this.ecdhPrivateKey = privateKey;\n this.ecdhPublicKey = publicKey;\n }\n\n /**\n * Register ECDH public key on-chain.\n */\n async registerPublicKey(): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n if (!this.ecdhPublicKey) throw new Error('ECDH key not derived yet');\n\n const hasKey = await this.keyManager.hasPublicKey(this.wallet.address);\n if (hasKey) {\n throw new Error('Public key already registered on-chain');\n }\n\n return this.keyManager.registerPublicKey(this.ecdhPublicKey);\n }\n\n /**\n * Fetch and decrypt the topic symmetric key from an on-chain ECDH grant.\n */\n async fetchAndDecryptTopicKey(topicId: number): Promise<Uint8Array> {\n if (!this.ecdhPrivateKey) throw new Error('ECDH key not derived yet');\n\n const grant = await this.keyManager.getMyKey(topicId);\n const encryptedKey = ethers.getBytes(grant.encryptedKey);\n const granterPubKey = ethers.getBytes(grant.granterPublicKey);\n\n const topicKey = decryptTopicKey(encryptedKey, this.ecdhPrivateKey, granterPubKey);\n this.topicKeys.set(topicId, topicKey);\n return topicKey;\n }\n\n /**\n * Grant a user access to a private topic's symmetric key.\n */\n async grantKeyAccess(\n topicId: number,\n userAddress: string,\n topicKey: Uint8Array\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n if (!this.ecdhPrivateKey) throw new Error('ECDH key not derived yet');\n\n const userPubKeyBytes = ethers.getBytes(await this.keyManager.getPublicKey(userAddress));\n const encrypted = encryptTopicKeyForUser(topicKey, this.ecdhPrivateKey, userPubKeyBytes);\n return this.keyManager.grantKeyAccess(topicId, userAddress, encrypted);\n }\n\n /**\n * Store a pre-known topic key (e.g. loaded from credentials).\n */\n setTopicKey(topicId: number, key: Uint8Array): void {\n this.topicKeys.set(topicId, key);\n }\n\n /**\n * Check if an address has an ECDH public key registered on-chain.\n */\n async hasPublicKey(address: string): Promise<boolean> {\n return this.keyManager.hasPublicKey(address);\n }\n\n /**\n * Get an address's ECDH public key from chain.\n */\n async getPublicKey(address: string): Promise<Uint8Array> {\n const key = await this.keyManager.getPublicKey(address);\n return ethers.getBytes(key);\n }\n\n /**\n * Check if a user has key access for a topic.\n */\n async hasKeyAccess(topicId: number, address: string): Promise<boolean> {\n return this.keyManager.hasKeyAccess(topicId, address);\n }\n\n /**\n * Get the key grant details for a user on a topic.\n */\n async getKeyGrant(topicId: number, address: string): Promise<{\n encryptedKey: Uint8Array;\n granterPublicKey: Uint8Array;\n granter: string;\n keyVersion: bigint;\n grantedAt: bigint;\n }> {\n const g = await this.keyManager.getKeyGrant(topicId, address);\n return {\n encryptedKey: ethers.getBytes(g.encryptedKey),\n granterPublicKey: ethers.getBytes(g.granterPublicKey),\n granter: g.granter,\n keyVersion: g.keyVersion,\n grantedAt: g.grantedAt,\n };\n }\n\n /**\n * Get the current key version for a topic.\n */\n async getKeyVersion(topicId: number): Promise<bigint> {\n return this.keyManager.keyVersions(topicId);\n }\n\n /**\n * Batch grant key access to multiple users at once (max 50).\n */\n async batchGrantKeyAccess(\n topicId: number,\n users: string[],\n topicKey: Uint8Array\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n if (!this.ecdhPrivateKey) throw new Error('ECDH key not derived yet');\n\n const encryptedKeys: Uint8Array[] = [];\n for (const user of users) {\n const userPubKeyBytes = ethers.getBytes(await this.keyManager.getPublicKey(user));\n const encrypted = encryptTopicKeyForUser(topicKey, this.ecdhPrivateKey, userPubKeyBytes);\n encryptedKeys.push(encrypted);\n }\n\n return this.keyManager.batchGrantKeyAccess(topicId, users, encryptedKeys);\n }\n\n /**\n * Revoke a user's key access for a topic.\n */\n async revokeKeyAccess(topicId: number, address: string): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.keyManager.revokeKeyAccess(topicId, address);\n }\n\n /**\n * Rotate the key version for a topic. Existing grants become stale.\n */\n async rotateKey(topicId: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.keyManager.rotateKey(topicId);\n }\n\n // ===== SCHEMAS =====\n\n /**\n * Create a schema scoped to an application. Requires app admin role.\n */\n async createAppSchema(\n appId: number,\n name: string,\n description: string,\n body: string\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.schemaRegistry.createAppSchema(appId, name, description, body);\n }\n\n /**\n * Publish a new version of an existing schema.\n */\n async publishSchemaVersion(\n schemaId: number,\n body: string\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.schemaRegistry.publishSchemaVersion(schemaId, body);\n }\n\n /**\n * Deactivate a schema.\n */\n async deactivateSchema(schemaId: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.schemaRegistry.deactivateSchema(schemaId);\n }\n\n /**\n * Get schema info including application scope.\n */\n async getSchema(schemaId: number): Promise<SchemaInfo> {\n const s = await this.schemaRegistry.getSchemaWithApp(schemaId);\n return {\n id: Number(s.id),\n name: s.name,\n description: s.description,\n creator: s.creator,\n createdAt: Number(s.createdAt),\n versionCount: Number(s.versionCount),\n active: s.active,\n applicationId: Number(s.applicationId),\n };\n }\n\n /**\n * Get all schemas scoped to an application.\n */\n async getApplicationSchemas(appId: number): Promise<SchemaInfo[]> {\n const ids: bigint[] = await this.schemaRegistry.getApplicationSchemas(appId);\n const schemas: SchemaInfo[] = [];\n for (const id of ids) {\n const s = await this.getSchema(Number(id));\n schemas.push(s);\n }\n return schemas;\n }\n\n /**\n * Get schema body for a specific version.\n */\n async getSchemaBody(schemaId: number, version: number): Promise<string> {\n return this.schemaRegistry.getSchemaBody(schemaId, version);\n }\n\n /**\n * Get the schema binding for a topic.\n */\n async getTopicSchema(topicId: number): Promise<TopicSchemaBinding> {\n const s = await this.schemaRegistry.getTopicSchema(topicId);\n return {\n schemaId: Number(s.schemaId),\n version: Number(s.version),\n body: s.body,\n };\n }\n\n /**\n * Bind a schema version to a topic. Requires topic admin.\n */\n async setTopicSchema(\n topicId: number,\n schemaId: number,\n version: number\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.schemaRegistry.setTopicSchema(topicId, schemaId, version);\n }\n\n /**\n * Remove schema binding from a topic.\n */\n async clearTopicSchema(topicId: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.schemaRegistry.clearTopicSchema(topicId);\n }\n\n // ===== AGENT IDENTITY (V5) =====\n\n /**\n * Register your ERC-8004 agent identity for an application (V5).\n * Verifies ownership via ownerOf on the identity registry.\n */\n async registerAgentIdentity(appId: number, tokenId: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.registerAgentIdentity(appId, tokenId);\n }\n\n /**\n * Clear your agent identity registration for an application (V5).\n */\n async clearAgentIdentity(appId: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.clearAgentIdentity(appId);\n }\n\n /**\n * Get the registered agent token ID for a user in an application (V5/V6).\n * Returns 0 if not registered. V6 validates ownership live via ownerOf.\n */\n async getAgentTokenId(appId: number, address: string): Promise<bigint> {\n return this.registry.getAgentTokenId(appId, address);\n }\n\n /**\n * Check if a user has a registered agent identity for an application (V5/V6).\n * V6 validates ownership live — returns false if the token was transferred.\n */\n async hasAgentIdentity(appId: number, address: string): Promise<boolean> {\n return this.registry.hasAgentIdentity(appId, address);\n }\n\n // ===== ERC-8004 IDENTITY REGISTRY =====\n\n private requireIdentityRegistry(): ethers.Contract {\n if (!this.identityRegistry) {\n throw new Error('ERC-8004 Identity Registry not available on this chain. Use Base.');\n }\n return this.identityRegistry;\n }\n\n /**\n * Register as an agent on the ERC-8004 Identity Registry.\n * Optionally provide a URI for the agent's metadata.\n */\n async registerAgent(agentURI?: string): Promise<{ agentId: bigint; tx: ethers.TransactionResponse }> {\n if (!this.wallet) throw new Error('Wallet required');\n const registry = this.requireIdentityRegistry();\n\n const tx: ethers.TransactionResponse = agentURI\n ? await registry['register(string)'](agentURI)\n : await registry['register()']();\n\n const receipt = await tx.wait();\n let agentId = 0n;\n if (receipt) {\n for (const log of receipt.logs) {\n try {\n const parsed = registry.interface.parseLog(log);\n if (parsed?.name === 'Registered') {\n agentId = parsed.args.agentId;\n break;\n }\n } catch { /* skip non-matching logs */ }\n }\n }\n\n return { agentId, tx };\n }\n\n /**\n * Register as an agent with metadata entries.\n */\n async registerAgentWithMetadata(\n agentURI: string,\n metadata: Array<{ metadataKey: string; metadataValue: Uint8Array }>\n ): Promise<{ agentId: bigint; tx: ethers.TransactionResponse }> {\n if (!this.wallet) throw new Error('Wallet required');\n const registry = this.requireIdentityRegistry();\n\n const tx: ethers.TransactionResponse = await registry['register(string,(string,bytes)[])'](agentURI, metadata);\n\n const receipt = await tx.wait();\n let agentId = 0n;\n if (receipt) {\n for (const log of receipt.logs) {\n try {\n const parsed = registry.interface.parseLog(log);\n if (parsed?.name === 'Registered') {\n agentId = parsed.args.agentId;\n break;\n }\n } catch { /* skip non-matching logs */ }\n }\n }\n\n return { agentId, tx };\n }\n\n /**\n * Check if an address has an ERC-8004 agent identity NFT.\n * Defaults to the connected wallet address.\n */\n async isRegisteredAgent(address?: string): Promise<boolean> {\n const registry = this.requireIdentityRegistry();\n const addr = address ?? this.wallet?.address;\n if (!addr) throw new Error('Address required');\n\n const balance: bigint = await registry.balanceOf(addr);\n return balance > 0n;\n }\n\n /**\n * Get agent info by agent ID.\n */\n async getAgentInfo(agentId: number): Promise<{ owner: string; uri: string; wallet: string }> {\n const registry = this.requireIdentityRegistry();\n\n const [owner, uri, wallet] = await Promise.all([\n registry.ownerOf(agentId) as Promise<string>,\n registry.tokenURI(agentId) as Promise<string>,\n registry.getAgentWallet(agentId) as Promise<string>,\n ]);\n\n return { owner, uri, wallet };\n }\n\n /**\n * Set metadata for an agent.\n */\n async setAgentMetadata(\n agentId: number,\n key: string,\n value: Uint8Array\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n const registry = this.requireIdentityRegistry();\n return registry.setMetadata(agentId, key, value);\n }\n\n /**\n * Get metadata for an agent by key.\n */\n async getAgentMetadata(agentId: number, key: string): Promise<string> {\n const registry = this.requireIdentityRegistry();\n const data: string = await registry.getMetadata(agentId, key);\n return ethers.toUtf8String(data);\n }\n\n /**\n * Update the URI for an agent registration.\n */\n async setAgentURI(agentId: number, newURI: string): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n const registry = this.requireIdentityRegistry();\n return registry.setAgentURI(agentId, newURI);\n }\n\n /**\n * Look up an agent by its wallet address using the V5/V6 on-chain registry mapping.\n * V6 validates ownership live — stale registrations (transferred tokens) return 0.\n * Returns registration status, token ID, owner, URI, wallet, and parsed metadata.\n */\n async getAgentByAddress(address: string, appId: number): Promise<\n | { registered: false }\n | { registered: true; agentId: number; owner: string; uri: string; wallet: string; metadata: Record<string, unknown> | null }\n > {\n const onChainTokenId: bigint = await this.registry.getAgentTokenId(appId, address);\n if (onChainTokenId === 0n) return { registered: false };\n\n const agentId = Number(onChainTokenId);\n const { owner, uri, wallet } = await this.getAgentInfo(agentId);\n let metadata: Record<string, unknown> | null = null;\n if (uri) {\n metadata = await this.parseTokenURI(uri);\n }\n return { registered: true, agentId, owner, uri, wallet, metadata };\n }\n\n /**\n * Parse a tokenURI into JSON metadata.\n * Handles data:application/json;base64, data:application/json, HTTP(S), and ipfs:// URIs.\n */\n private async parseTokenURI(uri: string): Promise<Record<string, unknown> | null> {\n try {\n if (uri.startsWith('data:application/json;base64,')) {\n const json = atob(uri.slice('data:application/json;base64,'.length));\n return JSON.parse(json);\n }\n if (uri.startsWith('data:application/json,')) {\n return JSON.parse(decodeURIComponent(uri.slice('data:application/json,'.length)));\n }\n let url = uri;\n if (url.startsWith('ipfs://')) {\n url = 'https://ipfs.io/ipfs/' + url.slice('ipfs://'.length);\n }\n const resp = await fetch(url);\n if (!resp.ok) return null;\n return await resp.json();\n } catch {\n return null;\n }\n }\n\n // ===== DATA EXPORT =====\n\n /**\n * Look up an application ID by its name.\n */\n async getApplicationByName(name: string): Promise<number> {\n const id = await this.registry.applicationNames(name);\n return Number(id);\n }\n\n /**\n * Get schema version details including body and publish timestamp.\n */\n async getSchemaVersion(schemaId: number, version: number): Promise<{ body: string; publishedAt: bigint }> {\n const [body, publishedAt] = await this.schemaRegistry.getSchemaVersion(schemaId, version);\n return { body, publishedAt };\n }\n\n /**\n * Export member data for a user in an application.\n */\n async exportMemberData(appId: number, user: string): Promise<string> {\n const data = await this.registry.exportMemberData(appId, user);\n return ethers.hexlify(data);\n }\n\n /**\n * Export all application data.\n */\n async exportApplicationData(appId: number): Promise<string> {\n const data = await this.registry.exportApplicationData(appId);\n return ethers.hexlify(data);\n }\n\n /**\n * Export user data from the key manager for specified topics.\n */\n async exportUserData(user: string, topicIds: number[]): Promise<string> {\n const data = await this.keyManager.exportUserData(user, topicIds);\n return ethers.hexlify(data);\n }\n\n // ===== INTERNAL =====\n\n /**\n * Get the encryption key for a topic, determining the type automatically.\n */\n private async getEncryptionKey(topicId: number): Promise<Uint8Array> {\n // Check for a stored private topic key first\n const storedKey = this.topicKeys.get(topicId);\n if (storedKey) return storedKey;\n\n // Fetch topic metadata to determine access level\n const topic = await this.getTopic(topicId);\n\n if (topic.accessLevel === AccessLevel.PRIVATE) {\n throw new Error(\n `Topic ${topicId} is PRIVATE. Call fetchAndDecryptTopicKey() or setTopicKey() first.`\n );\n }\n\n // Public or public_limited: derive deterministic key\n return derivePublicTopicKey(topicId);\n }\n}\n","import type { ChainConfig, ChainName } from './types.js';\n\nexport const CHAINS: Record<ChainName, ChainConfig> = {\n baseSepolia: {\n chainId: 84532,\n name: 'Base Sepolia',\n shortName: 'Sepolia',\n rpc: 'https://sepolia.base.org',\n explorer: 'https://sepolia.basescan.org',\n registry: '0xf39b193aedC1Ec9FD6C5ccc24fBAe58ba9f52413',\n keyManager: '0x5562B553a876CBdc8AA4B3fb0687f22760F4759e',\n schemaRegistry: '0xB7eB50e9058198b99b5b2589E6D70b2d99d5440a',\n identityRegistry: '0x8004A169FB4a3325136EB29fA0ceB6D2e539a432',\n },\n base: {\n chainId: 8453,\n name: 'Base',\n shortName: 'Base',\n rpc: 'https://mainnet.base.org',\n explorer: 'https://basescan.org',\n registry: '0x5fF6BF04F1B5A78ae884D977a3C80A0D8E2072bF',\n keyManager: '0xdc302ff43a34F6aEa19426D60C9D150e0661E4f4',\n schemaRegistry: '0x5c11d2eA4470eD9025D810A21a885FE16dC987Bd',\n identityRegistry: '0x8004A169FB4a3325136EB29fA0ceB6D2e539a432',\n },\n avalanche: {\n chainId: 43114,\n name: 'Avalanche C-Chain',\n shortName: 'Avalanche',\n rpc: 'https://api.avax.network/ext/bc/C/rpc',\n explorer: 'https://snowtrace.io',\n registry: '0x3Ca2FF0bD1b3633513299EB5d3e2d63e058b0713',\n keyManager: '0x5a5ea9D408FBA984fFf6e243Dcc71ff6E00C73E4',\n schemaRegistry: '0x23D96e610E8E3DA5341a75B77F1BFF7EA9c3A62B',\n identityRegistry: '0x8004A169FB4a3325136EB29fA0ceB6D2e539a432',\n },\n};\n\nexport const CHAIN_IDS: Record<number, ChainName> = {\n 84532: 'baseSepolia',\n 8453: 'base',\n 43114: 'avalanche',\n};\n\nexport function getChain(nameOrId: ChainName | number): ChainConfig {\n if (typeof nameOrId === 'number') {\n const name = CHAIN_IDS[nameOrId];\n if (!name) throw new Error(`Unsupported chain ID: ${nameOrId}`);\n return CHAINS[name];\n }\n const chain = CHAINS[nameOrId];\n if (!chain) throw new Error(`Unsupported chain: ${nameOrId}`);\n return chain;\n}\n","export const REGISTRY_ABI = [\n // ===== READ FUNCTIONS =====\n\n // Applications\n 'function applications(uint256) view returns (uint256 id, string name, string description, string frontendUrl, address owner, uint64 createdAt, uint32 memberCount, uint32 topicCount, bool active, bool allowPublicTopicCreation)',\n 'function getApplication(uint256 appId) view returns (tuple(uint256 id, string name, string description, string frontendUrl, address owner, uint64 createdAt, uint32 memberCount, uint32 topicCount, bool active, bool allowPublicTopicCreation, address topicCreationFeeToken, uint256 topicCreationFeeAmount))',\n 'function applicationCount() view returns (uint256)',\n 'function applicationNames(string) view returns (uint256)',\n\n // Topics\n 'function topics(uint256) view returns (uint256 id, uint256 applicationId, string name, string description, address owner, address creator, uint64 createdAt, uint64 lastMessageAt, uint256 messageCount, uint8 accessLevel, bool active)',\n 'function getTopic(uint256 topicId) view returns (tuple(uint256 id, uint256 applicationId, string name, string description, address owner, address creator, uint64 createdAt, uint64 lastMessageAt, uint256 messageCount, uint8 accessLevel, bool active))',\n 'function topicCount() view returns (uint256)',\n 'function getApplicationTopics(uint256 appId) view returns (uint256[])',\n\n // Members\n 'function members(uint256 appId, address user) view returns (address account, string nickname, uint8 roles, uint64 joinedAt)',\n 'function getMember(uint256 appId, address account) view returns (tuple(address account, string nickname, uint8 roles, uint64 joinedAt))',\n 'function isMember(uint256 appId, address account) view returns (bool)',\n 'function getApplicationMembers(uint256 appId) view returns (address[])',\n\n // Permissions\n 'function canReadTopic(uint256 topicId, address user) view returns (bool)',\n 'function canWriteToTopic(uint256 topicId, address user) view returns (bool)',\n 'function getTopicPermission(uint256 topicId, address user) view returns (uint8)',\n 'function topicPermissions(uint256, address) view returns (uint8)',\n\n // Nicknames\n 'function getNickname(uint256 appId, address user) view returns (string)',\n 'function hasNickname(uint256 appId, address user) view returns (bool)',\n 'function canChangeNickname(uint256 appId, address user) view returns (bool canChange, uint256 timeRemaining)',\n 'function appNicknameCooldown(uint256 appId) view returns (uint256)',\n\n // Fees\n 'function getTopicMessageFee(uint256 topicId) view returns (address token, uint256 amount)',\n\n // ===== WRITE FUNCTIONS =====\n\n // Applications\n 'function createApplication(string name, string description, string frontendUrl, bool allowPublicTopicCreation) returns (uint256)',\n 'function updateApplicationFrontendUrl(uint256 appId, string frontendUrl)',\n\n // Topics\n 'function createTopic(uint256 appId, string name, string description, uint8 accessLevel) returns (uint256)',\n 'function setTopicPermission(uint256 topicId, address user, uint8 permission)',\n\n // Members\n 'function addMember(uint256 appId, address member, string nickname, uint8 roles)',\n 'function removeMember(uint256 appId, address member)',\n 'function updateMemberRoles(uint256 appId, address member, uint8 roles)',\n 'function updateMemberNickname(uint256 appId, string nickname)',\n\n // Nicknames (V3)\n 'function setNickname(uint256 appId, string nickname)',\n 'function clearNickname(uint256 appId)',\n 'function setNicknameCooldown(uint256 appId, uint256 cooldownSeconds)',\n\n // Messaging\n 'function sendMessage(uint256 topicId, bytes payload)',\n\n // Fees\n 'function setTopicCreationFee(uint256 appId, address feeTokenAddr, uint256 feeAmount)',\n 'function setTopicMessageFee(uint256 topicId, address feeTokenAddr, uint256 feeAmount)',\n\n // ===== EVENTS =====\n 'event ApplicationCreated(uint256 indexed applicationId, string name, address indexed owner)',\n 'event TopicCreated(uint256 indexed topicId, uint256 indexed applicationId, string name, address indexed creator, uint8 accessLevel)',\n 'event MemberAdded(uint256 indexed applicationId, address indexed member, string nickname, uint8 roles)',\n 'event MemberRemoved(uint256 indexed applicationId, address indexed member)',\n 'event MemberRolesUpdated(uint256 indexed applicationId, address indexed member, uint8 roles)',\n 'event NicknameUpdated(uint256 indexed applicationId, address indexed member, string nickname)',\n 'event UserNicknameSet(uint256 indexed applicationId, address indexed user, string nickname)',\n 'event TopicPermissionSet(uint256 indexed topicId, address indexed user, uint8 permission)',\n 'event MessageSent(uint256 indexed topicId, address indexed sender, bytes payload, uint256 timestamp)',\n 'event TopicMessageFeeUpdated(uint256 indexed topicId, address token, uint256 amount)',\n\n // Agent identity (V5)\n 'function registerAgentIdentity(uint256 appId, uint256 tokenId)',\n 'function clearAgentIdentity(uint256 appId)',\n 'function getAgentTokenId(uint256 appId, address user) view returns (uint256)',\n 'function hasAgentIdentity(uint256 appId, address user) view returns (bool)',\n 'event AgentIdentityRegistered(uint256 indexed applicationId, address indexed user, uint256 tokenId)',\n 'event AgentIdentityCleared(uint256 indexed applicationId, address indexed user)',\n\n // Data export\n 'function exportMemberData(uint256 appId, address user) view returns (bytes)',\n 'function exportApplicationData(uint256 appId) view returns (bytes)',\n] as const;\n\nexport const SCHEMA_REGISTRY_ABI = [\n // ===== READ FUNCTIONS =====\n\n // Schema queries\n 'function schemaCount() view returns (uint256)',\n 'function getSchema(uint256 schemaId) view returns (uint256 id, string name, string description, address creator, uint64 createdAt, uint256 versionCount, bool active)',\n 'function getSchemaWithApp(uint256 schemaId) view returns (uint256 id, string name, string description, address creator, uint64 createdAt, uint256 versionCount, bool active, uint256 applicationId)',\n 'function getSchemaBody(uint256 schemaId, uint256 version) view returns (string)',\n 'function getSchemaVersion(uint256 schemaId, uint256 version) view returns (string body, uint64 publishedAt)',\n 'function schemaApplicationId(uint256 schemaId) view returns (uint256)',\n\n // App-scoped queries (V2)\n 'function getApplicationSchemas(uint256 applicationId) view returns (uint256[])',\n 'function getApplicationSchemaCount(uint256 applicationId) view returns (uint256)',\n\n // Topic binding\n 'function getTopicSchema(uint256 topicId) view returns (uint256 schemaId, uint256 version, string body)',\n\n // Version\n 'function contractVersion() view returns (string)',\n\n // ===== WRITE FUNCTIONS =====\n\n // Schema creation (V2 app-scoped)\n 'function createAppSchema(uint256 applicationId, string name, string description, string body) returns (uint256)',\n 'function publishSchemaVersion(uint256 schemaId, string body) returns (uint256)',\n 'function deactivateSchema(uint256 schemaId)',\n\n // Topic binding\n 'function setTopicSchema(uint256 topicId, uint256 schemaId, uint256 version)',\n 'function clearTopicSchema(uint256 topicId)',\n\n // ===== EVENTS =====\n 'event AppSchemaCreated(uint256 indexed schemaId, uint256 indexed applicationId, string name, address indexed creator)',\n 'event SchemaVersionPublished(uint256 indexed schemaId, uint256 version)',\n 'event SchemaDeactivated(uint256 indexed schemaId)',\n 'event TopicSchemaSet(uint256 indexed topicId, uint256 indexed schemaId, uint256 version)',\n 'event TopicSchemaCleared(uint256 indexed topicId)',\n 'event SchemaAssignedToApp(uint256 indexed schemaId, uint256 indexed applicationId)',\n] as const;\n\nexport const IDENTITY_REGISTRY_ABI = [\n 'function register() returns (uint256)',\n 'function register(string agentURI) returns (uint256)',\n 'function register(string agentURI, tuple(string metadataKey, bytes metadataValue)[] metadata) returns (uint256)',\n 'function getMetadata(uint256 agentId, string metadataKey) view returns (bytes)',\n 'function setMetadata(uint256 agentId, string metadataKey, bytes metadataValue)',\n 'function setAgentURI(uint256 agentId, string newURI)',\n 'function getAgentWallet(uint256 agentId) view returns (address)',\n 'function setAgentWallet(uint256 agentId, address newWallet, uint256 deadline, bytes signature)',\n 'function unsetAgentWallet(uint256 agentId)',\n 'function ownerOf(uint256 tokenId) view returns (address)',\n 'function balanceOf(address owner) view returns (uint256)',\n 'function tokenOfOwnerByIndex(address owner, uint256 index) view returns (uint256)',\n 'function tokenURI(uint256 tokenId) view returns (string)',\n 'function isAuthorizedOrOwner(address spender, uint256 agentId) view returns (bool)',\n 'function getVersion() pure returns (string)',\n 'event Registered(uint256 indexed agentId, string agentURI, address indexed owner)',\n] as const;\n\nexport const KEY_MANAGER_ABI = [\n // ===== READ FUNCTIONS =====\n 'function hasPublicKey(address user) view returns (bool)',\n 'function getPublicKey(address user) view returns (bytes)',\n 'function publicKeys(address) view returns (bytes)',\n 'function hasKeyAccess(uint256 topicId, address user) view returns (bool)',\n 'function getMyKey(uint256 topicId) view returns (bytes encryptedKey, bytes granterPublicKey, address granter, uint256 keyVersion, uint256 currentVersion)',\n 'function getKeyGrant(uint256 topicId, address user) view returns (tuple(bytes encryptedKey, bytes granterPublicKey, address granter, uint256 keyVersion, uint64 grantedAt))',\n 'function keyVersions(uint256 topicId) view returns (uint256)',\n\n // ===== WRITE FUNCTIONS =====\n 'function registerPublicKey(bytes publicKey)',\n 'function grantKeyAccess(uint256 topicId, address user, bytes encryptedKey)',\n 'function batchGrantKeyAccess(uint256 topicId, address[] users, bytes[] encryptedKeys)',\n 'function revokeKeyAccess(uint256 topicId, address user)',\n 'function rotateKey(uint256 topicId)',\n\n // Data export\n 'function exportUserData(address user, uint256[] topicIds) view returns (bytes)',\n\n // ===== EVENTS =====\n 'event PublicKeyRegistered(address indexed user, bytes publicKey)',\n 'event PublicKeyUpdated(address indexed user, bytes publicKey)',\n 'event KeyAccessGranted(uint256 indexed topicId, address indexed user, address indexed granter, uint256 version)',\n 'event KeyAccessRevoked(uint256 indexed topicId, address indexed user)',\n 'event KeyRotated(uint256 indexed topicId, uint256 newVersion)',\n] as const;\n","import { pbkdf2 } from '@noble/hashes/pbkdf2';\nimport { sha256 } from '@noble/hashes/sha256';\nimport { gcm } from '@noble/ciphers/aes';\nimport { randomBytes } from '@noble/hashes/utils';\nimport {\n PUBLIC_KEY_MATERIAL_PREFIX,\n SALT_PREFIX,\n PBKDF2_ITERATIONS,\n} from '../constants.js';\nimport type { EncryptedPayload, MessageContent } from '../types.js';\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\n/**\n * Derive AES-256 key for a public/public_limited topic.\n * Uses PBKDF2 with SHA-256, matching the web frontend exactly.\n */\nexport function derivePublicTopicKey(topicId: number | bigint): Uint8Array {\n const keyMaterial = PUBLIC_KEY_MATERIAL_PREFIX + topicId;\n const salt = encoder.encode(SALT_PREFIX + topicId);\n return pbkdf2(sha256, keyMaterial, salt, { c: PBKDF2_ITERATIONS, dkLen: 32 });\n}\n\n/**\n * Derive AES-256 key from arbitrary passphrase (for private topics with manual passphrase).\n */\nexport function deriveKeyFromPassphrase(passphrase: string, topicId: number | bigint): Uint8Array {\n const salt = encoder.encode(SALT_PREFIX + topicId);\n return pbkdf2(sha256, passphrase, salt, { c: PBKDF2_ITERATIONS, dkLen: 32 });\n}\n\n/**\n * Encrypt a message payload with AES-256-GCM.\n * Output format: `{ e: true, v: 2, iv: base64, ct: base64 }`\n * The ciphertext includes the GCM auth tag (last 16 bytes).\n */\nexport function encrypt(plaintext: string, key: Uint8Array): string {\n const iv = randomBytes(12);\n const aes = gcm(key, iv);\n const ciphertext = aes.encrypt(encoder.encode(plaintext));\n\n const payload: EncryptedPayload = {\n e: true,\n v: 2,\n iv: toBase64(iv),\n ct: toBase64(ciphertext),\n };\n return JSON.stringify(payload);\n}\n\n/**\n * Decrypt a message payload. Handles both v1 and v2 formats.\n * Returns the decrypted string or null on failure.\n */\nexport function decrypt(jsonStr: string, key: Uint8Array): string | null {\n try {\n const data = JSON.parse(jsonStr) as EncryptedPayload;\n if (!data.e) {\n // Not encrypted — return raw for caller to parse\n return jsonStr;\n }\n\n const iv = fromBase64(data.iv);\n const ct = fromBase64(data.ct);\n\n const aes = gcm(key, iv);\n const decrypted = aes.decrypt(ct);\n return decoder.decode(decrypted);\n } catch {\n return null;\n }\n}\n\n/**\n * Encrypt a structured message (text + optional replyTo/mentions).\n */\nexport function encryptMessage(\n text: string,\n key: Uint8Array,\n options?: { replyTo?: string; replyText?: string; replyAuthor?: string; mentions?: string[] }\n): string {\n const content: MessageContent = { text };\n if (options?.replyTo) content.replyTo = options.replyTo;\n if (options?.replyText) content.replyText = options.replyText;\n if (options?.replyAuthor) content.replyAuthor = options.replyAuthor;\n if (options?.mentions) content.mentions = options.mentions;\n return encrypt(JSON.stringify(content), key);\n}\n\n/**\n * Decrypt and parse a message payload into structured content.\n */\nexport function decryptMessage(\n jsonStr: string,\n key: Uint8Array\n): { text: string; replyTo: string | null; replyText: string | null; replyAuthor: string | null; mentions: string[] | null } | null {\n const decrypted = decrypt(jsonStr, key);\n if (!decrypted) return null;\n\n try {\n const content = JSON.parse(decrypted);\n if (typeof content === 'object' && content.text) {\n return {\n text: content.text,\n replyTo: content.replyTo || null,\n replyText: content.replyText || null,\n replyAuthor: content.replyAuthor || null,\n mentions: content.mentions || null,\n };\n }\n // Plain text string was JSON-stringified\n return { text: decrypted, replyTo: null, replyText: null, replyAuthor: null, mentions: null };\n } catch {\n // Raw string, not JSON\n return { text: decrypted, replyTo: null, replyText: null, replyAuthor: null, mentions: null };\n }\n}\n\n// ===== Base64 helpers (isomorphic) =====\n\nfunction toBase64(bytes: Uint8Array): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(bytes).toString('base64');\n }\n return btoa(String.fromCharCode(...bytes));\n}\n\nfunction fromBase64(str: string): Uint8Array {\n if (typeof Buffer !== 'undefined') {\n return new Uint8Array(Buffer.from(str, 'base64'));\n }\n return Uint8Array.from(atob(str), (c) => c.charCodeAt(0));\n}\n","// ===== Enums =====\n\nexport enum AccessLevel {\n PUBLIC = 0,\n PUBLIC_LIMITED = 1,\n PRIVATE = 2,\n}\n\nexport enum Permission {\n NONE = 0,\n READ = 1,\n WRITE = 2,\n READ_WRITE = 3,\n ADMIN = 4,\n}\n\nexport enum Role {\n MEMBER = 1,\n SUPPORT_MANAGER = 2,\n TOPIC_MANAGER = 4,\n ADMIN = 8,\n OWNER_DELEGATE = 16,\n}\n\n// ===== Chain types =====\n\nexport interface ChainConfig {\n chainId: number;\n name: string;\n shortName: string;\n rpc: string;\n explorer: string;\n registry: string;\n keyManager: string;\n schemaRegistry: string;\n identityRegistry?: string;\n}\n\nexport type ChainName = 'base' | 'avalanche' | 'baseSepolia';\n\n// ===== Contract return types =====\n\nexport interface Application {\n id: bigint;\n name: string;\n description: string;\n frontendUrl: string;\n owner: string;\n createdAt: bigint;\n memberCount: number;\n topicCount: number;\n active: boolean;\n allowPublicTopicCreation: boolean;\n topicCreationFeeToken?: string;\n topicCreationFeeAmount?: bigint;\n}\n\nexport interface Topic {\n id: bigint;\n applicationId: bigint;\n name: string;\n description: string;\n owner: string;\n creator: string;\n createdAt: bigint;\n lastMessageAt: bigint;\n messageCount: bigint;\n accessLevel: number;\n active: boolean;\n}\n\nexport interface Member {\n account: string;\n nickname: string;\n roles: number;\n joinedAt: bigint;\n}\n\nexport interface KeyGrant {\n encryptedKey: Uint8Array;\n granterPublicKey: Uint8Array;\n granter: string;\n keyVersion: bigint;\n grantedAt?: bigint;\n currentVersion?: bigint;\n}\n\n// ===== Message types =====\n\nexport interface MessageContent {\n text: string;\n replyTo?: string;\n replyText?: string;\n replyAuthor?: string;\n mentions?: string[];\n}\n\nexport interface EncryptedPayload {\n e: boolean;\n v: number;\n iv: string;\n ct: string;\n}\n\nexport interface Message {\n topicId: bigint;\n sender: string;\n text: string;\n replyTo: string | null;\n mentions: string[] | null;\n timestamp: bigint;\n txHash: string;\n blockNumber: number;\n}\n\nexport interface TopicMessageFee {\n token: string;\n amount: bigint;\n}\n\nexport interface SchemaInfo {\n id: number;\n name: string;\n description: string;\n creator: string;\n createdAt: number;\n versionCount: number;\n active: boolean;\n applicationId: number;\n}\n\nexport interface TopicSchemaBinding {\n schemaId: number;\n version: number;\n body: string;\n}\n\n// ===== Client options =====\n\nexport interface ClawtennaOptions {\n chain?: ChainName;\n chainId?: number;\n rpcUrl?: string;\n privateKey?: string;\n registryAddress?: string;\n keyManagerAddress?: string;\n schemaRegistryAddress?: string;\n}\n\nexport interface ReadOptions {\n limit?: number;\n fromBlock?: number;\n}\n\nexport interface SendOptions {\n replyTo?: string;\n replyText?: string;\n replyAuthor?: string;\n mentions?: string[];\n}\n\n// ===== Credentials =====\n\nexport interface Credentials {\n version: 2;\n wallet: {\n address: string;\n privateKey: string;\n };\n chains: Record<string, CredentialChain>;\n}\n\nexport interface CredentialChain {\n name: string;\n ecdh: {\n privateKey: string;\n publicKey: string;\n registered: boolean;\n } | null;\n apps: Record<string, CredentialApp>;\n}\n\nexport interface CredentialApp {\n name: string;\n nickname: string;\n agentTokenId: number | null;\n topicKeys: Record<string, string>;\n}\n\n// Legacy v1 format for migration\nexport interface CredentialsV1 {\n wallet: {\n address: string;\n privateKey: string;\n };\n apps: Record<string, {\n name: string;\n nickname: string;\n ecdh: {\n privateKey: string;\n publicKey: string;\n registeredOnChain: boolean;\n topicKeys: Record<string, string>;\n } | null;\n }>;\n}\n","import { AccessLevel, Permission, Role } from './types.js';\n\n// Re-export enums as individual constants for convenience\nexport const ACCESS_PUBLIC = AccessLevel.PUBLIC;\nexport const ACCESS_PUBLIC_LIMITED = AccessLevel.PUBLIC_LIMITED;\nexport const ACCESS_PRIVATE = AccessLevel.PRIVATE;\n\nexport const PERMISSION_NONE = Permission.NONE;\nexport const PERMISSION_READ = Permission.READ;\nexport const PERMISSION_WRITE = Permission.WRITE;\nexport const PERMISSION_READ_WRITE = Permission.READ_WRITE;\nexport const PERMISSION_ADMIN = Permission.ADMIN;\n\nexport const ROLE_MEMBER = Role.MEMBER;\nexport const ROLE_SUPPORT_MANAGER = Role.SUPPORT_MANAGER;\nexport const ROLE_TOPIC_MANAGER = Role.TOPIC_MANAGER;\nexport const ROLE_ADMIN = Role.ADMIN;\nexport const ROLE_OWNER_DELEGATE = Role.OWNER_DELEGATE;\n\n// Encryption constants\nexport const PUBLIC_KEY_MATERIAL_PREFIX = 'antenna-public-topic-';\nexport const SALT_PREFIX = 'antenna-v2-salt-';\nexport const PBKDF2_ITERATIONS = 100_000;\nexport const ECDH_HKDF_SALT = 'antenna-ecdh-v1';\nexport const ECDH_HKDF_INFO = 'topic-key-encryption';\nexport const ECDH_DERIVATION_MESSAGE = (address: string, appId: number) =>\n `Clawntenna ECDH Key Derivation\\n\\nThis signature generates your encryption key.\\nIt never leaves your device.\\n\\nWallet: ${address}\\nApp: ${appId}\\nChain: Base (8453)`;\n","import { secp256k1 } from '@noble/curves/secp256k1';\nimport { hkdf } from '@noble/hashes/hkdf';\nimport { sha256 } from '@noble/hashes/sha256';\nimport { gcm } from '@noble/ciphers/aes';\nimport { randomBytes } from '@noble/hashes/utils';\nimport { ECDH_HKDF_SALT, ECDH_HKDF_INFO, ECDH_DERIVATION_MESSAGE } from '../constants.js';\n\nconst encoder = new TextEncoder();\n\n/**\n * Derive an ECDH keypair deterministically from a wallet signature.\n * This produces the same keypair as the web frontend for the same wallet + app.\n */\nexport async function deriveKeypairFromSignature(\n walletAddress: string,\n signMessage: (message: string) => Promise<string>,\n appId: number = 1\n): Promise<{ privateKey: Uint8Array; publicKey: Uint8Array }> {\n const message = ECDH_DERIVATION_MESSAGE(walletAddress, appId);\n const signature = await signMessage(message);\n\n // Hash the signature string (as UTF-8 bytes) to get private key\n const sigBytes = encoder.encode(signature);\n const hashBuffer = sha256(sigBytes);\n const privateKey = new Uint8Array(hashBuffer);\n\n // Derive compressed public key\n const publicKey = secp256k1.getPublicKey(privateKey, true);\n\n return { privateKey, publicKey };\n}\n\n/**\n * Derive ECDH keypair from a raw private key (e.g. from stored credentials).\n */\nexport function keypairFromPrivateKey(privateKeyHex: string): {\n privateKey: Uint8Array;\n publicKey: Uint8Array;\n} {\n const cleaned = privateKeyHex.startsWith('0x') ? privateKeyHex.slice(2) : privateKeyHex;\n const privateKey = hexToBytes(cleaned);\n const publicKey = secp256k1.getPublicKey(privateKey, true);\n return { privateKey, publicKey };\n}\n\n/**\n * Compute ECDH shared secret (x-coordinate of shared point).\n */\nexport function computeSharedSecret(\n ourPrivateKey: Uint8Array,\n theirPublicKey: Uint8Array\n): Uint8Array {\n const sharedPoint = secp256k1.getSharedSecret(ourPrivateKey, theirPublicKey);\n // Return x-coordinate only (skip the 0x04 prefix byte)\n return sharedPoint.slice(1, 33);\n}\n\n/**\n * Derive AES-256 key from an ECDH shared secret using HKDF.\n * Matches the web frontend: salt='antenna-ecdh-v1', info='topic-key-encryption'.\n */\nexport function deriveAESKeyFromSecret(\n sharedSecret: Uint8Array,\n info: string = ECDH_HKDF_INFO\n): Uint8Array {\n return hkdf(sha256, sharedSecret, encoder.encode(ECDH_HKDF_SALT), info, 32);\n}\n\n/**\n * Encrypt a topic symmetric key for a recipient using ECDH.\n * Returns IV (12 bytes) + ciphertext (includes GCM auth tag).\n */\nexport function encryptTopicKeyForUser(\n topicKey: Uint8Array,\n ourPrivateKey: Uint8Array,\n recipientPublicKey: Uint8Array\n): Uint8Array {\n const shared = computeSharedSecret(ourPrivateKey, recipientPublicKey);\n const aesKey = deriveAESKeyFromSecret(shared);\n const iv = randomBytes(12);\n const aes = gcm(aesKey, iv);\n const ciphertext = aes.encrypt(topicKey);\n\n // Combine: IV + ciphertext\n const result = new Uint8Array(iv.length + ciphertext.length);\n result.set(iv);\n result.set(ciphertext, iv.length);\n return result;\n}\n\n/**\n * Decrypt a topic symmetric key received via ECDH grant.\n * Input format: IV (12 bytes) + ciphertext (includes GCM auth tag).\n */\nexport function decryptTopicKey(\n encryptedKey: Uint8Array,\n ourPrivateKey: Uint8Array,\n granterPublicKey: Uint8Array\n): Uint8Array {\n const shared = computeSharedSecret(ourPrivateKey, granterPublicKey);\n const aesKey = deriveAESKeyFromSecret(shared);\n const iv = encryptedKey.slice(0, 12);\n const ciphertext = encryptedKey.slice(12);\n const aes = gcm(aesKey, iv);\n return aes.decrypt(ciphertext);\n}\n\n// ===== Hex helpers =====\n\nexport function bytesToHex(bytes: Uint8Array): string {\n return '0x' + Array.from(bytes).map((b) => b.toString(16).padStart(2, '0')).join('');\n}\n\nexport function hexToBytes(hex: string): Uint8Array {\n const cleaned = hex.startsWith('0x') ? hex.slice(2) : hex;\n return new Uint8Array(cleaned.match(/.{1,2}/g)!.map((b) => parseInt(b, 16)));\n}\n"],"mappings":"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","names":["AccessLevel","Permission","Role","sha256","gcm","randomBytes","encoder","sha256","randomBytes","gcm"]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/chains.ts","../src/contracts.ts","../src/crypto/encrypt.ts","../src/types.ts","../src/constants.ts","../src/crypto/ecdh.ts"],"sourcesContent":["import { ethers } from 'ethers';\nimport { CHAINS } from './chains.js';\nimport { REGISTRY_ABI, KEY_MANAGER_ABI, SCHEMA_REGISTRY_ABI, IDENTITY_REGISTRY_ABI } from './contracts.js';\nimport {\n derivePublicTopicKey,\n deriveKeyFromPassphrase,\n encryptMessage,\n decryptMessage,\n} from './crypto/encrypt.js';\nimport {\n deriveKeypairFromSignature,\n keypairFromPrivateKey,\n computeSharedSecret,\n deriveAESKeyFromSecret,\n encryptTopicKeyForUser,\n decryptTopicKey,\n bytesToHex,\n hexToBytes,\n} from './crypto/ecdh.js';\nimport { AccessLevel } from './types.js';\nimport type {\n ClawtennaOptions,\n ReadOptions,\n SendOptions,\n Message,\n Topic,\n Application,\n Member,\n TopicMessageFee,\n SchemaInfo,\n TopicSchemaBinding,\n ChainName,\n} from './types.js';\n\nexport class Clawntenna {\n readonly provider: ethers.JsonRpcProvider;\n readonly wallet: ethers.Wallet | null;\n readonly registry: ethers.Contract;\n readonly keyManager: ethers.Contract;\n readonly schemaRegistry: ethers.Contract;\n readonly identityRegistry: ethers.Contract | null;\n readonly chainName: ChainName;\n\n // In-memory ECDH state\n private ecdhPrivateKey: Uint8Array | null = null;\n private ecdhPublicKey: Uint8Array | null = null;\n private topicKeys: Map<number, Uint8Array> = new Map();\n\n constructor(options: ClawtennaOptions = {}) {\n const chainName = options.chain ?? 'base';\n const chain = CHAINS[chainName];\n if (!chain) throw new Error(`Unsupported chain: ${chainName}`);\n this.chainName = chainName;\n\n const rpcUrl = options.rpcUrl ?? chain.rpc;\n this.provider = new ethers.JsonRpcProvider(rpcUrl);\n\n const registryAddr = options.registryAddress ?? chain.registry;\n const keyManagerAddr = options.keyManagerAddress ?? chain.keyManager;\n const schemaRegistryAddr = options.schemaRegistryAddress ?? chain.schemaRegistry;\n\n if (options.privateKey) {\n this.wallet = new ethers.Wallet(options.privateKey, this.provider);\n this.registry = new ethers.Contract(registryAddr, REGISTRY_ABI, this.wallet);\n this.keyManager = new ethers.Contract(keyManagerAddr, KEY_MANAGER_ABI, this.wallet);\n this.schemaRegistry = new ethers.Contract(schemaRegistryAddr, SCHEMA_REGISTRY_ABI, this.wallet);\n this.identityRegistry = chain.identityRegistry\n ? new ethers.Contract(chain.identityRegistry, IDENTITY_REGISTRY_ABI, this.wallet)\n : null;\n } else {\n this.wallet = null;\n this.registry = new ethers.Contract(registryAddr, REGISTRY_ABI, this.provider);\n this.keyManager = new ethers.Contract(keyManagerAddr, KEY_MANAGER_ABI, this.provider);\n this.schemaRegistry = new ethers.Contract(schemaRegistryAddr, SCHEMA_REGISTRY_ABI, this.provider);\n this.identityRegistry = chain.identityRegistry\n ? new ethers.Contract(chain.identityRegistry, IDENTITY_REGISTRY_ABI, this.provider)\n : null;\n }\n }\n\n get address(): string | null {\n return this.wallet?.address ?? null;\n }\n\n // ===== MESSAGING =====\n\n /**\n * Send an encrypted message to a topic.\n * Automatically determines encryption key based on topic access level.\n */\n async sendMessage(topicId: number, text: string, options?: SendOptions): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required to send messages');\n\n let replyText = options?.replyText;\n let replyAuthor = options?.replyAuthor;\n\n // Auto-resolve reply metadata if replyTo is set but text/author aren't\n if (options?.replyTo && (!replyText || !replyAuthor)) {\n try {\n const messages = await this.readMessages(topicId, { limit: 50 });\n const original = messages.find(m => m.txHash === options.replyTo);\n if (original) {\n replyText = replyText || original.text.slice(0, 100);\n replyAuthor = replyAuthor || original.sender;\n }\n } catch {\n // Non-fatal: reply will still work, just without cached text/author\n }\n }\n\n const key = await this.getEncryptionKey(topicId);\n const encrypted = encryptMessage(text, key, {\n replyTo: options?.replyTo,\n replyText,\n replyAuthor,\n mentions: options?.mentions,\n });\n\n return this.registry.sendMessage(topicId, ethers.toUtf8Bytes(encrypted));\n }\n\n /**\n * Read and decrypt recent messages from a topic.\n */\n async readMessages(topicId: number, options?: ReadOptions): Promise<Message[]> {\n const limit = options?.limit ?? 50;\n const key = await this.getEncryptionKey(topicId);\n const filter = this.registry.filters.MessageSent(topicId);\n\n // Chunked log fetching to stay within RPC limits (e.g. Avalanche 2048 block cap)\n const CHUNK_SIZE = 2000;\n const currentBlock = await this.provider.getBlockNumber();\n const maxRange = options?.fromBlock != null ? currentBlock - options.fromBlock : 100_000;\n const startBlock = currentBlock - maxRange;\n\n const allEvents: ethers.EventLog[] = [];\n let toBlock = currentBlock;\n\n while (toBlock > startBlock && allEvents.length < limit) {\n const chunkFrom = Math.max(toBlock - CHUNK_SIZE + 1, startBlock);\n const events = await this.registry.queryFilter(filter, chunkFrom, toBlock);\n // Prepend since we're walking backwards\n allEvents.unshift(...(events as ethers.EventLog[]));\n toBlock = chunkFrom - 1;\n }\n\n const recent = allEvents.slice(-limit);\n const messages: Message[] = [];\n\n for (const log of recent) {\n const payloadStr = ethers.toUtf8String(log.args.payload);\n const parsed = decryptMessage(payloadStr, key);\n\n messages.push({\n topicId: BigInt(topicId),\n sender: log.args.sender,\n text: parsed?.text ?? '[decryption failed]',\n replyTo: parsed?.replyTo ?? null,\n mentions: parsed?.mentions ?? null,\n timestamp: log.args.timestamp,\n txHash: log.transactionHash,\n blockNumber: log.blockNumber,\n });\n }\n\n return messages;\n }\n\n /**\n * Subscribe to real-time messages on a topic.\n * Returns an unsubscribe function.\n */\n onMessage(\n topicId: number,\n callback: (msg: Message) => void\n ): () => void {\n let key: Uint8Array | null = null;\n\n // Pre-derive key, then start listening\n this.getEncryptionKey(topicId).then((k) => {\n key = k;\n });\n\n const handler = (\n tId: bigint,\n sender: string,\n payload: string,\n timestamp: bigint,\n event: ethers.EventLog\n ) => {\n if (!key) return;\n const payloadStr = ethers.toUtf8String(payload);\n const parsed = decryptMessage(payloadStr, key);\n\n callback({\n topicId: tId,\n sender,\n text: parsed?.text ?? '[decryption failed]',\n replyTo: parsed?.replyTo ?? null,\n mentions: parsed?.mentions ?? null,\n timestamp,\n txHash: event.transactionHash,\n blockNumber: event.blockNumber,\n });\n };\n\n this.registry.on(this.registry.filters.MessageSent(topicId), handler);\n return () => {\n this.registry.off(this.registry.filters.MessageSent(topicId), handler);\n };\n }\n\n // ===== NICKNAMES =====\n\n async setNickname(appId: number, nickname: string): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.setNickname(appId, nickname);\n }\n\n async getNickname(appId: number, address: string): Promise<string> {\n return this.registry.getNickname(appId, address);\n }\n\n async hasNickname(appId: number, address: string): Promise<boolean> {\n return this.registry.hasNickname(appId, address);\n }\n\n async canChangeNickname(appId: number, address: string): Promise<{ canChange: boolean; timeRemaining: bigint }> {\n const [canChange, timeRemaining] = await this.registry.canChangeNickname(appId, address);\n return { canChange, timeRemaining };\n }\n\n async clearNickname(appId: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.clearNickname(appId);\n }\n\n async setNicknameCooldown(appId: number, cooldownSeconds: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.setNicknameCooldown(appId, cooldownSeconds);\n }\n\n async getNicknameCooldown(appId: number): Promise<bigint> {\n return this.registry.appNicknameCooldown(appId);\n }\n\n // ===== TOPICS =====\n\n async createTopic(\n appId: number,\n name: string,\n description: string,\n accessLevel: AccessLevel\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.createTopic(appId, name, description, accessLevel);\n }\n\n async getTopic(topicId: number): Promise<Topic> {\n const t = await this.registry.getTopic(topicId);\n return {\n id: t.id,\n applicationId: t.applicationId,\n name: t.name,\n description: t.description,\n owner: t.owner,\n creator: t.creator,\n createdAt: t.createdAt,\n lastMessageAt: t.lastMessageAt,\n messageCount: t.messageCount,\n accessLevel: Number(t.accessLevel),\n active: t.active,\n };\n }\n\n async getApplicationTopics(appId: number): Promise<bigint[]> {\n return this.registry.getApplicationTopics(appId);\n }\n\n async getTopicCount(): Promise<number> {\n const count = await this.registry.topicCount();\n return Number(count);\n }\n\n async setTopicPermission(\n topicId: number,\n user: string,\n permission: number\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.setTopicPermission(topicId, user, permission);\n }\n\n async getTopicPermission(topicId: number, user: string): Promise<number> {\n const perm = await this.registry.getTopicPermission(topicId, user);\n return Number(perm);\n }\n\n // ===== MEMBERS =====\n\n async addMember(\n appId: number,\n address: string,\n nickname: string,\n roles: number\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.addMember(appId, address, nickname, roles);\n }\n\n async removeMember(appId: number, address: string): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.removeMember(appId, address);\n }\n\n async updateMemberRoles(\n appId: number,\n address: string,\n roles: number\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.updateMemberRoles(appId, address, roles);\n }\n\n async getMember(appId: number, address: string): Promise<Member> {\n const m = await this.registry.getMember(appId, address);\n return {\n account: m.account,\n nickname: m.nickname,\n roles: Number(m.roles),\n joinedAt: m.joinedAt,\n };\n }\n\n async isMember(appId: number, address: string): Promise<boolean> {\n return this.registry.isMember(appId, address);\n }\n\n async getApplicationMembers(appId: number): Promise<string[]> {\n return this.registry.getApplicationMembers(appId);\n }\n\n // ===== ACCESS CHECKS =====\n\n async canRead(topicId: number, address: string): Promise<boolean> {\n return this.registry.canReadTopic(topicId, address);\n }\n\n async canWrite(topicId: number, address: string): Promise<boolean> {\n return this.registry.canWriteToTopic(topicId, address);\n }\n\n // ===== APPLICATIONS =====\n\n async createApplication(\n name: string,\n description: string,\n frontendUrl: string,\n allowPublicTopicCreation: boolean\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.createApplication(name, description, frontendUrl, allowPublicTopicCreation);\n }\n\n async getApplicationCount(): Promise<number> {\n const count = await this.registry.applicationCount();\n return Number(count);\n }\n\n async updateFrontendUrl(appId: number, frontendUrl: string): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.updateApplicationFrontendUrl(appId, frontendUrl);\n }\n\n async getApplication(appId: number): Promise<Application> {\n const a = await this.registry.getApplication(appId);\n return {\n id: a.id,\n name: a.name,\n description: a.description,\n frontendUrl: a.frontendUrl,\n owner: a.owner,\n createdAt: a.createdAt,\n memberCount: Number(a.memberCount),\n topicCount: Number(a.topicCount),\n active: a.active,\n allowPublicTopicCreation: a.allowPublicTopicCreation,\n topicCreationFeeToken: a.topicCreationFeeToken,\n topicCreationFeeAmount: a.topicCreationFeeAmount,\n };\n }\n\n // ===== FEES =====\n\n async getTopicMessageFee(topicId: number): Promise<TopicMessageFee> {\n const [token, amount] = await this.registry.getTopicMessageFee(topicId);\n return { token, amount };\n }\n\n async setTopicCreationFee(\n appId: number,\n feeToken: string,\n feeAmount: bigint\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.setTopicCreationFee(appId, feeToken, feeAmount);\n }\n\n async setTopicMessageFee(\n topicId: number,\n feeToken: string,\n feeAmount: bigint\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.setTopicMessageFee(topicId, feeToken, feeAmount);\n }\n\n // ===== ECDH (Private Topics) =====\n\n /**\n * Derive ECDH keypair from wallet signature (deterministic).\n * Requires a signer capable of signing messages.\n */\n async deriveECDHFromWallet(appId: number = 1): Promise<{ publicKey: Uint8Array }> {\n if (!this.wallet) throw new Error('Wallet required');\n\n const { privateKey, publicKey } = await deriveKeypairFromSignature(\n this.wallet.address,\n (msg) => this.wallet!.signMessage(msg),\n appId\n );\n\n this.ecdhPrivateKey = privateKey;\n this.ecdhPublicKey = publicKey;\n return { publicKey };\n }\n\n /**\n * Load ECDH keypair from a hex private key (e.g. from credentials file).\n */\n loadECDHKeypair(privateKeyHex: string): void {\n const { privateKey, publicKey } = keypairFromPrivateKey(privateKeyHex);\n this.ecdhPrivateKey = privateKey;\n this.ecdhPublicKey = publicKey;\n }\n\n /**\n * Register ECDH public key on-chain.\n */\n async registerPublicKey(): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n if (!this.ecdhPublicKey) throw new Error('ECDH key not derived yet');\n\n const hasKey = await this.keyManager.hasPublicKey(this.wallet.address);\n if (hasKey) {\n throw new Error('Public key already registered on-chain');\n }\n\n return this.keyManager.registerPublicKey(this.ecdhPublicKey);\n }\n\n /**\n * Fetch and decrypt the topic symmetric key from an on-chain ECDH grant.\n */\n async fetchAndDecryptTopicKey(topicId: number): Promise<Uint8Array> {\n if (!this.ecdhPrivateKey) throw new Error('ECDH key not derived yet');\n\n const grant = await this.keyManager.getMyKey(topicId);\n const encryptedKey = ethers.getBytes(grant.encryptedKey);\n const granterPubKey = ethers.getBytes(grant.granterPublicKey);\n\n const topicKey = decryptTopicKey(encryptedKey, this.ecdhPrivateKey, granterPubKey);\n this.topicKeys.set(topicId, topicKey);\n return topicKey;\n }\n\n /**\n * Grant a user access to a private topic's symmetric key.\n */\n async grantKeyAccess(\n topicId: number,\n userAddress: string,\n topicKey: Uint8Array\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n if (!this.ecdhPrivateKey) throw new Error('ECDH key not derived yet');\n\n const hasKey = await this.keyManager.hasPublicKey(userAddress);\n if (!hasKey) {\n throw new Error(\n `User ${userAddress} has no ECDH public key registered. They must run 'keys register' first.`\n );\n }\n\n const userPubKeyBytes = ethers.getBytes(await this.keyManager.getPublicKey(userAddress));\n const encrypted = encryptTopicKeyForUser(topicKey, this.ecdhPrivateKey, userPubKeyBytes);\n return this.keyManager.grantKeyAccess(topicId, userAddress, encrypted);\n }\n\n /**\n * Store a pre-known topic key (e.g. loaded from credentials).\n */\n setTopicKey(topicId: number, key: Uint8Array): void {\n this.topicKeys.set(topicId, key);\n }\n\n /**\n * Check if an address has an ECDH public key registered on-chain.\n */\n async hasPublicKey(address: string): Promise<boolean> {\n return this.keyManager.hasPublicKey(address);\n }\n\n /**\n * Get an address's ECDH public key from chain.\n */\n async getPublicKey(address: string): Promise<Uint8Array> {\n const key = await this.keyManager.getPublicKey(address);\n return ethers.getBytes(key);\n }\n\n /**\n * Check if a user has key access for a topic.\n */\n async hasKeyAccess(topicId: number, address: string): Promise<boolean> {\n return this.keyManager.hasKeyAccess(topicId, address);\n }\n\n /**\n * Get the key grant details for a user on a topic.\n */\n async getKeyGrant(topicId: number, address: string): Promise<{\n encryptedKey: Uint8Array;\n granterPublicKey: Uint8Array;\n granter: string;\n keyVersion: bigint;\n grantedAt: bigint;\n }> {\n const g = await this.keyManager.getKeyGrant(topicId, address);\n return {\n encryptedKey: ethers.getBytes(g.encryptedKey),\n granterPublicKey: ethers.getBytes(g.granterPublicKey),\n granter: g.granter,\n keyVersion: g.keyVersion,\n grantedAt: g.grantedAt,\n };\n }\n\n /**\n * Get the current key version for a topic.\n */\n async getKeyVersion(topicId: number): Promise<bigint> {\n return this.keyManager.keyVersions(topicId);\n }\n\n /**\n * Batch grant key access to multiple users at once (max 50).\n */\n async batchGrantKeyAccess(\n topicId: number,\n users: string[],\n topicKey: Uint8Array\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n if (!this.ecdhPrivateKey) throw new Error('ECDH key not derived yet');\n\n const encryptedKeys: Uint8Array[] = [];\n for (const user of users) {\n const userPubKeyBytes = ethers.getBytes(await this.keyManager.getPublicKey(user));\n const encrypted = encryptTopicKeyForUser(topicKey, this.ecdhPrivateKey, userPubKeyBytes);\n encryptedKeys.push(encrypted);\n }\n\n return this.keyManager.batchGrantKeyAccess(topicId, users, encryptedKeys);\n }\n\n /**\n * Revoke a user's key access for a topic.\n */\n async revokeKeyAccess(topicId: number, address: string): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.keyManager.revokeKeyAccess(topicId, address);\n }\n\n /**\n * Rotate the key version for a topic. Existing grants become stale.\n */\n async rotateKey(topicId: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.keyManager.rotateKey(topicId);\n }\n\n // ===== SCHEMAS =====\n\n /**\n * Create a schema scoped to an application. Requires app admin role.\n */\n async createAppSchema(\n appId: number,\n name: string,\n description: string,\n body: string\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.schemaRegistry.createAppSchema(appId, name, description, body);\n }\n\n /**\n * Publish a new version of an existing schema.\n */\n async publishSchemaVersion(\n schemaId: number,\n body: string\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.schemaRegistry.publishSchemaVersion(schemaId, body);\n }\n\n /**\n * Deactivate a schema.\n */\n async deactivateSchema(schemaId: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.schemaRegistry.deactivateSchema(schemaId);\n }\n\n /**\n * Get schema info including application scope.\n */\n async getSchema(schemaId: number): Promise<SchemaInfo> {\n const s = await this.schemaRegistry.getSchemaWithApp(schemaId);\n return {\n id: Number(s.id),\n name: s.name,\n description: s.description,\n creator: s.creator,\n createdAt: Number(s.createdAt),\n versionCount: Number(s.versionCount),\n active: s.active,\n applicationId: Number(s.applicationId),\n };\n }\n\n /**\n * Get all schemas scoped to an application.\n */\n async getApplicationSchemas(appId: number): Promise<SchemaInfo[]> {\n const ids: bigint[] = await this.schemaRegistry.getApplicationSchemas(appId);\n const schemas: SchemaInfo[] = [];\n for (const id of ids) {\n const s = await this.getSchema(Number(id));\n schemas.push(s);\n }\n return schemas;\n }\n\n /**\n * Get schema body for a specific version.\n */\n async getSchemaBody(schemaId: number, version: number): Promise<string> {\n return this.schemaRegistry.getSchemaBody(schemaId, version);\n }\n\n /**\n * Get the schema binding for a topic.\n */\n async getTopicSchema(topicId: number): Promise<TopicSchemaBinding> {\n const s = await this.schemaRegistry.getTopicSchema(topicId);\n return {\n schemaId: Number(s.schemaId),\n version: Number(s.version),\n body: s.body,\n };\n }\n\n /**\n * Bind a schema version to a topic. Requires topic admin.\n */\n async setTopicSchema(\n topicId: number,\n schemaId: number,\n version: number\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.schemaRegistry.setTopicSchema(topicId, schemaId, version);\n }\n\n /**\n * Remove schema binding from a topic.\n */\n async clearTopicSchema(topicId: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.schemaRegistry.clearTopicSchema(topicId);\n }\n\n // ===== AGENT IDENTITY (V5) =====\n\n /**\n * Register your ERC-8004 agent identity for an application (V5).\n * Verifies ownership via ownerOf on the identity registry.\n */\n async registerAgentIdentity(appId: number, tokenId: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.registerAgentIdentity(appId, tokenId);\n }\n\n /**\n * Clear your agent identity registration for an application (V5).\n */\n async clearAgentIdentity(appId: number): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n return this.registry.clearAgentIdentity(appId);\n }\n\n /**\n * Get the registered agent token ID for a user in an application (V5/V6).\n * Returns 0 if not registered. V6 validates ownership live via ownerOf.\n */\n async getAgentTokenId(appId: number, address: string): Promise<bigint> {\n return this.registry.getAgentTokenId(appId, address);\n }\n\n /**\n * Check if a user has a registered agent identity for an application (V5/V6).\n * V6 validates ownership live — returns false if the token was transferred.\n */\n async hasAgentIdentity(appId: number, address: string): Promise<boolean> {\n return this.registry.hasAgentIdentity(appId, address);\n }\n\n // ===== ERC-8004 IDENTITY REGISTRY =====\n\n private requireIdentityRegistry(): ethers.Contract {\n if (!this.identityRegistry) {\n throw new Error('ERC-8004 Identity Registry not available on this chain. Use Base.');\n }\n return this.identityRegistry;\n }\n\n /**\n * Register as an agent on the ERC-8004 Identity Registry.\n * Optionally provide a URI for the agent's metadata.\n */\n async registerAgent(agentURI?: string): Promise<{ agentId: bigint; tx: ethers.TransactionResponse }> {\n if (!this.wallet) throw new Error('Wallet required');\n const registry = this.requireIdentityRegistry();\n\n const tx: ethers.TransactionResponse = agentURI\n ? await registry['register(string)'](agentURI)\n : await registry['register()']();\n\n const receipt = await tx.wait();\n let agentId = 0n;\n if (receipt) {\n for (const log of receipt.logs) {\n try {\n const parsed = registry.interface.parseLog(log);\n if (parsed?.name === 'Registered') {\n agentId = parsed.args.agentId;\n break;\n }\n } catch { /* skip non-matching logs */ }\n }\n }\n\n return { agentId, tx };\n }\n\n /**\n * Register as an agent with metadata entries.\n */\n async registerAgentWithMetadata(\n agentURI: string,\n metadata: Array<{ metadataKey: string; metadataValue: Uint8Array }>\n ): Promise<{ agentId: bigint; tx: ethers.TransactionResponse }> {\n if (!this.wallet) throw new Error('Wallet required');\n const registry = this.requireIdentityRegistry();\n\n const tx: ethers.TransactionResponse = await registry['register(string,(string,bytes)[])'](agentURI, metadata);\n\n const receipt = await tx.wait();\n let agentId = 0n;\n if (receipt) {\n for (const log of receipt.logs) {\n try {\n const parsed = registry.interface.parseLog(log);\n if (parsed?.name === 'Registered') {\n agentId = parsed.args.agentId;\n break;\n }\n } catch { /* skip non-matching logs */ }\n }\n }\n\n return { agentId, tx };\n }\n\n /**\n * Check if an address has an ERC-8004 agent identity NFT.\n * Defaults to the connected wallet address.\n */\n async isRegisteredAgent(address?: string): Promise<boolean> {\n const registry = this.requireIdentityRegistry();\n const addr = address ?? this.wallet?.address;\n if (!addr) throw new Error('Address required');\n\n const balance: bigint = await registry.balanceOf(addr);\n return balance > 0n;\n }\n\n /**\n * Get agent info by agent ID.\n */\n async getAgentInfo(agentId: number): Promise<{ owner: string; uri: string; wallet: string }> {\n const registry = this.requireIdentityRegistry();\n\n const [owner, uri, wallet] = await Promise.all([\n registry.ownerOf(agentId) as Promise<string>,\n registry.tokenURI(agentId) as Promise<string>,\n registry.getAgentWallet(agentId) as Promise<string>,\n ]);\n\n return { owner, uri, wallet };\n }\n\n /**\n * Set metadata for an agent.\n */\n async setAgentMetadata(\n agentId: number,\n key: string,\n value: Uint8Array\n ): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n const registry = this.requireIdentityRegistry();\n return registry.setMetadata(agentId, key, value);\n }\n\n /**\n * Get metadata for an agent by key.\n */\n async getAgentMetadata(agentId: number, key: string): Promise<string> {\n const registry = this.requireIdentityRegistry();\n const data: string = await registry.getMetadata(agentId, key);\n return ethers.toUtf8String(data);\n }\n\n /**\n * Update the URI for an agent registration.\n */\n async setAgentURI(agentId: number, newURI: string): Promise<ethers.TransactionResponse> {\n if (!this.wallet) throw new Error('Wallet required');\n const registry = this.requireIdentityRegistry();\n return registry.setAgentURI(agentId, newURI);\n }\n\n /**\n * Look up an agent by its wallet address using the V5/V6 on-chain registry mapping.\n * V6 validates ownership live — stale registrations (transferred tokens) return 0.\n * Returns registration status, token ID, owner, URI, wallet, and parsed metadata.\n */\n async getAgentByAddress(address: string, appId: number): Promise<\n | { registered: false }\n | { registered: true; agentId: number; owner: string; uri: string; wallet: string; metadata: Record<string, unknown> | null }\n > {\n const onChainTokenId: bigint = await this.registry.getAgentTokenId(appId, address);\n if (onChainTokenId === 0n) return { registered: false };\n\n const agentId = Number(onChainTokenId);\n const { owner, uri, wallet } = await this.getAgentInfo(agentId);\n let metadata: Record<string, unknown> | null = null;\n if (uri) {\n metadata = await this.parseTokenURI(uri);\n }\n return { registered: true, agentId, owner, uri, wallet, metadata };\n }\n\n /**\n * Parse a tokenURI into JSON metadata.\n * Handles data:application/json;base64, data:application/json, HTTP(S), and ipfs:// URIs.\n */\n private async parseTokenURI(uri: string): Promise<Record<string, unknown> | null> {\n try {\n if (uri.startsWith('data:application/json;base64,')) {\n const json = atob(uri.slice('data:application/json;base64,'.length));\n return JSON.parse(json);\n }\n if (uri.startsWith('data:application/json,')) {\n return JSON.parse(decodeURIComponent(uri.slice('data:application/json,'.length)));\n }\n let url = uri;\n if (url.startsWith('ipfs://')) {\n url = 'https://ipfs.io/ipfs/' + url.slice('ipfs://'.length);\n }\n const resp = await fetch(url);\n if (!resp.ok) return null;\n return await resp.json();\n } catch {\n return null;\n }\n }\n\n // ===== DATA EXPORT =====\n\n /**\n * Look up an application ID by its name.\n */\n async getApplicationByName(name: string): Promise<number> {\n const id = await this.registry.applicationNames(name);\n return Number(id);\n }\n\n /**\n * Get schema version details including body and publish timestamp.\n */\n async getSchemaVersion(schemaId: number, version: number): Promise<{ body: string; publishedAt: bigint }> {\n const [body, publishedAt] = await this.schemaRegistry.getSchemaVersion(schemaId, version);\n return { body, publishedAt };\n }\n\n /**\n * Export member data for a user in an application.\n */\n async exportMemberData(appId: number, user: string): Promise<string> {\n const data = await this.registry.exportMemberData(appId, user);\n return ethers.hexlify(data);\n }\n\n /**\n * Export all application data.\n */\n async exportApplicationData(appId: number): Promise<string> {\n const data = await this.registry.exportApplicationData(appId);\n return ethers.hexlify(data);\n }\n\n /**\n * Export user data from the key manager for specified topics.\n */\n async exportUserData(user: string, topicIds: number[]): Promise<string> {\n const data = await this.keyManager.exportUserData(user, topicIds);\n return ethers.hexlify(data);\n }\n\n // ===== INTERNAL =====\n\n /**\n * Get the encryption key for a topic, determining the type automatically.\n */\n private async getEncryptionKey(topicId: number): Promise<Uint8Array> {\n // Check for a stored private topic key first\n const storedKey = this.topicKeys.get(topicId);\n if (storedKey) return storedKey;\n\n // Fetch topic metadata to determine access level\n const topic = await this.getTopic(topicId);\n\n if (topic.accessLevel === AccessLevel.PRIVATE) {\n // Auto-fetch if ECDH keys are loaded\n if (this.ecdhPrivateKey) {\n return this.fetchAndDecryptTopicKey(topicId);\n }\n throw new Error(\n `Topic ${topicId} is PRIVATE. Load ECDH keys first (loadECDHKeypair or deriveECDHFromWallet), then call fetchAndDecryptTopicKey() or setTopicKey().`\n );\n }\n\n // Public or public_limited: derive deterministic key\n return derivePublicTopicKey(topicId);\n }\n}\n","import type { ChainConfig, ChainName } from './types.js';\n\nexport const CHAINS: Record<ChainName, ChainConfig> = {\n baseSepolia: {\n chainId: 84532,\n name: 'Base Sepolia',\n shortName: 'Sepolia',\n rpc: 'https://sepolia.base.org',\n explorer: 'https://sepolia.basescan.org',\n registry: '0xf39b193aedC1Ec9FD6C5ccc24fBAe58ba9f52413',\n keyManager: '0x5562B553a876CBdc8AA4B3fb0687f22760F4759e',\n schemaRegistry: '0xB7eB50e9058198b99b5b2589E6D70b2d99d5440a',\n identityRegistry: '0x8004A169FB4a3325136EB29fA0ceB6D2e539a432',\n },\n base: {\n chainId: 8453,\n name: 'Base',\n shortName: 'Base',\n rpc: 'https://mainnet.base.org',\n explorer: 'https://basescan.org',\n registry: '0x5fF6BF04F1B5A78ae884D977a3C80A0D8E2072bF',\n keyManager: '0xdc302ff43a34F6aEa19426D60C9D150e0661E4f4',\n schemaRegistry: '0x5c11d2eA4470eD9025D810A21a885FE16dC987Bd',\n identityRegistry: '0x8004A169FB4a3325136EB29fA0ceB6D2e539a432',\n },\n avalanche: {\n chainId: 43114,\n name: 'Avalanche C-Chain',\n shortName: 'Avalanche',\n rpc: 'https://api.avax.network/ext/bc/C/rpc',\n explorer: 'https://snowtrace.io',\n registry: '0x3Ca2FF0bD1b3633513299EB5d3e2d63e058b0713',\n keyManager: '0x5a5ea9D408FBA984fFf6e243Dcc71ff6E00C73E4',\n schemaRegistry: '0x23D96e610E8E3DA5341a75B77F1BFF7EA9c3A62B',\n identityRegistry: '0x8004A169FB4a3325136EB29fA0ceB6D2e539a432',\n },\n};\n\nexport const CHAIN_IDS: Record<number, ChainName> = {\n 84532: 'baseSepolia',\n 8453: 'base',\n 43114: 'avalanche',\n};\n\nexport function getChain(nameOrId: ChainName | number): ChainConfig {\n if (typeof nameOrId === 'number') {\n const name = CHAIN_IDS[nameOrId];\n if (!name) throw new Error(`Unsupported chain ID: ${nameOrId}`);\n return CHAINS[name];\n }\n const chain = CHAINS[nameOrId];\n if (!chain) throw new Error(`Unsupported chain: ${nameOrId}`);\n return chain;\n}\n","export const REGISTRY_ABI = [\n // ===== READ FUNCTIONS =====\n\n // Applications\n 'function applications(uint256) view returns (uint256 id, string name, string description, string frontendUrl, address owner, uint64 createdAt, uint32 memberCount, uint32 topicCount, bool active, bool allowPublicTopicCreation)',\n 'function getApplication(uint256 appId) view returns (tuple(uint256 id, string name, string description, string frontendUrl, address owner, uint64 createdAt, uint32 memberCount, uint32 topicCount, bool active, bool allowPublicTopicCreation, address topicCreationFeeToken, uint256 topicCreationFeeAmount))',\n 'function applicationCount() view returns (uint256)',\n 'function applicationNames(string) view returns (uint256)',\n\n // Topics\n 'function topics(uint256) view returns (uint256 id, uint256 applicationId, string name, string description, address owner, address creator, uint64 createdAt, uint64 lastMessageAt, uint256 messageCount, uint8 accessLevel, bool active)',\n 'function getTopic(uint256 topicId) view returns (tuple(uint256 id, uint256 applicationId, string name, string description, address owner, address creator, uint64 createdAt, uint64 lastMessageAt, uint256 messageCount, uint8 accessLevel, bool active))',\n 'function topicCount() view returns (uint256)',\n 'function getApplicationTopics(uint256 appId) view returns (uint256[])',\n\n // Members\n 'function members(uint256 appId, address user) view returns (address account, string nickname, uint8 roles, uint64 joinedAt)',\n 'function getMember(uint256 appId, address account) view returns (tuple(address account, string nickname, uint8 roles, uint64 joinedAt))',\n 'function isMember(uint256 appId, address account) view returns (bool)',\n 'function getApplicationMembers(uint256 appId) view returns (address[])',\n\n // Permissions\n 'function canReadTopic(uint256 topicId, address user) view returns (bool)',\n 'function canWriteToTopic(uint256 topicId, address user) view returns (bool)',\n 'function getTopicPermission(uint256 topicId, address user) view returns (uint8)',\n 'function topicPermissions(uint256, address) view returns (uint8)',\n\n // Nicknames\n 'function getNickname(uint256 appId, address user) view returns (string)',\n 'function hasNickname(uint256 appId, address user) view returns (bool)',\n 'function canChangeNickname(uint256 appId, address user) view returns (bool canChange, uint256 timeRemaining)',\n 'function appNicknameCooldown(uint256 appId) view returns (uint256)',\n\n // Fees\n 'function getTopicMessageFee(uint256 topicId) view returns (address token, uint256 amount)',\n\n // ===== WRITE FUNCTIONS =====\n\n // Applications\n 'function createApplication(string name, string description, string frontendUrl, bool allowPublicTopicCreation) returns (uint256)',\n 'function updateApplicationFrontendUrl(uint256 appId, string frontendUrl)',\n\n // Topics\n 'function createTopic(uint256 appId, string name, string description, uint8 accessLevel) returns (uint256)',\n 'function setTopicPermission(uint256 topicId, address user, uint8 permission)',\n\n // Members\n 'function addMember(uint256 appId, address member, string nickname, uint8 roles)',\n 'function removeMember(uint256 appId, address member)',\n 'function updateMemberRoles(uint256 appId, address member, uint8 roles)',\n 'function updateMemberNickname(uint256 appId, string nickname)',\n\n // Nicknames (V3)\n 'function setNickname(uint256 appId, string nickname)',\n 'function clearNickname(uint256 appId)',\n 'function setNicknameCooldown(uint256 appId, uint256 cooldownSeconds)',\n\n // Messaging\n 'function sendMessage(uint256 topicId, bytes payload)',\n\n // Fees\n 'function setTopicCreationFee(uint256 appId, address feeTokenAddr, uint256 feeAmount)',\n 'function setTopicMessageFee(uint256 topicId, address feeTokenAddr, uint256 feeAmount)',\n\n // ===== EVENTS =====\n 'event ApplicationCreated(uint256 indexed applicationId, string name, address indexed owner)',\n 'event TopicCreated(uint256 indexed topicId, uint256 indexed applicationId, string name, address indexed creator, uint8 accessLevel)',\n 'event MemberAdded(uint256 indexed applicationId, address indexed member, string nickname, uint8 roles)',\n 'event MemberRemoved(uint256 indexed applicationId, address indexed member)',\n 'event MemberRolesUpdated(uint256 indexed applicationId, address indexed member, uint8 roles)',\n 'event NicknameUpdated(uint256 indexed applicationId, address indexed member, string nickname)',\n 'event UserNicknameSet(uint256 indexed applicationId, address indexed user, string nickname)',\n 'event TopicPermissionSet(uint256 indexed topicId, address indexed user, uint8 permission)',\n 'event MessageSent(uint256 indexed topicId, address indexed sender, bytes payload, uint256 timestamp)',\n 'event TopicMessageFeeUpdated(uint256 indexed topicId, address token, uint256 amount)',\n\n // Agent identity (V5)\n 'function registerAgentIdentity(uint256 appId, uint256 tokenId)',\n 'function clearAgentIdentity(uint256 appId)',\n 'function getAgentTokenId(uint256 appId, address user) view returns (uint256)',\n 'function hasAgentIdentity(uint256 appId, address user) view returns (bool)',\n 'event AgentIdentityRegistered(uint256 indexed applicationId, address indexed user, uint256 tokenId)',\n 'event AgentIdentityCleared(uint256 indexed applicationId, address indexed user)',\n\n // Data export\n 'function exportMemberData(uint256 appId, address user) view returns (bytes)',\n 'function exportApplicationData(uint256 appId) view returns (bytes)',\n] as const;\n\nexport const SCHEMA_REGISTRY_ABI = [\n // ===== READ FUNCTIONS =====\n\n // Schema queries\n 'function schemaCount() view returns (uint256)',\n 'function getSchema(uint256 schemaId) view returns (uint256 id, string name, string description, address creator, uint64 createdAt, uint256 versionCount, bool active)',\n 'function getSchemaWithApp(uint256 schemaId) view returns (uint256 id, string name, string description, address creator, uint64 createdAt, uint256 versionCount, bool active, uint256 applicationId)',\n 'function getSchemaBody(uint256 schemaId, uint256 version) view returns (string)',\n 'function getSchemaVersion(uint256 schemaId, uint256 version) view returns (string body, uint64 publishedAt)',\n 'function schemaApplicationId(uint256 schemaId) view returns (uint256)',\n\n // App-scoped queries (V2)\n 'function getApplicationSchemas(uint256 applicationId) view returns (uint256[])',\n 'function getApplicationSchemaCount(uint256 applicationId) view returns (uint256)',\n\n // Topic binding\n 'function getTopicSchema(uint256 topicId) view returns (uint256 schemaId, uint256 version, string body)',\n\n // Version\n 'function contractVersion() view returns (string)',\n\n // ===== WRITE FUNCTIONS =====\n\n // Schema creation (V2 app-scoped)\n 'function createAppSchema(uint256 applicationId, string name, string description, string body) returns (uint256)',\n 'function publishSchemaVersion(uint256 schemaId, string body) returns (uint256)',\n 'function deactivateSchema(uint256 schemaId)',\n\n // Topic binding\n 'function setTopicSchema(uint256 topicId, uint256 schemaId, uint256 version)',\n 'function clearTopicSchema(uint256 topicId)',\n\n // ===== EVENTS =====\n 'event AppSchemaCreated(uint256 indexed schemaId, uint256 indexed applicationId, string name, address indexed creator)',\n 'event SchemaVersionPublished(uint256 indexed schemaId, uint256 version)',\n 'event SchemaDeactivated(uint256 indexed schemaId)',\n 'event TopicSchemaSet(uint256 indexed topicId, uint256 indexed schemaId, uint256 version)',\n 'event TopicSchemaCleared(uint256 indexed topicId)',\n 'event SchemaAssignedToApp(uint256 indexed schemaId, uint256 indexed applicationId)',\n] as const;\n\nexport const IDENTITY_REGISTRY_ABI = [\n 'function register() returns (uint256)',\n 'function register(string agentURI) returns (uint256)',\n 'function register(string agentURI, tuple(string metadataKey, bytes metadataValue)[] metadata) returns (uint256)',\n 'function getMetadata(uint256 agentId, string metadataKey) view returns (bytes)',\n 'function setMetadata(uint256 agentId, string metadataKey, bytes metadataValue)',\n 'function setAgentURI(uint256 agentId, string newURI)',\n 'function getAgentWallet(uint256 agentId) view returns (address)',\n 'function setAgentWallet(uint256 agentId, address newWallet, uint256 deadline, bytes signature)',\n 'function unsetAgentWallet(uint256 agentId)',\n 'function ownerOf(uint256 tokenId) view returns (address)',\n 'function balanceOf(address owner) view returns (uint256)',\n 'function tokenOfOwnerByIndex(address owner, uint256 index) view returns (uint256)',\n 'function tokenURI(uint256 tokenId) view returns (string)',\n 'function isAuthorizedOrOwner(address spender, uint256 agentId) view returns (bool)',\n 'function getVersion() pure returns (string)',\n 'event Registered(uint256 indexed agentId, string agentURI, address indexed owner)',\n] as const;\n\nexport const KEY_MANAGER_ABI = [\n // ===== READ FUNCTIONS =====\n 'function hasPublicKey(address user) view returns (bool)',\n 'function getPublicKey(address user) view returns (bytes)',\n 'function publicKeys(address) view returns (bytes)',\n 'function hasKeyAccess(uint256 topicId, address user) view returns (bool)',\n 'function getMyKey(uint256 topicId) view returns (bytes encryptedKey, bytes granterPublicKey, address granter, uint256 keyVersion, uint256 currentVersion)',\n 'function getKeyGrant(uint256 topicId, address user) view returns (tuple(bytes encryptedKey, bytes granterPublicKey, address granter, uint256 keyVersion, uint64 grantedAt))',\n 'function keyVersions(uint256 topicId) view returns (uint256)',\n\n // ===== WRITE FUNCTIONS =====\n 'function registerPublicKey(bytes publicKey)',\n 'function grantKeyAccess(uint256 topicId, address user, bytes encryptedKey)',\n 'function batchGrantKeyAccess(uint256 topicId, address[] users, bytes[] encryptedKeys)',\n 'function revokeKeyAccess(uint256 topicId, address user)',\n 'function rotateKey(uint256 topicId)',\n\n // Data export\n 'function exportUserData(address user, uint256[] topicIds) view returns (bytes)',\n\n // ===== EVENTS =====\n 'event PublicKeyRegistered(address indexed user, bytes publicKey)',\n 'event PublicKeyUpdated(address indexed user, bytes publicKey)',\n 'event KeyAccessGranted(uint256 indexed topicId, address indexed user, address indexed granter, uint256 version)',\n 'event KeyAccessRevoked(uint256 indexed topicId, address indexed user)',\n 'event KeyRotated(uint256 indexed topicId, uint256 newVersion)',\n] as const;\n","import { pbkdf2 } from '@noble/hashes/pbkdf2';\nimport { sha256 } from '@noble/hashes/sha256';\nimport { gcm } from '@noble/ciphers/aes';\nimport { randomBytes } from '@noble/hashes/utils';\nimport {\n PUBLIC_KEY_MATERIAL_PREFIX,\n SALT_PREFIX,\n PBKDF2_ITERATIONS,\n} from '../constants.js';\nimport type { EncryptedPayload, MessageContent } from '../types.js';\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\n/**\n * Derive AES-256 key for a public/public_limited topic.\n * Uses PBKDF2 with SHA-256, matching the web frontend exactly.\n */\nexport function derivePublicTopicKey(topicId: number | bigint): Uint8Array {\n const keyMaterial = PUBLIC_KEY_MATERIAL_PREFIX + topicId;\n const salt = encoder.encode(SALT_PREFIX + topicId);\n return pbkdf2(sha256, keyMaterial, salt, { c: PBKDF2_ITERATIONS, dkLen: 32 });\n}\n\n/**\n * Derive AES-256 key from arbitrary passphrase (for private topics with manual passphrase).\n */\nexport function deriveKeyFromPassphrase(passphrase: string, topicId: number | bigint): Uint8Array {\n const salt = encoder.encode(SALT_PREFIX + topicId);\n return pbkdf2(sha256, passphrase, salt, { c: PBKDF2_ITERATIONS, dkLen: 32 });\n}\n\n/**\n * Encrypt a message payload with AES-256-GCM.\n * Output format: `{ e: true, v: 2, iv: base64, ct: base64 }`\n * The ciphertext includes the GCM auth tag (last 16 bytes).\n */\nexport function encrypt(plaintext: string, key: Uint8Array): string {\n const iv = randomBytes(12);\n const aes = gcm(key, iv);\n const ciphertext = aes.encrypt(encoder.encode(plaintext));\n\n const payload: EncryptedPayload = {\n e: true,\n v: 2,\n iv: toBase64(iv),\n ct: toBase64(ciphertext),\n };\n return JSON.stringify(payload);\n}\n\n/**\n * Decrypt a message payload. Handles both v1 and v2 formats.\n * Returns the decrypted string or null on failure.\n */\nexport function decrypt(jsonStr: string, key: Uint8Array): string | null {\n try {\n const data = JSON.parse(jsonStr) as EncryptedPayload;\n if (!data.e) {\n // Not encrypted — return raw for caller to parse\n return jsonStr;\n }\n\n const iv = fromBase64(data.iv);\n const ct = fromBase64(data.ct);\n\n const aes = gcm(key, iv);\n const decrypted = aes.decrypt(ct);\n return decoder.decode(decrypted);\n } catch {\n return null;\n }\n}\n\n/**\n * Encrypt a structured message (text + optional replyTo/mentions).\n */\nexport function encryptMessage(\n text: string,\n key: Uint8Array,\n options?: { replyTo?: string; replyText?: string; replyAuthor?: string; mentions?: string[] }\n): string {\n const content: MessageContent = { text };\n if (options?.replyTo) content.replyTo = options.replyTo;\n if (options?.replyText) content.replyText = options.replyText;\n if (options?.replyAuthor) content.replyAuthor = options.replyAuthor;\n if (options?.mentions) content.mentions = options.mentions;\n return encrypt(JSON.stringify(content), key);\n}\n\n/**\n * Decrypt and parse a message payload into structured content.\n */\nexport function decryptMessage(\n jsonStr: string,\n key: Uint8Array\n): { text: string; replyTo: string | null; replyText: string | null; replyAuthor: string | null; mentions: string[] | null } | null {\n const decrypted = decrypt(jsonStr, key);\n if (!decrypted) return null;\n\n try {\n const content = JSON.parse(decrypted);\n if (typeof content === 'object' && content.text) {\n return {\n text: content.text,\n replyTo: content.replyTo || null,\n replyText: content.replyText || null,\n replyAuthor: content.replyAuthor || null,\n mentions: content.mentions || null,\n };\n }\n // Plain text string was JSON-stringified\n return { text: decrypted, replyTo: null, replyText: null, replyAuthor: null, mentions: null };\n } catch {\n // Raw string, not JSON\n return { text: decrypted, replyTo: null, replyText: null, replyAuthor: null, mentions: null };\n }\n}\n\n// ===== Base64 helpers (isomorphic) =====\n\nfunction toBase64(bytes: Uint8Array): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(bytes).toString('base64');\n }\n return btoa(String.fromCharCode(...bytes));\n}\n\nfunction fromBase64(str: string): Uint8Array {\n if (typeof Buffer !== 'undefined') {\n return new Uint8Array(Buffer.from(str, 'base64'));\n }\n return Uint8Array.from(atob(str), (c) => c.charCodeAt(0));\n}\n","// ===== Enums =====\n\nexport enum AccessLevel {\n PUBLIC = 0,\n PUBLIC_LIMITED = 1,\n PRIVATE = 2,\n}\n\nexport enum Permission {\n NONE = 0,\n READ = 1,\n WRITE = 2,\n READ_WRITE = 3,\n ADMIN = 4,\n}\n\nexport enum Role {\n MEMBER = 1,\n SUPPORT_MANAGER = 2,\n TOPIC_MANAGER = 4,\n ADMIN = 8,\n OWNER_DELEGATE = 16,\n}\n\n// ===== Chain types =====\n\nexport interface ChainConfig {\n chainId: number;\n name: string;\n shortName: string;\n rpc: string;\n explorer: string;\n registry: string;\n keyManager: string;\n schemaRegistry: string;\n identityRegistry?: string;\n}\n\nexport type ChainName = 'base' | 'avalanche' | 'baseSepolia';\n\n// ===== Contract return types =====\n\nexport interface Application {\n id: bigint;\n name: string;\n description: string;\n frontendUrl: string;\n owner: string;\n createdAt: bigint;\n memberCount: number;\n topicCount: number;\n active: boolean;\n allowPublicTopicCreation: boolean;\n topicCreationFeeToken?: string;\n topicCreationFeeAmount?: bigint;\n}\n\nexport interface Topic {\n id: bigint;\n applicationId: bigint;\n name: string;\n description: string;\n owner: string;\n creator: string;\n createdAt: bigint;\n lastMessageAt: bigint;\n messageCount: bigint;\n accessLevel: number;\n active: boolean;\n}\n\nexport interface Member {\n account: string;\n nickname: string;\n roles: number;\n joinedAt: bigint;\n}\n\nexport interface KeyGrant {\n encryptedKey: Uint8Array;\n granterPublicKey: Uint8Array;\n granter: string;\n keyVersion: bigint;\n grantedAt?: bigint;\n currentVersion?: bigint;\n}\n\n// ===== Message types =====\n\nexport interface MessageContent {\n text: string;\n replyTo?: string;\n replyText?: string;\n replyAuthor?: string;\n mentions?: string[];\n}\n\nexport interface EncryptedPayload {\n e: boolean;\n v: number;\n iv: string;\n ct: string;\n}\n\nexport interface Message {\n topicId: bigint;\n sender: string;\n text: string;\n replyTo: string | null;\n mentions: string[] | null;\n timestamp: bigint;\n txHash: string;\n blockNumber: number;\n}\n\nexport interface TopicMessageFee {\n token: string;\n amount: bigint;\n}\n\nexport interface SchemaInfo {\n id: number;\n name: string;\n description: string;\n creator: string;\n createdAt: number;\n versionCount: number;\n active: boolean;\n applicationId: number;\n}\n\nexport interface TopicSchemaBinding {\n schemaId: number;\n version: number;\n body: string;\n}\n\n// ===== Client options =====\n\nexport interface ClawtennaOptions {\n chain?: ChainName;\n chainId?: number;\n rpcUrl?: string;\n privateKey?: string;\n registryAddress?: string;\n keyManagerAddress?: string;\n schemaRegistryAddress?: string;\n}\n\nexport interface ReadOptions {\n limit?: number;\n fromBlock?: number;\n}\n\nexport interface SendOptions {\n replyTo?: string;\n replyText?: string;\n replyAuthor?: string;\n mentions?: string[];\n}\n\n// ===== Credentials =====\n\nexport interface Credentials {\n version: 2;\n wallet: {\n address: string;\n privateKey: string;\n };\n chains: Record<string, CredentialChain>;\n}\n\nexport interface CredentialChain {\n name: string;\n ecdh: {\n privateKey: string;\n publicKey: string;\n registered: boolean;\n } | null;\n apps: Record<string, CredentialApp>;\n}\n\nexport interface CredentialApp {\n name: string;\n nickname: string;\n agentTokenId: number | null;\n topicKeys: Record<string, string>;\n}\n\n// Legacy v1 format for migration\nexport interface CredentialsV1 {\n wallet: {\n address: string;\n privateKey: string;\n };\n apps: Record<string, {\n name: string;\n nickname: string;\n ecdh: {\n privateKey: string;\n publicKey: string;\n registeredOnChain: boolean;\n topicKeys: Record<string, string>;\n } | null;\n }>;\n}\n","import { AccessLevel, Permission, Role } from './types.js';\n\n// Re-export enums as individual constants for convenience\nexport const ACCESS_PUBLIC = AccessLevel.PUBLIC;\nexport const ACCESS_PUBLIC_LIMITED = AccessLevel.PUBLIC_LIMITED;\nexport const ACCESS_PRIVATE = AccessLevel.PRIVATE;\n\nexport const PERMISSION_NONE = Permission.NONE;\nexport const PERMISSION_READ = Permission.READ;\nexport const PERMISSION_WRITE = Permission.WRITE;\nexport const PERMISSION_READ_WRITE = Permission.READ_WRITE;\nexport const PERMISSION_ADMIN = Permission.ADMIN;\n\nexport const ROLE_MEMBER = Role.MEMBER;\nexport const ROLE_SUPPORT_MANAGER = Role.SUPPORT_MANAGER;\nexport const ROLE_TOPIC_MANAGER = Role.TOPIC_MANAGER;\nexport const ROLE_ADMIN = Role.ADMIN;\nexport const ROLE_OWNER_DELEGATE = Role.OWNER_DELEGATE;\n\n// Encryption constants\nexport const PUBLIC_KEY_MATERIAL_PREFIX = 'antenna-public-topic-';\nexport const SALT_PREFIX = 'antenna-v2-salt-';\nexport const PBKDF2_ITERATIONS = 100_000;\nexport const ECDH_HKDF_SALT = 'antenna-ecdh-v1';\nexport const ECDH_HKDF_INFO = 'topic-key-encryption';\nexport const ECDH_DERIVATION_MESSAGE = (address: string, appId: number) =>\n `Clawntenna ECDH Key Derivation\\n\\nThis signature generates your encryption key.\\nIt never leaves your device.\\n\\nWallet: ${address}\\nApp: ${appId}\\nChain: Base (8453)`;\n","import { secp256k1 } from '@noble/curves/secp256k1';\nimport { hkdf } from '@noble/hashes/hkdf';\nimport { sha256 } from '@noble/hashes/sha256';\nimport { gcm } from '@noble/ciphers/aes';\nimport { randomBytes } from '@noble/hashes/utils';\nimport { ECDH_HKDF_SALT, ECDH_HKDF_INFO, ECDH_DERIVATION_MESSAGE } from '../constants.js';\n\nconst encoder = new TextEncoder();\n\n/**\n * Derive an ECDH keypair deterministically from a wallet signature.\n * This produces the same keypair as the web frontend for the same wallet + app.\n */\nexport async function deriveKeypairFromSignature(\n walletAddress: string,\n signMessage: (message: string) => Promise<string>,\n appId: number = 1\n): Promise<{ privateKey: Uint8Array; publicKey: Uint8Array }> {\n const message = ECDH_DERIVATION_MESSAGE(walletAddress, appId);\n const signature = await signMessage(message);\n\n // Hash the signature string (as UTF-8 bytes) to get private key\n const sigBytes = encoder.encode(signature);\n const hashBuffer = sha256(sigBytes);\n const privateKey = new Uint8Array(hashBuffer);\n\n // Derive compressed public key\n const publicKey = secp256k1.getPublicKey(privateKey, true);\n\n return { privateKey, publicKey };\n}\n\n/**\n * Derive ECDH keypair from a raw private key (e.g. from stored credentials).\n */\nexport function keypairFromPrivateKey(privateKeyHex: string): {\n privateKey: Uint8Array;\n publicKey: Uint8Array;\n} {\n const cleaned = privateKeyHex.startsWith('0x') ? privateKeyHex.slice(2) : privateKeyHex;\n const privateKey = hexToBytes(cleaned);\n const publicKey = secp256k1.getPublicKey(privateKey, true);\n return { privateKey, publicKey };\n}\n\n/**\n * Compute ECDH shared secret (x-coordinate of shared point).\n */\nexport function computeSharedSecret(\n ourPrivateKey: Uint8Array,\n theirPublicKey: Uint8Array\n): Uint8Array {\n const sharedPoint = secp256k1.getSharedSecret(ourPrivateKey, theirPublicKey);\n // Return x-coordinate only (skip the 0x04 prefix byte)\n return sharedPoint.slice(1, 33);\n}\n\n/**\n * Derive AES-256 key from an ECDH shared secret using HKDF.\n * Matches the web frontend: salt='antenna-ecdh-v1', info='topic-key-encryption'.\n */\nexport function deriveAESKeyFromSecret(\n sharedSecret: Uint8Array,\n info: string = ECDH_HKDF_INFO\n): Uint8Array {\n return hkdf(sha256, sharedSecret, encoder.encode(ECDH_HKDF_SALT), info, 32);\n}\n\n/**\n * Encrypt a topic symmetric key for a recipient using ECDH.\n * Returns IV (12 bytes) + ciphertext (includes GCM auth tag).\n */\nexport function encryptTopicKeyForUser(\n topicKey: Uint8Array,\n ourPrivateKey: Uint8Array,\n recipientPublicKey: Uint8Array\n): Uint8Array {\n const shared = computeSharedSecret(ourPrivateKey, recipientPublicKey);\n const aesKey = deriveAESKeyFromSecret(shared);\n const iv = randomBytes(12);\n const aes = gcm(aesKey, iv);\n const ciphertext = aes.encrypt(topicKey);\n\n // Combine: IV + ciphertext\n const result = new Uint8Array(iv.length + ciphertext.length);\n result.set(iv);\n result.set(ciphertext, iv.length);\n return result;\n}\n\n/**\n * Decrypt a topic symmetric key received via ECDH grant.\n * Input format: IV (12 bytes) + ciphertext (includes GCM auth tag).\n */\nexport function decryptTopicKey(\n encryptedKey: Uint8Array,\n ourPrivateKey: Uint8Array,\n granterPublicKey: Uint8Array\n): Uint8Array {\n const shared = computeSharedSecret(ourPrivateKey, granterPublicKey);\n const aesKey = deriveAESKeyFromSecret(shared);\n const iv = encryptedKey.slice(0, 12);\n const ciphertext = encryptedKey.slice(12);\n const aes = gcm(aesKey, iv);\n return aes.decrypt(ciphertext);\n}\n\n// ===== Hex helpers =====\n\nexport function bytesToHex(bytes: Uint8Array): string {\n return '0x' + Array.from(bytes).map((b) => b.toString(16).padStart(2, '0')).join('');\n}\n\nexport function hexToBytes(hex: string): Uint8Array {\n const cleaned = hex.startsWith('0x') ? hex.slice(2) : hex;\n return new Uint8Array(cleaned.match(/.{1,2}/g)!.map((b) => parseInt(b, 16)));\n}\n"],"mappings":"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","names":["AccessLevel","Permission","Role","sha256","gcm","randomBytes","encoder","sha256","randomBytes","gcm"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clawntenna",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.4",
|
|
4
4
|
"description": "On-chain encrypted messaging SDK for AI agents. Permissionless public channels, ECDH-secured private channels. Application-scoped schemas.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -28,20 +28,24 @@
|
|
|
28
28
|
"scripts": {
|
|
29
29
|
"build": "tsup",
|
|
30
30
|
"dev": "tsup --watch",
|
|
31
|
-
"typecheck": "tsc --noEmit"
|
|
31
|
+
"typecheck": "tsc --noEmit",
|
|
32
|
+
"test": "vitest run",
|
|
33
|
+
"test:watch": "vitest",
|
|
34
|
+
"test:coverage": "vitest run --coverage"
|
|
32
35
|
},
|
|
33
36
|
"peerDependencies": {
|
|
34
37
|
"ethers": "^6.0.0"
|
|
35
38
|
},
|
|
36
39
|
"dependencies": {
|
|
40
|
+
"@noble/ciphers": "^1.2.0",
|
|
37
41
|
"@noble/curves": "^1.8.0",
|
|
38
|
-
"@noble/hashes": "^1.7.0"
|
|
39
|
-
"@noble/ciphers": "^1.2.0"
|
|
42
|
+
"@noble/hashes": "^1.7.0"
|
|
40
43
|
},
|
|
41
44
|
"devDependencies": {
|
|
42
45
|
"ethers": "^6.16.0",
|
|
43
46
|
"tsup": "^8.4.0",
|
|
44
|
-
"typescript": "^5.7.0"
|
|
47
|
+
"typescript": "^5.7.0",
|
|
48
|
+
"vitest": "^4.0.18"
|
|
45
49
|
},
|
|
46
50
|
"keywords": [
|
|
47
51
|
"clawntenna",
|