w3pk 0.7.0 → 0.7.2

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/index.mjs CHANGED
@@ -1,6 +1,777 @@
1
- var Ae=Object.defineProperty;var S=(o,e)=>()=>(o&&(e=o(o=0)),e);var re=(o,e)=>{for(var t in e)Ae(o,t,{get:e[t],enumerable:!0})};var d,b,I,h,l,u,L,p=S(()=>{"use strict";d=class extends Error{constructor(t,r,n){super(t);this.code=r;this.originalError=n;this.name="Web3PasskeyError"}},b=class extends d{constructor(e,t){super(e,"AUTHENTICATION_ERROR",t),this.name="AuthenticationError"}},I=class extends d{constructor(e,t){super(e,"REGISTRATION_ERROR",t),this.name="RegistrationError"}},h=class extends d{constructor(e,t){super(e,"WALLET_ERROR",t),this.name="WalletError"}},l=class extends d{constructor(e,t){super(e,"CRYPTO_ERROR",t),this.name="CryptoError"}},u=class extends d{constructor(e,t){super(e,"STORAGE_ERROR",t),this.name="StorageError"}},L=class extends d{constructor(t,r,n){super(t,"API_ERROR",n);this.statusCode=r;this.name="ApiError"}}});var F={};re(F,{CredentialStorage:()=>P});var k,T,P,A=S(()=>{"use strict";p();k="w3pk_credential_",T="w3pk_credential_index",P=class{constructor(e){if(e)this.storage=e;else if(typeof window<"u"&&window.localStorage)this.storage=window.localStorage;else throw new u("localStorage is not available")}saveCredential(e){try{let t=`${k}${e.id}`;this.storage.setItem(t,JSON.stringify(e)),this.addToIndex(e.id)}catch(t){throw new u("Failed to save credential",t)}}getCredentialById(e){try{let t=`${k}${e}`,r=this.storage.getItem(t);return r?JSON.parse(r):null}catch(t){throw new u("Failed to retrieve credential",t)}}getCredentialByUsername(e){try{return this.getAllCredentials().find(r=>r.username===e)||null}catch(t){throw new u("Failed to retrieve credential",t)}}getCredentialByAddress(e){try{return this.getAllCredentials().find(r=>r.ethereumAddress.toLowerCase()===e.toLowerCase())||null}catch(t){throw new u("Failed to retrieve credential",t)}}getAllCredentials(){try{return this.getIndex().map(t=>this.getCredentialById(t)).filter(t=>t!==null)}catch(e){throw new u("Failed to retrieve credentials",e)}}userExists(e){return this.getCredentialByUsername(e)!==null}updateLastUsed(e){try{let t=this.getCredentialById(e);t&&(t.lastUsed=Date.now(),this.saveCredential(t))}catch(t){throw new u("Failed to update timestamp",t)}}deleteCredential(e){try{let t=`${k}${e}`;this.storage.removeItem(t),this.removeFromIndex(e)}catch(t){throw new u("Failed to delete credential",t)}}clearAll(){try{this.getIndex().forEach(t=>{let r=`${k}${t}`;this.storage.removeItem(r)}),this.storage.removeItem(T)}catch(e){throw new u("Failed to clear credentials",e)}}getIndex(){try{let e=this.storage.getItem(T);return e?JSON.parse(e):[]}catch{return[]}}addToIndex(e){let t=this.getIndex();t.includes(e)||(t.push(e),this.storage.setItem(T,JSON.stringify(t)))}removeFromIndex(e){let r=this.getIndex().filter(n=>n!==e);this.storage.setItem(T,JSON.stringify(r))}}});function C(o){let e=0n;for(let t=0;t<o.length;t++)e=e<<8n|BigInt(o[t]);return e}var Q=S(()=>{"use strict"});var B,me=S(()=>{"use strict";p();Q();B=class{constructor(){this.circuits=new Map;this.loadSnarkJS()}async loadSnarkJS(){try{this.snarkjs=await import("snarkjs")}catch{this.snarkjs=null}}registerCircuit(e,t){this.circuits.set(e,t)}async generateMembershipProof(e){let t=this.circuits.get("membership");if(!t)throw new l("Membership circuit not registered");let r={leaf:e.value,pathIndices:e.pathIndices,pathElements:e.pathElements,root:e.root};return this.generateProof("membership",t,r)}async generateThresholdProof(e){let t=this.circuits.get("threshold");if(!t)throw new l("Threshold circuit not registered");let r={value:e.value.toString(),blinding:e.blinding.toString(),threshold:e.threshold.toString(),commitment:e.commitment};return this.generateProof("threshold",t,r)}async generateRangeProof(e){let t=this.circuits.get("range");if(!t)throw new l("Range circuit not registered");let r={value:e.value.toString(),blinding:e.blinding.toString(),min:e.min.toString(),max:e.max.toString(),commitment:e.commitment};return this.generateProof("range",t,r)}async generateOwnershipProof(e){let t=this.circuits.get("ownership");if(!t)throw new l("Ownership circuit not registered");let r={privateKey:e.privateKey,nonce:e.nonce.toString(),address:e.address,challenge:e.challenge};return this.generateProof("ownership",t,r)}async generateNFTOwnershipProof(e){let t=this.circuits.get("nft");if(!t)throw new l("NFT ownership circuit not registered");let r=await this.getPoseidonHash(),n=e.ownerAddress.startsWith("0x")?e.ownerAddress:"0x"+e.ownerAddress,s=e.contractAddress.startsWith("0x")?e.contractAddress:"0x"+e.contractAddress,i=r([BigInt(n)]),c=r([BigInt(s)]),a=i instanceof Uint8Array?C(i):i,g=c instanceof Uint8Array?C(c):c,y={ownerAddress:a.toString(),pathElements:e.pathElements,pathIndices:e.pathIndices,root:e.holdersRoot,contractAddress:g.toString(),minBalance:(e.minBalance||1n).toString()};return this.generateProof("nft",t,y)}async generateProof(e,t,r){try{if(!this.snarkjs&&(await this.loadSnarkJS(),!this.snarkjs))throw new l(`ZK proofs require snarkjs. Install with: npm install snarkjs
2
- For more info: https://github.com/w3hc/w3pk#zero-knowledge-proofs`);let{proof:n,publicSignals:s}=await this.snarkjs.groth16.fullProve(r,t.wasmPath,t.zkeyPath);return{type:e,proof:{pi_a:n.pi_a.map(i=>i.toString()),pi_b:n.pi_b.map(i=>i.map(c=>c.toString())),pi_c:n.pi_c.map(i=>i.toString()),protocol:n.protocol||"groth16",curve:n.curve||"bn128"},publicSignals:s.map(i=>i.toString()),timestamp:Date.now()}}catch(n){throw new l(`Failed to generate ${e} proof: ${n instanceof Error?n.message:"Unknown error"}`,n)}}async createCommitment(e,t){try{return(await this.getPoseidonHash())([e,t]).toString()}catch(r){throw new l("Failed to create commitment",r)}}async getPoseidonHash(){try{return await(await import("circomlibjs")).buildPoseidon()}catch(e){throw new l(`ZK proofs require circomlibjs. Install with: npm install circomlibjs
3
- For more info: https://github.com/w3hc/w3pk#zero-knowledge-proofs`,e)}}async computeMerkleRoot(e,t,r){try{let n=await this.getPoseidonHash(),s=BigInt(e);for(let i=0;i<r.length;i++){let c=BigInt(r[i]);t[i]===0?s=n([s,c]):s=n([c,s])}return s.toString()}catch(n){throw new l("Failed to compute merkle root",n)}}}});var Z,pe=S(()=>{"use strict";p();Q();Z=class{constructor(){this.circuits=new Map;this.loadSnarkJS()}async loadSnarkJS(){try{this.snarkjs=await import("snarkjs")}catch{this.snarkjs=null}}registerCircuit(e,t){this.circuits.set(e,t)}async verifyProof(e){try{let t=this.circuits.get(e.type);if(!t)throw new l(`Circuit not registered for type: ${e.type}`);if(!this.snarkjs&&(await this.loadSnarkJS(),!this.snarkjs))throw new l(`ZK proof verification requires snarkjs. Install with: npm install snarkjs
4
- For more info: https://github.com/w3hc/w3pk#zero-knowledge-proofs`);return{valid:await this.snarkjs.groth16.verify(t.verificationKey,e.publicSignals,e.proof),type:e.type,publicSignals:this.parsePublicSignals(e.type,e.publicSignals),timestamp:e.timestamp}}catch(t){throw new l(`Proof verification failed: ${t instanceof Error?t.message:"Unknown error"}`,t)}}async verifyBatch(e){return await Promise.all(e.map(r=>this.verifyProof(r)))}async verifyMembershipProof(e,t){if(e.type!=="membership")throw new l("Invalid proof type for membership verification");let r=await this.verifyProof(e),n=e.publicSignals[0];return r.valid&&n===t}async verifyThresholdProof(e,t,r){if(e.type!=="threshold")throw new l("Invalid proof type for threshold verification");let n=await this.verifyProof(e),s=e.publicSignals[0],i=BigInt(e.publicSignals[1]);return n.valid&&s===t&&i===r}async verifyRangeProof(e,t,r,n){if(e.type!=="range")throw new l("Invalid proof type for range verification");let s=await this.verifyProof(e),i=e.publicSignals[0],c=BigInt(e.publicSignals[1]),a=BigInt(e.publicSignals[2]);return s.valid&&i===t&&c===r&&a===n}async verifyOwnershipProof(e,t,r){if(e.type!=="ownership")throw new l("Invalid proof type for ownership verification");let n=await this.verifyProof(e),s=e.publicSignals[0],i=e.publicSignals[1];return n.valid&&s.toLowerCase()===t.toLowerCase()&&i===r}async verifyNFTOwnershipProof(e,t,r,n=1n){if(e.type!=="nft")throw new l("Invalid proof type for NFT ownership verification");let s=await this.verifyProof(e),i=e.publicSignals[0],c=e.publicSignals[1],a=BigInt(e.publicSignals[2]);try{let y=await(await import("circomlibjs")).buildPoseidon(),v=t.startsWith("0x")?t:"0x"+t,w=y([BigInt(v)]),H=w instanceof Uint8Array?C(w):w;return s.valid&&i===r&&c===H.toString()&&a>=n}catch(g){throw new l(`ZK proof verification requires circomlibjs. Install with: npm install circomlibjs
5
- For more info: https://github.com/w3hc/w3pk#zero-knowledge-proofs`,g)}}parsePublicSignals(e,t){switch(e){case"membership":return{root:t[0]};case"threshold":return{commitment:t[0],threshold:t[1]};case"range":return{commitment:t[0],min:t[1],max:t[2]};case"ownership":return{address:t[0],challenge:t[1]};case"nft":return{holdersRoot:t[0],contractAddress:t[1],minBalance:t[2],nullifierHash:t[3]};default:return t.reduce((r,n,s)=>(r[`signal_${s}`]=n,r),{})}}isProofExpired(e,t=36e5){return Date.now()-e.timestamp>t}}});var fe={};re(fe,{ZKProofModule:()=>ee});var ee,ye=S(()=>{"use strict";p();me();pe();ee=class{constructor(e={}){this.config=e,this.generator=new B,this.verifier=new Z,this.initializeCircuits()}async initializeCircuits(){}async proveMembership(e){try{return this.assertProofEnabled("membership"),await this.generator.generateMembershipProof(e)}catch(t){throw new d("Failed to generate membership proof","ZK_MEMBERSHIP_ERROR",t)}}async proveThreshold(e){try{return this.assertProofEnabled("threshold"),await this.generator.generateThresholdProof(e)}catch(t){throw new d("Failed to generate threshold proof","ZK_THRESHOLD_ERROR",t)}}async proveRange(e){try{return this.assertProofEnabled("range"),await this.generator.generateRangeProof(e)}catch(t){throw new d("Failed to generate range proof","ZK_RANGE_ERROR",t)}}async proveOwnership(e){try{return this.assertProofEnabled("ownership"),await this.generator.generateOwnershipProof(e)}catch(t){throw new d("Failed to generate ownership proof","ZK_OWNERSHIP_ERROR",t)}}async proveNFTOwnership(e){try{return this.assertProofEnabled("nft"),await this.generator.generateNFTOwnershipProof(e)}catch(t){throw new d("Failed to generate NFT ownership proof","ZK_NFT_OWNERSHIP_ERROR",t)}}async verify(e){try{return await this.verifier.verifyProof(e)}catch(t){throw new d("Failed to verify proof","ZK_VERIFICATION_ERROR",t)}}async verifyBatch(e){try{return await this.verifier.verifyBatch(e)}catch(t){throw new d("Failed to verify proofs","ZK_BATCH_VERIFICATION_ERROR",t)}}async verifyMembership(e,t){try{return await this.verifier.verifyMembershipProof(e,t)}catch(r){throw new d("Failed to verify membership proof","ZK_MEMBERSHIP_VERIFICATION_ERROR",r)}}async verifyThreshold(e,t,r){try{return await this.verifier.verifyThresholdProof(e,t,r)}catch(n){throw new d("Failed to verify threshold proof","ZK_THRESHOLD_VERIFICATION_ERROR",n)}}async verifyRange(e,t,r,n){try{return await this.verifier.verifyRangeProof(e,t,r,n)}catch(s){throw new d("Failed to verify range proof","ZK_RANGE_VERIFICATION_ERROR",s)}}async verifyOwnership(e,t,r){try{return await this.verifier.verifyOwnershipProof(e,t,r)}catch(n){throw new d("Failed to verify ownership proof","ZK_OWNERSHIP_VERIFICATION_ERROR",n)}}async verifyNFTOwnership(e,t,r,n=1n){try{return await this.verifier.verifyNFTOwnershipProof(e,t,r,n)}catch(s){throw new d("Failed to verify NFT ownership proof","ZK_NFT_OWNERSHIP_VERIFICATION_ERROR",s)}}async createCommitment(e,t){try{return await this.generator.createCommitment(e,t)}catch(r){throw new d("Failed to create commitment","ZK_COMMITMENT_ERROR",r)}}async computeMerkleRoot(e,t,r){try{return await this.generator.computeMerkleRoot(e,t,r)}catch(n){throw new d("Failed to compute merkle root","ZK_MERKLE_ERROR",n)}}registerCircuit(e,t){this.generator.registerCircuit(e,t),this.verifier.registerCircuit(e,t)}assertProofEnabled(e){if(this.config.enabledProofs&&!this.config.enabledProofs.includes(e))throw new d(`Proof type '${e}' is not enabled`,"ZK_PROOF_DISABLED")}get isAvailable(){return!0}}});p();import{startRegistration as Ie}from"@simplewebauthn/browser";function ve(o){return/^0x[a-fA-F0-9]{40}$/.test(o)}function Se(o){return o.length>=3&&o.length<=50}function ne(o){if(!ve(o))throw new Error("Invalid Ethereum address format")}function oe(o){if(!Se(o))throw new Error("Username must be between 3 and 50 characters")}A();function Fe(){let o=new Uint8Array(32);return crypto.getRandomValues(o),btoa(String.fromCharCode(...o)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}async function se(o){try{let{username:e,ethereumAddress:t}=o;oe(e),ne(t);let r=new P;if(r.userExists(e))throw new Error("Username already registered");let s={challenge:Fe(),rp:{name:"w3pk",id:window.location.hostname},user:{id:e,name:e,displayName:e},pubKeyCredParams:[{type:"public-key",alg:-7},{type:"public-key",alg:-257}],authenticatorSelection:{authenticatorAttachment:"platform",userVerification:"required",residentKey:"required",requireResidentKey:!0},timeout:6e4,attestation:"none"},i=await Ie({optionsJSON:s}),c=i.response.publicKey;if(!c)throw new Error("Public key not returned from authenticator");r.saveCredential({id:i.id,publicKey:c,username:e,ethereumAddress:t,createdAt:Date.now(),lastUsed:Date.now()}),console.log("[register] Credential response:",i.response);let a=i.response.attestationObject;if(console.log("[register] Attestation object:",a),!a)throw new Error("Attestation object not returned from authenticator");let g=Ee(a);return console.log("[register] Attestation buffer length:",g.byteLength),{signature:g}}catch(e){throw new I(e instanceof Error?e.message:"Registration failed",e)}}function Ee(o){let e=o.replace(/-/g,"+").replace(/_/g,"/"),t=atob(e),r=new Uint8Array(t.length);for(let n=0;n<t.length;n++)r[n]=t.charCodeAt(n);return r.buffer}p();A();import{startAuthentication as xe}from"@simplewebauthn/browser";function Ke(){let o=new Uint8Array(32);return crypto.getRandomValues(o),btoa(String.fromCharCode(...o)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}async function O(){try{let o=new P,t={challenge:Ke(),rpId:window.location.hostname,userVerification:"required",timeout:6e4},r=await xe({optionsJSON:t}),n=o.getCredentialById(r.id);if(!n)throw new Error("Credential not found");if(!await Ce(r,n))throw new Error("Signature verification failed");o.updateLastUsed(n.id);let i=M(r.response.signature);return{verified:!0,user:{username:n.username,ethereumAddress:n.ethereumAddress,credentialId:n.id},signature:i}}catch(o){throw new b(o instanceof Error?o.message:"Authentication failed",o)}}async function Ce(o,e){try{let t=M(e.publicKey),r=await crypto.subtle.importKey("spki",t,{name:"ECDSA",namedCurve:"P-256"},!1,["verify"]),n=M(o.response.authenticatorData),s=o.response.clientDataJSON,i;s.startsWith("eyJ")?i=atob(s.replace(/-/g,"+").replace(/_/g,"/")):i=s;let c=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(i)),a=new Uint8Array(n.byteLength+c.byteLength);a.set(new Uint8Array(n),0),a.set(new Uint8Array(c),n.byteLength);let g=M(o.response.signature),y=Re(new Uint8Array(g));return await crypto.subtle.verify({name:"ECDSA",hash:"SHA-256"},r,y,a)}catch(t){return console.error("Signature verification error:",t),!1}}function M(o){let e=o.replace(/-/g,"+").replace(/_/g,"/"),t=atob(e),r=new Uint8Array(t.length);for(let n=0;n<t.length;n++)r[n]=t.charCodeAt(n);return r.buffer}function Re(o){let e=2;e++;let t=o[e++];t>32&&(e++,t--);let r=o.slice(e,e+t);e+=t,e++;let n=o[e++];n>32&&(e++,n--);let s=o.slice(e,e+n),i=new Uint8Array(64);return i.set(r,32-r.length),i.set(s,64-s.length),i.buffer}p();var ke="Web3PasskeyWallet",Te=1,f="wallets",_=class{constructor(){this.db=null}async init(){return new Promise((e,t)=>{let r=indexedDB.open(ke,Te);r.onerror=()=>t(new u("Failed to open database",r.error)),r.onsuccess=()=>{this.db=r.result,e()},r.onupgradeneeded=()=>{let n=r.result;n.objectStoreNames.contains(f)||n.createObjectStore(f,{keyPath:"ethereumAddress"})}})}async store(e){return this.db||await this.init(),new Promise((t,r)=>{let i=this.db.transaction([f],"readwrite").objectStore(f).put(e);i.onerror=()=>r(new u("Failed to store wallet data",i.error)),i.onsuccess=()=>t()})}async retrieve(e){return this.db||await this.init(),new Promise((t,r)=>{let i=this.db.transaction([f],"readonly").objectStore(f).get(e);i.onerror=()=>r(new u("Failed to retrieve wallet data",i.error)),i.onsuccess=()=>t(i.result||null)})}async delete(e){return this.db||await this.init(),new Promise((t,r)=>{let i=this.db.transaction([f],"readwrite").objectStore(f).delete(e);i.onerror=()=>r(new u("Failed to delete wallet data",i.error)),i.onsuccess=()=>t()})}async clear(){return this.db||await this.init(),new Promise((e,t)=>{let s=this.db.transaction([f],"readwrite").objectStore(f).clear();s.onerror=()=>t(new u("Failed to clear wallet data",s.error)),s.onsuccess=()=>e()})}};p();import{ethers as U}from"ethers";function $(){try{let o=U.Wallet.createRandom().mnemonic;if(!o)throw new Error("Failed to generate mnemonic");let e=o.phrase;return{address:U.HDNodeWallet.fromPhrase(e,void 0,"m/44'/60'/0'/0/0").address,mnemonic:e}}catch(o){throw new h("Wallet generation failed",o)}}function Me(o){try{if(!o||o.trim().split(/\s+/).length<12)throw new Error("Invalid mnemonic: must be at least 12 words");return U.HDNodeWallet.fromPhrase(o.trim(),void 0,"m/44'/60'/0'/0/0")}catch(e){throw new h(`Wallet creation failed: ${e instanceof Error?e.message:"Invalid mnemonic"}`,e)}}function q(o,e=0){try{if(!o||o.trim().split(/\s+/).length<12)throw new Error("Invalid mnemonic: must be at least 12 words");if(e<0||!Number.isInteger(e))throw new Error("Index must be a non-negative integer");let t=`m/44'/60'/0'/0/${e}`,r=U.HDNodeWallet.fromPhrase(o.trim(),void 0,t);return{address:r.address,privateKey:r.privateKey}}catch(t){throw new h(`HD wallet derivation failed: ${t instanceof Error?t.message:"Unknown error"}`,t)}}p();async function E(o,e){try{let t=e?`w3pk-v4:${o}:${e}`:`w3pk-v4:${o}`,r=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(t)),n=await crypto.subtle.importKey("raw",r,{name:"PBKDF2"},!1,["deriveKey"]),s=await crypto.subtle.digest("SHA-256",new TextEncoder().encode("w3pk-salt-v4"));return crypto.subtle.deriveKey({name:"PBKDF2",salt:new Uint8Array(s),iterations:21e4,hash:"SHA-256"},n,{name:"AES-GCM",length:256},!1,["encrypt","decrypt"])}catch(t){throw new l("Failed to derive encryption key from WebAuthn",t)}}async function V(o,e){try{let t=crypto.getRandomValues(new Uint8Array(12)),r=new TextEncoder().encode(o),n=await crypto.subtle.encrypt({name:"AES-GCM",iv:t},e,r),s=new Uint8Array(t.length+n.byteLength);return s.set(t),s.set(new Uint8Array(n),t.length),btoa(String.fromCharCode(...s))}catch(t){throw new l("Failed to encrypt data",t)}}async function z(o,e){try{if(!o||o.length<16)throw new Error("Invalid encrypted data: too small");let t=new Uint8Array(atob(o).split("").map(i=>i.charCodeAt(0)));if(t.length<12)throw new Error("Invalid encrypted data: missing IV");let r=t.slice(0,12),n=t.slice(12);if(n.length===0)throw new Error("Invalid encrypted data: no content");let s=await crypto.subtle.decrypt({name:"AES-GCM",iv:r},e,n);return new TextDecoder().decode(s)}catch(t){throw new l(`Data decryption failed: ${t instanceof Error?t.message:"Unknown error"}`,t)}}p();p();import{ethers as m}from"ethers";function x(o){try{let e=m.HDNodeWallet.fromPhrase(o,void 0,"m/44'/60'/1'/0/0"),t=m.HDNodeWallet.fromPhrase(o,void 0,"m/44'/60'/1'/0/1"),r=t.signingKey.compressedPublicKey,n=e.signingKey.compressedPublicKey,s=r+n.slice(2),i=_e(e.signingKey.publicKey,t.signingKey.publicKey);return{stealthMetaAddress:s,spendingPubKey:r,viewingPubKey:n,viewingKey:e.privateKey,spendingKey:t.privateKey,metaAddress:i}}catch(e){throw new l("Failed to derive stealth keys",e)}}function J(o){try{let e="0x"+o.slice(2,68),t="0x"+o.slice(68),r=m.Wallet.createRandom(),n=r.signingKey.compressedPublicKey,s=Y(r.privateKey,t),i=m.keccak256(s),c="0x"+i.slice(2,4),a=le(e,ce(i));return{stealthAddress:de(a),ephemeralPubKey:n,viewTag:c,ephemeralPubkey:n}}catch(e){throw new l("Failed to generate stealth address",e)}}function N(o,e,t,r,n){try{let s=Y(o,t),i=m.keccak256(s);if(n&&("0x"+i.slice(2,4)).toLowerCase()!==n.toLowerCase())return{isForUser:!1};let c=le(e,ce(i)),a=de(c);return a.toLowerCase()!==r.toLowerCase()?{isForUser:!1}:{isForUser:!0,stealthAddress:a}}catch{return{isForUser:!1}}}function G(o,e,t){try{let r=Y(o,t),n=m.keccak256(r);return Oe(e,n)}catch(r){throw new l("Failed to compute stealth private key",r)}}function Y(o,e){try{return new m.Wallet(o).signingKey.computeSharedSecret(e)}catch(t){throw new l("Failed to compute shared secret",t)}}function ce(o){try{return new m.Wallet(o).signingKey.compressedPublicKey}catch(e){throw new l("Failed to multiply generator by scalar",e)}}function le(o,e){try{let t=m.SigningKey.computePublicKey(o,!1),r=m.SigningKey.computePublicKey(e,!1),n=BigInt("0x"+t.slice(4,68)),s=BigInt("0x"+t.slice(68)),i=BigInt("0x"+r.slice(4,68)),c=BigInt("0x"+r.slice(68)),a=BigInt("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F");if(n===i&&s===c){let we=3n*n*n%a,be=2n*s%a,j=we*ae(be,a)%a,te=(j*j-2n*n)%a,Pe=(j*(n-te)-s)%a;return ie((te+a)%a,(Pe+a)%a)}let g=((c-s)%a+a)%a,y=((i-n)%a+a)%a,v=g*ae(y,a)%a,w=(v*v-n-i)%a,H=(v*(n-w)-s)%a;return ie((w+a)%a,(H+a)%a)}catch(t){throw new l("Failed to add public keys",t)}}function Oe(o,e){try{let t=BigInt("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"),r=BigInt(o),n=BigInt(e);return"0x"+((r+n)%t).toString(16).padStart(64,"0")}catch(t){throw new l("Failed to add private keys",t)}}function ie(o,e){return"0x"+(e%2n===0n?"02":"03")+o.toString(16).padStart(64,"0")}function ae(o,e){o=(o%e+e)%e;let[t,r]=[o,e],[n,s]=[1n,0n];for(;r!==0n;){let i=t/r;[t,r]=[r,t-i*r],[n,s]=[s,n-i*s]}return(n%e+e)%e}function de(o){try{let e=m.SigningKey.computePublicKey(o,!1),t=m.keccak256("0x"+e.slice(4));return m.getAddress("0x"+t.slice(-40))}catch(e){throw new l("Failed to derive address from public key",e)}}function _e(o,e){let t=m.solidityPackedKeccak256(["bytes","bytes"],[o,e]);return m.getAddress("0x"+t.slice(26))}function Ue(o,e,t,r){let n=new m.Wallet(e);return N(o,n.signingKey.compressedPublicKey,t,r).isForUser}var K=class{constructor(e,t){this.getMnemonic=t}async generateStealthAddress(e){try{let t=await this.getMnemonic(e?.requireAuth),r=x(t),n=J(r.stealthMetaAddress);return{stealthAddress:n.stealthAddress,ephemeralPublicKey:n.ephemeralPubKey,viewTag:n.viewTag}}catch(t){throw new d("Failed to generate stealth address","STEALTH_GENERATION_ERROR",t)}}async parseAnnouncement(e,t){try{let r=await this.getMnemonic(t?.requireAuth),n=x(r),s=N(n.viewingKey,n.spendingPubKey,e.ephemeralPublicKey,e.stealthAddress,e.viewTag);if(!s.isForUser)return{isForUser:!1};let i=G(n.viewingKey,n.spendingKey,e.ephemeralPublicKey);return{isForUser:!0,stealthAddress:s.stealthAddress,stealthPrivateKey:i}}catch(r){throw new d("Failed to parse announcement","ANNOUNCEMENT_PARSE_ERROR",r)}}async scanAnnouncements(e,t){let r=[];for(let n of e){let s=await this.parseAnnouncement(n,t);s.isForUser&&r.push(s)}return r}async getKeys(e){try{let t=await this.getMnemonic(e?.requireAuth);return x(t)}catch(t){throw new d("Failed to get stealth keys","STEALTH_KEYS_ERROR",t)}}async getStealthMetaAddress(e){try{return(await this.getKeys(e)).stealthMetaAddress}catch(t){throw new d("Failed to get stealth meta-address","STEALTH_META_ADDRESS_ERROR",t)}}get isAvailable(){return!0}};var D=class{constructor(e=1){this.session=null;this.sessionDuration=e*60*60*1e3}startSession(e,t){let r=Date.now()+this.sessionDuration;this.session={mnemonic:e,expiresAt:r,credentialId:t}}getMnemonic(){return this.session?Date.now()>this.session.expiresAt?(this.clearSession(),null):this.session.mnemonic:null}getCredentialId(){return this.session?Date.now()>this.session.expiresAt?(this.clearSession(),null):this.session.credentialId:null}isActive(){return this.getMnemonic()!==null}getRemainingTime(){return this.session?Date.now()>this.session.expiresAt?(this.clearSession(),0):Math.floor((this.session.expiresAt-Date.now())/1e3):0}extendSession(){if(!this.session)throw new Error("No active session to extend");if(Date.now()>this.session.expiresAt)throw this.clearSession(),new Error("Session expired, cannot extend");this.session.expiresAt=Date.now()+this.sessionDuration}clearSession(){this.session&&(this.session.mnemonic="0".repeat(this.session.mnemonic.length)),this.session=null}setSessionDuration(e){this.sessionDuration=e*60*60*1e3}};var X={debug:!1,sessionDuration:1,onError:o=>{X.debug&&console.error("[w3pk]",o)}};p();var he="https://chainid.network/chains.json";var W=null,Ne=[/\$\{[\w_]+\}/i,/\{[\w_]+\}/i,/<[\w_]+>/i,/YOUR[-_]?API[-_]?KEY/i,/INSERT[-_]?API[-_]?KEY/i,/API[-_]?KEY[-_]?HERE/i];function De(o){return Ne.some(e=>e.test(o))}async function We(o=he){let e=await fetch(o);if(!e.ok)throw new Error(`Failed to fetch chains data: ${e.status} ${e.statusText}`);return await e.json()}async function Be(o){let e=o?.chainsJsonUrl??he,t=o?.cacheDuration??36e5,r=Date.now();if(W&&r-W.timestamp<t)return W.data;let n=await We(e);return W={data:n,timestamp:r},n}async function ue(o,e){let r=(await Be(e)).find(n=>n.chainId===o);return r?r.rpc.filter(n=>!De(n)&&!n.startsWith("wss://")&&!n.startsWith("ws://")):[]}var Ze=new Set([1,10,8453,42161,57073,100,42220,137,42,15,40,41,44,46,47,50,51,56,61,71,82,83,95,97,112,123,130,146,151,153,171,180,183,185,195,215,228,247,248,252,261,267,291,293,311,332,336,395,401,416,466,480,488,510,545,634,647,648,747,831,919,938,945,957,964,970,980,995,997,1001,1003,1024,1030,1114,1125,1135,1149,1188,1284,1285,1287,1300,1301,1315,1337,1338,1339,1424,1514,1687,1727,1729,1740,1750,1829,1868,1946,1961,1962,1969,1989,1995,2017,2020,2031,2043,2109,2241,2340,2345,2440,2522,2559,2649,3068,3109,3338,3502,3799,3888,3889,4e3,4048,4078,4162,4201,4202,4460,4488,4661,4689,4690,4888,5e3,5003,5124,5234,5330,5424,5522,6283,6342,6398,6678,6806,6934,6942,6969,7117,7171,7200,7208,7368,7518,7668,7672,7744,7771,7869,7897,8008,8118,8217,8408,8700,8726,8727,8844,8880,8881,8882,8889,9372,9496,9700,9745,9746,9899,9990,9996,10011,10085,10143,10200,11221,11501,11504,11891,13370,14853,16602,16661,17e3,18880,18881,19991,21e3,21816,21912,25327,32323,33401,34443,41923,42170,43111,44787,47805,48898,48900,49049,49088,5e4,50312,53302,53456,53457,55244,56288,59141,60808,60850,62320,62850,64002,71402,72080,73114,73115,75338,78281,80002,80008,80069,80094,80451,80931,84532,88899,91342,92278,94524,96970,97476,97477,98985,100021,100501,101010,102030,102031,102032,112358,120893,121212,121213,121214,121215,129399,161803,175188,192940,193939,198989,212013,222222,240241,325e3,355110,355113,421614,555777,560048,656476,713715,743111,747474,763373,763375,777777,806582,808813,810180,839999,888991,2019775,2222222,4278608,5734951,6666689,6985385,7080969,7777777,9999999,11142220,11155111,11155420,11155931,16969696,19850818,20180427,20250825,28122024,34949059,37084624,52164803,61022448,79479957,96969696,420420421,420420422,888888888,974399131,999999999,1020352220,1273227453,1313161560,1350216234,1380996178,1417429182,1444673419,1482601649,1564830818,2046399126,11297108099,11297108109,88153591557,123420000220,123420001114]);async function He(o,e=1e4){try{let t=new AbortController,r=setTimeout(()=>t.abort(),e),n=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},signal:t.signal,body:JSON.stringify({jsonrpc:"2.0",method:"eth_estimateGas",params:[{from:"0xdeadbeef00000000000000000000000000000000",to:"0xdeadbeef00000000000000000000000000000000",data:"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",value:"0x0"},"latest",{"0xdeadbeef00000000000000000000000000000000":{code:"0xef01000000000000000000000000000000000000000001"}}],id:1})});if(clearTimeout(r),!n.ok)return!1;let s=await n.json();if(s.error){let i=(s.error.message||"").toLowerCase();return!["unsupported","not supported","unknown","invalid","unrecognized","does not support","not implemented"].some(a=>i.includes(a))}return s.result!==void 0}catch{return!1}}async function ge(o,e,t){if(Ze.has(o))return!0;let r=t?.maxEndpoints||3,n=t?.timeout||1e4;try{let s=await e(o);if(s.length===0)return!1;let i=s.slice(0,r);for(let c of i)if(await He(c,n))return!0;return!1}catch{return!1}}var R=class{constructor(e={}){this.currentUser=null;this.currentWallet=null;this.config={...X,...e},this.walletStorage=new _,this.sessionManager=new D(e.sessionDuration||1),e.stealthAddresses!==void 0&&(this.stealth=new K(e.stealthAddresses,t=>this.getMnemonicFromSession(t))),e.zkProofs&&this.initializeZKModule(e.zkProofs)}async initializeZKModule(e){try{let{ZKProofModule:t}=await Promise.resolve().then(()=>(ye(),fe));this.zkModule=new t(e)}catch{console.warn("ZK module not available. Install dependencies: npm install snarkjs circomlibjs")}}async getMnemonicFromSession(e=!1){if(!e){let g=this.sessionManager.getMnemonic();if(g)return g}if(!this.currentUser)throw new h("Must be authenticated. Call login() first.");let t=await this.walletStorage.retrieve(this.currentUser.ethereumAddress);if(!t)throw new h("No wallet found. Generate a wallet first.");if(!(await O()).user)throw new h("Authentication failed");let i=new(await Promise.resolve().then(()=>(A(),F))).CredentialStorage().getCredentialById(t.credentialId)?.publicKey,c=await E(t.credentialId,i),a=await z(t.encryptedMnemonic,c);return this.sessionManager.startSession(a,t.credentialId),a}async register(e){try{this.currentWallet?.address||await this.generateWallet();let t=this.currentWallet.address,r=this.currentWallet.mnemonic,n=await se({username:e.username,ethereumAddress:t});this.currentUser={id:t,username:e.username,displayName:e.username,ethereumAddress:t};let i=new(await Promise.resolve().then(()=>(A(),F))).CredentialStorage().getCredentialByAddress(t);if(!i)throw new h("Credential not found after registration");let c=i.id,a=i.publicKey,g=await E(c,a),y=await V(r,g);return await this.walletStorage.store({ethereumAddress:this.currentUser.ethereumAddress,encryptedMnemonic:y,credentialId:c,createdAt:Date.now()}),this.sessionManager.startSession(r,c),this.config.onAuthStateChanged?.(!0,this.currentUser),{mnemonic:r}}catch(t){throw this.config.onError?.(t),t}}async login(){try{let e=await O();if(!e.verified||!e.user)throw new b("Login failed");this.currentUser={id:e.user.ethereumAddress,username:e.user.username,displayName:e.user.username,ethereumAddress:e.user.ethereumAddress};let t=await this.walletStorage.retrieve(this.currentUser.ethereumAddress);if(!t)throw new h("No wallet found for this user. You may need to register first.");let s=new(await Promise.resolve().then(()=>(A(),F))).CredentialStorage().getCredentialById(t.credentialId)?.publicKey,i=await E(t.credentialId,s),c=await z(t.encryptedMnemonic,i);return this.sessionManager.startSession(c,t.credentialId),this.config.onAuthStateChanged?.(!0,this.currentUser),this.currentUser}catch(e){throw this.config.onError?.(e),e}}async logout(){this.currentUser=null,this.currentWallet=null,this.sessionManager.clearSession(),this.config.onAuthStateChanged?.(!1,void 0)}get isAuthenticated(){return this.currentUser!==null}get user(){return this.currentUser}async generateWallet(){try{let e=$();return this.currentWallet={address:e.address,mnemonic:e.mnemonic},{mnemonic:e.mnemonic}}catch(e){throw this.config.onError?.(e),new h("Failed to generate wallet",e)}}async deriveWallet(e,t){try{if(!this.currentUser)throw new h("Must be authenticated to derive wallet");let r=await this.getMnemonicFromSession(t?.requireAuth),n=q(r,e);return{address:n.address,privateKey:n.privateKey}}catch(r){throw this.config.onError?.(r),new h("Failed to derive wallet",r)}}async exportMnemonic(e){try{if(!this.currentUser)throw new h("Must be authenticated to export mnemonic");return await this.getMnemonicFromSession(e?.requireAuth)}catch(t){throw this.config.onError?.(t),new h("Failed to export mnemonic",t)}}async importMnemonic(e){try{if(!this.currentUser)throw new h("Must be authenticated to import mnemonic");if(!e||e.trim().split(/\s+/).length<12)throw new h("Invalid mnemonic: must be at least 12 words");let t=await O();if(!t.user)throw new h("Authentication failed");let r=t.user.credentialId,i=new(await Promise.resolve().then(()=>(A(),F))).CredentialStorage().getCredentialById(r)?.publicKey,c=await E(r,i),a=await V(e.trim(),c);await this.walletStorage.store({ethereumAddress:this.currentUser.ethereumAddress,encryptedMnemonic:a,credentialId:r,createdAt:Date.now()}),this.currentWallet={address:this.currentUser.ethereumAddress,mnemonic:e.trim()},this.sessionManager.startSession(e.trim(),r)}catch(t){throw this.config.onError?.(t),new h("Failed to import mnemonic",t)}}async signMessage(e,t){try{if(!this.currentUser)throw new h("Must be authenticated to sign message");let r=await this.getMnemonicFromSession(t?.requireAuth),{Wallet:n}=await import("ethers");return await n.fromPhrase(r).signMessage(e)}catch(r){throw this.config.onError?.(r),new h("Failed to sign message",r)}}async getEndpoints(e){return ue(e)}async supportsEIP7702(e,t){return ge(e,this.getEndpoints.bind(this),t)}get zk(){if(!this.zkModule)throw new Error("ZK module not available. Install dependencies: npm install snarkjs circomlibjs");return this.zkModule}hasActiveSession(){return this.sessionManager.isActive()}getSessionRemainingTime(){return this.sessionManager.getRemainingTime()}extendSession(){try{this.sessionManager.extendSession()}catch(e){throw new h("Cannot extend session",e)}}clearSession(){this.sessionManager.clearSession()}setSessionDuration(e){this.sessionManager.setSessionDuration(e)}get version(){return"0.7.0"}};p();function je(o={}){return new R(o)}var jt=je;export{L as ApiError,b as AuthenticationError,l as CryptoError,I as RegistrationError,K as StealthAddressModule,u as StorageError,h as WalletError,R as Web3Passkey,d as Web3PasskeyError,Ue as canControlStealthAddress,N as checkStealthAddress,G as computeStealthPrivateKey,Me as createWalletFromMnemonic,je as createWeb3Passkey,jt as default,x as deriveStealthKeys,q as deriveWalletFromMnemonic,$ as generateBIP39Wallet,J as generateStealthAddress};
1
+ var ft=Object.defineProperty;var u=(s,e)=>()=>(s&&(e=s(s=0)),e);var A=(s,e)=>{for(var t in e)ft(s,t,{get:e[t],enumerable:!0})};var g,E,K,d,y,h,ge,w=u(()=>{"use strict";g=class extends Error{constructor(t,r,n){super(t);this.code=r;this.originalError=n;this.name="Web3PasskeyError"}},E=class extends g{constructor(e,t){super(e,"AUTHENTICATION_ERROR",t),this.name="AuthenticationError"}},K=class extends g{constructor(e,t){super(e,"REGISTRATION_ERROR",t),this.name="RegistrationError"}},d=class extends g{constructor(e,t){super(e,"WALLET_ERROR",t),this.name="WalletError"}},y=class extends g{constructor(e,t){super(e,"CRYPTO_ERROR",t),this.name="CryptoError"}},h=class extends g{constructor(e,t){super(e,"STORAGE_ERROR",t),this.name="StorageError"}},ge=class extends g{constructor(t,r,n){super(t,"API_ERROR",n);this.statusCode=r;this.name="ApiError"}}});var O={};A(O,{CredentialStorage:()=>P});var j,J,P,C=u(()=>{"use strict";w();j="w3pk_credential_",J="w3pk_credential_index",P=class{constructor(e){if(e)this.storage=e;else if(typeof window<"u"&&window.localStorage)this.storage=window.localStorage;else throw new h("localStorage is not available")}saveCredential(e){try{let t=`${j}${e.id}`;this.storage.setItem(t,JSON.stringify(e)),this.addToIndex(e.id)}catch(t){throw new h("Failed to save credential",t)}}getCredentialById(e){try{let t=`${j}${e}`,r=this.storage.getItem(t);return r?JSON.parse(r):null}catch(t){throw new h("Failed to retrieve credential",t)}}getCredentialByUsername(e){try{return this.getAllCredentials().find(r=>r.username===e)||null}catch(t){throw new h("Failed to retrieve credential",t)}}getCredentialByAddress(e){try{return this.getAllCredentials().find(r=>r.ethereumAddress.toLowerCase()===e.toLowerCase())||null}catch(t){throw new h("Failed to retrieve credential",t)}}getAllCredentials(){try{return this.getIndex().map(t=>this.getCredentialById(t)).filter(t=>t!==null)}catch(e){throw new h("Failed to retrieve credentials",e)}}userExists(e){return this.getCredentialByUsername(e)!==null}updateLastUsed(e){try{let t=this.getCredentialById(e);t&&(t.lastUsed=Date.now(),this.saveCredential(t))}catch(t){throw new h("Failed to update timestamp",t)}}deleteCredential(e){try{let t=`${j}${e}`;this.storage.removeItem(t),this.removeFromIndex(e)}catch(t){throw new h("Failed to delete credential",t)}}clearAll(){try{this.getIndex().forEach(t=>{let r=`${j}${t}`;this.storage.removeItem(r)}),this.storage.removeItem(J)}catch(e){throw new h("Failed to clear credentials",e)}}getIndex(){try{let e=this.storage.getItem(J);return e?JSON.parse(e):[]}catch{return[]}}addToIndex(e){let t=this.getIndex();t.includes(e)||(t.push(e),this.storage.setItem(J,JSON.stringify(t)))}removeFromIndex(e){let r=this.getIndex().filter(n=>n!==e);this.storage.setItem(J,JSON.stringify(r))}}});var Se={};A(Se,{decryptData:()=>re,deriveEncryptionKey:()=>Bt,deriveEncryptionKeyFromSignature:()=>Tt,deriveEncryptionKeyFromWebAuthn:()=>R,encryptData:()=>te,generateChallenge:()=>Dt});async function R(s,e){try{let t=e?`w3pk-v4:${s}:${e}`:`w3pk-v4:${s}`,r=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(t)),n=await crypto.subtle.importKey("raw",r,{name:"PBKDF2"},!1,["deriveKey"]),o=await crypto.subtle.digest("SHA-256",new TextEncoder().encode("w3pk-salt-v4"));return crypto.subtle.deriveKey({name:"PBKDF2",salt:new Uint8Array(o),iterations:21e4,hash:"SHA-256"},n,{name:"AES-GCM",length:256},!1,["encrypt","decrypt"])}catch(t){throw new y("Failed to derive encryption key from WebAuthn",t)}}async function Bt(s,e){try{let t=e?`w3pk-v2:${s}:${e}`:`w3pk-v2:${s}`,r=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(t)),n=await crypto.subtle.importKey("raw",r,{name:"PBKDF2"},!1,["deriveKey"]),o=await crypto.subtle.digest("SHA-256",new TextEncoder().encode("w3pk-salt-v2"));return crypto.subtle.deriveKey({name:"PBKDF2",salt:new Uint8Array(o),iterations:21e4,hash:"SHA-256"},n,{name:"AES-GCM",length:256},!1,["encrypt","decrypt"])}catch(t){throw new y("Failed to derive encryption key",t)}}async function Tt(s,e){try{if(typeof window<"u"&&window.PublicKeyCredential)return R(e);let t=await crypto.subtle.digest("SHA-256",s),r=await crypto.subtle.importKey("raw",t,{name:"PBKDF2"},!1,["deriveKey"]),n=await crypto.subtle.digest("SHA-256",new TextEncoder().encode("w3pk-salt-v3:"+e));return crypto.subtle.deriveKey({name:"PBKDF2",salt:new Uint8Array(n),iterations:21e4,hash:"SHA-256"},r,{name:"AES-GCM",length:256},!1,["encrypt","decrypt"])}catch(t){throw new y("Failed to derive encryption key",t)}}function Dt(){let s=new Uint8Array(32);return crypto.getRandomValues(s),btoa(String.fromCharCode(...s)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}async function te(s,e){try{let t=crypto.getRandomValues(new Uint8Array(12)),r=new TextEncoder().encode(s),n=await crypto.subtle.encrypt({name:"AES-GCM",iv:t},e,r),o=new Uint8Array(t.length+n.byteLength);return o.set(t),o.set(new Uint8Array(n),t.length),btoa(String.fromCharCode(...o))}catch(t){throw new y("Failed to encrypt data",t)}}async function re(s,e){try{if(!s||s.length<16)throw new Error("Invalid encrypted data: too small");let t=new Uint8Array(atob(s).split("").map(i=>i.charCodeAt(0)));if(t.length<12)throw new Error("Invalid encrypted data: missing IV");let r=t.slice(0,12),n=t.slice(12);if(n.length===0)throw new Error("Invalid encrypted data: no content");let o=await crypto.subtle.decrypt({name:"AES-GCM",iv:r},e,n);return new TextDecoder().decode(o)}catch(t){throw new y(`Data decryption failed: ${t instanceof Error?t.message:"Unknown error"}`,t)}}var ne=u(()=>{"use strict";w()});var x,Ce=u(()=>{"use strict";x=class{constructor(){this.dbName="Web3PasskeyBackup";this.version=1;this.db=null}async init(){return typeof indexedDB>"u"?Promise.resolve():new Promise((e,t)=>{let r=indexedDB.open(this.dbName,this.version);r.onerror=()=>t(r.error),r.onsuccess=()=>{this.db=r.result,e()},r.onupgradeneeded=n=>{let o=n.target.result;if(!o.objectStoreNames.contains("backups")){let i=o.createObjectStore("backups",{keyPath:"id"});i.createIndex("ethereumAddress","ethereumAddress",{unique:!1}),i.createIndex("method","method",{unique:!1}),i.createIndex("createdAt","createdAt",{unique:!1})}}})}async storeBackupMetadata(e){return this.db||await this.init(),this.db?new Promise((t,r)=>{let i=this.db.transaction(["backups"],"readwrite").objectStore("backups").put(e);i.onsuccess=()=>t(),i.onerror=()=>r(i.error)}):Promise.resolve()}async getBackupsByAddress(e){return this.db||await this.init(),this.db?new Promise((t,r)=>{let c=this.db.transaction(["backups"],"readonly").objectStore("backups").index("ethereumAddress").getAll(e);c.onsuccess=()=>t(c.result),c.onerror=()=>r(c.error)}):Promise.resolve([])}async getBackupById(e){return this.db||await this.init(),this.db?new Promise((t,r)=>{let i=this.db.transaction(["backups"],"readonly").objectStore("backups").get(e);i.onsuccess=()=>t(i.result||null),i.onerror=()=>r(i.error)}):Promise.resolve(null)}async deleteBackup(e){return this.db||await this.init(),this.db?new Promise((t,r)=>{let i=this.db.transaction(["backups"],"readwrite").objectStore("backups").delete(e);i.onsuccess=()=>t(),i.onerror=()=>r(i.error)}):Promise.resolve()}async getBackupCountByMethod(e){return this.db||await this.init(),this.db?new Promise((t,r)=>{let c=this.db.transaction(["backups"],"readonly").objectStore("backups").index("method").count(e);c.onsuccess=()=>t(c.result),c.onerror=()=>r(c.error)}):Promise.resolve(0)}}});var xe={};A(xe,{decryptWithPassword:()=>Ze,deriveAddressChecksum:()=>b,deriveKeyFromPassword:()=>ae,encryptWithPassword:()=>B,getDeviceFingerprint:()=>f,validatePasswordStrength:()=>ce});async function ae(s,e,t=31e4){let n=new TextEncoder().encode(s),o=await crypto.subtle.importKey("raw",n,"PBKDF2",!1,["deriveBits","deriveKey"]);return crypto.subtle.deriveKey({name:"PBKDF2",salt:e,iterations:t,hash:"SHA-256"},o,{name:"AES-GCM",length:256},!1,["encrypt","decrypt"])}async function B(s,e,t){let n=await ae(e,t,31e4),i=new TextEncoder().encode(s),c=crypto.getRandomValues(new Uint8Array(12)),a=await crypto.subtle.encrypt({name:"AES-GCM",iv:c},n,i);return{encrypted:N(a),iv:N(c),salt:N(t),iterations:31e4}}async function Ze(s,e,t,r,n=31e4){let o=Re(t),i=await ae(e,o,n),c=Re(r),a=Re(s),l=await crypto.subtle.decrypt({name:"AES-GCM",iv:c},i,a);return new TextDecoder().decode(l)}async function f(){let e=[navigator.userAgent,navigator.language,new Date().getTimezoneOffset().toString(),screen.width+"x"+screen.height,screen.colorDepth.toString()].join("|"),r=new TextEncoder().encode(e),n=await crypto.subtle.digest("SHA-256",r);return N(n)}async function b(s){let t=new TextEncoder().encode(s.toLowerCase()),r=await crypto.subtle.digest("SHA-256",t);return N(r).substring(0,16)}function ce(s){let e=[],t=0;return s.length<12?e.push("Password must be at least 12 characters"):t+=25,/[A-Z]/.test(s)?t+=15:e.push("Add at least one uppercase letter"),/[a-z]/.test(s)?t+=15:e.push("Add at least one lowercase letter"),/[0-9]/.test(s)?t+=15:e.push("Add at least one number"),/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(s)?t+=15:e.push("Add at least one special character"),s.length>=16&&(t+=10),s.length>=20&&(t+=5),["password","12345678","qwerty","abc123","password123","admin","letmein"].some(n=>s.toLowerCase().includes(n))&&(e.push("Password is too common"),t=Math.min(t,25)),{valid:t>=50&&e.length===0,score:Math.min(t,100),feedback:e}}function N(s){let e=s instanceof Uint8Array?s:new Uint8Array(s),t="";for(let r=0;r<e.byteLength;r++)t+=String.fromCharCode(e[r]);return btoa(t)}function Re(s){let e=atob(s),t=new Uint8Array(e.length);for(let r=0;r<e.length;r++)t[r]=e.charCodeAt(r);return t}var S=u(()=>{"use strict"});var T,Ie=u(()=>{"use strict";S();T=class{async createZipBackup(e,t,r){let n=ce(r.password);if(!n.valid)throw new Error(`Weak password: ${n.feedback.join(", ")}`);let o=crypto.getRandomValues(new Uint8Array(32)),i=await B(e,r.password,o),c=r.deviceBinding?await f():void 0,a={id:crypto.randomUUID(),ethereumAddress:t,method:"zip",createdAt:Date.now(),deviceFingerprint:c,addressChecksum:await b(t)},l={version:1,encrypted:i.encrypted,iv:i.iv,salt:i.salt,iterations:i.iterations,metadata:a,deviceFingerprint:c},p=new Map;return p.set("recovery-phrase.txt.enc",JSON.stringify(l,null,2)),p.set("metadata.json",JSON.stringify({address:t,createdAt:new Date(a.createdAt).toISOString(),backupId:a.id,addressChecksum:a.addressChecksum},null,2)),r.includeInstructions!==!1&&p.set("RECOVERY_INSTRUCTIONS.txt",this.getRecoveryInstructions()),{blob:await this.createSimpleZip(p),metadata:a}}async createSimpleZip(e){let t={};return e.forEach((r,n)=>{t[n]=r}),new Blob([JSON.stringify(t,null,2)],{type:"application/json"})}getRecoveryInstructions(){return`
2
+ W3PK WALLET RECOVERY INSTRUCTIONS
3
+ =================================
4
+
5
+ This backup contains your encrypted wallet recovery phrase.
6
+
7
+ IMPORTANT:
8
+ - Keep this file safe and secure
9
+ - You NEED the password you set during backup creation
10
+ - Without the password, this backup cannot be decrypted
11
+
12
+ RECOVERY STEPS:
13
+ --------------
14
+
15
+ 1. Go to the w3pk recovery page
16
+ URL: https://your-w3pk-app.com/recover
17
+
18
+ 2. Click "Import from Backup"
19
+
20
+ 3. Upload this backup file
21
+
22
+ 4. Enter your backup password
23
+
24
+ 5. System will decrypt and verify your wallet
25
+
26
+ 6. Your wallet will be restored!
27
+
28
+ VERIFICATION:
29
+ ------------
30
+
31
+ After recovery, verify that the Ethereum address matches:
32
+ - Check the address in metadata.json
33
+ - Compare with your known wallet address
34
+ - If they match, recovery was successful \u2713
35
+
36
+ SECURITY NOTES:
37
+ --------------
38
+
39
+ \u2713 This backup is encrypted with AES-256-GCM
40
+ \u2713 Your password is required to decrypt
41
+ \u2713 Store this backup in a secure location:
42
+ - Password manager (1Password, Bitwarden)
43
+ - Encrypted cloud storage (Google Drive, Dropbox)
44
+ - USB drive in safe
45
+ - Physical storage in safe deposit box
46
+
47
+ \u2717 Do NOT store in:
48
+ - Unencrypted email
49
+ - Shared drives
50
+ - Public cloud without password protection
51
+
52
+ ALTERNATIVE RECOVERY:
53
+ -------------------
54
+
55
+ If you lose this backup, you can still recover using:
56
+ - Your 12-word recovery phrase (if saved separately)
57
+ - Social recovery (if configured)
58
+ - Passkey sync (if enabled on another device)
59
+
60
+ NEED HELP?
61
+ ---------
62
+
63
+ Visit: https://docs.w3pk.org/recovery
64
+ Email: support@w3pk.org
65
+
66
+ Generated: ${new Date().toISOString()}
67
+ `}async restoreFromZipBackup(e,t){let r=JSON.parse(e);if(r.version!==1)throw new Error("Unsupported backup version");let{decryptWithPassword:n}=await Promise.resolve().then(()=>(S(),xe)),o=await n(r.encrypted,t,r.salt,r.iv,r.iterations),{Wallet:i}=await import("ethers"),c=i.fromPhrase(o);if(await b(c.address)!==r.metadata.addressChecksum)throw new Error("Address checksum mismatch - corrupted backup or wrong password");return{mnemonic:o,metadata:r.metadata}}}});var D,Be=u(()=>{"use strict";S();D=class{async createQRBackup(e,t,r={}){let n=r.errorCorrection||"H",o;if(r.password){let l=crypto.getRandomValues(new Uint8Array(32)),p=await B(e,r.password,l);o={version:1,type:"encrypted",data:p.encrypted,iv:p.iv,salt:p.salt,iterations:p.iterations,checksum:await b(t)}}else o={version:1,type:"plain",data:e,checksum:await b(t)};let i=JSON.stringify(o),c=await this.generateQRCode(i,n),a=this.getInstructions(t,!!r.password);return{qrCodeDataURL:c,rawData:i,instructions:a}}async generateQRCode(e,t){try{return await(await import("qrcode")).default.toDataURL(e,{errorCorrectionLevel:t,width:512,margin:2})}catch{return this.createFallbackQRDataURL(e)}}createFallbackQRDataURL(e){if(typeof document>"u")return`data:text/plain;base64,${Buffer.from(e).toString("base64")}`;let t=document.createElement("canvas");t.width=512,t.height=512;let r=t.getContext("2d");if(!r)throw new Error("Canvas not supported");r.fillStyle="white",r.fillRect(0,0,512,512),r.fillStyle="black",r.font="12px monospace",r.textAlign="center",r.textBaseline="middle";let n=this.wrapText(e,50),o=14,i=256-n.length*o/2;return n.forEach((c,a)=>{r.fillText(c,256,i+a*o)}),r.font="bold 16px sans-serif",r.fillText('Install "qrcode" package for QR codes',256,480),t.toDataURL("image/png")}wrapText(e,t){let r=[];for(let n=0;n<e.length;n+=t)r.push(e.substring(n,n+t));return r}getInstructions(e,t){return`
68
+ W3PK QR CODE BACKUP
69
+ ==================
70
+
71
+ Ethereum Address: ${e}
72
+ Backup Date: ${new Date().toISOString()}
73
+ Type: ${t?"Encrypted":"Plain"}
74
+
75
+ STORAGE INSTRUCTIONS:
76
+ --------------------
77
+
78
+ \u{1F4F8} Print this QR code and store securely:
79
+ \u2713 Safe deposit box
80
+ \u2713 Home safe
81
+ \u2713 Trusted family member
82
+ \u2713 Multiple physical locations
83
+
84
+ \u26A0\uFE0F DO NOT:
85
+ \u2717 Store digitally (screenshot/photo)
86
+ \u2717 Upload to cloud
87
+ \u2717 Email or message
88
+ \u2717 Share publicly
89
+
90
+ RECOVERY STEPS:
91
+ --------------
92
+
93
+ 1. Scan QR code with your phone camera or QR scanner app
94
+
95
+ 2. Import the scanned data to w3pk recovery page
96
+
97
+ 3. ${t?"Enter your backup password":"No password needed (plain backup)"}
98
+
99
+ 4. Wallet will be restored
100
+
101
+ SECURITY NOTES:
102
+ --------------
103
+
104
+ ${t?`
105
+ \u2713 This QR code is ENCRYPTED
106
+ \u2713 Password required to decrypt
107
+ \u2713 Safe to store in multiple locations
108
+ \u2713 Share with trusted family (they can't access without password)
109
+ `:`
110
+ \u26A0\uFE0F This QR code is NOT ENCRYPTED
111
+ \u26A0\uFE0F Anyone with access can recover your wallet
112
+ \u26A0\uFE0F Store with maximum security
113
+ \u26A0\uFE0F Do not photograph or copy
114
+ `}
115
+
116
+ ERROR CORRECTION:
117
+ ----------------
118
+
119
+ This QR code has HIGH error correction (30% damage tolerance)
120
+ - Can survive partial damage
121
+ - Can handle folding/creasing
122
+ - Ensure good print quality
123
+
124
+ VERIFICATION:
125
+ ------------
126
+
127
+ Address checksum is embedded in QR code
128
+ After scanning, verify address matches: ${e}
129
+
130
+ ALTERNATIVE RECOVERY:
131
+ -------------------
132
+
133
+ If QR code is lost/damaged, you can still recover using:
134
+ - Encrypted ZIP backup
135
+ - Social recovery
136
+ - Passkey sync
137
+ - Manual mnemonic entry (if you wrote down the words)
138
+
139
+ ---
140
+
141
+ Generated by w3pk Recovery System
142
+ https://docs.w3pk.org/recovery
143
+ `}async restoreFromQR(e,t){let r=JSON.parse(e);if(r.version!==1)throw new Error("Unsupported QR backup version");let n;if(r.type==="encrypted"){if(!t)throw new Error("Password required for encrypted QR backup");let{decryptWithPassword:a}=await Promise.resolve().then(()=>(S(),xe));n=await a(r.data,t,r.salt,r.iv,r.iterations)}else if(r.type==="plain")n=r.data;else throw new Error("Unknown QR backup type");let{Wallet:o}=await import("ethers"),i=o.fromPhrase(n);if(await b(i.address)!==r.checksum)throw new Error("Address checksum mismatch - corrupted QR or wrong password");return{mnemonic:n,ethereumAddress:i.address}}}});var L,Xe=u(()=>{"use strict";Ce();Ie();Be();L=class{constructor(){this.storage=new x,this.zipCreator=new T,this.qrCreator=new D}async getBackupStatus(e){let t=await this.storage.getBackupsByAddress(e),r=await this.detectPasskeySync(),n=t.map(i=>({id:i.id,method:i.method,location:"local",createdAt:i.createdAt,deviceFingerprint:i.deviceFingerprint}));return{passkeySync:r,recoveryPhrase:{verified:!1,verificationCount:0,encryptedBackups:n},securityScore:this.calculateSecurityScore(r,n)}}async detectPasskeySync(){if(typeof navigator>"u")return{enabled:!1,deviceCount:0,lastSyncTime:void 0,platform:"unknown"};let e=navigator.userAgent.toLowerCase(),t="unknown";e.includes("mac")||e.includes("iphone")||e.includes("ipad")?t="apple":e.includes("android")?t="google":e.includes("windows")&&(t="microsoft");let r=await this.checkResidentKeySupport();return{enabled:r,deviceCount:1,lastSyncTime:r?Date.now():void 0,platform:t}}async checkResidentKeySupport(){if(typeof window>"u"||!window.PublicKeyCredential)return!1;try{return await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()}catch{return!1}}calculateSecurityScore(e,t){let r={passkeyActive:e.enabled?20:0,passkeyMultiDevice:e.deviceCount>1?10:0,phraseVerified:0,encryptedBackup:t.length>0?20:0,socialRecovery:0},n=Object.values(r).reduce((c,a)=>c+a,0),o,i;return n<=20?(o="vulnerable",i='Create encrypted backup to reach "protected" (40+ pts)'):n<=50?(o="protected",i='Set up social recovery to reach "secured" (70+ pts)'):n<=80?(o="secured",i='Enable all methods to reach "fort-knox" (100 pts)'):(o="fort-knox",i="Maximum security achieved! \u{1F3C6}"),{total:n,breakdown:r,level:o,nextMilestone:i}}async createZipBackup(e,t,r){let{blob:n,metadata:o}=await this.zipCreator.createZipBackup(e,t,r);return await this.storage.storeBackupMetadata(o),n}async createQRBackup(e,t,r){let{qrCodeDataURL:n,rawData:o,instructions:i}=await this.qrCreator.createQRBackup(e,t,r),c={id:crypto.randomUUID(),ethereumAddress:t,method:"qr",createdAt:Date.now(),addressChecksum:JSON.parse(o).checksum};return await this.storage.storeBackupMetadata(c),{qrCodeDataURL:n,instructions:i}}async restoreFromZipBackup(e,t){let{mnemonic:r,metadata:n}=await this.zipCreator.restoreFromZipBackup(e,t);return{mnemonic:r,ethereumAddress:n.ethereumAddress}}async restoreFromQR(e,t){return await this.qrCreator.restoreFromQR(e,t)}async simulateRecoveryScenario(e,t){let r=[];switch(e.type){case"lost-device":t.passkeySync.enabled&&t.passkeySync.deviceCount>1&&r.push({method:"Passkey Sync (iCloud/Google)",success:!0,time:"5 minutes",requirements:["Sign in to cloud account","Authenticate on new device"]}),t.recoveryPhrase.encryptedBackups.length>0&&r.push({method:"Encrypted ZIP Backup",success:!0,time:"2 minutes",requirements:["Backup file","Password"]}),t.socialRecovery?.enabled&&r.push({method:"Social Recovery",success:!0,time:"24 hours",requirements:[`${t.socialRecovery.threshold} guardian shares`]});break;case"lost-phrase":t.passkeySync.enabled&&r.push({method:"Passkey (current device)",success:!0,time:"instant",requirements:["Current device","Biometric/PIN"]}),t.socialRecovery?.enabled&&r.push({method:"Social Recovery",success:!0,time:"24 hours",requirements:[`${t.socialRecovery.threshold} guardian shares`]});break;case"lost-both":t.passkeySync.enabled&&t.passkeySync.deviceCount>1&&r.push({method:"Passkey Sync",success:!0,time:"5 minutes",requirements:["Cloud account access","New device"]}),t.socialRecovery?.enabled&&r.push({method:"Social Recovery",success:!0,time:"24 hours",requirements:[`${t.socialRecovery.threshold} guardian shares`]});break;case"switch-platform":t.recoveryPhrase.encryptedBackups.length>0&&r.push({method:"Encrypted Backup",success:!0,time:"2 minutes",requirements:["Backup file","Password"]}),t.socialRecovery?.enabled&&r.push({method:"Social Recovery",success:!0,time:"24 hours",requirements:[`${t.socialRecovery.threshold} guardian shares`]});break}let n=r.length>0&&r.some(i=>i.success),o="";return n?(o=`\u2705 You're safe! ${r.length} way${r.length>1?"s":""} to recover.
144
+
145
+ `,o+=`Available recovery methods:
146
+ `,r.forEach(i=>{o+=`- ${i.method} (~${i.time})
147
+ `})):(o=`\u274C Wallet cannot be recovered in this scenario.
148
+
149
+ `,o+="Recommendation: Set up at least 2 backup methods to prevent total loss."),{scenario:e,success:n,availableMethods:r,timeEstimate:n?r[0].time:"N/A",educationalNote:o}}}});var et=u(()=>{"use strict"});var F={};A(F,{BackupManager:()=>L,BackupStorage:()=>x,QRBackupCreator:()=>D,ZipBackupCreator:()=>T,decryptWithPassword:()=>Ze,deriveAddressChecksum:()=>b,deriveKeyFromPassword:()=>ae,encryptWithPassword:()=>B,getDeviceFingerprint:()=>f,validatePasswordStrength:()=>ce});var I=u(()=>{"use strict";Xe();Ce();Ie();Be();et();S()});function Gt(){let s=new Uint8Array(1);return crypto.getRandomValues(s),s[0]}function Te(s,e,t){if(e>t)throw new Error("Threshold cannot be greater than total shares");if(e<2)throw new Error("Threshold must be at least 2");if(t>255)throw new Error("Cannot create more than 255 shares");let r=[];for(let n=0;n<t;n++)r[n]=new Uint8Array(s.length+1),r[n][0]=n+1;for(let n=0;n<s.length;n++){let i=[s[n]];for(let c=1;c<e;c++)i.push(Gt());for(let c=0;c<t;c++){let a=c+1,l=de.evaluatePolynomial(i,a);r[c][n+1]=l}}return r}function De(s,e){if(s.length<e)throw new Error(`Need at least ${e} shares to recover secret, got ${s.length}`);let t=s.slice(0,e),r=t[0].length;for(let i of t)if(i.length!==r)throw new Error("All shares must have the same length");let n=r-1,o=new Uint8Array(n);for(let i=0;i<n;i++){let c=[];for(let a of t)c.push({x:a[0],y:a[i+1]});o[i]=de.interpolate(c)}return o}function Fe(s){return new TextEncoder().encode(s)}function Me(s){return new TextDecoder().decode(s)}function Ke(s){return Array.from(s).map(e=>e.toString(16).padStart(2,"0")).join("")}function Oe(s){let e=new Uint8Array(s.length/2);for(let t=0;t<s.length;t+=2)e[t/2]=parseInt(s.substring(t,t+2),16);return e}var k,de,Ue=u(()=>{"use strict";k=class k{static multiply(e,t){return e===0||t===0?0:this.EXP_TABLE[(this.LOG_TABLE[e]+this.LOG_TABLE[t])%255]}static divide(e,t){if(t===0)throw new Error("Division by zero in GF(256)");return e===0?0:this.EXP_TABLE[(this.LOG_TABLE[e]-this.LOG_TABLE[t]+255)%255]}static add(e,t){return e^t}static evaluatePolynomial(e,t){let r=0;for(let n=e.length-1;n>=0;n--)r=this.add(this.multiply(r,t),e[n]);return r}static interpolate(e){let t=0;for(let r=0;r<e.length;r++){let n=e[r].y,o=1;for(let i=0;i<e.length;i++)r!==i&&(n=this.multiply(n,e[i].x),o=this.multiply(o,this.add(e[r].x,e[i].x)));t=this.add(t,this.divide(n,o))}return t}};k.LOG_TABLE=[],k.EXP_TABLE=[],(()=>{let e=(r,n)=>{let o=0;for(let i=0;i<8;i++){n&1&&(o^=r);let c=r&128;r<<=1,c&&(r^=283),n>>=1}return o&255},t=1;for(let r=0;r<255;r++)k.EXP_TABLE[r]=t,k.LOG_TABLE[t]=r,t=e(t,3);k.EXP_TABLE[255]=k.EXP_TABLE[0]})();de=k});var tt,q,rt=u(()=>{"use strict";Ue();tt=new Map,q=class{constructor(){this.storageKey="w3pk_social_recovery"}getItem(e){return typeof localStorage<"u"?localStorage.getItem(e):tt.get(e)||null}setItem(e,t){typeof localStorage<"u"?localStorage.setItem(e,t):tt.set(e,t)}async setupSocialRecovery(e,t,r,n){if(n>r.length)throw new Error("Threshold cannot be greater than number of guardians");if(n<2)throw new Error("Threshold must be at least 2");if(r.length>255)throw new Error("Cannot have more than 255 guardians");let o=Fe(e),i=Te(o,n,r.length),c=r.map((l,p)=>({id:crypto.randomUUID(),name:l.name,email:l.email,phone:l.phone,shareEncrypted:Ke(i[p]),status:"pending",addedAt:Date.now()})),a={threshold:n,totalGuardians:r.length,guardians:c,createdAt:Date.now(),ethereumAddress:t};return this.setItem(this.storageKey,JSON.stringify(a)),c}getSocialRecoveryConfig(){let e=this.getItem(this.storageKey);if(!e)return null;try{return JSON.parse(e)}catch{return null}}async generateGuardianInvite(e){let t=this.getSocialRecoveryConfig();if(!t)throw new Error("Social recovery not configured");let r=t.guardians.findIndex(a=>a.id===e.id);if(r===-1)throw new Error("Guardian not found");let n={version:1,guardianId:e.id,guardianName:e.name,guardianIndex:r+1,totalGuardians:t.totalGuardians,threshold:t.threshold,share:e.shareEncrypted,ethereumAddress:t.ethereumAddress,createdAt:t.createdAt},o=JSON.stringify(n),i=await this.generateQRCode(o),c=this.getGuardianExplainer(e.name,r+1,t.totalGuardians,t.threshold);return{guardianId:e.id,qrCode:i,shareCode:o,explainer:c}}async generateQRCode(e){try{return await(await import("qrcode")).toDataURL(e,{errorCorrectionLevel:"H",width:512,margin:2})}catch{return this.createPlaceholderQR(e)}}createPlaceholderQR(e){if(typeof document>"u")return`data:text/plain;base64,${Buffer.from(e).toString("base64")}`;let t=document.createElement("canvas");t.width=512,t.height=512;let r=t.getContext("2d");return r?(r.fillStyle="white",r.fillRect(0,0,512,512),r.fillStyle="black",r.font="bold 20px sans-serif",r.textAlign="center",r.textBaseline="middle",r.fillText("Guardian Recovery Share",256,100),r.font="14px monospace",r.fillText('Install "qrcode" for QR codes',256,480),r.font="10px monospace",this.wrapText(e.substring(0,200)+"...",60).forEach((o,i)=>{r.fillText(o,256,150+i*12)}),t.toDataURL("image/png")):""}wrapText(e,t){let r=[];for(let n=0;n<e.length;n+=t)r.push(e.substring(n,n+t));return r}getGuardianExplainer(e,t,r,n){return`
150
+ \u{1F6E1}\uFE0F GUARDIAN RECOVERY SHARE
151
+
152
+ Dear ${e},
153
+
154
+ You have been chosen as Guardian ${t} of ${r}
155
+
156
+ YOUR ROLE:
157
+ ----------
158
+
159
+ Your friend has entrusted you with a recovery share for their cryptocurrency wallet.
160
+
161
+ WHAT THIS MEANS:
162
+ - You hold 1 piece of a ${n}-piece puzzle
163
+ - ${n} guardians needed to recover the wallet
164
+ - You cannot access the wallet alone
165
+ - This is a responsibility and honor
166
+
167
+ HOW IT WORKS:
168
+ -------------
169
+
170
+ If your friend loses access to their wallet:
171
+
172
+ 1. They will contact you to request your share
173
+ 2. You provide the QR code or share code below
174
+ 3. System collects shares from ${n} guardians
175
+ 4. Wallet is mathematically reconstructed
176
+ 5. Your friend regains access \u2713
177
+
178
+ SECURITY:
179
+ ---------
180
+
181
+ \u2713 Your share is encrypted
182
+ \u2713 Cannot be used alone
183
+ \u2713 ${n-1} other guardians needed
184
+ \u2713 Safe to store digitally
185
+
186
+ YOUR RESPONSIBILITIES:
187
+ ---------------------
188
+
189
+ DO:
190
+ \u2713 Keep this share safe and accessible
191
+ \u2713 Store in password manager or secure location
192
+ \u2713 Respond promptly if friend requests recovery
193
+ \u2713 Verify identity before sharing
194
+
195
+ DON'T:
196
+ \u2717 Share unless friend explicitly requests it
197
+ \u2717 Post publicly or send unsecured
198
+ \u2717 Lose or delete (friend depends on you!)
199
+
200
+ VERIFICATION:
201
+ ------------
202
+
203
+ Before sharing, verify your friend's identity:
204
+ - Video call to confirm
205
+ - Ask security questions
206
+ - Check contact method is authentic
207
+
208
+ RECOVERY PROCESS:
209
+ ----------------
210
+
211
+ If requested:
212
+ 1. Friend will contact you
213
+ 2. Verify their identity
214
+ 3. Provide this QR code or share code
215
+ 4. System handles the rest
216
+
217
+ HOW TO STORE:
218
+ ------------
219
+
220
+ Recommended storage:
221
+ \u2713 Password manager (1Password, Bitwarden)
222
+ \u2713 Encrypted note
223
+ \u2713 Print and store physically
224
+ \u2713 Screenshot (encrypted phone)
225
+
226
+ ---
227
+
228
+ Guardian ${t}/${r} | Threshold: ${n}/${r}
229
+ Created: ${new Date().toISOString()}
230
+
231
+ Thank you for being a trusted guardian!
232
+
233
+ ---
234
+
235
+ NEED HELP?
236
+ Visit: https://docs.w3pk.org/social-recovery
237
+ `}async recoverFromGuardians(e){let t=this.getSocialRecoveryConfig();if(!t)throw new Error("Social recovery not configured");if(e.length<t.threshold)throw new Error(`Need at least ${t.threshold} shares, got ${e.length}`);let n=e.map(l=>{let p=JSON.parse(l);return{guardianId:p.guardianId,share:p.share,index:p.guardianIndex}}).map(l=>Oe(l.share)),o=De(n,t.threshold),i=Me(o),{Wallet:c}=await import("ethers"),a=c.fromPhrase(i);if(a.address.toLowerCase()!==t.ethereumAddress.toLowerCase())throw new Error("Recovered address does not match - invalid shares");return{mnemonic:i,ethereumAddress:a.address}}getRecoveryProgress(e){let t=this.getSocialRecoveryConfig();if(!t)throw new Error("Social recovery not configured");let r=new Set(e.map(n=>{try{return JSON.parse(n).guardianId}catch{return null}}).filter(Boolean));return{collected:r.size,required:t.threshold,guardians:t.guardians.map(n=>({id:n.id,name:n.name,hasProvided:r.has(n.id)})),canRecover:r.size>=t.threshold}}markGuardianVerified(e){let t=this.getSocialRecoveryConfig();if(!t)throw new Error("Social recovery not configured");let r=t.guardians.find(n=>n.id===e);if(!r)throw new Error("Guardian not found");r.status="active",r.lastVerified=Date.now(),this.setItem(this.storageKey,JSON.stringify(t))}revokeGuardian(e){let t=this.getSocialRecoveryConfig();if(!t)throw new Error("Social recovery not configured");let r=t.guardians.find(n=>n.id===e);if(!r)throw new Error("Guardian not found");r.status="revoked",this.setItem(this.storageKey,JSON.stringify(t))}async addGuardian(e,t){let r=this.getSocialRecoveryConfig();if(!r)throw new Error("Social recovery not configured");let n=[...r.guardians.filter(i=>i.status!=="revoked").map(i=>({name:i.name,email:i.email,phone:i.phone})),t],o=await this.setupSocialRecovery(e,r.ethereumAddress,n,r.threshold);return o[o.length-1]}}});var nt=u(()=>{"use strict"});var le={};A(le,{SocialRecoveryManager:()=>q,bytesToHex:()=>Ke,bytesToString:()=>Me,combineShares:()=>De,hexToBytes:()=>Oe,splitSecret:()=>Te,stringToBytes:()=>Fe});var G=u(()=>{"use strict";rt();Ue();nt()});var $,st=u(()=>{"use strict";S();$=class{async createSyncPackage(e,t,r){let n=await f(),{deriveEncryptionKeyFromWebAuthn:o,encryptData:i}=await Promise.resolve().then(()=>(ne(),Se)),c=await o(t,r),a=await i(e,c),l=this.detectSyncMethod();return{id:crypto.randomUUID(),encryptedData:a,deviceFingerprints:[n],syncMethod:l,version:1,updatedAt:Date.now()}}detectSyncMethod(){let e=navigator.userAgent.toLowerCase();return e.includes("mac")||e.includes("iphone")||e.includes("ipad")?"icloud":e.includes("android")||e.includes("chrome")?"google":e.includes("windows")?"microsoft":"custom"}async restoreFromSync(e,t,r){let{deriveEncryptionKeyFromWebAuthn:n,decryptData:o}=await Promise.resolve().then(()=>(ne(),Se)),i=await n(t,r),c=await o(e.encryptedData,i),a=await f();return e.deviceFingerprints.includes(a)||(e.deviceFingerprints.push(a),e.updatedAt=Date.now()),c}async getSetupFlow(){return{success:!1,steps:[{title:"1. Authenticate on New Device",action:"Use Touch ID/Face ID",educational:"Your passkey is automatically synced via iCloud/Google",status:"waiting"},{title:"2. Decrypt Wallet Data",action:"System validates device",educational:"Only your trusted devices can decrypt the wallet",status:"waiting"},{title:"3. Verify Recovery",action:"Check partial address",educational:"Confirm address matches your wallet",status:"waiting"},{title:"4. Ready!",action:"Wallet synced",educational:"All devices now have access",status:"waiting"}]}}getSyncExplainer(){return`
238
+ HOW CROSS-DEVICE SYNC WORKS
239
+ ===========================
240
+
241
+ Your wallet is protected by TWO layers:
242
+
243
+ Layer 1: Passkey (Auto-Syncs)
244
+ -----------------------------
245
+ \u2713 Your fingerprint/Face ID credential
246
+ \u2713 Syncs via iCloud Keychain or Google
247
+ \u2713 Available on all your devices
248
+ \u2713 Cannot be stolen (hardware-protected)
249
+
250
+ Layer 2: Encrypted Wallet (In Browser)
251
+ --------------------------------------
252
+ \u2713 Your 12-word mnemonic (encrypted)
253
+ \u2713 Stored in browser's IndexedDB
254
+ \u2713 Can ONLY be decrypted with passkey
255
+ \u2713 Requires authentication to access
256
+
257
+ How They Work Together:
258
+ ----------------------
259
+
260
+ Device 1 (iPhone) iCloud Keychain Device 2 (Mac)
261
+ | | |
262
+ |-- Passkey Created -------->| |
263
+ | | |
264
+ |-- Wallet Encrypted --------| |
265
+ | (in IndexedDB) | |
266
+ | | |
267
+ | |<----- Login on Mac -----|
268
+ | | |
269
+ | Passkey Synced |
270
+ | |------- Unlock --------->|
271
+ | | Passkey |
272
+ | | |
273
+ | |<----- Decrypt ----------|
274
+ | | Wallet |
275
+
276
+ Security Benefits:
277
+ -----------------
278
+
279
+ \u2713 Passkey syncs automatically (convenient)
280
+ \u2713 Wallet ONLY decrypts with passkey (secure)
281
+ \u2713 Cannot access wallet without biometric
282
+ \u2713 Works seamlessly across devices
283
+ \u2713 No passwords to remember
284
+
285
+ What Gets Synced:
286
+ ----------------
287
+
288
+ \u2713 Passkey credential (via platform sync)
289
+ \u2713 Credential metadata (localStorage)
290
+
291
+ What Stays Local:
292
+ ----------------
293
+
294
+ \u2717 Encrypted mnemonic (in IndexedDB)
295
+ - Must authenticate to decrypt
296
+ - Requires passkey access
297
+ - Platform-specific storage
298
+
299
+ Recovery Options:
300
+ ----------------
301
+
302
+ If you lose a device:
303
+ 1. \u2713 Sign in on new device
304
+ 2. \u2713 Passkey auto-syncs
305
+ 3. \u2713 Authenticate to decrypt wallet
306
+ 4. \u2713 Wallet restored!
307
+
308
+ If passkey doesn't sync:
309
+ 1. \u2713 Use encrypted backup
310
+ 2. \u2713 Use 12-word recovery phrase
311
+ 3. \u2713 Use social recovery
312
+
313
+ ---
314
+
315
+ Think of it like:
316
+ \u{1F511} Passkey = Your car key (syncs via keychain)
317
+ \u{1F697} Wallet = Your car (locked, needs key to start)
318
+ `}}});var Y,ot=u(()=>{"use strict";S();Y=class{constructor(){this.storageKey="w3pk_devices"}async registerDevice(){let e={id:await f(),name:this.getDeviceName(),platform:this.detectPlatform(),lastActive:Date.now(),trusted:!0,canRevoke:!1},t=await this.getDevices(),r=t.find(n=>n.id===e.id);return r?(r.lastActive=Date.now(),localStorage.setItem(this.storageKey,JSON.stringify(t))):(t.push(e),localStorage.setItem(this.storageKey,JSON.stringify(t))),e}async getDevices(){let e=localStorage.getItem(this.storageKey);if(!e)return[];try{return JSON.parse(e)}catch{return[]}}async getSyncStatus(){let e=await this.getDevices(),t=await f();e.forEach(o=>{o.canRevoke=o.id!==t}),e.sort((o,i)=>i.lastActive-o.lastActive);let r=this.detectPlatform(),n=e.length>1?Math.max(...e.map(o=>o.lastActive)):void 0;return{enabled:e.length>1,devices:e,lastSyncTime:n,platform:this.getPlatformName(r)}}async revokeDevice(e){let t=await this.getDevices(),r=await f();if(e===r)throw new Error("Cannot revoke current device");let n=t.filter(o=>o.id!==e);localStorage.setItem(this.storageKey,JSON.stringify(n))}async updateLastActive(){let e=await this.getDevices(),t=await f(),r=e.find(n=>n.id===t);r&&(r.lastActive=Date.now(),localStorage.setItem(this.storageKey,JSON.stringify(e)))}getDeviceName(){let e=this.detectPlatform(),t=navigator.userAgent;if(e==="ios")return t.includes("iPhone")?"iPhone":t.includes("iPad")?"iPad":t.includes("iPod")?"iPod":"iOS Device";if(e==="android"){let r=t.match(/Android.*;\s([^)]+)\)/);return r?r[1]:"Android Device"}return e==="macos"?"Mac":e==="windows"?"Windows PC":e==="linux"?"Linux PC":"Unknown Device"}detectPlatform(){let e=navigator.userAgent.toLowerCase();return e.includes("iphone")||e.includes("ipad")||e.includes("ipod")?"ios":e.includes("android")?"android":e.includes("mac")?"macos":e.includes("windows")?"windows":e.includes("linux")?"linux":"unknown"}getPlatformName(e){switch(e){case"ios":return"iOS (iCloud Keychain)";case"android":return"Android (Google)";case"macos":return"macOS (iCloud Keychain)";case"windows":return"Windows (Microsoft)";case"linux":return"Linux";default:return"Unknown"}}async getDeviceListFormatted(){let e=await this.getSyncStatus();if(e.devices.length===0)return"No devices registered";let t=`Your Devices (${e.devices.length}):
319
+
320
+ `;return e.devices.forEach((r,n)=>{let o=Date.now()-r.lastActive,i=this.formatTimeDiff(o);t+=`${n+1}. ${r.name}
321
+ `,t+=` Platform: ${this.getPlatformName(r.platform)}
322
+ `,t+=` Last active: ${i}
323
+ `,t+=` ${r.canRevoke?"":"(Current device)"}
324
+
325
+ `}),t}formatTimeDiff(e){let t=Math.floor(e/1e3),r=Math.floor(t/60),n=Math.floor(r/60),o=Math.floor(n/24);return o>0?`${o} day${o>1?"s":""} ago`:n>0?`${n} hour${n>1?"s":""} ago`:r>0?`${r} minute${r>1?"s":""} ago`:"just now"}}});var H,it=u(()=>{"use strict";H=class{async detectSyncCapabilities(){let e=this.detectPlatform(),t=await this.checkPasskeySync(),r=this.estimateDeviceCount();return{passkeysSync:t,platform:e,estimatedDevices:r,syncEnabled:t&&e!=="none"}}detectPlatform(){let e=navigator.userAgent.toLowerCase();return e.includes("mac")||e.includes("iphone")||e.includes("ipad")||e.includes("ipod")?"apple":e.includes("android")?"google":e.includes("windows")?"microsoft":"none"}async checkPasskeySync(){if(!window.PublicKeyCredential)return!1;try{return await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()}catch{return!1}}estimateDeviceCount(){return 1}getPlatformEducation(e){switch(e){case"apple":return`
326
+ \u{1F34E} Apple iCloud Keychain
327
+
328
+ Your passkey automatically syncs across:
329
+ - iPhone
330
+ - iPad
331
+ - Mac
332
+ - Apple Watch (for authentication)
333
+
334
+ Requirements:
335
+ \u2713 iCloud Keychain enabled in Settings
336
+ \u2713 Signed in to iCloud
337
+ \u2713 Two-factor authentication enabled
338
+
339
+ How it works:
340
+ 1. Create passkey on this device
341
+ 2. iCloud encrypts and syncs it
342
+ 3. Available on all your Apple devices
343
+ 4. End-to-end encrypted
344
+
345
+ Security:
346
+ - Apple cannot access your passkeys
347
+ - Synced with end-to-end encryption
348
+ - Requires device unlock to use
349
+ `;case"google":return`
350
+ \u{1F916} Google Password Manager
351
+
352
+ Your passkey automatically syncs across:
353
+ - Android phones/tablets
354
+ - Chrome browser (all platforms)
355
+ - ChromeOS devices
356
+
357
+ Requirements:
358
+ \u2713 Signed in to Google Account
359
+ \u2713 Sync enabled in Chrome
360
+ \u2713 Screen lock configured
361
+
362
+ How it works:
363
+ 1. Create passkey on this device
364
+ 2. Google encrypts and syncs it
365
+ 3. Available on all signed-in devices
366
+ 4. End-to-end encrypted
367
+
368
+ Security:
369
+ - Google cannot access your passkeys
370
+ - Synced with end-to-end encryption
371
+ - Requires screen unlock to use
372
+ `;case"microsoft":return`
373
+ \u{1FA9F} Windows Hello
374
+
375
+ Your passkey is tied to this Windows device.
376
+
377
+ Note: Limited cross-device sync
378
+ - Passkeys stored in Windows Credential Manager
379
+ - Tied to this PC's TPM chip
380
+ - Does NOT sync to other devices automatically
381
+
382
+ For cross-device access:
383
+ - Use encrypted backup instead
384
+ - Or set up social recovery
385
+
386
+ Security:
387
+ - Hardware-protected (TPM)
388
+ - Requires Windows Hello (PIN/biometric)
389
+ - Very secure but not portable
390
+ `;case"none":return`
391
+ \u26A0\uFE0F Platform Sync Not Available
392
+
393
+ Your passkey will be stored on this device only.
394
+
395
+ Recommendation:
396
+ \u2713 Create encrypted backup (password-protected)
397
+ \u2713 Set up social recovery
398
+ \u2713 Save your 12-word recovery phrase
399
+
400
+ This ensures you can recover if:
401
+ - Device is lost/stolen
402
+ - Device is damaged
403
+ - You switch devices
404
+ `}}getSyncInstructions(e){switch(e){case"apple":return["Open Settings on your iPhone/iPad/Mac","Tap your name at the top",'Tap "iCloud"','Enable "Keychain"','Enable "iCloud Backup" (recommended)'];case"google":return["Open Chrome Settings",'Click "You and Google"','Enable "Sync"','Ensure "Passwords" is checked',"Sign in on other devices with same Google account"];case"microsoft":return["Windows Hello sync is limited","Consider using:","- Encrypted ZIP backup","- Social recovery","- Cloud backup (password-protected)"];case"none":return["Platform sync not available","Use alternative backup methods:","- Create encrypted ZIP backup","- Set up social recovery","- Save recovery phrase securely"]}}}});var at=u(()=>{"use strict"});var We={};A(We,{DeviceManager:()=>Y,PlatformDetector:()=>H,VaultSync:()=>$});var ue=u(()=>{"use strict";st();ot();it();at()});var _,ct=u(()=>{"use strict";_=class{getScenarios(){return[{type:"lost-device",description:"Your phone fell in the ocean"},{type:"lost-phrase",description:"Your paper backup burned in a fire"},{type:"lost-both",description:"Phone stolen AND forgot recovery phrase"},{type:"switch-platform",description:"Switching from iPhone to Android"}]}async simulateScenario(e,t){let r=[];switch(e.type){case"lost-device":t.passkeySync.enabled&&t.passkeySync.deviceCount>1&&r.push({method:`Passkey Sync (${t.passkeySync.platform})`,success:!0,time:"5 minutes",requirements:["Sign in to cloud account on new device","Authenticate with biometric/PIN"]}),t.recoveryPhrase.encryptedBackups.length>0&&t.recoveryPhrase.encryptedBackups.forEach(i=>{r.push({method:`Encrypted ${i.method.toUpperCase()} Backup`,success:!0,time:"2 minutes",requirements:["Backup file/QR code","Backup password"]})}),t.socialRecovery?.enabled&&r.push({method:"Social Recovery",success:!0,time:"24 hours",requirements:[`Contact ${t.socialRecovery.threshold} guardians`,"Collect shares from guardians","Verify identity with each guardian"]});break;case"lost-phrase":t.passkeySync.enabled&&r.push({method:"Passkey (current device)",success:!0,time:"Instant",requirements:["Access to current device","Biometric/PIN authentication"]}),t.passkeySync.deviceCount>1&&r.push({method:"Passkey Sync",success:!0,time:"5 minutes",requirements:["Any synced device","Biometric authentication"]}),t.recoveryPhrase.encryptedBackups.length>0&&r.push({method:"Encrypted Backup",success:!0,time:"2 minutes",requirements:["Backup file","Password"]}),t.socialRecovery?.enabled&&r.push({method:"Social Recovery",success:!0,time:"24 hours",requirements:[`${t.socialRecovery.threshold} guardian shares`]});break;case"lost-both":t.passkeySync.deviceCount>1&&r.push({method:"Passkey Sync",success:!0,time:"5 minutes",requirements:["Cloud account access","New device","Biometric setup"]}),t.socialRecovery?.enabled&&r.push({method:"Social Recovery",success:!0,time:"24 hours",requirements:[`${t.socialRecovery.threshold} guardian shares`,"Identity verification with guardians"]});break;case"switch-platform":t.recoveryPhrase.encryptedBackups.length>0&&r.push({method:"Encrypted Backup",success:!0,time:"2 minutes",requirements:["Backup file","Password"]}),t.socialRecovery?.enabled&&r.push({method:"Social Recovery",success:!0,time:"24 hours",requirements:[`${t.socialRecovery.threshold} guardian shares`]});break}let n=r.length>0,o=this.getEducationalNote(e,r,t);return{scenario:e,success:n,availableMethods:r,timeEstimate:n?this.estimateFastestRecovery(r):"Cannot recover",educationalNote:o}}estimateFastestRecovery(e){let t=e.map(r=>r.time.toLowerCase());if(t.some(r=>r.includes("instant")))return"Instant";if(t.some(r=>r.includes("minute"))){let r=t.filter(n=>n.includes("minute")).map(n=>parseInt(n));return`${Math.min(...r)} minutes`}return t.some(r=>r.includes("hour"))?"24 hours":"Unknown"}getEducationalNote(e,t,r){if(t.length===0)return`
405
+ \u274C WALLET CANNOT BE RECOVERED
406
+
407
+ Scenario: ${e.description}
408
+
409
+ Unfortunately, you have no recovery options for this scenario.
410
+
411
+ This is why backup is critical!
412
+
413
+ IMMEDIATE ACTION REQUIRED:
414
+ -------------------------
415
+
416
+ To prevent permanent loss, set up AT LEAST TWO of these:
417
+
418
+ 1. Encrypted Backup
419
+ - Takes 2 minutes
420
+ - Works anywhere
421
+ - Requires password
422
+ [Create backup now]
423
+
424
+ 2. Social Recovery
425
+ - Takes 30 minutes setup
426
+ - Most secure
427
+ - Requires 3-5 trusted friends
428
+ [Set up guardians]
429
+
430
+ 3. Verify Passkey Sync
431
+ - Check if enabled
432
+ - Test on another device
433
+ - Platform-specific
434
+ [Check sync status]
435
+
436
+ ---
437
+
438
+ Don't wait until it's too late!
439
+ `;let n=`\u2705 YOU CAN RECOVER!
440
+
441
+ Scenario: ${e.description}
442
+
443
+ `;return n+=`Available recovery methods (${t.length}):
444
+
445
+ `,t.forEach((o,i)=>{n+=`${i+1}. ${o.method}
446
+ `,n+=` \u23F1 Time: ~${o.time}
447
+ `,n+=` Requirements:
448
+ `,o.requirements.forEach(c=>{n+=` - ${c}
449
+ `}),n+=`
450
+ `}),n+=`
451
+ RECOMMENDATIONS:
452
+ `,t.length===1?(n+=`\u26A0\uFE0F You only have ONE recovery method.
453
+ `,n+=` Add more backups for better security:
454
+ `,r.recoveryPhrase.encryptedBackups.length||(n+=` - Create encrypted backup
455
+ `),r.socialRecovery?.enabled||(n+=` - Set up social recovery
456
+ `)):t.length===2?(n+=`\u{1F7E1} Good! You have ${t.length} recovery methods.
457
+ `,r.socialRecovery?.enabled||(n+=` Consider adding social recovery for maximum security.
458
+ `)):(n+=`\u{1F7E2} Excellent! You have ${t.length} recovery methods.
459
+ `,n+=` Your wallet is well-protected!
460
+ `),n}async runInteractiveTest(e){let t=this.getScenarios(),r=[];for(let c of t){let a=await this.simulateScenario(c,e);r.push(a)}let n=r.filter(c=>c.success).length,o=n/t.length*100,i="";return o===100?i=`\u{1F3C6} PERFECT SCORE!
461
+
462
+ You can recover in ALL scenarios.
463
+ Your wallet is extremely well-protected!`:o>=75?i=`\u{1F7E2} GREAT JOB!
464
+
465
+ You can recover in ${n}/${t.length} scenarios.
466
+ Consider adding more backup methods for complete coverage.`:o>=50?i=`\u{1F7E1} GOOD START!
467
+
468
+ You can recover in ${n}/${t.length} scenarios.
469
+ Add more backup methods to improve security.`:i=`\u26A0\uFE0F AT RISK!
470
+
471
+ You can only recover in ${n}/${t.length} scenarios.
472
+ Urgently add backup methods to protect your wallet!`,{scenarios:r,overallScore:o,feedback:i}}}});function dt(s){return pe[s]||null}function lt(){return Object.keys(pe)}function ut(s){let e=s.toLowerCase();return Object.values(pe).filter(t=>t.title.toLowerCase().includes(e)||t.content.toLowerCase().includes(e))}var pe,pt=u(()=>{"use strict";pe={whatIsPasskey:{title:"What is a Passkey?",content:`
473
+ Think of a passkey like your house smart lock:
474
+
475
+ \u{1F511} Traditional Key (Password):
476
+ - Can be stolen
477
+ - Can be forgotten
478
+ - Same key opens many doors
479
+ - Written on paper or in memory
480
+
481
+ \u{1F3E0} Smart Lock (Passkey):
482
+ - Biometric only (your face/finger)
483
+ - Can't be stolen or forgotten
484
+ - Unique per device
485
+ - Auto-syncs to your other devices
486
+
487
+ HOW IT WORKS:
488
+ 1. You create a passkey with your fingerprint
489
+ 2. Your device stores it securely (hardware-protected)
490
+ 3. It syncs to your other devices automatically
491
+ 4. Only YOU can use it (with your biometric)
492
+
493
+ SECURITY:
494
+ - The private key never leaves your device
495
+ - Cannot be phished (checks website domain)
496
+ - Cannot be guessed or brute-forced
497
+ - Requires physical access to your device + your biometric
498
+ `,interactive:"Try authenticating now \u2192"},whatIsRecoveryPhrase:{title:"What is a Recovery Phrase?",content:`
499
+ Your 12 words are like a master key to your cryptocurrency wallet.
500
+
501
+ \u{1F331} 12 Words = Your Entire Wallet:
502
+ - Generate unlimited accounts
503
+ - Sign transactions
504
+ - Prove ownership of funds
505
+ - Recover on ANY wallet app
506
+
507
+ HOW IT WORKS:
508
+ 1. 12 random words from dictionary
509
+ 2. Creates a "seed" using math
510
+ 3. Seed generates your private keys
511
+ 4. Private keys control your crypto
512
+
513
+ WHY 12 WORDS?
514
+ - 2^128 possible combinations
515
+ - Would take billions of years to guess
516
+ - Easy for humans to write down
517
+ - Works in any BIP39 wallet
518
+
519
+ CRITICAL SECURITY:
520
+ \u26A0\uFE0F Anyone with these words = OWNS your wallet
521
+ \u26A0\uFE0F Cannot be changed or reset
522
+ \u26A0\uFE0F Lost words = Lost wallet forever
523
+ \u26A0\uFE0F Store safely offline
524
+
525
+ BEST PRACTICES:
526
+ \u2713 Write on paper (offline)
527
+ \u2713 Store in safe/vault
528
+ \u2713 Never type in computer (except during setup)
529
+ \u2713 Never share with anyone
530
+ \u2713 Make encrypted backup (password-protected)
531
+ \u2713 Consider social recovery (split among friends)
532
+ `},howDoesSyncWork:{title:"How Does Passkey Sync Work?",content:`
533
+ Your passkey and wallet use TWO layers of protection:
534
+
535
+ LAYER 1: PASSKEY (Auto-Syncs)
536
+ ------------------------------
537
+ Your passkey credential syncs via:
538
+ - iCloud Keychain (Apple)
539
+ - Google Password Manager (Android)
540
+ - Microsoft Account (Windows)
541
+
542
+ What syncs:
543
+ \u2713 Passkey credential
544
+ \u2713 Public key
545
+ \u2713 User info
546
+
547
+ What DOESN'T sync:
548
+ \u2717 Your 12-word mnemonic
549
+ \u2717 Private keys
550
+ \u2717 Wallet contents
551
+
552
+ LAYER 2: ENCRYPTED WALLET (Local)
553
+ ----------------------------------
554
+ Your wallet is encrypted and stored in:
555
+ - Browser's IndexedDB
556
+ - Encrypted with passkey-derived key
557
+ - Can ONLY be decrypted with passkey
558
+
559
+ THE SYNC FLOW:
560
+
561
+ Device 1 (iPhone) \u2192 iCloud \u2192 Device 2 (Mac)
562
+ Passkey created Syncs Passkey available
563
+ Wallet encrypted ---- Decrypt with passkey
564
+
565
+
566
+ WHAT THIS MEANS:
567
+ - Passkey syncs = Convenient
568
+ - Wallet encrypted = Secure
569
+ - Need both = Protected
570
+ - Works across devices = Seamless
571
+
572
+ SECURITY:
573
+ - Even if iCloud is hacked, wallet stays encrypted
574
+ - Even if wallet is copied, can't decrypt without passkey
575
+ - Both layers needed to access funds
576
+ `,visual:`
577
+ \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
578
+ \u2502 DEVICE 1 DEVICE 2 \u2502
579
+ \u2502 \u2502
580
+ \u2502 \u{1F511} Passkey \u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250C\u2500\u2500\u2500\u2500\u2500 \u{1F511} Passkey \u2502
581
+ \u2502 \u2502 \u2502 \u2502
582
+ \u2502 \u{1F512} Wallet \u25C4\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u25BA \u{1F512} Wallet \u2502
583
+ \u2502 (encrypted) \u2502 \u2502 (encrypted) \u2502
584
+ \u2502 \u2502 \u2502 \u2502
585
+ \u2502 \u2601\uFE0F iCloud \u2601\uFE0F \u2502
586
+ \u2502 (passkey syncs) \u2502
587
+ \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
588
+ `},whyBackupMatters:{title:"Why Backup Matters",content:`
589
+ Cryptocurrency is PERMANENT. There's no "reset password" button.
590
+
591
+ REAL SCENARIOS:
592
+ ---------------
593
+
594
+ \u{1F4B8} James lost $7.4M in Bitcoin
595
+ - Hard drive thrown away
596
+ - No backup
597
+ - Bitcoin lost forever
598
+
599
+ \u{1F4B8} Stefan lost $200K in Ethereum
600
+ - Forgot password
601
+ - No recovery phrase saved
602
+ - Wallet locked permanently
603
+
604
+ \u{1F4B8} Maria recovered $500K
605
+ - Phone stolen
606
+ - Had encrypted backup
607
+ - Recovered in 5 minutes \u2713
608
+
609
+ THE MATH:
610
+ ---------
611
+
612
+ No backup:
613
+ - Lose device = Lose wallet (100% loss)
614
+ - Forget password = Lose wallet (100% loss)
615
+ - Device breaks = Lose wallet (100% loss)
616
+
617
+ With backup:
618
+ - Lose device = Recover from backup \u2713
619
+ - Forget password = Use recovery phrase \u2713
620
+ - Device breaks = Use social recovery \u2713
621
+
622
+ BACKUP LAYERS:
623
+ --------------
624
+
625
+ Layer 1: Passkey (convenience)
626
+ - Auto-syncs
627
+ - Easy to use
628
+ - Platform-dependent
629
+
630
+ Layer 2: Encrypted Backup (universal)
631
+ - Works anywhere
632
+ - Password-protected
633
+ - Portable
634
+
635
+ Layer 3: Social Recovery (ultimate)
636
+ - Friends help you
637
+ - No single point of failure
638
+ - Most secure
639
+
640
+ RECOMMENDATION:
641
+ Use ALL THREE for maximum security!
642
+ `},socialRecoveryExplained:{title:"Social Recovery Explained",content:`
643
+ Social recovery lets trusted friends/family help you recover your wallet.
644
+
645
+ HOW IT WORKS (3-of-5 Example):
646
+ ------------------------------
647
+
648
+ 1. Your 12-word phrase is split into 5 pieces
649
+ 2. Each piece goes to a trusted guardian
650
+ 3. Any 3 pieces can reconstruct your phrase
651
+ 4. Any 2 pieces reveal NOTHING
652
+
653
+ THE MATH (Shamir Secret Sharing):
654
+ ---------------------------------
655
+
656
+ Example: Secret = "apple"
657
+
658
+ Split into 5 shares:
659
+ - Share 1: "x7k9m"
660
+ - Share 2: "p2n4q"
661
+ - Share 3: "w8j3z"
662
+ - Share 4: "r5h1v"
663
+ - Share 5: "c6y2b"
664
+
665
+ Combine any 3 \u2192 Get "apple"
666
+ Have only 2 \u2192 Impossible to recover
667
+
668
+ CHOOSING GUARDIANS:
669
+ ------------------
670
+
671
+ \u2713 GOOD Guardians:
672
+ - Family members
673
+ - Close friends
674
+ - Geographically distributed
675
+ - Tech-savvy
676
+ - Long-term relationships
677
+
678
+ \u2717 BAD Guardians:
679
+ - Strangers
680
+ - Same location (house fire risk)
681
+ - Not reliable
682
+ - Can't handle QR codes
683
+
684
+ SECURITY:
685
+ ---------
686
+
687
+ Math proves:
688
+ - Need EXACTLY 3 shares
689
+ - 2 shares = 0% information
690
+ - 4 shares = 100% redundancy
691
+ - Guardians can't collude unless threshold met
692
+
693
+ RECOVERY FLOW:
694
+ -------------
695
+
696
+ 1. You lose wallet access
697
+ 2. Contact 3 guardians
698
+ 3. Each provides their share
699
+ 4. System combines shares
700
+ 5. Wallet reconstructed \u2713
701
+
702
+ Timeline: ~24 hours
703
+ (depends on guardian availability)
704
+ `},encryptedBackupSecurity:{title:"Encrypted Backup Security",content:`
705
+ Your encrypted backup uses military-grade encryption.
706
+
707
+ ENCRYPTION DETAILS:
708
+ ------------------
709
+
710
+ Password \u2192 PBKDF2 (310,000 iterations)
711
+ \u2193
712
+ 256-bit AES Key
713
+ \u2193
714
+ AES-256-GCM Encryption
715
+ \u2193
716
+ Encrypted Backup
717
+
718
+ WHAT THIS MEANS:
719
+ ---------------
720
+
721
+ 310,000 iterations:
722
+ - Makes brute-force extremely slow
723
+ - Would take centuries to crack
724
+ - OWASP 2025 standard
725
+
726
+ AES-256-GCM:
727
+ - Used by governments/military
728
+ - Authenticated encryption
729
+ - Cannot be tampered with
730
+
731
+ ATTACK SCENARIOS:
732
+ ----------------
733
+
734
+ Q: What if Google Drive is hacked?
735
+ A: Attacker gets encrypted file (useless without password)
736
+
737
+ Q: What if someone gets your password?
738
+ A: Need BOTH password AND backup file
739
+
740
+ Q: What if someone tries to brute-force?
741
+ A: 310,000 iterations makes this impractical
742
+
743
+ Q: Can the backup be tampered with?
744
+ A: No - GCM mode detects any changes
745
+
746
+ PASSWORD STRENGTH:
747
+ -----------------
748
+
749
+ WEAK (\u274C Rejected):
750
+ - "password123"
751
+ - "qwerty"
752
+ - "12345678"
753
+
754
+ GOOD (\u2713 Accepted):
755
+ - "MyDog-Loves-Pizza-2025!"
756
+ - "Tr0ub4dor&3-Extra-Secure"
757
+ - "correct-horse-battery-staple-99"
758
+
759
+ STRONG (\u2713\u2713 Recommended):
760
+ - 16+ characters
761
+ - Mix of upper/lower/numbers/symbols
762
+ - Not in dictionary
763
+ - Unique to this backup
764
+
765
+ STORAGE OPTIONS:
766
+ ---------------
767
+
768
+ \u2713 Safe to store encrypted backup in:
769
+ - Google Drive
770
+ - Dropbox
771
+ - iCloud
772
+ - USB drive
773
+ - Email to yourself
774
+
775
+ The encryption makes it safe even in cloud!
776
+ `}}});var he={};A(he,{RecoverySimulator:()=>_,educationalModules:()=>pe,getAllTopics:()=>lt,getExplainer:()=>dt,searchExplainers:()=>ut});var V=u(()=>{"use strict";ct();pt()});w();import{startRegistration as bt}from"@simplewebauthn/browser";function Le(s){return/^0x[a-fA-F0-9]{40}$/.test(s)}function qe(s){return s.length>=3&&s.length<=50}function Ge(s){let e=s.trim().split(/\s+/);return e.length===12||e.length===24}function fe(s){if(!Le(s))throw new Error("Invalid Ethereum address format")}function we(s){if(!qe(s))throw new Error("Username must be between 3 and 50 characters")}function wt(s){if(!Ge(s))throw new Error("Invalid mnemonic: must be 12 or 24 words")}function vt(s){if(s.length<12)return!1;let e=/[A-Z]/.test(s),t=/[a-z]/.test(s),r=/[0-9]/.test(s),n=/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(s);return!(!e||!t||!r||!n||["password","12345678","qwerty","abc123","password123","admin","letmein"].some(i=>s.toLowerCase().includes(i)))}C();function St(){let s=new Uint8Array(32);return crypto.getRandomValues(s),btoa(String.fromCharCode(...s)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}async function $e(s){try{let{username:e,ethereumAddress:t}=s;we(e),fe(t);let r=new P;if(r.userExists(e))throw new Error("Username already registered");let o={challenge:St(),rp:{name:"w3pk",id:window.location.hostname},user:{id:e,name:e,displayName:e},pubKeyCredParams:[{type:"public-key",alg:-7},{type:"public-key",alg:-257}],authenticatorSelection:{authenticatorAttachment:"platform",userVerification:"required",residentKey:"required",requireResidentKey:!0},timeout:6e4,attestation:"none"},i=await bt({optionsJSON:o}),c=i.response.publicKey;if(!c)throw new Error("Public key not returned from authenticator");r.saveCredential({id:i.id,publicKey:c,username:e,ethereumAddress:t,createdAt:Date.now(),lastUsed:Date.now()}),console.log("[register] Credential response:",i.response);let a=i.response.attestationObject;if(console.log("[register] Attestation object:",a),!a)throw new Error("Attestation object not returned from authenticator");let l=kt(a);return console.log("[register] Attestation buffer length:",l.byteLength),{signature:l}}catch(e){throw new K(e instanceof Error?e.message:"Registration failed",e)}}function kt(s){let e=s.replace(/-/g,"+").replace(/_/g,"/"),t=atob(e),r=new Uint8Array(t.length);for(let n=0;n<t.length;n++)r[n]=t.charCodeAt(n);return r.buffer}w();C();import{startAuthentication as At}from"@simplewebauthn/browser";function Et(){let s=new Uint8Array(32);return crypto.getRandomValues(s),btoa(String.fromCharCode(...s)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}async function Z(){try{let s=new P,t={challenge:Et(),rpId:window.location.hostname,userVerification:"required",timeout:6e4},r=await At({optionsJSON:t}),n=s.getCredentialById(r.id);if(!n)throw new Error("Credential not found");if(!await Pt(r,n))throw new Error("Signature verification failed");s.updateLastUsed(n.id);let i=z(r.response.signature);return{verified:!0,user:{username:n.username,ethereumAddress:n.ethereumAddress,credentialId:n.id},signature:i}}catch(s){throw new E(s instanceof Error?s.message:"Authentication failed",s)}}async function Pt(s,e){try{let t=z(e.publicKey),r=await crypto.subtle.importKey("spki",t,{name:"ECDSA",namedCurve:"P-256"},!1,["verify"]),n=z(s.response.authenticatorData),o=s.response.clientDataJSON,i;o.startsWith("eyJ")?i=atob(o.replace(/-/g,"+").replace(/_/g,"/")):i=o;let c=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(i)),a=new Uint8Array(n.byteLength+c.byteLength);a.set(new Uint8Array(n),0),a.set(new Uint8Array(c),n.byteLength);let l=z(s.response.signature),p=Ct(new Uint8Array(l));return await crypto.subtle.verify({name:"ECDSA",hash:"SHA-256"},r,p,a)}catch(t){return console.error("Signature verification error:",t),!1}}function z(s){let e=s.replace(/-/g,"+").replace(/_/g,"/"),t=atob(e),r=new Uint8Array(t.length);for(let n=0;n<t.length;n++)r[n]=t.charCodeAt(n);return r.buffer}function Ct(s){let e=2;e++;let t=s[e++];t>32&&(e++,t--);let r=s.slice(e,e+t);e+=t,e++;let n=s[e++];n>32&&(e++,n--);let o=s.slice(e,e+n),i=new Uint8Array(64);return i.set(r,32-r.length),i.set(o,64-o.length),i.buffer}w();var Rt="Web3PasskeyWallet",xt=1,v="wallets",X=class{constructor(){this.db=null}async init(){return new Promise((e,t)=>{let r=indexedDB.open(Rt,xt);r.onerror=()=>t(new h("Failed to open database",r.error)),r.onsuccess=()=>{this.db=r.result,e()},r.onupgradeneeded=()=>{let n=r.result;n.objectStoreNames.contains(v)||n.createObjectStore(v,{keyPath:"ethereumAddress"})}})}async store(e){return this.db||await this.init(),new Promise((t,r)=>{let i=this.db.transaction([v],"readwrite").objectStore(v).put(e);i.onerror=()=>r(new h("Failed to store wallet data",i.error)),i.onsuccess=()=>t()})}async retrieve(e){return this.db||await this.init(),new Promise((t,r)=>{let i=this.db.transaction([v],"readonly").objectStore(v).get(e);i.onerror=()=>r(new h("Failed to retrieve wallet data",i.error)),i.onsuccess=()=>t(i.result||null)})}async delete(e){return this.db||await this.init(),new Promise((t,r)=>{let i=this.db.transaction([v],"readwrite").objectStore(v).delete(e);i.onerror=()=>r(new h("Failed to delete wallet data",i.error)),i.onsuccess=()=>t()})}async clear(){return this.db||await this.init(),new Promise((e,t)=>{let o=this.db.transaction([v],"readwrite").objectStore(v).clear();o.onerror=()=>t(new h("Failed to clear wallet data",o.error)),o.onsuccess=()=>e()})}};w();import{ethers as ee}from"ethers";function ve(){try{let s=ee.Wallet.createRandom().mnemonic;if(!s)throw new Error("Failed to generate mnemonic");let e=s.phrase;return{address:ee.HDNodeWallet.fromPhrase(e,void 0,"m/44'/60'/0'/0/0").address,mnemonic:e}}catch(s){throw new d("Wallet generation failed",s)}}function It(s){try{if(!s||s.trim().split(/\s+/).length<12)throw new Error("Invalid mnemonic: must be at least 12 words");return ee.HDNodeWallet.fromPhrase(s.trim(),void 0,"m/44'/60'/0'/0/0")}catch(e){throw new d(`Wallet creation failed: ${e instanceof Error?e.message:"Invalid mnemonic"}`,e)}}function be(s,e=0){try{if(!s||s.trim().split(/\s+/).length<12)throw new Error("Invalid mnemonic: must be at least 12 words");if(e<0||!Number.isInteger(e))throw new Error("Index must be a non-negative integer");let t=`m/44'/60'/0'/0/${e}`,r=ee.HDNodeWallet.fromPhrase(s.trim(),void 0,t);return{address:r.address,privateKey:r.privateKey}}catch(t){throw new d(`HD wallet derivation failed: ${t instanceof Error?t.message:"Unknown error"}`,t)}}ne();w();w();import{ethers as m}from"ethers";function U(s){try{let e=m.HDNodeWallet.fromPhrase(s,void 0,"m/44'/60'/1'/0/0"),t=m.HDNodeWallet.fromPhrase(s,void 0,"m/44'/60'/1'/0/1"),r=t.signingKey.compressedPublicKey,n=e.signingKey.compressedPublicKey,o=r+n.slice(2),i=Mt(e.signingKey.publicKey,t.signingKey.publicKey);return{stealthMetaAddress:o,spendingPubKey:r,viewingPubKey:n,viewingKey:e.privateKey,spendingKey:t.privateKey,metaAddress:i}}catch(e){throw new y("Failed to derive stealth keys",e)}}function ke(s){try{let e="0x"+s.slice(2,68),t="0x"+s.slice(68),r=m.Wallet.createRandom(),n=r.signingKey.compressedPublicKey,o=Ee(r.privateKey,t),i=m.keccak256(o),c="0x"+i.slice(2,4),a=Ve(e,_e(i));return{stealthAddress:Qe(a),ephemeralPubKey:n,viewTag:c,ephemeralPubkey:n}}catch(e){throw new y("Failed to generate stealth address",e)}}function se(s,e,t,r,n){try{let o=Ee(s,t),i=m.keccak256(o);if(n&&("0x"+i.slice(2,4)).toLowerCase()!==n.toLowerCase())return{isForUser:!1};let c=Ve(e,_e(i)),a=Qe(c);return a.toLowerCase()!==r.toLowerCase()?{isForUser:!1}:{isForUser:!0,stealthAddress:a}}catch{return{isForUser:!1}}}function Ae(s,e,t){try{let r=Ee(s,t),n=m.keccak256(r);return Ft(e,n)}catch(r){throw new y("Failed to compute stealth private key",r)}}function Ee(s,e){try{return new m.Wallet(s).signingKey.computeSharedSecret(e)}catch(t){throw new y("Failed to compute shared secret",t)}}function _e(s){try{return new m.Wallet(s).signingKey.compressedPublicKey}catch(e){throw new y("Failed to multiply generator by scalar",e)}}function Ve(s,e){try{let t=m.SigningKey.computePublicKey(s,!1),r=m.SigningKey.computePublicKey(e,!1),n=BigInt("0x"+t.slice(4,68)),o=BigInt("0x"+t.slice(68)),i=BigInt("0x"+r.slice(4,68)),c=BigInt("0x"+r.slice(68)),a=BigInt("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F");if(n===i&&o===c){let yt=3n*n*n%a,mt=2n*o%a,me=yt*He(mt,a)%a,Ne=(me*me-2n*n)%a,gt=(me*(n-Ne)-o)%a;return Ye((Ne+a)%a,(gt+a)%a)}let l=((c-o)%a+a)%a,p=((i-n)%a+a)%a,M=l*He(p,a)%a,ye=(M*M-n-i)%a,ht=(M*(n-ye)-o)%a;return Ye((ye+a)%a,(ht+a)%a)}catch(t){throw new y("Failed to add public keys",t)}}function Ft(s,e){try{let t=BigInt("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"),r=BigInt(s),n=BigInt(e);return"0x"+((r+n)%t).toString(16).padStart(64,"0")}catch(t){throw new y("Failed to add private keys",t)}}function Ye(s,e){return"0x"+(e%2n===0n?"02":"03")+s.toString(16).padStart(64,"0")}function He(s,e){s=(s%e+e)%e;let[t,r]=[s,e],[n,o]=[1n,0n];for(;r!==0n;){let i=t/r;[t,r]=[r,t-i*r],[n,o]=[o,n-i*o]}return(n%e+e)%e}function Qe(s){try{let e=m.SigningKey.computePublicKey(s,!1),t=m.keccak256("0x"+e.slice(4));return m.getAddress("0x"+t.slice(-40))}catch(e){throw new y("Failed to derive address from public key",e)}}function Mt(s,e){let t=m.solidityPackedKeccak256(["bytes","bytes"],[s,e]);return m.getAddress("0x"+t.slice(26))}function Kt(s,e,t,r){let n=new m.Wallet(e);return se(s,n.signingKey.compressedPublicKey,t,r).isForUser}var W=class{constructor(e,t){this.getMnemonic=t}async generateStealthAddress(e){try{let t=await this.getMnemonic(e?.requireAuth),r=U(t),n=ke(r.stealthMetaAddress);return{stealthAddress:n.stealthAddress,ephemeralPublicKey:n.ephemeralPubKey,viewTag:n.viewTag}}catch(t){throw new g("Failed to generate stealth address","STEALTH_GENERATION_ERROR",t)}}async parseAnnouncement(e,t){try{let r=await this.getMnemonic(t?.requireAuth),n=U(r),o=se(n.viewingKey,n.spendingPubKey,e.ephemeralPublicKey,e.stealthAddress,e.viewTag);if(!o.isForUser)return{isForUser:!1};let i=Ae(n.viewingKey,n.spendingKey,e.ephemeralPublicKey);return{isForUser:!0,stealthAddress:o.stealthAddress,stealthPrivateKey:i}}catch(r){throw new g("Failed to parse announcement","ANNOUNCEMENT_PARSE_ERROR",r)}}async scanAnnouncements(e,t){let r=[];for(let n of e){let o=await this.parseAnnouncement(n,t);o.isForUser&&r.push(o)}return r}async getKeys(e){try{let t=await this.getMnemonic(e?.requireAuth);return U(t)}catch(t){throw new g("Failed to get stealth keys","STEALTH_KEYS_ERROR",t)}}async getStealthMetaAddress(e){try{return(await this.getKeys(e)).stealthMetaAddress}catch(t){throw new g("Failed to get stealth meta-address","STEALTH_META_ADDRESS_ERROR",t)}}get isAvailable(){return!0}};var oe=class{constructor(e=1){this.session=null;this.sessionDuration=e*60*60*1e3}startSession(e,t){let r=Date.now()+this.sessionDuration;this.session={mnemonic:e,expiresAt:r,credentialId:t}}getMnemonic(){return this.session?Date.now()>this.session.expiresAt?(this.clearSession(),null):this.session.mnemonic:null}getCredentialId(){return this.session?Date.now()>this.session.expiresAt?(this.clearSession(),null):this.session.credentialId:null}isActive(){return this.getMnemonic()!==null}getRemainingTime(){return this.session?Date.now()>this.session.expiresAt?(this.clearSession(),0):Math.floor((this.session.expiresAt-Date.now())/1e3):0}extendSession(){if(!this.session)throw new Error("No active session to extend");if(Date.now()>this.session.expiresAt)throw this.clearSession(),new Error("Session expired, cannot extend");this.session.expiresAt=Date.now()+this.sessionDuration}clearSession(){this.session&&(this.session.mnemonic="0".repeat(this.session.mnemonic.length)),this.session=null}setSessionDuration(e){this.sessionDuration=e*60*60*1e3}};var Pe={debug:!1,sessionDuration:1,onError:s=>{Pe.debug&&console.error("[w3pk]",s)}};w();var je="https://chainid.network/chains.json";var ie=null,Ot=[/\$\{[\w_]+\}/i,/\{[\w_]+\}/i,/<[\w_]+>/i,/YOUR[-_]?API[-_]?KEY/i,/INSERT[-_]?API[-_]?KEY/i,/API[-_]?KEY[-_]?HERE/i];function Ut(s){return Ot.some(e=>e.test(s))}async function Wt(s=je){let e=await fetch(s);if(!e.ok)throw new Error(`Failed to fetch chains data: ${e.status} ${e.statusText}`);return await e.json()}async function Nt(s){let e=s?.chainsJsonUrl??je,t=s?.cacheDuration??36e5,r=Date.now();if(ie&&r-ie.timestamp<t)return ie.data;let n=await Wt(e);return ie={data:n,timestamp:r},n}async function Je(s,e){let r=(await Nt(e)).find(n=>n.chainId===s);return r?r.rpc.filter(n=>!Ut(n)&&!n.startsWith("wss://")&&!n.startsWith("ws://")):[]}var Lt=new Set([1,10,8453,42161,57073,100,42220,137,42,15,40,41,44,46,47,50,51,56,61,71,82,83,95,97,112,123,130,146,151,153,171,180,183,185,195,215,228,247,248,252,261,267,291,293,311,332,336,395,401,416,466,480,488,510,545,634,647,648,747,831,919,938,945,957,964,970,980,995,997,1001,1003,1024,1030,1114,1125,1135,1149,1188,1284,1285,1287,1300,1301,1315,1337,1338,1339,1424,1514,1687,1727,1729,1740,1750,1829,1868,1946,1961,1962,1969,1989,1995,2017,2020,2031,2043,2109,2241,2340,2345,2440,2522,2559,2649,3068,3109,3338,3502,3799,3888,3889,4e3,4048,4078,4162,4201,4202,4460,4488,4661,4689,4690,4888,5e3,5003,5124,5234,5330,5424,5522,6283,6342,6398,6678,6806,6934,6942,6969,7117,7171,7200,7208,7368,7518,7668,7672,7744,7771,7869,7897,8008,8118,8217,8408,8700,8726,8727,8844,8880,8881,8882,8889,9372,9496,9700,9745,9746,9899,9990,9996,10011,10085,10143,10200,11221,11501,11504,11891,13370,14853,16602,16661,17e3,18880,18881,19991,21e3,21816,21912,25327,32323,33401,34443,41923,42170,43111,44787,47805,48898,48900,49049,49088,5e4,50312,53302,53456,53457,55244,56288,59141,60808,60850,62320,62850,64002,71402,72080,73114,73115,75338,78281,80002,80008,80069,80094,80451,80931,84532,88899,91342,92278,94524,96970,97476,97477,98985,100021,100501,101010,102030,102031,102032,112358,120893,121212,121213,121214,121215,129399,161803,175188,192940,193939,198989,212013,222222,240241,325e3,355110,355113,421614,555777,560048,656476,713715,743111,747474,763373,763375,777777,806582,808813,810180,839999,888991,2019775,2222222,4278608,5734951,6666689,6985385,7080969,7777777,9999999,11142220,11155111,11155420,11155931,16969696,19850818,20180427,20250825,28122024,34949059,37084624,52164803,61022448,79479957,96969696,420420421,420420422,888888888,974399131,999999999,1020352220,1273227453,1313161560,1350216234,1380996178,1417429182,1444673419,1482601649,1564830818,2046399126,11297108099,11297108109,88153591557,123420000220,123420001114]);async function qt(s,e=1e4){try{let t=new AbortController,r=setTimeout(()=>t.abort(),e),n=await fetch(s,{method:"POST",headers:{"Content-Type":"application/json"},signal:t.signal,body:JSON.stringify({jsonrpc:"2.0",method:"eth_estimateGas",params:[{from:"0xdeadbeef00000000000000000000000000000000",to:"0xdeadbeef00000000000000000000000000000000",data:"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",value:"0x0"},"latest",{"0xdeadbeef00000000000000000000000000000000":{code:"0xef01000000000000000000000000000000000000000001"}}],id:1})});if(clearTimeout(r),!n.ok)return!1;let o=await n.json();if(o.error){let i=(o.error.message||"").toLowerCase();return!["unsupported","not supported","unknown","invalid","unrecognized","does not support","not implemented"].some(a=>i.includes(a))}return o.result!==void 0}catch{return!1}}async function ze(s,e,t){if(Lt.has(s))return!0;let r=t?.maxEndpoints||3,n=t?.timeout||1e4;try{let o=await e(s);if(o.length===0)return!1;let i=o.slice(0,r);for(let c of i)if(await qt(c,n))return!0;return!1}catch{return!1}}var Q=class{constructor(e={}){this.currentUser=null;this.currentWallet=null;this.config={...Pe,...e},this.walletStorage=new X,this.sessionManager=new oe(e.sessionDuration||1),e.stealthAddresses!==void 0&&(this.stealth=new W(e.stealthAddresses,t=>this.getMnemonicFromSession(t)))}async loadZKModule(){if(this.zkModule)return this.zkModule;try{let e=new Function("path","return import(path)"),{ZKProofModule:t}=await e("w3pk/zk"),r=this.config.zkProofs||{};return this.zkModule=new t(r),this.zkModule}catch{throw new Error("ZK module not available. Install dependencies: npm install snarkjs circomlibjs")}}async getMnemonicFromSession(e=!1){if(!e){let l=this.sessionManager.getMnemonic();if(l)return l}if(!this.currentUser)throw new d("Must be authenticated. Call login() first.");let t=await this.walletStorage.retrieve(this.currentUser.ethereumAddress);if(!t)throw new d("No wallet found. Generate a wallet first.");if(!(await Z()).user)throw new d("Authentication failed");let i=new(await Promise.resolve().then(()=>(C(),O))).CredentialStorage().getCredentialById(t.credentialId)?.publicKey,c=await R(t.credentialId,i),a=await re(t.encryptedMnemonic,c);return this.sessionManager.startSession(a,t.credentialId),a}async register(e){try{this.currentWallet?.address||await this.generateWallet();let t=this.currentWallet.address,r=this.currentWallet.mnemonic;await $e({username:e.username,ethereumAddress:t}),this.currentUser={id:t,username:e.username,displayName:e.username,ethereumAddress:t};let o=new(await Promise.resolve().then(()=>(C(),O))).CredentialStorage().getCredentialByAddress(t);if(!o)throw new d("Credential not found after registration");let i=o.id,c=o.publicKey,a=await R(i,c),l=await te(r,a);return await this.walletStorage.store({ethereumAddress:this.currentUser.ethereumAddress,encryptedMnemonic:l,credentialId:i,createdAt:Date.now()}),this.sessionManager.startSession(r,i),this.currentWallet={address:t,mnemonic:r},this.config.onAuthStateChanged?.(!0,this.currentUser),{address:t,username:e.username}}catch(t){throw this.config.onError?.(t),t}}async login(){try{let e=await Z();if(!e.verified||!e.user)throw new E("Login failed");this.currentUser={id:e.user.ethereumAddress,username:e.user.username,displayName:e.user.username,ethereumAddress:e.user.ethereumAddress};let t=await this.walletStorage.retrieve(this.currentUser.ethereumAddress);if(!t)throw new d("No wallet found for this user. You may need to register first.");let o=new(await Promise.resolve().then(()=>(C(),O))).CredentialStorage().getCredentialById(t.credentialId)?.publicKey,i=await R(t.credentialId,o),c=await re(t.encryptedMnemonic,i);return this.sessionManager.startSession(c,t.credentialId),this.config.onAuthStateChanged?.(!0,this.currentUser),this.currentUser}catch(e){throw this.config.onError?.(e),e}}async logout(){this.currentUser=null,this.currentWallet=null,this.sessionManager.clearSession(),this.config.onAuthStateChanged?.(!1,void 0)}get isAuthenticated(){return this.currentUser!==null}get user(){return this.currentUser}async generateWallet(){try{let e=ve();return this.currentWallet={address:e.address,mnemonic:e.mnemonic},{mnemonic:e.mnemonic}}catch(e){throw this.config.onError?.(e),new d("Failed to generate wallet",e)}}async deriveWallet(e,t){try{if(!this.currentUser)throw new d("Must be authenticated to derive wallet");let r=await this.getMnemonicFromSession(t?.requireAuth),n=be(r,e);return{address:n.address,privateKey:n.privateKey}}catch(r){throw this.config.onError?.(r),new d("Failed to derive wallet",r)}}async exportMnemonic(e){try{if(!this.currentUser)throw new d("Must be authenticated to export mnemonic");return await this.getMnemonicFromSession(e?.requireAuth)}catch(t){throw this.config.onError?.(t),new d("Failed to export mnemonic",t)}}async importMnemonic(e){try{if(!this.currentUser)throw new d("Must be authenticated to import mnemonic");if(!e||e.trim().split(/\s+/).length<12)throw new d("Invalid mnemonic: must be at least 12 words");let t=await Z();if(!t.user)throw new d("Authentication failed");let r=t.user.credentialId,i=new(await Promise.resolve().then(()=>(C(),O))).CredentialStorage().getCredentialById(r)?.publicKey,c=await R(r,i),a=await te(e.trim(),c);await this.walletStorage.store({ethereumAddress:this.currentUser.ethereumAddress,encryptedMnemonic:a,credentialId:r,createdAt:Date.now()}),this.currentWallet={address:this.currentUser.ethereumAddress,mnemonic:e.trim()},this.sessionManager.startSession(e.trim(),r)}catch(t){throw this.config.onError?.(t),new d("Failed to import mnemonic",t)}}async signMessage(e,t){try{if(!this.currentUser)throw new d("Must be authenticated to sign message");let r=await this.getMnemonicFromSession(t?.requireAuth),{Wallet:n}=await import("ethers");return await n.fromPhrase(r).signMessage(e)}catch(r){throw this.config.onError?.(r),new d("Failed to sign message",r)}}async getEndpoints(e){return Je(e)}async supportsEIP7702(e,t){return ze(e,this.getEndpoints.bind(this),t)}get zk(){return new Proxy({},{get:(e,t)=>async(...r)=>(await this.loadZKModule())[t](...r)})}async getBackupStatus(){if(!this.currentUser)throw new d("Must be authenticated to check backup status");let{BackupManager:e}=await Promise.resolve().then(()=>(I(),F));return new e().getBackupStatus(this.currentUser.ethereumAddress)}async createZipBackup(e,t){if(!this.currentUser)throw new d("Must be authenticated to create backup");let r=await this.getMnemonicFromSession(!0),{BackupManager:n}=await Promise.resolve().then(()=>(I(),F));return new n().createZipBackup(r,this.currentUser.ethereumAddress,{password:e,...t})}async createQRBackup(e,t){if(!this.currentUser)throw new d("Must be authenticated to create backup");let r=await this.getMnemonicFromSession(!0),{BackupManager:n}=await Promise.resolve().then(()=>(I(),F));return new n().createQRBackup(r,this.currentUser.ethereumAddress,{password:e,...t})}async setupSocialRecovery(e,t){if(!this.currentUser)throw new d("Must be authenticated to set up social recovery");let r=await this.getMnemonicFromSession(!0),{SocialRecoveryManager:n}=await Promise.resolve().then(()=>(G(),le));return new n().setupSocialRecovery(r,this.currentUser.ethereumAddress,e,t)}async generateGuardianInvite(e){let{SocialRecoveryManager:t}=await Promise.resolve().then(()=>(G(),le)),r=new t,n=r.getSocialRecoveryConfig();if(!n)throw new d("Social recovery not configured");let o=n.guardians.find(i=>i.id===e);if(!o)throw new d("Guardian not found");return r.generateGuardianInvite(o)}async recoverFromGuardians(e){let{SocialRecoveryManager:t}=await Promise.resolve().then(()=>(G(),le));return new t().recoverFromGuardians(e)}async restoreFromBackup(e,t){let{BackupManager:r}=await Promise.resolve().then(()=>(I(),F));return new r().restoreFromZipBackup(e,t)}async restoreFromQR(e,t){let{BackupManager:r}=await Promise.resolve().then(()=>(I(),F));return new r().restoreFromQR(e,t)}async getSyncStatus(){let{DeviceManager:e}=await Promise.resolve().then(()=>(ue(),We));return new e().getSyncStatus()}async detectSyncCapabilities(){let{PlatformDetector:e}=await Promise.resolve().then(()=>(ue(),We));return new e().detectSyncCapabilities()}async simulateRecoveryScenario(e){if(!this.currentUser)throw new d("Must be authenticated to run recovery simulation");let t=await this.getBackupStatus(),{RecoverySimulator:r}=await Promise.resolve().then(()=>(V(),he));return new r().simulateScenario(e,t)}async runRecoveryTest(){if(!this.currentUser)throw new d("Must be authenticated to run recovery test");let e=await this.getBackupStatus(),{RecoverySimulator:t}=await Promise.resolve().then(()=>(V(),he));return new t().runInteractiveTest(e)}async getEducation(e){let{getExplainer:t}=await Promise.resolve().then(()=>(V(),he)),r=t(e);if(!r)throw new d(`Unknown education topic: ${e}`);return r}hasActiveSession(){return this.sessionManager.isActive()}getSessionRemainingTime(){return this.sessionManager.getRemainingTime()}extendSession(){try{this.sessionManager.extendSession()}catch(e){throw new d("Cannot extend session",e)}}clearSession(){this.sessionManager.clearSession()}setSessionDuration(e){this.sessionManager.setSessionDuration(e)}get version(){return"0.7.2"}};w();I();G();ue();V();function $t(s={}){return new Q(s)}var hn=$t;export{ge as ApiError,E as AuthenticationError,L as BackupManager,x as BackupStorage,y as CryptoError,Y as DeviceManager,H as PlatformDetector,_ as RecoverySimulator,K as RegistrationError,q as SocialRecoveryManager,W as StealthAddressModule,h as StorageError,$ as VaultSync,d as WalletError,Q as Web3Passkey,g as Web3PasskeyError,fe as assertEthereumAddress,wt as assertMnemonic,we as assertUsername,Kt as canControlStealthAddress,se as checkStealthAddress,Ae as computeStealthPrivateKey,It as createWalletFromMnemonic,$t as createWeb3Passkey,hn as default,U as deriveStealthKeys,be as deriveWalletFromMnemonic,ve as generateBIP39Wallet,ke as generateStealthAddress,lt as getAllTopics,dt as getExplainer,vt as isStrongPassword,ut as searchExplainers,Le as validateEthereumAddress,Ge as validateMnemonic,qe as validateUsername};
6
777
  //# sourceMappingURL=index.mjs.map