NEXUS Smart Wallet

ERC-4337 v0.7 account technology — deploy console.
Every action below is a real wallet signature on your device. Nothing is simulated.

1 · Connect wallet

not connected

2 · Factory (deploys the real SmartWalletFactory bytecode — bytes)

3 · Your smart wallet (counterfactual — address known before deploy)

4 · Session keys (scoped, expiring, revocable — for AI agents)

5 · Social recovery (guardians rotate ownership after 48h timelock)

Log

let provider, signer, signerAddr; let factory, factoryAddr, walletAddr, wallet; let chainId; const $ = id => document.getElementById(id); function log(msg, cls) { const d = document.createElement("div"); if (cls) d.className = cls; d.textContent = new Date().toLocaleTimeString() + " " + msg; $("log").prepend(d); } function needSigner() { if (!signer) { log("Connect your wallet first.", "warn"); return false; } return true; } $("byteLen").textContent = (FACTORY_BYTECODE.length - 2) / 2; $("bConnect").onclick = async () => { try { if (!window.ethereum) { log("No injected wallet found.", "bad"); return; } provider = new ethers.BrowserProvider(window.ethereum); await provider.send("eth_requestAccounts", []); signer = await provider.getSigner(); signerAddr = await signer.getAddress(); const net = await provider.getNetwork(); chainId = Number(net.chainId); $("addrOut").textContent = signerAddr + " (chain " + chainId + ")"; $("addrOut").className = "mono ok"; const pill = chainId === 1 || chainId === 8453 ? 'MAINNET' : 'TEST / LOCAL — no real funds at risk'; $("netpill").innerHTML = pill; ["bDeployFactory","bUseFactory","bPredict"].forEach(id => $(id).disabled = false); log("Connected: " + signerAddr + " on chain " + chainId, "ok"); } catch (e) { log("Connect failed: " + (e.info?.error?.message || e.message), "bad"); } }; $("bDeployFactory").onclick = async () => { if (!needSigner()) return; try { const ep = $("inEP").value.trim(); log("Deploying SmartWalletFactory (EntryPoint " + ep + ")..."); const F = new ethers.ContractFactory(FACTORY_ABI, FACTORY_BYTECODE, signer); factory = await F.deploy(ep); await factory.waitForDeployment(); factoryAddr = await factory.getAddress(); $("factoryOut").innerHTML = 'deployed: ' + factoryAddr + ""; $("bDeployWallet").disabled = false; log("Factory deployed: " + factoryAddr, "ok"); } catch (e) { log("Factory deploy failed: " + (e.info?.error?.message || e.message), "bad"); } }; $("bUseFactory").onclick = async () => { if (!needSigner()) return; const a = $("inFactory").value.trim(); if (!ethers.isAddress(a)) { log("Enter a valid factory address.", "warn"); return; } factory = new ethers.Contract(FACTORY_ABI, a, signer); factoryAddr = a; $("factoryOut").innerHTML = 'using: ' + a + ""; $("bDeployWallet").disabled = false; log("Using existing factory: " + a, "ok"); }; $("bPredict").onclick = async () => { if (!needSigner() || !factory) { log("Deploy or select a factory first.", "warn"); return; } try { const tag = $("inTag").value.trim(); const ep = $("inEP").value.trim(); walletAddr = await factory.predictWalletAddress(signerAddr, ep, tag); const code = await provider.getCode(walletAddr); $("walletOut").innerHTML = "counterfactual address: " + walletAddr + "
" + "" + (code === "0x" ? "not yet deployed" : "already deployed (" + ((code.length-2)/2) + " bytes)") + ""; if (code !== "0x") { $("bDeployWallet").disabled = false; } log("Predicted wallet: " + walletAddr, "ok"); } catch (e) { log("Predict failed: " + (e.info?.error?.message || e.message), "bad"); } }; $("bDeployWallet").onclick = async () => { if (!needSigner() || !factory) return; try { const tag = $("inTag").value.trim(); const ep = $("inEP").value.trim(); log("Deploying your smart wallet (tag: " + tag + ")..."); const tx = await factory.createAccount(signerAddr, ep, tag); const rc = await tx.wait(); log("Wallet deployed. Tx: " + rc.hash, "ok"); await $("bPredict").onclick(); wallet = new ethers.Contract(walletAddr, WALLET_ABI, signer); $("bGrant").disabled = $("bRevoke").disabled = $("bGuardians").disabled = false; } catch (e) { log("Wallet deploy failed: " + (e.info?.error?.message || e.message), "bad"); } }; $("bGrant").onclick = async () => { if (!needSigner() || !wallet) return; try { const key = $("skAddr").value.trim(), target = $("skTarget").value.trim(); if (!ethers.isAddress(key) || !ethers.isAddress(target)) { log("Enter valid key and target addresses.", "warn"); return; } const now = Math.floor(Date.now() / 1000); const hours = parseInt($("skHours").value) || 24; const tx = await wallet.grantSession( key, now, now + hours * 3600, ethers.parseEther($("skSpend").value || "0"), parseInt($("skUses").value) || 1, [target] ); await tx.wait(); $("skOut").innerHTML = "session granted to " + key + ""; log("Session key granted: " + key + " -> " + target, "ok"); } catch (e) { log("Grant failed: " + (e.info?.error?.message || e.message), "bad"); } }; $("bRevoke").onclick = async () => { if (!needSigner() || !wallet) return; try { const key = $("skAddr").value.trim(); const tx = await wallet.revokeSession(key); await tx.wait(); $("skOut").innerHTML = "session revoked: " + key + ""; log("Session key revoked: " + key, "warn"); } catch (e) { log("Revoke failed: " + (e.info?.error?.message || e.message), "bad"); } }; $("bGuardians").onclick = async () => { if (!needSigner() || !wallet) return; try { const gs = $("inGuardians").value.split(",").map(s => s.trim()).filter(s => ethers.isAddress(s)); const th = parseInt($("inThreshold").value) || 1; if (!gs.length) { log("Enter at least one guardian address.", "warn"); return; } const tx = await wallet.setGuardians(gs, th); await tx.wait(); $("recOut").innerHTML = "" + gs.length + " guardians set, threshold " + th + ""; log("Guardians set: " + gs.length + " of " + th, "ok"); } catch (e) { log("Set guardians failed: " + (e.info?.error?.message || e.message), "bad"); } }; log("Console ready. Real bytecode injected at build time (" + $("byteLen").textContent + " bytes).");