UNPKG

@intuweb3/sdk

Version:

INTU SDK - Modern blockchain interaction toolkit

1,028 lines 75.7 kB
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("❌