@intuweb3/sdk
Version:
INTU SDK - Modern blockchain interaction toolkit
1,028 lines • 75.7 kB
JavaScript
import { createRandomMessage, createSeed, formTransaction, getMasterPublicKey, parseTransaction, preRegister, registerStepOne, registerStepTwo, registerStepThree, signTransaction, signTransactionWithoutLambda, getPolybaseKey, getUniqueHashFromSignature, registerWasmAsset, } from "./cryptography/index.native.js";
import { createVault, preRegisterStep, proposeTransaction, userCompleteVault, userConfirmTx, registerUserAll, } from "./web3/signerfunctions.js";
import { getFilteredUserInitializedLogs, getFilteredUserInitializedLogsSingle, _getProposal, _getTransaction, _getTransactions, getVault, } from "./web3/providerfunctions.js";
import { getUserPreRegisterInfos, getUserSignature, getUtilsParams, getUserRegistrationAllInfos, } from "./web3/utils.js";
import { ethers } from "ethers";
import { hexToBytes } from "@noble/hashes/utils";
import { finalizeEvent, getPublicKey } from "nostr-tools/pure";
import { Relay } from "nostr-tools/relay";
import { SimplePool } from "nostr-tools";
import { parseEther } from "viem";
import { createViemClientForTransaction, getVaultContract, } from "./web3/helper/index.js";
import { getProviderForChain } from "../tools/chainList.js";
import { getBestProvider, } from "../utils/reliable-provider.js";
import { getUserPreRegisterInfosDirect, getUserRegistrationAllInfosDirect, getTransactionLeanDirect, getDataWithFallback, } from "./direct/index.js";
let getGraphEndpoint;
(async () => {
const constants = await import("../tools/constants.js");
getGraphEndpoint = constants.getGraphEndpoint;
})();
export async function createIntuAccount(addressList, vaultName, rotationThreshold, transactionThreshold, adminThreshold, signer, returnHash = false) {
const { seed } = await createSeed();
const { message } = await createRandomMessage();
let createVaultResult = await createVault(addressList, vaultName, rotationThreshold, transactionThreshold, adminThreshold, message, seed, signer, returnHash);
if (createVaultResult instanceof Error) {
throw createVaultResult;
}
return createVaultResult;
}
export async function createPolybaseKey(vaultAddress, signer, intuSignature) {
let signature = "";
intuSignature
? (signature = intuSignature)
: (signature = await getUserSignature(vaultAddress, signer));
return await getPolybaseKey(signature);
}
export async function preRegistration(vaultAddress, signer, intuSignature, returnHash) {
let userAddress = await signer.getAddress();
const chainId = (await signer.provider.getNetwork()).chainId;
const provider = await getBestProvider(chainId, signer);
const indexerUrl = process.env.INDEXER_URL ||
process.env.REACT_APP_INDEXER_URL ||
getGraphEndpoint(chainId);
const preRegCheck = await getDataWithFallback(() => getUserPreRegisterInfos(vaultAddress, userAddress, provider), provider, indexerUrl, 50, () => getUserPreRegisterInfosDirect(vaultAddress, userAddress, provider));
if (preRegCheck.registered) {
console.log("user already preregistered : ", userAddress);
return `User already preregistered : ${userAddress}`;
}
let signature;
intuSignature
? (signature = intuSignature)
: (signature = await getUserSignature(vaultAddress, signer));
const { encryptionKey, megaPublicKey, encMegaSecretKey } = await preRegister(signature);
let dbKey = await getPolybaseKey(signature);
let sk = dbKey.key;
let pkfinal = getPublicKey(hexToBytes(String(sk)));
let rh = returnHash || false;
return await preRegisterStep(vaultAddress, encryptionKey, megaPublicKey, encMegaSecretKey, pkfinal, signer, rh);
}
export async function automateRegistration(vaultAddress, signer, nostrNode, intuSignature) {
console.log("Starting automated registration for vault:", vaultAddress);
const startTime = Date.now();
let retries = 0;
let maxRetries = 200;
let signature = "";
const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay));
intuSignature
? (signature = intuSignature)
: (signature = await getUserSignature(vaultAddress, signer));
const userAddress = await signer.getAddress();
const chainId = (await signer.provider.getNetwork()).chainId;
const provider = await getBestProvider(chainId, signer);
const vaultContract = await getVaultContract(vaultAddress, provider);
let { users, createdDate } = await vaultContract.vaultInfos();
if (typeof global !== "undefined" &&
global.__intuWebViewWasmService &&
global.__intuWebViewWasmService.isWasmReady()) {
console.log("✅ [AutoReg] WebView WASM is ready - registration steps will use WebView WASM");
}
else {
console.warn("⚠️ [AutoReg] WebView WASM not ready - registration steps may fail");
}
let seed, threshold, index, megaPkArray, encMegaSecretKey, dbKeyArray;
const userIndex = users.findIndex((address) => userAddress == address);
let result = await getUtilsParams(vaultAddress, userAddress, provider);
seed = result.seed;
index = result.index;
megaPkArray = result.megaPkArray;
encMegaSecretKey = result.encMegaSecretKey[userIndex];
threshold = result.threshold;
dbKeyArray = result.dbKeyArray;
console.log("Waiting for all users to complete pre-registration...");
while (dbKeyArray.filter(Boolean).length < users.length) {
if (retries < maxRetries) {
console.log(`Waiting for pre-registration: ${dbKeyArray.filter(Boolean).length}/${users.length} users ready`);
await sleep(750);
result = await getUtilsParams(vaultAddress, userAddress, provider);
dbKeyArray = [...result.dbKeyArray];
seed = result.seed;
index = result.index;
megaPkArray = result.megaPkArray;
encMegaSecretKey = result.encMegaSecretKey[userIndex];
threshold = result.threshold;
retries++;
}
else {
console.log("⚠️ [AutoReg] Max retries reached, proceeding anyway (this will probably fail)");
break;
}
}
console.log("All users pre-registered, proceeding to NOSTR setup");
retries = 0;
let true_threshold = Math.ceil((megaPkArray.length * threshold) / 100);
let round1counter = [];
let round2counter = [];
let round3counter = [];
let pedersenDealingsKeyArray = [];
let pedersenDealingsKappaArray = [];
let pedersenDealingsLambdaArray = [];
let pedersenOpeningsKeyArray = [];
let pedersenOpeningsKappaArray = [];
let pedersenOpeningsLambdaArray = [];
let simpleKeyDealingsArray = [];
let simpleLambdaDealingsArray = [];
let pedersenKeyTranscriptsArray = [];
let pedersenKappaTranscriptsArray = [];
let pedersenLambdaTranscriptsArray = [];
let dealingsKappaTimesLambdaArray = [];
let simpleKeyOpeningsArray = [];
let simpleKappaOpeningsArray = [];
let dealingKeyTimesLambdaArray = [];
let simpleKeyTranscriptsArray = [];
let simpleKappaTranscriptsArray = [];
dealingsKappaTimesLambdaArray = new Array(users.length).fill("");
simpleKeyOpeningsArray = new Array(users.length).fill("");
simpleKappaOpeningsArray = new Array(users.length).fill("");
dealingKeyTimesLambdaArray = new Array(users.length).fill("");
simpleKeyTranscriptsArray = new Array(users.length).fill("");
simpleKappaTranscriptsArray = new Array(users.length).fill("");
console.log("Setting up NOSTR relays...");
let preRelays = ["wss://nostr.intu.xyz"];
if (nostrNode) {
preRelays.unshift(nostrNode);
}
let relays = [];
for (const relayUrl of preRelays) {
try {
await Relay.connect(relayUrl);
relays.push(relayUrl);
console.log(`Connected to NOSTR relay: ${relayUrl}`);
break;
}
catch (error) {
console.error(`Error connecting to relay ${relayUrl}:`, error);
}
}
if (relays.length === 0) {
console.error("No NOSTR relays available");
throw new Error("No relays available");
}
relays = [...new Set(relays)];
const pool = new SimplePool();
let dbKey = await getPolybaseKey(signature);
let sk = dbKey.key;
const { getPublicKey } = await import("nostr-tools");
const ourPubkey = getPublicKey(hexToBytes(String(sk)));
console.log(`[AutoReg-Native] 🔑 Our NOSTR pubkey: ${ourPubkey.substring(0, 16)}... (full: ${ourPubkey})`);
console.log(`[AutoReg-Native] 🔑 Our NOSTR secret key (first 16 chars): ${sk.substring(0, 16)}...`);
let h;
let eoseReceivedFlag = false;
let eventHandlers = null;
try {
let sinceTimestamp;
if (createdDate) {
if (typeof createdDate.toNumber === "function") {
sinceTimestamp = createdDate.toNumber();
}
else {
sinceTimestamp = Number(createdDate);
}
if (isNaN(sinceTimestamp) || sinceTimestamp <= 0) {
sinceTimestamp = undefined;
}
}
const filterClone = {
kinds: [1],
};
if (sinceTimestamp) {
filterClone.since = sinceTimestamp;
}
eventHandlers = {
onevent(event) {
try {
let data = JSON.parse(event.content);
if (data.vaultAddress && data.vaultAddress !== vaultAddress) {
return;
}
if ("pedersen_key_dealing" in data) {
pedersenDealingsKeyArray[data.u] = data.pedersen_key_dealing;
pedersenDealingsKappaArray[data.u] = data.pedersen_kappa_dealing;
pedersenDealingsLambdaArray[data.u] = data.pedersen_lambda_dealing;
round1counter[data.u] = 1;
}
else if ("simple_key_dealing" in data) {
pedersenOpeningsKeyArray[data.u] = data.pedersen_key_opening;
pedersenOpeningsKappaArray[data.u] = data.pedersen_kappa_opening;
pedersenOpeningsLambdaArray[data.u] = data.pedersen_lambda_opening;
simpleKeyDealingsArray[data.u] = data.simple_key_dealing;
simpleLambdaDealingsArray[data.u] = data.simple_lambda_dealing;
pedersenKeyTranscriptsArray[data.u] = data.pedersen_key_transcript;
pedersenKappaTranscriptsArray[data.u] =
data.pedersen_kappa_transcript;
pedersenLambdaTranscriptsArray[data.u] =
data.pedersen_lambda_transcript;
round2counter[data.u] = 1;
}
else if ("dealing_kappa_times_lambda" in data) {
simpleKeyOpeningsArray[data.u] = data.simple_key_opening;
simpleKappaOpeningsArray[data.u] = data.simple_kappa_opening;
dealingKeyTimesLambdaArray[data.u] = data.dealing_key_times_lambda;
dealingsKappaTimesLambdaArray[data.u] =
data.dealing_kappa_times_lambda;
simpleKeyTranscriptsArray[data.u] = data.simple_key_transcript;
simpleKappaTranscriptsArray[data.u] = data.simple_kappa_transcript;
round3counter[data.u] = 1;
}
}
catch (error) {
console.error("❌ [AutoReg] Unexpected error processing event:", error);
}
},
onnotice(notice) {
},
oneose() {
eoseReceivedFlag = true;
},
};
if (sinceTimestamp) {
console.log(`[AutoReg-Native] 📡 Filter 'since' timestamp: ${sinceTimestamp} (${new Date(sinceTimestamp * 1000).toISOString()})`);
console.log(`[AutoReg-Native] 📡 Current timestamp: ${Math.floor(Date.now() / 1000)} (${new Date().toISOString()})`);
console.log(`[AutoReg-Native] ⚠️ Events published before ${sinceTimestamp} will be filtered out!`);
}
h = pool.subscribeMany(relays, filterClone, eventHandlers);
if (!h) {
throw new Error("Failed to create subscription");
}
try {
const poolAny = pool;
const relayUrlVariations = relays
.map((url) => [
url,
url.endsWith("/") ? url.slice(0, -1) : url + "/",
])
.flat();
let relay = null;
if (poolAny.relays && poolAny.relays instanceof Map) {
for (const url of relayUrlVariations) {
relay = poolAny.relays.get(url);
if (relay) {
console.log(`[AutoReg-Native] 🔧 Found relay for WebSocket interception: ${url}`);
break;
}
}
if (!relay && poolAny.relays.size > 0) {
const firstKey = poolAny.relays.keys().next().value;
if (firstKey) {
relay = poolAny.relays.get(firstKey);
console.log(`[AutoReg-Native] 🔧 Using first relay for WebSocket interception: ${firstKey}`);
}
}
}
if (relay && relay.ws) {
const ws = relay.ws;
const originalOnMessage = ws.onmessage;
ws.onmessage = (event) => {
try {
const data = typeof event.data === "string"
? JSON.parse(event.data)
: event.data;
if (Array.isArray(data) && data[0] === "EVENT" && data[2]) {
const nostrEvent = data[2];
try {
const eventContent = JSON.parse(nostrEvent.content || "{}");
const eventVaultAddress = eventContent.vaultAddress;
const vaultMatch = eventVaultAddress === vaultAddress;
const hasVaultAddress = !!eventVaultAddress;
if (hasVaultAddress && !vaultMatch) {
if (originalOnMessage) {
try {
originalOnMessage.call(ws, event);
}
catch (e) {
}
}
return;
}
}
catch (parseError) {
if (process.env.DEBUG) {
console.warn(`[AutoReg-Native] Could not parse event content for vaultAddress check:`, parseError);
}
}
if (eventHandlers.onevent) {
eventHandlers.onevent(nostrEvent);
}
return;
}
}
catch (e) {
if (process.env.DEBUG) {
console.warn(`[AutoReg-Native] Error parsing intercepted message:`, e);
}
}
if (originalOnMessage) {
try {
originalOnMessage.call(ws, event);
}
catch (e) {
if (process.env.DEBUG) {
console.warn(`[AutoReg-Native] Error in original onmessage:`, e);
}
}
}
};
}
else {
console.warn(`[AutoReg-Native] ⚠️ Could not find relay WebSocket for interception`);
}
}
catch (e) {
console.warn(`[AutoReg-Native] ⚠️ Failed to set up WebSocket interception:`, e);
}
await sleep(500);
}
catch (error) {
console.error("Error connecting to NOSTR relay(s): " + relays + " ERROR: ", error);
throw error;
}
console.log("⏳ [AutoReg] Waiting for EOSE before starting registration...");
let eoseReceived = false;
for (let i = 0; i < 20; i++) {
await sleep(250);
if (eoseReceivedFlag) {
eoseReceived = true;
console.log("✅ [AutoReg] EOSE received - subscription ready, starting registration");
break;
}
}
if (!eoseReceived) {
console.warn("⚠️ [AutoReg] EOSE not received, but proceeding anyway...");
}
const { pedersenDealingArray } = await registerStepOne(seed, true_threshold, index, megaPkArray);
let step1Data = {
u: userIndex,
vaultAddress: vaultAddress,
pedersen_key_dealing: pedersenDealingArray[0],
pedersen_kappa_dealing: pedersenDealingArray[1],
pedersen_lambda_dealing: pedersenDealingArray[2],
};
if (typeof global !== "undefined") {
if (!global.crypto) {
global.crypto = {};
}
if (!global.crypto.getRandomValues) {
global.crypto.getRandomValues = function (array) {
const uint8Array = new Uint8Array(array.buffer, array.byteOffset, array.byteLength);
for (let i = 0; i < uint8Array.length; i++) {
uint8Array[i] = Math.floor(Math.random() * 256);
}
return array;
};
}
}
let eventTemplate1 = {
kind: 1,
created_at: Math.floor(Date.now() / 1000),
content: JSON.stringify(step1Data),
tags: [],
};
const signedEvent1 = finalizeEvent(eventTemplate1, hexToBytes(String(sk)));
await pool.publish(relays, signedEvent1);
if (eventHandlers.onevent) {
eventHandlers.onevent(signedEvent1);
}
let completeProcess = false;
const bigUpdater = async () => {
try {
if (retries > maxRetries) {
console.log("⚠️ [AutoReg] Max retries reached in bigUpdater");
return false;
}
const round1Complete = round1counter.filter(Number).length;
const round2Complete = round2counter.filter(Number).length;
const round3Complete = round3counter.filter(Number).length;
if (pedersenDealingsKeyArray.length === users.length &&
simpleKeyDealingsArray.length === users.length &&
dealingsKappaTimesLambdaArray.length === users.length) {
if (dealingsKappaTimesLambdaArray.every(Boolean)) {
console.log("🎉 [AutoReg] All rounds complete!");
completeProcess = true;
}
}
if (!completeProcess) {
if (round1counter.filter(Number).length === users.length &&
!pedersenOpeningsKeyArray[userIndex]) {
console.log("🚀 [AutoReg] All users completed step 1, starting step 2...");
const { pedersenOpeningArray, simpleDealingArray, pedersenTranscriptArray, } = await registerStepTwo(seed, true_threshold, index, megaPkArray, signature, encMegaSecretKey, pedersenDealingsKeyArray, pedersenDealingsKappaArray, pedersenDealingsLambdaArray);
let step2Data = {
u: userIndex,
vaultAddress: vaultAddress,
pedersen_key_opening: pedersenOpeningArray[0],
pedersen_kappa_opening: pedersenOpeningArray[1],
pedersen_lambda_opening: pedersenOpeningArray[2],
simple_key_dealing: simpleDealingArray[0],
simple_lambda_dealing: simpleDealingArray[1],
pedersen_key_transcript: pedersenTranscriptArray[0],
pedersen_kappa_transcript: pedersenTranscriptArray[1],
pedersen_lambda_transcript: pedersenTranscriptArray[2],
};
let eventTemplate2 = {
kind: 1,
created_at: Math.floor(Date.now() / 1000),
content: JSON.stringify(step2Data),
tags: [],
};
const signedEvent2 = finalizeEvent(eventTemplate2, hexToBytes(String(sk)));
await pool.publish(relays, signedEvent2);
if (eventHandlers.onevent) {
eventHandlers.onevent(signedEvent2);
}
}
else if (pedersenOpeningsKeyArray[userIndex] &&
round2counter.filter(Number).length !== users.length) {
if (retries % 20 === 0) {
console.log("⏳ [AutoReg] Waiting for other users to complete step 2...");
}
}
if (round2counter.filter(Number).length === users.length &&
!dealingsKappaTimesLambdaArray[userIndex]) {
console.log("🚀 [AutoReg] All users completed step 2, starting step 3...");
const { simpleOpeningArray, multiplyDealingArray, simpleTranscriptArray, } = await registerStepThree(seed, true_threshold, index, megaPkArray, signature, encMegaSecretKey, simpleKeyDealingsArray, simpleLambdaDealingsArray, pedersenKeyTranscriptsArray[userIndex], pedersenKappaTranscriptsArray[userIndex], pedersenOpeningsLambdaArray[userIndex]);
let step3Data = {
u: userIndex,
vaultAddress: vaultAddress,
simple_key_opening: simpleOpeningArray[0],
simple_kappa_opening: simpleOpeningArray[1],
dealing_key_times_lambda: multiplyDealingArray[0],
dealing_kappa_times_lambda: multiplyDealingArray[1],
simple_key_transcript: simpleTranscriptArray[0],
simple_kappa_transcript: simpleTranscriptArray[1],
};
let eventTemplate3 = {
kind: 1,
created_at: Math.floor(Date.now() / 1000),
content: JSON.stringify(step3Data),
tags: [],
};
const signedEvent3 = finalizeEvent(eventTemplate3, hexToBytes(String(sk)));
await pool.publish(relays, signedEvent3);
if (eventHandlers.onevent) {
eventHandlers.onevent(signedEvent3);
}
}
else if (dealingsKappaTimesLambdaArray[userIndex] &&
round3counter.filter(Number).length !== users.length) {
if (retries % 20 === 0) {
console.log("⏳ [AutoReg] Current user completed all steps! Waiting for others to complete step 3...");
}
}
if (round3counter.filter(Number).length === users.length) {
completeProcess = true;
if (h._checkInterval) {
clearInterval(h._checkInterval);
}
await h.close();
return true;
}
}
}
catch (error) {
console.error("An error occurred in bigUpdater:", error);
}
return true;
};
async function keepCheckingUntilTrue() {
try {
while (!completeProcess) {
try {
await sleep(500);
retries++;
if (retries % 20 === 0) {
}
const bigUpdaterResult = await bigUpdater();
if (!bigUpdaterResult) {
console.log("❌ [AutoReg] bigUpdater returned false - max retries reached, exiting loop");
break;
}
}
catch (error) {
console.error("❌ [AutoReg] Error in processing loop:", error);
break;
}
}
const endTime = Date.now();
console.log(`Registration completed in ${endTime - startTime} ms`);
return completeProcess;
}
catch (error) {
console.error("❌ [AutoReg] Error in keepCheckingUntilTrue:", error);
return false;
}
}
const finalResult = await keepCheckingUntilTrue();
if (h && h._checkInterval) {
clearInterval(h._checkInterval);
}
await h.close();
return finalResult;
}
export async function testNostrSubscriptionNative() {
try {
console.log("🧪 [SDK Test] Starting NOSTR subscription test...\n");
if (typeof global !== "undefined") {
if (!global.crypto) {
global.crypto = {};
}
if (!global.crypto.getRandomValues) {
if (process.env.DEBUG) {
console.log("⚠️ [SDK Test] crypto.getRandomValues not available, adding polyfill for finalizeEvent...");
}
global.crypto.getRandomValues = function (array) {
if (array === null) {
return array;
}
const uint8Array = new Uint8Array(array.buffer, array.byteOffset, array.byteLength);
for (let i = 0; i < uint8Array.length; i++) {
uint8Array[i] = Math.floor(Math.random() * 256);
}
return array;
};
if (process.env.DEBUG) {
console.log("✅ [SDK Test] crypto.getRandomValues polyfill added");
}
}
}
const relayUrl = "wss://nostr.intu.xyz";
const pool = new SimplePool();
const testSk = "0000000000000000000000000000000000000000000000000000000000000001";
const testPk = getPublicKey(hexToBytes(testSk));
console.log(`🔑 [SDK Test] Test pubkey: ${testPk}`);
console.log(`📡 [SDK Test] Connecting to relay: ${relayUrl}\n`);
const subscriptionTime = Math.floor(Date.now() / 1000);
const filterClone = {
kinds: [1],
};
filterClone.since = subscriptionTime - 5;
let eventReceived = false;
let receivedEventId = null;
let receivedEventPubkey = null;
let eoseReceived = false;
console.log(`📡 [SDK Test] Creating subscription with filter:`, JSON.stringify(filterClone));
const eventHandlers = {
onevent(event) {
console.log(`\n🔔 [SDK Test] onevent HANDLER CALLED!`);
eventReceived = true;
receivedEventId = event.id;
receivedEventPubkey = event.pubkey;
console.log(`\n✅ [SDK Test] EVENT RECEIVED!`);
console.log(` Event ID: ${event.id.substring(0, 16)}...`);
console.log(` Full Event ID: ${event.id}`);
console.log(` Pubkey: ${event.pubkey.substring(0, 16)}...`);
console.log(` Created at: ${event.created_at}`);
console.log(` Filter since: ${filterClone.since}`);
console.log(` Event matches filter: ${event.created_at >= filterClone.since}`);
console.log(` Content: ${event.content.substring(0, 100)}...`);
},
onnotice(notice) {
console.log(`📢 [SDK Test] NOSTR Notice:`, notice);
},
oneose() {
eoseReceived = true;
console.log(`✅ [SDK Test] EOSE received - subscription active\n`);
},
};
const sub = pool.subscribeMany([relayUrl], filterClone, eventHandlers);
if (!sub) {
throw new Error("Failed to create subscription");
}
console.log(`✅ [SDK Test] Subscription created\n`);
try {
const poolAny = pool;
console.log(`🔍 [SDK Test] Pool internals:`, {
relays: poolAny.relays,
_relays: poolAny._relays,
_subs: poolAny._subs,
subs: poolAny.subs,
});
let relay = null;
const relayUrlVariations = [
relayUrl,
relayUrl.endsWith("/") ? relayUrl.slice(0, -1) : relayUrl + "/",
];
if (poolAny.relays && poolAny.relays instanceof Map) {
for (const url of relayUrlVariations) {
relay = poolAny.relays.get(url);
if (relay) {
console.log(`🔍 [SDK Test] Found relay in pool.relays Map with URL: ${url}`);
break;
}
}
if (!relay && poolAny.relays.size > 0) {
console.log(`🔍 [SDK Test] Map keys:`, Array.from(poolAny.relays.keys()));
const firstKey = poolAny.relays.keys().next().value;
if (firstKey) {
relay = poolAny.relays.get(firstKey);
console.log(`🔍 [SDK Test] Using first relay from Map: ${firstKey}`);
}
}
}
else if (poolAny._relays && poolAny._relays instanceof Map) {
for (const url of relayUrlVariations) {
relay = poolAny._relays.get(url);
if (relay) {
console.log(`🔍 [SDK Test] Found relay in pool._relays Map with URL: ${url}`);
break;
}
}
}
else if (poolAny.relays && typeof poolAny.relays === "object") {
for (const url of relayUrlVariations) {
relay = poolAny.relays[url];
if (relay) {
console.log(`🔍 [SDK Test] Found relay in pool.relays object with URL: ${url}`);
break;
}
}
}
if (relay) {
console.log(`🔍 [SDK Test] Relay object:`, relay);
console.log(`🔍 [SDK Test] Relay keys:`, Object.keys(relay));
const isConnected = relay._connected !== false && relay.ws?.readyState === 1;
console.log(`🔍 [SDK Test] Relay connection status:`, {
_connected: relay._connected,
wsReadyState: relay.ws?.readyState,
isConnected: isConnected,
});
const ws = relay.ws || relay._ws || relay.websocket || relay.socket;
console.log(`🔍 [SDK Test] Relay WebSocket:`, ws);
console.log(`🔍 [SDK Test] WebSocket type:`, typeof ws);
if (ws) {
console.log(`🔍 [SDK Test] WebSocket readyState:`, ws.readyState);
console.log(`🔍 [SDK Test] WebSocket URL:`, ws.url);
if (ws.readyState !== 1) {
console.warn(`⚠️ [SDK Test] WebSocket is not OPEN (readyState: ${ws.readyState})`);
}
const originalOnMessage = ws.onmessage;
ws.onmessage = (event) => {
console.log(`🔍 [SDK Test] RAW WebSocket message intercepted:`, typeof event.data === "string"
? event.data.substring(0, 300)
: event.data);
try {
const data = typeof event.data === "string"
? JSON.parse(event.data)
: event.data;
console.log(`🔍 [SDK Test] Parsed message type:`, Array.isArray(data) ? data[0] : typeof data);
if (Array.isArray(data) && data[0] === "EVENT") {
console.log(`🔍 [SDK Test] ✅ This is an EVENT message!`);
console.log(`🔍 [SDK Test] Event ID:`, data[2]?.id?.substring(0, 16));
console.log(`🔍 [SDK Test] Event pubkey:`, data[2]?.pubkey?.substring(0, 16));
if (eventHandlers.onevent) {
console.log(`🔍 [SDK Test] ✅ Manually calling onevent handler`);
eventHandlers.onevent(data[2]);
}
else {
console.warn(`⚠️ [SDK Test] onevent handler not available!`);
}
}
else if (Array.isArray(data) && data[0] === "EOSE") {
console.log(`🔍 [SDK Test] EOSE message received`);
}
else if (Array.isArray(data) && data[0] === "NOTICE") {
console.log(`🔍 [SDK Test] NOTICE:`, data[1]);
}
else {
console.log(`🔍 [SDK Test] Unknown message type:`, data);
}
}
catch (e) {
console.log(`🔍 [SDK Test] Error parsing message:`, e);
}
if (originalOnMessage) {
try {
originalOnMessage.call(ws, event);
}
catch (e) {
console.warn(`⚠️ [SDK Test] Error calling original onmessage:`, e);
}
}
};
console.log(`🔍 [SDK Test] ✅ Added direct WebSocket onmessage listener (intercepting all messages)`);
if (ws.addEventListener) {
ws.addEventListener("message", (event) => {
console.log(`🔍 [SDK Test] RAW WebSocket message (addEventListener):`, typeof event.data === "string"
? event.data.substring(0, 200)
: event.data);
});
console.log(`🔍 [SDK Test] Also added addEventListener listener`);
}
}
}
else {
console.log(`🔍 [SDK Test] Could not find relay connection in pool`);
}
}
catch (e) {
console.log(`🔍 [SDK Test] Could not inspect pool internals:`, e);
}
console.log(`⏳ [SDK Test] Waiting for EOSE...`);
for (let i = 0; i < 10; i++) {
await new Promise((resolve) => setTimeout(resolve, 500));
if (eoseReceived) {
console.log(`✅ [SDK Test] EOSE received - subscription active\n`);
break;
}
}
if (!eoseReceived) {
console.warn(`⚠️ [SDK Test] EOSE not received, but continuing test...`);
}
const testContent = JSON.stringify({
u: 0,
vaultAddress: "0xTEST",
test: true,
timestamp: Date.now(),
});
const eventTimestamp = Math.floor(Date.now() / 1000);
const eventTemplate = {
kind: 1,
created_at: eventTimestamp,
content: testContent,
tags: [],
};
console.log(` [SDK Test] Event timestamp: ${eventTimestamp}, Filter since: ${filterClone.since}`);
console.log(`📤 [SDK Test] Publishing test event...`);
const signedEvent = finalizeEvent(eventTemplate, hexToBytes(testSk));
console.log(` Event ID: ${signedEvent.id.substring(0, 16)}...`);
console.log(` Pubkey: ${signedEvent.pubkey.substring(0, 16)}...`);
const publishResult = await pool.publish([relayUrl], signedEvent);
console.log(`✅ [SDK Test] Test event published\n`);
console.log(`🔍 [SDK Test] Publish result:`, publishResult);
console.log(` Published Event ID: ${signedEvent.id}`);
console.log(`🔍 [SDK Test] Subscription still exists:`, !!sub);
console.log(`🔍 [SDK Test] Event handlers still bound:`, {
hasOnevent: typeof eventHandlers.onevent === "function",
hasOneose: typeof eventHandlers.oneose === "function",
});
console.log(` Published Event Pubkey: ${signedEvent.pubkey}`);
console.log(`⏳ [SDK Test] Waiting up to 10 seconds for OUR event to be received...`);
let ourEventReceived = false;
for (let i = 0; i < 20; i++) {
await new Promise((resolve) => setTimeout(resolve, 500));
if (eventReceived && receivedEventId !== null) {
const eventId = receivedEventId;
const eventPubkey = receivedEventPubkey !== null
? receivedEventPubkey
: "unknown";
if (signedEvent.id === eventId) {
ourEventReceived = true;
console.log(`\n🎉 [SDK Test] SUCCESS! OUR event was received by subscription!`);
console.log(` Expected ID: ${signedEvent.id.substring(0, 16)}...`);
console.log(` Received ID: ${eventId.substring(0, 16)}...`);
console.log(` Match: ${signedEvent.id === eventId}`);
break;
}
else {
console.log(` [SDK Test] Received event from another author (ID: ${eventId.substring(0, 16)}..., Pubkey: ${eventPubkey.substring(0, 16)}...)`);
}
}
if (i % 2 === 0) {
console.log(` [SDK Test] Still waiting for our event... (${i * 0.5}s)`);
}
}
sub.close();
await pool.close([relayUrl]);
const success = ourEventReceived;
console.log(`\n🏁 [SDK Test] Test completed - Success: ${success}`);
return {
success,
eoseReceived,
eventReceived,
expectedEventId: signedEvent.id.substring(0, 16) + "...",
receivedEventId: receivedEventId !== null
? receivedEventId.substring(0, 16) + "..."
: "none",
pubkey: testPk.substring(0, 16) + "...",
};
}
catch (error) {
console.error("❌ [SDK Test] Test failed:", error);
return {
success: false,
eoseReceived: false,
eventReceived: false,
error: error?.message || String(error),
};
}
}
export async function testVaultAddressFiltering(vaultAddress) {
try {
console.log(`🧪 [SDK Filter Test] Starting vaultAddress filtering test with vault: ${vaultAddress}`);
if (typeof global !== "undefined") {
if (!global.crypto) {
global.crypto = {};
}
if (!global.crypto.getRandomValues) {
global.crypto.getRandomValues = function (array) {
if (array === null) {
return array;
}
const uint8Array = new Uint8Array(array.buffer, array.byteOffset, array.byteLength);
for (let i = 0; i < uint8Array.length; i++) {
uint8Array[i] = Math.floor(Math.random() * 256);
}
return array;
};
}
}
const relayUrl = "wss://nostr.intu.xyz";
const pool = new SimplePool();
const testSk = "0000000000000000000000000000000000000000000000000000000000000001";
const testPk = getPublicKey(hexToBytes(testSk));
console.log(`🔑 [SDK Filter Test] Test pubkey: ${testPk}`);
const subscriptionTime = Math.floor(Date.now() / 1000);
const filterClone = {
kinds: [1],
since: subscriptionTime - 5,
};
let eventWithVaultReceived = false;
let eventWithoutVaultReceived = false;
let receivedEventIds = [];
const eventHandlers = {
onevent(event) {
try {
const data = JSON.parse(event.content || "{}");
const hasVaultAddress = !!data.vaultAddress;
const vaultMatches = data.vaultAddress === vaultAddress;
console.log(`\n✅ [SDK Filter Test] EVENT RECEIVED!`);
console.log(` Event ID: ${event.id.substring(0, 16)}...`);
console.log(` Has vaultAddress: ${hasVaultAddress}`);
console.log(` vaultAddress: ${data.vaultAddress || "MISSING"}`);
console.log(` Expected: ${vaultAddress}`);
console.log(` Match: ${vaultMatches
? "✅"
: hasVaultAddress
? "❌"
: "⚠️ (backward compat)"}`);
receivedEventIds.push(event.id);
if (hasVaultAddress && vaultMatches) {
eventWithVaultReceived = true;
console.log(` ✅ Event WITH matching vaultAddress received!`);
}
else if (!hasVaultAddress) {
eventWithoutVaultReceived = true;
console.log(` ✅ Event WITHOUT vaultAddress received (backward compat)!`);
}
}
catch (e) {
console.error(`[SDK Filter Test] Error parsing event:`, e);
}
},
onnotice(notice) {
console.log(`📢 [SDK Filter Test] NOSTR Notice:`, notice);
},
oneose() {
console.log(`✅ [SDK Filter Test] EOSE received - subscription active\n`);
},
};
const sub = pool.subscribeMany([relayUrl], filterClone, eventHandlers);
if (!sub) {
throw new Error("Failed to create subscription");
}
console.log(`✅ [SDK Filter Test] Subscription created\n`);
try {
const poolAny = pool;
const relayUrlVariations = [
relayUrl,
relayUrl.endsWith("/") ? relayUrl.slice(0, -1) : relayUrl + "/",
];
let relay = null;
if (poolAny.relays && poolAny.relays instanceof Map) {
for (const url of relayUrlVariations) {
relay = poolAny.relays.get(url);
if (relay) {
break;
}
}
if (!relay && poolAny.relays.size > 0) {
const firstKey = poolAny.relays.keys().next().value;
if (firstKey) {
relay = poolAny.relays.get(firstKey);
}
}
}
if (relay && relay.ws) {
const ws = relay.ws;
const originalOnMessage = ws.onmessage;
ws.onmessage = (event) => {
try {
const data = typeof event.data === "string"
? JSON.parse(event.data)
: event.data;
if (Array.isArray(data) && data[0] === "EVENT" && data[2]) {
const nostrEvent = data[2];
try {
const eventContent = JSON.parse(nostrEvent.content || "{}");
const eventVaultAddress = eventContent.vaultAddress;
const vaultMatch = eventVaultAddress === vaultAddress;
const hasVaultAddress = !!eventVaultAddress;
console.log(`[SDK Filter Test] 🔍 VAULT FILTER - Event vault: ${eventVaultAddress || "MISSING"}, ` +
`Expected: ${vaultAddress}, ` +
`Match: ${vaultMatch
? "✅"
: hasVaultAddress
? "❌ MISMATCH"
: "⚠️ NO VAULT (backward compat)"}`);
if (hasVaultAddress && !vaultMatch) {
console.log(`[SDK Filter Test] 🚫 FILTERED OUT - Wrong vault: "${eventVaultAddress}" !== "${vaultAddress}"`);
if (originalOnMessage) {
try {
originalOnMessage.call(ws, event);
}
catch (e) {
}
}
return;
}
if (!hasVaultAddress) {
console.log(`[SDK Filter Test] ⚠️ NO vaultAddress field - accepting (backward compatibility)`);
}
else {
console.log(`[SDK Filter Test] ✅ VAULT MATCH - Forwarding to handler`);
}
}
catch (parseError) {
console.warn(`[SDK Filter Test] ⚠️ Could not parse event content:`, parseError);
}
if (eventHandlers.onevent) {
eventHandlers.onevent(nostrEvent);
}
return;
}
}
catch (e) {
}
if (originalOnMessage) {
try {
originalOnMessage.call(ws, event);
}
catch (e) {
}
}
};
console.log(`[SDK Filter Test] ✅ WebSocket interception enabled for React Native compatibility`);
}
else {
console.warn(`[SDK Filter Test] ⚠️ Could not find relay WebSocket for interception`);
}
}
catch (e) {
console.warn(`[SDK Filter Test] ⚠️ Failed to set up WebSocket interception:`, e);
}
await new Promise((resolve) => setTimeout(resolve, 1500));
const event1Content = JSON.stringify({
u: 0,
vaultAddress: vaultAddress,
test: "withVault",
timestamp: Date.now(),
});
const eventTemplate1 = {
kind: 1,
created_at: Math.floor(Date.now() / 1000),
content: event1Content,
tags: [],
};
console.log(`📤 [SDK Filter Test] Publishing event 1 WITH vaultAddress: ${vaultAddress}`);
const signedEvent1 = finalizeEvent(eventTemplate1, hexToBytes(testSk));
await pool.publish([relayUrl], signedEvent1);
console.log(`✅ [SDK Filter Test] Event 1 published: ${signedEvent1.id.substring(0, 16)}...`);
await new Promise((resolve) => setTimeout(resolve, 1000));
const event2Content = JSON.stringify({
u: 1,
test: "withoutVault",
timestamp: Date.now(),
});
const eventTemplate2 = {
kind: 1,
created_at: Math.floor(Date.now() / 1000),
content: event2Content,
tags: [],
};
console.log(`📤 [SDK Filter Test] Publishing event 2 WITHOUT vaultAddress (backward compat)`);
const signedEvent2 = finalizeEvent(eventTemplate2, hexToBytes(testSk));
await pool.publish([relayUrl], signedEvent2);
console.log(`✅ [SDK Filter Test] Event 2 published: ${signedEvent2.id.substring(0, 16)}...`);
console.log(`⏳ [SDK Filter Test] Waiting up to 10 seconds for both events...`);
for (let i = 0; i < 20; i++) {
await new Promise((resolve) => setTimeout(resolve, 500));
if (eventWithVaultReceived && eventWithoutVaultReceived) {
console.log(`\n🎉 [SDK Filter Test] SUCCESS! Both events received!`);
break;
}
if (i % 2 === 0) {
console.log(` [SDK Filter Test] Waiting... WithVault: ${eventWithVaultReceived}, WithoutVault: ${eventWithoutVaultReceived}`);
}
}
sub.close();
await pool.close([relayUrl]);
const success = eventWithVaultReceived && eventWithoutVaultReceived;
console.log(`\n🏁 [SDK Filter Test] Test completed:`);
console.log(` Event WITH vaultAddress: ${eventWithVaultReceived ? "✅" : "❌"}`);
console.log(` Event WITHOUT vaultAddress: ${eventWithoutVaultReceived ? "✅" : "❌"}`);
console.log(` Success: ${success}`);
return {
success,
eventWithVaultReceived,
eventWithoutVaultReceived,
};
}
catch (error) {
console.error("❌