From e7154eec0394cb56e8dd415b47817a9b62acc5c7 Mon Sep 17 00:00:00 2001 From: highlander Date: Tue, 14 Jul 2026 22:06:21 -0300 Subject: [PATCH 1/3] =?UTF-8?q?feat(hive):=20spike=20=E2=80=94=20window.hi?= =?UTF-8?q?ve=5Fkeychain=20shim=20+=20transfer=20via=20vault/Pioneer=20(fw?= =?UTF-8?q?=207.15.0=20gate)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - injected: Keychain-compatible window.hive_keychain (requestHandshake + requestTransfer; the rest fail keychain-style so dApps degrade gracefully), mounted only when the real Keychain is absent - background: hiveHandler — pubkey via vault POST /addresses/hive (SLIP-0048 m/48'/13'/1'/0'/0'), account/tx-params/broadcast via Pioneer, signing via new vault POST /hive/sign-transfer (device serializes + signs) - firmware gate: requireHiveFirmware (7.15.0), parametrized meetsMin - chain config: hive:beeab0de / slip44:1275 maps; approval UI routes hive to the generic OtherTransaction view Depends on vault branch feature/hive-rest-sign (POST /hive/sign-transfer). Co-Authored-By: Claude Fable 5 --- chrome-extension/public/injected.js | 34 +-- .../src/background/chainConfig.ts | 2 + .../src/background/chains/hiveHandler.ts | 263 ++++++++++++++++++ chrome-extension/src/background/firmware.ts | 31 ++- chrome-extension/src/background/methods.ts | 5 + .../src/injected/hive-provider.ts | 126 +++++++++ chrome-extension/src/injected/injected.ts | 15 + chrome-extension/src/injected/types.ts | 3 +- packages/shared/lib/utils/chainConfig.ts | 4 + pages/side-panel/src/approval/Transaction.tsx | 3 + 10 files changed, 464 insertions(+), 22 deletions(-) create mode 100644 chrome-extension/src/background/chains/hiveHandler.ts create mode 100644 chrome-extension/src/injected/hive-provider.ts diff --git a/chrome-extension/public/injected.js b/chrome-extension/public/injected.js index ab33e36..09de9dc 100644 --- a/chrome-extension/public/injected.js +++ b/chrome-extension/public/injected.js @@ -1,19 +1,19 @@ -"use strict";(()=>{var X="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";function F(r){let e=[0];for(let n of r){let s=X.indexOf(n);if(s===-1)throw new Error("Invalid base58 character");let t=s;for(let i=0;i>=8;for(;t>0;)e.push(t&255),t>>=8}for(let n of r){if(n!=="1")break;e.push(0)}return new Uint8Array(e.reverse())}var R=class r{#a;#e=[];#n=null;#r=new Set;version="1.0.0";name="KeepKey";icon="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAIKADAAQAAAABAAAAIAAAAACshmLzAAADUklEQVRYCb1XTUgUYRie3bXEWhVLQaUsgwVLoUtEQjUJiZX0A0GX7BIZXurkOTSvdo2kvETHAsOshFgqOqhlRD9C7SGS1JTCsj1krU7PM+w7zMzOzuzMqi88+73v9z7vz3zzzTeziuIgmqbFgG5gBPguFOgq4CXLIMwCo0AXEJN4zxHkEuA6kAIMkUBMqMZk7so/UG8AUcnjOIKwFXgHZIgEwKFmOHOfYO4aySVjmAoc7O4R0EB7lYS5h9K1jBJ6A7CuAfXG7OopbKLXkh4dccNZ7jlsi0gAJlWLI5jBPWFsTK5AGxCRImswFqDGWanDBo6IsYbjUanFbmrFWIHxD3IsmfJsgB4y2aJuF4UrUC5GnuNtxJeEQqEoAb3LJV+F4ctlHwkZXDULv8fEKQCHB4+rCJ9ngKcIGUTVRubT027y8yR9bOM4mhKTTwNJZD4miaDXAG8dqzlMShw3YRCZRVAr7vU4g5F/D4ZBoJK2H+Em9CsfEdBoKn4K9jPAd3G9sMPqZEzpRPzAwRfWJpN9EfZSRkAOE5LD7wrw8dkpwRh55VMm27fqt4FiVBjGBTaxEm4Db8d+4BPtIOK3AdbYCPC1qh/haGIS9gHgDeBbgjTAIkXAfTRxkgaamMNwCHgB+BMk4Decq0hGkFQbka/WMyZ/EeyHNo6TuSwx3Nn8gHQVIYOkOhB5Gp4zcdbBHiDvZ2pRuzozru2euKuDOucg/KliTAjKKMa9ksBpxBLrbzRwVfifOnB4RR2g3QSH3Cfx5FRdc2KoGstroUeQKh47vnAwWvUKjsPcA/wWdBUkjRAgZdsznO8D5xLGC/Opxc3NiQeV9uIsgkNDaUoMFpNDLleAn0cTQNBjGaFW6fn2Wrky/dI6abPOl9eN9deoWhjLloCv3+bPy7w3/9kzfvjX120g1cuSdsJ47xm1CgS9AaxCErlbV6qJ02W1nq22lG75AtIHWQEeJpOYaAT6gBQQWC5XNCjc7dkkHFKWe6v3FcLfbzRAMlcC6IC6C+gGxgCectZnCRMuopVG1v+Nx04sYINlxLH4wI6W52UFhT+Q41b2Nl0qeLnwZPGQucNHrXN6ZDG94RQuO688XbwNFzvjlSuwH03wEW8H+Bf/dxrUOWdc+H8mKXtEpGpY3AAAAABJRU5ErkJggg==";chains=["solana:mainnet"];static ACCOUNT_FEATURES=["solana:signTransaction","solana:signAndSendTransaction","solana:signMessage"];get accounts(){return this.#e}features={"standard:connect":{version:"1.0.0",connect:async()=>{if(this.#e.length>0)return{accounts:this.#e};let e=await this.#s("solana_getAccounts",[]).catch(()=>null);if(!Array.isArray(e)||e.length===0){let n=this.#n||await this.#s("solana_connect",[]);e=n?[{address:n}]:[]}return this.#o(e),{accounts:this.#e}}},"standard:disconnect":{version:"1.0.0",disconnect:async()=>{await this.#s("solana_disconnect",[]).catch(()=>{}),this.#e=[];try{localStorage.removeItem("keepkey-solana")}catch{}this.#t()}},"standard:events":{version:"1.0.0",on:(e,n)=>(e==="change"&&this.#r.add(n),()=>{this.#r.delete(n)})},"solana:signMessage":{version:"1.0.0",signMessage:async(...e)=>{let n=[];for(let{message:s,account:t}of e){let i=await this.#s("solana_signMessage",[Array.from(s),{accountAddress:t==null?void 0:t.address}]);n.push({signedMessage:s,signature:new Uint8Array(i)})}return n}},"solana:signTransaction":{version:"1.0.0",supportedTransactionVersions:new Set(["legacy",0]),signTransaction:async(...e)=>{let n=[];for(let{transaction:s,account:t}of e){let i=await this.#s("solana_signTransaction",[Array.from(s),{accountAddress:t==null?void 0:t.address}]);n.push({signedTransaction:new Uint8Array(i)})}return n}},"solana:signAndSendTransaction":{version:"1.0.0",supportedTransactionVersions:new Set(["legacy",0]),signAndSendTransaction:async(...e)=>{let n=[];for(let{transaction:s,account:t}of e){let i=await this.#s("solana_signAndSendTransaction",[Array.from(s),{accountAddress:t==null?void 0:t.address}]);n.push({signature:F(i)})}return n}},"keepkey:signOffchainMessage":{version:"1.0.0",signOffchainMessage:async e=>{let n=typeof e.message=="string"?Array.from(new TextEncoder().encode(e.message)):Array.from(e.message);return await this.#s("solana_signOffchainMessage",[{message:n,version:e.version,messageFormat:e.messageFormat}])}},"solana:signIn":{version:"1.0.0",signIn:async(...e)=>{var s;let n=[];for(let t of e){if(this.#e.length===0){let b=this.#n||await this.#s("solana_connect",[]);b&&this.#c(b)}let i=(t==null?void 0:t.address)&&this.#e.find(b=>b.address===t.address)||this.#e[0];if(!i)throw new Error("Not connected");let a=(t==null?void 0:t.domain)||location.host,g=(t==null?void 0:t.address)||i.address,f=(t==null?void 0:t.uri)||location.href,m=(t==null?void 0:t.version)||"1",v=(t==null?void 0:t.chainId)||"mainnet",E=(t==null?void 0:t.nonce)||Math.random().toString(36).substring(2),T=(t==null?void 0:t.issuedAt)||new Date().toISOString(),P=(t==null?void 0:t.statement)||"",p=`${a} wants you to sign in with your Solana account: -${g}`;if(P&&(p+=` +"use strict";(()=>{var V="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";function x(i){let e=[0];for(let n of i){let s=V.indexOf(n);if(s===-1)throw new Error("Invalid base58 character");let t=s;for(let a=0;a>=8;for(;t>0;)e.push(t&255),t>>=8}for(let n of i){if(n!=="1")break;e.push(0)}return new Uint8Array(e.reverse())}var U=class i{#r;#e=[];#n=null;#a=new Set;version="1.0.0";name="KeepKey";icon="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAIKADAAQAAAABAAAAIAAAAACshmLzAAADUklEQVRYCb1XTUgUYRie3bXEWhVLQaUsgwVLoUtEQjUJiZX0A0GX7BIZXurkOTSvdo2kvETHAsOshFgqOqhlRD9C7SGS1JTCsj1krU7PM+w7zMzOzuzMqi88+73v9z7vz3zzzTeziuIgmqbFgG5gBPguFOgq4CXLIMwCo0AXEJN4zxHkEuA6kAIMkUBMqMZk7so/UG8AUcnjOIKwFXgHZIgEwKFmOHOfYO4aySVjmAoc7O4R0EB7lYS5h9K1jBJ6A7CuAfXG7OopbKLXkh4dccNZ7jlsi0gAJlWLI5jBPWFsTK5AGxCRImswFqDGWanDBo6IsYbjUanFbmrFWIHxD3IsmfJsgB4y2aJuF4UrUC5GnuNtxJeEQqEoAb3LJV+F4ctlHwkZXDULv8fEKQCHB4+rCJ9ngKcIGUTVRubT027y8yR9bOM4mhKTTwNJZD4miaDXAG8dqzlMShw3YRCZRVAr7vU4g5F/D4ZBoJK2H+Em9CsfEdBoKn4K9jPAd3G9sMPqZEzpRPzAwRfWJpN9EfZSRkAOE5LD7wrw8dkpwRh55VMm27fqt4FiVBjGBTaxEm4Db8d+4BPtIOK3AdbYCPC1qh/haGIS9gHgDeBbgjTAIkXAfTRxkgaamMNwCHgB+BMk4Decq0hGkFQbka/WMyZ/EeyHNo6TuSwx3Nn8gHQVIYOkOhB5Gp4zcdbBHiDvZ2pRuzozru2euKuDOucg/KliTAjKKMa9ksBpxBLrbzRwVfifOnB4RR2g3QSH3Cfx5FRdc2KoGstroUeQKh47vnAwWvUKjsPcA/wWdBUkjRAgZdsznO8D5xLGC/Opxc3NiQeV9uIsgkNDaUoMFpNDLleAn0cTQNBjGaFW6fn2Wrky/dI6abPOl9eN9deoWhjLloCv3+bPy7w3/9kzfvjX120g1cuSdsJ47xm1CgS9AaxCErlbV6qJ02W1nq22lG75AtIHWQEeJpOYaAT6gBQQWC5XNCjc7dkkHFKWe6v3FcLfbzRAMlcC6IC6C+gGxgCectZnCRMuopVG1v+Nx04sYINlxLH4wI6W52UFhT+Q41b2Nl0qeLnwZPGQucNHrXN6ZDG94RQuO688XbwNFzvjlSuwH03wEW8H+Bf/dxrUOWdc+H8mKXtEpGpY3AAAAABJRU5ErkJggg==";chains=["solana:mainnet"];static ACCOUNT_FEATURES=["solana:signTransaction","solana:signAndSendTransaction","solana:signMessage"];get accounts(){return this.#e}features={"standard:connect":{version:"1.0.0",connect:async()=>{if(this.#e.length>0)return{accounts:this.#e};let e=await this.#s("solana_getAccounts",[]).catch(()=>null);if(!Array.isArray(e)||e.length===0){let n=this.#n||await this.#s("solana_connect",[]);e=n?[{address:n}]:[]}return this.#o(e),{accounts:this.#e}}},"standard:disconnect":{version:"1.0.0",disconnect:async()=>{await this.#s("solana_disconnect",[]).catch(()=>{}),this.#e=[];try{localStorage.removeItem("keepkey-solana")}catch{}this.#t()}},"standard:events":{version:"1.0.0",on:(e,n)=>(e==="change"&&this.#a.add(n),()=>{this.#a.delete(n)})},"solana:signMessage":{version:"1.0.0",signMessage:async(...e)=>{let n=[];for(let{message:s,account:t}of e){let a=await this.#s("solana_signMessage",[Array.from(s),{accountAddress:t==null?void 0:t.address}]);n.push({signedMessage:s,signature:new Uint8Array(a)})}return n}},"solana:signTransaction":{version:"1.0.0",supportedTransactionVersions:new Set(["legacy",0]),signTransaction:async(...e)=>{let n=[];for(let{transaction:s,account:t}of e){let a=await this.#s("solana_signTransaction",[Array.from(s),{accountAddress:t==null?void 0:t.address}]);n.push({signedTransaction:new Uint8Array(a)})}return n}},"solana:signAndSendTransaction":{version:"1.0.0",supportedTransactionVersions:new Set(["legacy",0]),signAndSendTransaction:async(...e)=>{let n=[];for(let{transaction:s,account:t}of e){let a=await this.#s("solana_signAndSendTransaction",[Array.from(s),{accountAddress:t==null?void 0:t.address}]);n.push({signature:x(a)})}return n}},"keepkey:signOffchainMessage":{version:"1.0.0",signOffchainMessage:async e=>{let n=typeof e.message=="string"?Array.from(new TextEncoder().encode(e.message)):Array.from(e.message);return await this.#s("solana_signOffchainMessage",[{message:n,version:e.version,messageFormat:e.messageFormat}])}},"solana:signIn":{version:"1.0.0",signIn:async(...e)=>{var s;let n=[];for(let t of e){if(this.#e.length===0){let k=this.#n||await this.#s("solana_connect",[]);k&&this.#c(k)}let a=(t==null?void 0:t.address)&&this.#e.find(k=>k.address===t.address)||this.#e[0];if(!a)throw new Error("Not connected");let r=(t==null?void 0:t.domain)||location.host,o=(t==null?void 0:t.address)||a.address,g=(t==null?void 0:t.uri)||location.href,y=(t==null?void 0:t.version)||"1",p=(t==null?void 0:t.chainId)||"mainnet",v=(t==null?void 0:t.nonce)||Math.random().toString(36).substring(2),T=(t==null?void 0:t.issuedAt)||new Date().toISOString(),I=(t==null?void 0:t.statement)||"",b=`${r} wants you to sign in with your Solana account: +${o}`;if(I&&(b+=` -${P}`),p+=` +${I}`),b+=` -URI: ${f}`,p+=` -Version: ${m}`,p+=` -Chain ID: ${v}`,p+=` -Nonce: ${E}`,p+=` -Issued At: ${T}`,t!=null&&t.expirationTime&&(p+=` -Expiration Time: ${t.expirationTime}`),t!=null&&t.notBefore&&(p+=` -Not Before: ${t.notBefore}`),t!=null&&t.requestId&&(p+=` -Request ID: ${t.requestId}`),(s=t==null?void 0:t.resources)!=null&&s.length){p+=` -Resources:`;for(let b of t.resources)p+=` -- ${b}`}let S=new TextEncoder().encode(p),I=await this.#s("solana_signMessage",[Array.from(S),{accountAddress:i.address}]);n.push({account:i,signedMessage:S,signature:new Uint8Array(I)})}return n}}};constructor(e){this.#a=e;try{let n=localStorage.getItem("keepkey-solana");if(n){let{address:s}=JSON.parse(n);s&&typeof s=="string"&&(this.#n=s)}}catch{}this.#l()}#i(e){return{address:e,publicKey:F(e),chains:["solana:mainnet"],features:[...r.ACCOUNT_FEATURES]}}#c(e){this.#e=[this.#i(e)];try{localStorage.setItem("keepkey-solana",JSON.stringify({address:e}))}catch{}this.#t()}#o(e){var n;this.#e=e.filter(s=>s==null?void 0:s.address).map(s=>this.#i(s.address));try{let s=(n=this.#e[0])==null?void 0:n.address;s&&localStorage.setItem("keepkey-solana",JSON.stringify({address:s}))}catch{}this.#t()}async#l(){try{let e=await this.#s("solana_connect",[]);if(e&&typeof e=="string"){this.#n=e;try{localStorage.setItem("keepkey-solana",JSON.stringify({address:e}))}catch{}}}catch{}}#t(){let e=this.#e,n=this.features;this.#r.forEach(s=>{try{s({accounts:e,features:n})}catch{}})}#s(e,n){return new Promise((s,t)=>{this.#a(e,n,"solana",(i,a)=>{i?t(i):s(a)})})}};function x(r){let e=({register:s})=>{s(r)};try{let s=window.navigator;s.wallets||(s.wallets=[]),Array.isArray(s.wallets)?s.wallets.push(e):typeof s.wallets.register=="function"&&s.wallets.register(r)}catch{}let n=()=>{try{window.dispatchEvent(new CustomEvent("wallet-standard:register-wallet",{detail:e}))}catch{}};n(),setTimeout(n,100),setTimeout(n,1e3),window.addEventListener("wallet-standard:app-ready",s=>{let t=s.detail;try{t&&typeof t.register=="function"?e(t):typeof t=="function"&&t(e)}catch{}})}var V="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";function Y(r){let e=[0];for(let n of r){let s=V.indexOf(n);if(s===-1)throw new Error("Invalid base58 character");let t=s;for(let i=0;i>=8;for(;t>0;)e.push(t&255),t>>=8}for(let n of r){if(n!=="1")break;e.push(0)}return new Uint8Array(e.reverse())}function Z(r){let e=Y(r);return{toString:()=>r,toBase58:()=>r,toBytes:()=>e,toBuffer:()=>e,equals:n=>{var s,t;try{return((s=n==null?void 0:n.toBase58)==null?void 0:s.call(n))===r||((t=n==null?void 0:n.toString)==null?void 0:t.call(n))===r}catch{return!1}}}}function j(r){return r!=null&&typeof r=="object"&&"version"in r&&r.message!=null}function q(r){if(!r||typeof r.serialize!="function")throw new Error("Unsupported transaction: missing serialize()");return j(r)?new Uint8Array(r.serialize()):new Uint8Array(r.serialize({requireAllSignatures:!1,verifySignatures:!1}))}function $(r){let e=r[0];if(!e)throw new Error("Signed transaction has no signatures");if(e&128)throw new Error("Unexpected multi-byte signature count");return r.slice(1,65)}function ee(r){var n,s;if(j(r)){let t=(n=r.message)==null?void 0:n.staticAccountKeys;if(t&&t.length)return t[0];throw new Error("Versioned transaction missing account keys")}if(r.feePayer)return r.feePayer;let e=(s=r.signatures)==null?void 0:s[0];if(e!=null&&e.publicKey)return e.publicKey;throw new Error("Legacy transaction missing fee payer")}var O=class{#a;#e=new Map;#n=null;#r=null;isPhantom=!0;isKeepKey=!0;constructor(e){this.#a=e;try{let n=localStorage.getItem("keepkey-solana");if(n){let{address:s}=JSON.parse(n);s&&typeof s=="string"&&(this.#r=s)}}catch{}this.#l()}get publicKey(){return this.#n}get isConnected(){return this.#n!==null}async connect(e){if(this.#n)return{publicKey:this.#n};let n=this.#r||await this.#t("solana_connect",[]);if(!n)throw new Error("Failed to connect to KeepKey");return this.#c(n),{publicKey:this.#n}}async disconnect(){await this.#t("solana_disconnect",[]).catch(()=>{}),this.#n=null;try{localStorage.removeItem("keepkey-solana")}catch{}this.#i("disconnect")}async signMessage(e,n){await this.#o();let s=await this.#t("solana_signMessage",[Array.from(e)]);return{signature:new Uint8Array(s),publicKey:this.#n}}async signTransaction(e){await this.#o();let n=q(e),s=await this.#t("solana_signTransaction",[Array.from(n)]),t=new Uint8Array(s),i=$(t);return e.addSignature(ee(e),i),e}async signAllTransactions(e){let n=[];for(let s of e)n.push(await this.signTransaction(s));return n}async signAndSendTransaction(e,n){await this.#o();let s=q(e);return{signature:await this.#t("solana_signAndSendTransaction",[Array.from(s)]),publicKey:this.#n}}async request({method:e,params:n}){var t,i,a,g,f,m;let s=Array.isArray(n)?n:n!=null?[n]:[];switch(e){case"connect":return this.connect(s[0]);case"disconnect":return this.disconnect();case"signTransaction":return this.signTransaction(((t=s[0])==null?void 0:t.transaction)??s[0]);case"signAllTransactions":return this.signAllTransactions(((i=s[0])==null?void 0:i.transactions)??s[0]);case"signAndSendTransaction":return this.signAndSendTransaction(((a=s[0])==null?void 0:a.transaction)??s[0],((g=s[0])==null?void 0:g.options)??s[1]);case"signMessage":return this.signMessage(((f=s[0])==null?void 0:f.message)??s[0],(m=s[0])==null?void 0:m.display);default:throw new Error(`KeepKey (Solana): unsupported method "${e}"`)}}on(e,n){return this.#e.has(e)||this.#e.set(e,new Set),this.#e.get(e).add(n),this}off(e,n){var s;return(s=this.#e.get(e))==null||s.delete(n),this}removeListener(e,n){return this.off(e,n)}removeAllListeners(e){return e?this.#e.delete(e):this.#e.clear(),this}#i(e,...n){var s;(s=this.#e.get(e))==null||s.forEach(t=>{try{t(...n)}catch{}})}#c(e){this.#n=Z(e),this.#r=e;try{localStorage.setItem("keepkey-solana",JSON.stringify({address:e}))}catch{}this.#i("connect",this.#n)}async#o(){this.#n||await this.connect()}async#l(){try{let e=await this.#t("solana_connect",[]);if(e&&typeof e=="string"){this.#r=e;try{localStorage.setItem("keepkey-solana",JSON.stringify({address:e}))}catch{}}}catch{}}#t(e,n){return new Promise((s,t)=>{this.#a(e,n,"solana",(i,a)=>{i?t(i):s(a)})})}};var N="https://api.trongrid.io",ne="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";function te(r){let e=[0];for(let n of r){let s=ne.indexOf(n);if(s===-1)throw new Error("Invalid base58 character");let t=s;for(let i=0;i>=8;for(;t>0;)e.push(t&255),t>>=8}for(let n of r){if(n!=="1")break;e.push(0)}return new Uint8Array(e.reverse())}function se(r){let e="";for(let n of r)e+=n.toString(16).padStart(2,"0");return e}function W(r){let e=te(r);if(e.length!==25||e[0]!==65)throw new Error(`Invalid Tron address: ${r}`);return se(e.slice(0,21))}function D(r){if(typeof r!="string"||r.length!==34||!r.startsWith("T"))return!1;try{return W(r),!0}catch{return!1}}var L=class{events=new Map;on(e,n){this.events.has(e)||this.events.set(e,new Set),this.events.get(e).add(n)}off(e,n){var s;(s=this.events.get(e))==null||s.delete(n)}emit(e,...n){var s;(s=this.events.get(e))==null||s.forEach(t=>{try{t(...n)}catch{}})}};function C(r,e,n){return new Promise((s,t)=>{r(e,n,"tron",(i,a)=>{i?t(i):s(a)})})}var re=8e3,ae=12e3,ie=r=>r>=500&&r<600;async function M(r,e){let s=r.includes("broadcasttransaction")?ae:re,t=2,i;for(let a=1;a<=t;a++)try{let g=await fetch(`${N}${r}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e),signal:AbortSignal.timeout(s)});if(!g.ok){if(ie(g.status)&&asetTimeout(m,200*a));continue}let f=await g.text().catch(()=>"");throw new Error(`TronGrid ${r} failed (${g.status}): ${f}`)}return await g.json()}catch(g){if(i=g,asetTimeout(f,200*a));continue}}throw i}var B=class{tronWeb;tronLink;address=null;hexAddress=null;emitter=new L;walletRequest;constructor(e){this.walletRequest=e,this.tronWeb=this.buildTronWeb(),this.tronLink=this.buildTronLink()}setAddress(e){!e||!D(e)||(this.address=e,this.hexAddress=W(e),this.tronWeb.ready=!0,this.tronWeb.defaultAddress={base58:e,hex:this.hexAddress,name:"KeepKey",type:1},this.tronLink.ready=!0,this.fireMessage("setAccount",{address:e,name:"KeepKey",type:1}),this.fireMessage("accountsChanged",{address:e}))}fireMessage(e,n){try{window.postMessage({message:{action:e,data:n},isTronLink:!0},window.location.origin)}catch{}this.emitter.emit(e,n)}buildTronLink(){return{isTronLink:!0,ready:!1,tronWeb:null,request:async({method:e,params:n})=>{switch(e){case"tron_requestAccounts":case"tron_accounts":{let s=await C(this.walletRequest,"tron_requestAccounts",[]);return!s||typeof s!="string"?{code:4001,message:"User denied account access"}:(this.setAddress(s),{code:200,message:"ok"})}default:return C(this.walletRequest,e,Array.isArray(n)?n:[n])}},on:(e,n)=>this.emitter.on(e,n),off:(e,n)=>this.emitter.off(e,n)}}buildTronWeb(){let e=this,n={sign:async(a,g,f,m)=>{if(typeof a=="string")throw new Error("tronWeb.trx.sign(message) not supported \u2014 use tronWeb.trx.signMessage()");if(!a||!a.raw_data_hex)throw new Error("tronWeb.trx.sign expects a built transaction with raw_data_hex");return await C(e.walletRequest,"tron_sign",[a])},signMessage:async(a,g)=>await C(e.walletRequest,"tron_signMessage",[a]),signMessageV2:async(a,g)=>await C(e.walletRequest,"signMessageV2",[a]),sendRawTransaction:async a=>M("/wallet/broadcasttransaction",a),broadcast:async a=>n.sendRawTransaction(a),getBalance:async a=>{let g=a||e.address;if(!g)throw new Error("No address \u2014 call tron_requestAccounts first");let f=await M("/wallet/getaccount",{address:g,visible:!0});return typeof(f==null?void 0:f.balance)=="number"?f.balance:0},getAccount:async a=>{let g=a||e.address;if(!g)throw new Error("No address \u2014 call tron_requestAccounts first");return M("/wallet/getaccount",{address:g,visible:!0})},getUnconfirmedAccount:async a=>{let g=a||e.address;if(!g)throw new Error("No address \u2014 call tron_requestAccounts first");return M("/wallet/getaccount",{address:g,visible:!0})},getTransaction:async a=>M("/wallet/gettransactionbyid",{value:a})},i={isTronLink:!0,ready:!1,defaultAddress:{base58:!1,hex:!1,name:!1,type:-1},fullNode:{host:N},solidityNode:{host:N},eventServer:{host:N},trx:n,transactionBuilder:{sendTrx:async(a,g,f)=>{let m=f||e.address;if(!m)throw new Error("No address \u2014 call tron_requestAccounts first");return M("/wallet/createtransaction",{owner_address:m,to_address:a,amount:g,visible:!0})},triggerSmartContract:async(a,g,f={},m=[],v)=>{let E=v||e.address;if(!E)throw new Error("No address \u2014 call tron_requestAccounts first");return M("/wallet/triggersmartcontract",{contract_address:a,function_selector:g,parameter:oe(m),fee_limit:f.feeLimit??1e8,call_value:f.callValue??0,owner_address:E,visible:!0})}},utils:{isAddress:a=>D(a),fromSun:a=>String(Number(a)/1e6),toSun:a=>String(Math.round(Number(a)*1e6)),toHex:a=>W(a)},on:(a,g)=>this.emitter.on(a,g),off:(a,g)=>this.emitter.off(a,g),setAddress:a=>{},isConnected:()=>this.address!==null};return queueMicrotask(()=>{this.tronLink&&(this.tronLink.tronWeb=i)}),i}};function oe(r){if(!Array.isArray(r)||r.length===0)return"";let e="";for(let n of r)if(n.type==="address"){let s=String(n.value),t=s.startsWith("T")?W(s).slice(2):s.replace(/^0x/,"").replace(/^41/,"");e+=t.padStart(64,"0")}else if(n.type==="uint256"||n.type==="uint"){let s=BigInt(n.value);e+=s.toString(16).padStart(64,"0")}else{let s=String(n.value).replace(/^0x/,"");e+=s.padStart(64,"0")}return e}(function(){let r="2.1.0",i=window,a={isInjected:!1,version:r,injectedAt:Date.now(),retryCount:0};if(i.keepkeyInjectionState&&i.keepkeyInjectionState.version>=r)return;i.keepkeyInjectionState=a;let g=(()=>{var c;let d={enableMetaMaskMasking:!1,enableXfiMasking:!1,enableKeplrMasking:!1,enablePhantomMasking:!1};try{let u=document.currentScript,o=document.getElementById("keepkey-injected-script"),l=(c=u==null?void 0:u.dataset)!=null&&c.masking?u:o,y=l==null?void 0:l.dataset.masking;if(!y)return d;let A=JSON.parse(y);return{enableMetaMaskMasking:A.enableMetaMaskMasking===!0,enableXfiMasking:A.enableXfiMasking===!0,enableKeplrMasking:A.enableKeplrMasking===!0,enablePhantomMasking:A.enablePhantomMasking===!0}}catch{return d}})();console.log(`[KeepKey] masking: metamask=${g.enableMetaMaskMasking?"on":"off"} xfi=${g.enableXfiMasking?"on":"off"} keplr=${g.enableKeplrMasking?"on":"off"} phantom=${g.enablePhantomMasking?"on":"off"}`);let f={siteUrl:window.location.href,scriptSource:"KeepKey Extension",version:r,injectedTime:new Date().toISOString(),origin:window.location.origin,protocol:window.location.protocol},m=0,v=new Map,E=[],T=!1;setInterval(()=>{let d=Date.now();v.forEach((c,u)=>{d-c.timestamp>3e5&&(c.callback(new Error("Request timeout")),v.delete(u))})},5e3);let p=d=>{E.length>=100&&E.shift(),E.push(d)},S=()=>{if(T)for(;E.length>0;){let d=E.shift();d&&window.postMessage(d,window.location.origin)}},I=(d=0)=>new Promise(c=>{let u=++m,o=setTimeout(()=>{d<3?setTimeout(()=>{I(d+1).then(c)},100*Math.pow(2,d)):(a.lastError="Failed to verify injection",c(!1))},1e3),l=y=>{var A,w,k;y.source===window&&((A=y.data)==null?void 0:A.source)==="keepkey-content"&&((w=y.data)==null?void 0:w.type)==="INJECTION_CONFIRMED"&&((k=y.data)==null?void 0:k.requestId)===u&&(clearTimeout(o),window.removeEventListener("message",l),T=!0,a.isInjected=!0,S(),c(!0))};window.addEventListener("message",l),window.postMessage({source:"keepkey-injected",type:"INJECTION_VERIFY",requestId:u,version:r,timestamp:Date.now()},window.location.origin)});function b(d,c=[],u,o){if(!d||typeof d!="string"){o(new Error("Invalid method"));return}Array.isArray(c)||(c=[c]);try{let l=++m,y={id:l,method:d,params:c,chain:u,siteUrl:f.siteUrl,scriptSource:f.scriptSource,version:f.version,requestTime:new Date().toISOString(),referrer:document.referrer,href:window.location.href,userAgent:navigator.userAgent,platform:navigator.platform,language:navigator.language};v.set(l,{callback:o,timestamp:Date.now(),method:d});let A={source:"keepkey-injected",type:"WALLET_REQUEST",requestId:l,requestInfo:y,timestamp:Date.now()};T?window.postMessage(A,window.location.origin):p(A)}catch(l){o(l)}}window.addEventListener("message",d=>{if(d.source!==window)return;let c=d.data;if(!(!c||typeof c!="object")){if(c.source==="keepkey-content"&&c.type==="INJECTION_CONFIRMED"){T=!0,S();return}if(c.source==="keepkey-content"&&c.type==="WALLET_RESPONSE"&&c.requestId){let u=v.get(c.requestId);u&&(c.error?u.callback(c.error):u.callback(null,c.result),v.delete(c.requestId))}}});class G{events=new Map;on(c,u){this.events.has(c)||this.events.set(c,new Set),this.events.get(c).add(u)}off(c,u){var o;(o=this.events.get(c))==null||o.delete(u)}removeListener(c,u){this.off(c,u)}removeAllListeners(c){c?this.events.delete(c):this.events.clear()}emit(c,...u){var o;(o=this.events.get(c))==null||o.forEach(l=>{try{l(...u)}catch{}})}once(c,u){let o=(...l)=>{u(...l),this.off(c,o)};this.on(c,o)}}function h(d){let c=new G,u={network:"mainnet",isKeepKey:!0,isMetaMask:g.enableMetaMaskMasking,isConnected:()=>T,request:({method:o,params:l=[]})=>new Promise((y,A)=>{b(o,l,d,(w,k)=>{if(w)console.log(`[HANDOFF] dApp \u2190 KeepKey (${d}/${o}) REJECT - params=${JSON.stringify(l)} - error=`,w),A(w);else{let _=typeof k,Q=_==="string"?`len=${k.length} value=${k}`:`value=${JSON.stringify(k)}`;console.log(`[HANDOFF] dApp \u2190 KeepKey (${d}/${o}) RESOLVE - params=${JSON.stringify(l)} - type=${_} ${Q}`),y(k)}})}),send:(o,l,y)=>{if(o.chain||(o.chain=d),typeof y=="function"){b(o.method,o.params||l,d,(A,w)=>{A?y(A):y(null,{id:o.id,jsonrpc:"2.0",result:w})});return}else return{id:o.id,jsonrpc:"2.0",result:null}},sendAsync:(o,l,y)=>{o.chain||(o.chain=d);let A=y||l;typeof A=="function"&&b(o.method,o.params||l,d,(w,k)=>{w?A(w):A(null,{id:o.id,jsonrpc:"2.0",result:k})})},on:(o,l)=>(c.on(o,l),u),off:(o,l)=>(c.off(o,l),u),removeListener:(o,l)=>(c.removeListener(o,l),u),removeAllListeners:o=>(c.removeAllListeners(o),u),emit:(o,...l)=>(c.emit(o,...l),u),once:(o,l)=>(c.once(o,l),u),enable:()=>u.request({method:"eth_requestAccounts"}),_metamask:{isUnlocked:()=>Promise.resolve(!0)}};return d==="ethereum"&&(u.chainId="0x1",u.networkVersion="1",u.selectedAddress=null,u._handleAccountsChanged=o=>{u.selectedAddress=o[0]||null,c.emit("accountsChanged",o)},u._handleChainChanged=o=>{u.chainId=o,c.emit("chainChanged",o)},u._handleConnect=o=>{c.emit("connect",o)},u._handleDisconnect=o=>{u.selectedAddress=null,c.emit("disconnect",o)}),u}let z="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAIKADAAQAAAABAAAAIAAAAACshmLzAAADUklEQVRYCb1XTUgUYRie3bXEWhVLQaUsgwVLoUtEQjUJiZX0A0GX7BIZXurkOTSvdo2kvETHAsOshFgqOqhlRD9C7SGS1JTCsj1krU7PM+w7zMzOzuzMqi88+73v9z7vz3zzzTeziuIgmqbFgG5gBPguFOgq4CXLIMwCo0AXEJN4zxHkEuA6kAIMkUBMqMZk7so/UG8AUcnjOIKwFXgHZIgEwKFmOHOfYO4aySVjmAoc7O4R0EB7lYS5h9K1jBJ6A7CuAfXG7OopbKLXkh4dccNZ7jlsi0gAJlWLI5jBPWFsTK5AGxCRImswFqDGWanDBo6IsYbjUanFbmrFWIHxD3IsmfJsgB4y2aJuF4UrUC5GnuNtxJeEQqEoAb3LJV+F4ctlHwkZXDULv8fEKQCHB4+rCJ9ngKcIGUTVRubT027y8yR9bOM4mhKTTwNJZD4miaDXAG8dqzlMShw3YRCZRVAr7vU4g5F/D4ZBoJK2H+Em9CsfEdBoKn4K9jPAd3G9sMPqZEzpRPzAwRfWJpN9EfZSRkAOE5LD7wrw8dkpwRh55VMm27fqt4FiVBjGBTaxEm4Db8d+4BPtIOK3AdbYCPC1qh/haGIS9gHgDeBbgjTAIkXAfTRxkgaamMNwCHgB+BMk4Decq0hGkFQbka/WMyZ/EeyHNo6TuSwx3Nn8gHQVIYOkOhB5Gp4zcdbBHiDvZ2pRuzozru2euKuDOucg/KliTAjKKMa9ksBpxBLrbzRwVfifOnB4RR2g3QSH3Cfx5FRdc2KoGstroUeQKh47vnAwWvUKjsPcA/wWdBUkjRAgZdsznO8D5xLGC/Opxc3NiQeV9uIsgkNDaUoMFpNDLleAn0cTQNBjGaFW6fn2Wrky/dI6abPOl9eN9deoWhjLloCv3+bPy7w3/9kzfvjX120g1cuSdsJ47xm1CgS9AaxCErlbV6qJ22W1nq22lG75AtIHWQEeJpOYaAT6gBQQWC5XNCjc7dkkHFKWe6v3FcLfbzRAMlcC6IC6C+gGxgCectZnCRMuopVG1v+Nx04sYINlxLH4wI6W52UFhT+Q41b2Nl0qeLnwZPGQucNHrXN6ZDG94RQuO688XbwNFzvjlSuwH03wEW8H+Bf/dxrUOWdc+H8mKXtEpGpY3AAAAABJRU5ErkJggg==",H="data:image/svg+xml;utf8,"+encodeURIComponent('MM');function K(d){let c={uuid:"350670db-19fa-4704-a166-e52e178b59d4",name:"KeepKey",icon:z,rdns:"com.keepkey.client"};if(window.dispatchEvent(new CustomEvent("eip6963:announceProvider",{detail:Object.freeze({info:c,provider:d})})),g.enableMetaMaskMasking){let u={uuid:"9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",name:"MetaMask",icon:H,rdns:"io.metamask"};window.dispatchEvent(new CustomEvent("eip6963:announceProvider",{detail:Object.freeze({info:u,provider:d})}))}}async function J(){let d=h("ethereum"),c={binance:h("binance"),bitcoin:h("bitcoin"),bitcoincash:h("bitcoincash"),dogecoin:h("dogecoin"),dash:h("dash"),ethereum:d,keplr:h("keplr"),litecoin:h("litecoin"),thorchain:h("thorchain"),mayachain:h("mayachain")},u={binance:h("binance"),bitcoin:h("bitcoin"),bitcoincash:h("bitcoincash"),dogecoin:h("dogecoin"),dash:h("dash"),ethereum:d,osmosis:h("osmosis"),cosmos:h("cosmos"),litecoin:h("litecoin"),thorchain:h("thorchain"),mayachain:h("mayachain"),ripple:h("ripple")},o=(l,y,{force:A=!1}={})=>{if(!(i[l]&&!A))try{Object.defineProperty(i,l,{value:y,writable:!1,configurable:!0})}catch{a.lastError=`Failed to mount ${l}`}};g.enableMetaMaskMasking&&o("ethereum",d,{force:!0}),g.enableXfiMasking&&o("xfi",c,{force:!0}),o("keepkey",u,{force:!0}),window.addEventListener("eip6963:requestProvider",()=>{K(d)}),K(d),setTimeout(()=>{K(d)},100);try{let l=new R(b);x(l)}catch{}if(g.enablePhantomMasking)try{let l=new O(b);Object.defineProperty(i,"solana",{value:l,writable:!1,configurable:!0})}catch{}try{let l=new B(b);i.tronLink||Object.defineProperty(i,"tronLink",{value:l.tronLink,writable:!1,configurable:!0}),i.tronWeb||Object.defineProperty(i,"tronWeb",{value:l.tronWeb,writable:!1,configurable:!0})}catch{}window.addEventListener("message",l=>{var y,A,w;((y=l.data)==null?void 0:y.type)==="CHAIN_CHANGED"&&d.emit("chainChanged",(A=l.data.provider)==null?void 0:A.chainId),((w=l.data)==null?void 0:w.type)==="ACCOUNTS_CHANGED"&&d._handleAccountsChanged&&d._handleAccountsChanged(l.data.accounts||[])}),I().then(l=>{l||(a.lastError="Injection not verified")})}J(),document.readyState==="loading"&&document.addEventListener("DOMContentLoaded",()=>{if(i.ethereum&&typeof i.dispatchEvent=="function"){let d=i.ethereum;K(d)}})})();})(); +URI: ${g}`,b+=` +Version: ${y}`,b+=` +Chain ID: ${p}`,b+=` +Nonce: ${v}`,b+=` +Issued At: ${T}`,t!=null&&t.expirationTime&&(b+=` +Expiration Time: ${t.expirationTime}`),t!=null&&t.notBefore&&(b+=` +Not Before: ${t.notBefore}`),t!=null&&t.requestId&&(b+=` +Request ID: ${t.requestId}`),(s=t==null?void 0:t.resources)!=null&&s.length){b+=` +Resources:`;for(let k of t.resources)b+=` +- ${k}`}let C=new TextEncoder().encode(b),_=await this.#s("solana_signMessage",[Array.from(C),{accountAddress:a.address}]);n.push({account:a,signedMessage:C,signature:new Uint8Array(_)})}return n}}};constructor(e){this.#r=e;try{let n=localStorage.getItem("keepkey-solana");if(n){let{address:s}=JSON.parse(n);s&&typeof s=="string"&&(this.#n=s)}}catch{}this.#l()}#i(e){return{address:e,publicKey:x(e),chains:["solana:mainnet"],features:[...i.ACCOUNT_FEATURES]}}#c(e){this.#e=[this.#i(e)];try{localStorage.setItem("keepkey-solana",JSON.stringify({address:e}))}catch{}this.#t()}#o(e){var n;this.#e=e.filter(s=>s==null?void 0:s.address).map(s=>this.#i(s.address));try{let s=(n=this.#e[0])==null?void 0:n.address;s&&localStorage.setItem("keepkey-solana",JSON.stringify({address:s}))}catch{}this.#t()}async#l(){try{let e=await this.#s("solana_connect",[]);if(e&&typeof e=="string"){this.#n=e;try{localStorage.setItem("keepkey-solana",JSON.stringify({address:e}))}catch{}}}catch{}}#t(){let e=this.#e,n=this.features;this.#a.forEach(s=>{try{s({accounts:e,features:n})}catch{}})}#s(e,n){return new Promise((s,t)=>{this.#r(e,n,"solana",(a,r)=>{a?t(a):s(r)})})}};function L(i){let e=({register:s})=>{s(i)};try{let s=window.navigator;s.wallets||(s.wallets=[]),Array.isArray(s.wallets)?s.wallets.push(e):typeof s.wallets.register=="function"&&s.wallets.register(i)}catch{}let n=()=>{try{window.dispatchEvent(new CustomEvent("wallet-standard:register-wallet",{detail:e}))}catch{}};n(),setTimeout(n,100),setTimeout(n,1e3),window.addEventListener("wallet-standard:app-ready",s=>{let t=s.detail;try{t&&typeof t.register=="function"?e(t):typeof t=="function"&&t(e)}catch{}})}var Y="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";function Z(i){let e=[0];for(let n of i){let s=Y.indexOf(n);if(s===-1)throw new Error("Invalid base58 character");let t=s;for(let a=0;a>=8;for(;t>0;)e.push(t&255),t>>=8}for(let n of i){if(n!=="1")break;e.push(0)}return new Uint8Array(e.reverse())}function $(i){let e=Z(i);return{toString:()=>i,toBase58:()=>i,toBytes:()=>e,toBuffer:()=>e,equals:n=>{var s,t;try{return((s=n==null?void 0:n.toBase58)==null?void 0:s.call(n))===i||((t=n==null?void 0:n.toString)==null?void 0:t.call(n))===i}catch{return!1}}}}function j(i){return i!=null&&typeof i=="object"&&"version"in i&&i.message!=null}function F(i){if(!i||typeof i.serialize!="function")throw new Error("Unsupported transaction: missing serialize()");return j(i)?new Uint8Array(i.serialize()):new Uint8Array(i.serialize({requireAllSignatures:!1,verifySignatures:!1}))}function ee(i){let e=i[0];if(!e)throw new Error("Signed transaction has no signatures");if(e&128)throw new Error("Unexpected multi-byte signature count");return i.slice(1,65)}function ne(i){var n,s;if(j(i)){let t=(n=i.message)==null?void 0:n.staticAccountKeys;if(t&&t.length)return t[0];throw new Error("Versioned transaction missing account keys")}if(i.feePayer)return i.feePayer;let e=(s=i.signatures)==null?void 0:s[0];if(e!=null&&e.publicKey)return e.publicKey;throw new Error("Legacy transaction missing fee payer")}var O=class{#r;#e=new Map;#n=null;#a=null;isPhantom=!0;isKeepKey=!0;constructor(e){this.#r=e;try{let n=localStorage.getItem("keepkey-solana");if(n){let{address:s}=JSON.parse(n);s&&typeof s=="string"&&(this.#a=s)}}catch{}this.#l()}get publicKey(){return this.#n}get isConnected(){return this.#n!==null}async connect(e){if(this.#n)return{publicKey:this.#n};let n=this.#a||await this.#t("solana_connect",[]);if(!n)throw new Error("Failed to connect to KeepKey");return this.#c(n),{publicKey:this.#n}}async disconnect(){await this.#t("solana_disconnect",[]).catch(()=>{}),this.#n=null;try{localStorage.removeItem("keepkey-solana")}catch{}this.#i("disconnect")}async signMessage(e,n){await this.#o();let s=await this.#t("solana_signMessage",[Array.from(e)]);return{signature:new Uint8Array(s),publicKey:this.#n}}async signTransaction(e){await this.#o();let n=F(e),s=await this.#t("solana_signTransaction",[Array.from(n)]),t=new Uint8Array(s),a=ee(t);return e.addSignature(ne(e),a),e}async signAllTransactions(e){let n=[];for(let s of e)n.push(await this.signTransaction(s));return n}async signAndSendTransaction(e,n){await this.#o();let s=F(e);return{signature:await this.#t("solana_signAndSendTransaction",[Array.from(s)]),publicKey:this.#n}}async request({method:e,params:n}){var t,a,r,o,g,y;let s=Array.isArray(n)?n:n!=null?[n]:[];switch(e){case"connect":return this.connect(s[0]);case"disconnect":return this.disconnect();case"signTransaction":return this.signTransaction(((t=s[0])==null?void 0:t.transaction)??s[0]);case"signAllTransactions":return this.signAllTransactions(((a=s[0])==null?void 0:a.transactions)??s[0]);case"signAndSendTransaction":return this.signAndSendTransaction(((r=s[0])==null?void 0:r.transaction)??s[0],((o=s[0])==null?void 0:o.options)??s[1]);case"signMessage":return this.signMessage(((g=s[0])==null?void 0:g.message)??s[0],(y=s[0])==null?void 0:y.display);default:throw new Error(`KeepKey (Solana): unsupported method "${e}"`)}}on(e,n){return this.#e.has(e)||this.#e.set(e,new Set),this.#e.get(e).add(n),this}off(e,n){var s;return(s=this.#e.get(e))==null||s.delete(n),this}removeListener(e,n){return this.off(e,n)}removeAllListeners(e){return e?this.#e.delete(e):this.#e.clear(),this}#i(e,...n){var s;(s=this.#e.get(e))==null||s.forEach(t=>{try{t(...n)}catch{}})}#c(e){this.#n=$(e),this.#a=e;try{localStorage.setItem("keepkey-solana",JSON.stringify({address:e}))}catch{}this.#i("connect",this.#n)}async#o(){this.#n||await this.connect()}async#l(){try{let e=await this.#t("solana_connect",[]);if(e&&typeof e=="string"){this.#a=e;try{localStorage.setItem("keepkey-solana",JSON.stringify({address:e}))}catch{}}}catch{}}#t(e,n){return new Promise((s,t)=>{this.#r(e,n,"solana",(a,r)=>{a?t(a):s(r)})})}};var B="https://api.trongrid.io",te="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";function se(i){let e=[0];for(let n of i){let s=te.indexOf(n);if(s===-1)throw new Error("Invalid base58 character");let t=s;for(let a=0;a>=8;for(;t>0;)e.push(t&255),t>>=8}for(let n of i){if(n!=="1")break;e.push(0)}return new Uint8Array(e.reverse())}function ae(i){let e="";for(let n of i)e+=n.toString(16).padStart(2,"0");return e}function W(i){let e=se(i);if(e.length!==25||e[0]!==65)throw new Error(`Invalid Tron address: ${i}`);return ae(e.slice(0,21))}function D(i){if(typeof i!="string"||i.length!==34||!i.startsWith("T"))return!1;try{return W(i),!0}catch{return!1}}var q=class{events=new Map;on(e,n){this.events.has(e)||this.events.set(e,new Set),this.events.get(e).add(n)}off(e,n){var s;(s=this.events.get(e))==null||s.delete(n)}emit(e,...n){var s;(s=this.events.get(e))==null||s.forEach(t=>{try{t(...n)}catch{}})}};function K(i,e,n){return new Promise((s,t)=>{i(e,n,"tron",(a,r)=>{a?t(a):s(r)})})}var re=8e3,ie=12e3,oe=i=>i>=500&&i<600;async function M(i,e){let s=i.includes("broadcasttransaction")?ie:re,t=2,a;for(let r=1;r<=t;r++)try{let o=await fetch(`${B}${i}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e),signal:AbortSignal.timeout(s)});if(!o.ok){if(oe(o.status)&&rsetTimeout(y,200*r));continue}let g=await o.text().catch(()=>"");throw new Error(`TronGrid ${i} failed (${o.status}): ${g}`)}return await o.json()}catch(o){if(a=o,rsetTimeout(g,200*r));continue}}throw a}var N=class{tronWeb;tronLink;address=null;hexAddress=null;emitter=new q;walletRequest;constructor(e){this.walletRequest=e,this.tronWeb=this.buildTronWeb(),this.tronLink=this.buildTronLink()}setAddress(e){!e||!D(e)||(this.address=e,this.hexAddress=W(e),this.tronWeb.ready=!0,this.tronWeb.defaultAddress={base58:e,hex:this.hexAddress,name:"KeepKey",type:1},this.tronLink.ready=!0,this.fireMessage("setAccount",{address:e,name:"KeepKey",type:1}),this.fireMessage("accountsChanged",{address:e}))}fireMessage(e,n){try{window.postMessage({message:{action:e,data:n},isTronLink:!0},window.location.origin)}catch{}this.emitter.emit(e,n)}buildTronLink(){return{isTronLink:!0,ready:!1,tronWeb:null,request:async({method:e,params:n})=>{switch(e){case"tron_requestAccounts":case"tron_accounts":{let s=await K(this.walletRequest,"tron_requestAccounts",[]);return!s||typeof s!="string"?{code:4001,message:"User denied account access"}:(this.setAddress(s),{code:200,message:"ok"})}default:return K(this.walletRequest,e,Array.isArray(n)?n:[n])}},on:(e,n)=>this.emitter.on(e,n),off:(e,n)=>this.emitter.off(e,n)}}buildTronWeb(){let e=this,n={sign:async(r,o,g,y)=>{if(typeof r=="string")throw new Error("tronWeb.trx.sign(message) not supported \u2014 use tronWeb.trx.signMessage()");if(!r||!r.raw_data_hex)throw new Error("tronWeb.trx.sign expects a built transaction with raw_data_hex");return await K(e.walletRequest,"tron_sign",[r])},signMessage:async(r,o)=>await K(e.walletRequest,"tron_signMessage",[r]),signMessageV2:async(r,o)=>await K(e.walletRequest,"signMessageV2",[r]),sendRawTransaction:async r=>M("/wallet/broadcasttransaction",r),broadcast:async r=>n.sendRawTransaction(r),getBalance:async r=>{let o=r||e.address;if(!o)throw new Error("No address \u2014 call tron_requestAccounts first");let g=await M("/wallet/getaccount",{address:o,visible:!0});return typeof(g==null?void 0:g.balance)=="number"?g.balance:0},getAccount:async r=>{let o=r||e.address;if(!o)throw new Error("No address \u2014 call tron_requestAccounts first");return M("/wallet/getaccount",{address:o,visible:!0})},getUnconfirmedAccount:async r=>{let o=r||e.address;if(!o)throw new Error("No address \u2014 call tron_requestAccounts first");return M("/wallet/getaccount",{address:o,visible:!0})},getTransaction:async r=>M("/wallet/gettransactionbyid",{value:r})},a={isTronLink:!0,ready:!1,defaultAddress:{base58:!1,hex:!1,name:!1,type:-1},fullNode:{host:B},solidityNode:{host:B},eventServer:{host:B},trx:n,transactionBuilder:{sendTrx:async(r,o,g)=>{let y=g||e.address;if(!y)throw new Error("No address \u2014 call tron_requestAccounts first");return M("/wallet/createtransaction",{owner_address:y,to_address:r,amount:o,visible:!0})},triggerSmartContract:async(r,o,g={},y=[],p)=>{let v=p||e.address;if(!v)throw new Error("No address \u2014 call tron_requestAccounts first");return M("/wallet/triggersmartcontract",{contract_address:r,function_selector:o,parameter:ce(y),fee_limit:g.feeLimit??1e8,call_value:g.callValue??0,owner_address:v,visible:!0})}},utils:{isAddress:r=>D(r),fromSun:r=>String(Number(r)/1e6),toSun:r=>String(Math.round(Number(r)*1e6)),toHex:r=>W(r)},on:(r,o)=>this.emitter.on(r,o),off:(r,o)=>this.emitter.off(r,o),setAddress:r=>{},isConnected:()=>this.address!==null};return queueMicrotask(()=>{this.tronLink&&(this.tronLink.tronWeb=a)}),a}};function ce(i){if(!Array.isArray(i)||i.length===0)return"";let e="";for(let n of i)if(n.type==="address"){let s=String(n.value),t=s.startsWith("T")?W(s).slice(2):s.replace(/^0x/,"").replace(/^41/,"");e+=t.padStart(64,"0")}else if(n.type==="uint256"||n.type==="uint"){let s=BigInt(n.value);e+=s.toString(16).padStart(64,"0")}else{let s=String(n.value).replace(/^0x/,"");e+=s.padStart(64,"0")}return e}function G(i){let e=1;function n(a,r,o,g,y){if(typeof a!="function")return;let p=g?(g==null?void 0:g.message)||String(g):null;a({success:!g,error:g?(g==null?void 0:g.code)===4001?"user_cancel":p:null,message:p,request_id:r,result:g?null:y??null,data:o})}function s(a,r,o){let g=e++;i(a,[r],"hive",(y,p)=>{n(o,g,r,y,p)})}function t(a,r,o){n(o,e++,r,{message:`KeepKey does not support Keychain request "${a}" yet`})}return{current_id:1,requestHandshake:function(a){s("hive_handshake",{},()=>{typeof a=="function"&&a()})},requestTransfer:function(a,r,o,g,y,p,v=!1,T){s("hive_transfer",{type:"transfer",username:a,to:r,amount:o,memo:g,enforce:v,currency:y,rpc:T},p)},requestSignBuffer:function(a,r,o,g){t("signBuffer",{type:"signBuffer"},g)},requestBroadcast:function(a,r,o,g){t("broadcast",{type:"broadcast"},g)},requestSignTx:function(a,r,o,g){t("signTx",{type:"signTx"},g)},requestVote:function(a,r,o,g,y){t("vote",{type:"vote"},y)},requestCustomJson:function(a,r,o,g,y,p){t("custom_json",{type:"custom"},p)},requestPost:function(...a){let r=a.find(o=>typeof o=="function");t("post",{type:"post"},r)},requestEncodeMessage:function(a,r,o,g,y){t("encode",{type:"encode"},y)},requestVerifyKey:function(a,r,o,g){t("decode",{type:"decode"},g)}}}(function(){let i="2.1.0",a=window,r={isInjected:!1,version:i,injectedAt:Date.now(),retryCount:0};if(a.keepkeyInjectionState&&a.keepkeyInjectionState.version>=i)return;a.keepkeyInjectionState=r;let o=(()=>{var l;let f={enableMetaMaskMasking:!1,enableXfiMasking:!1,enableKeplrMasking:!1,enablePhantomMasking:!1};try{let d=document.currentScript,c=document.getElementById("keepkey-injected-script"),u=(l=d==null?void 0:d.dataset)!=null&&l.masking?d:c,A=u==null?void 0:u.dataset.masking;if(!A)return f;let h=JSON.parse(A);return{enableMetaMaskMasking:h.enableMetaMaskMasking===!0,enableXfiMasking:h.enableXfiMasking===!0,enableKeplrMasking:h.enableKeplrMasking===!0,enablePhantomMasking:h.enablePhantomMasking===!0}}catch{return f}})();console.log(`[KeepKey] masking: metamask=${o.enableMetaMaskMasking?"on":"off"} xfi=${o.enableXfiMasking?"on":"off"} keplr=${o.enableKeplrMasking?"on":"off"} phantom=${o.enablePhantomMasking?"on":"off"}`);let g={siteUrl:window.location.href,scriptSource:"KeepKey Extension",version:i,injectedTime:new Date().toISOString(),origin:window.location.origin,protocol:window.location.protocol},y=0,p=new Map,v=[],T=!1;setInterval(()=>{let f=Date.now();p.forEach((l,d)=>{f-l.timestamp>3e5&&(l.callback(new Error("Request timeout")),p.delete(d))})},5e3);let b=f=>{v.length>=100&&v.shift(),v.push(f)},C=()=>{if(T)for(;v.length>0;){let f=v.shift();f&&window.postMessage(f,window.location.origin)}},_=(f=0)=>new Promise(l=>{let d=++y,c=setTimeout(()=>{f<3?setTimeout(()=>{_(f+1).then(l)},100*Math.pow(2,f)):(r.lastError="Failed to verify injection",l(!1))},1e3),u=A=>{var h,w,E;A.source===window&&((h=A.data)==null?void 0:h.source)==="keepkey-content"&&((w=A.data)==null?void 0:w.type)==="INJECTION_CONFIRMED"&&((E=A.data)==null?void 0:E.requestId)===d&&(clearTimeout(c),window.removeEventListener("message",u),T=!0,r.isInjected=!0,C(),l(!0))};window.addEventListener("message",u),window.postMessage({source:"keepkey-injected",type:"INJECTION_VERIFY",requestId:d,version:i,timestamp:Date.now()},window.location.origin)});function k(f,l=[],d,c){if(!f||typeof f!="string"){c(new Error("Invalid method"));return}Array.isArray(l)||(l=[l]);try{let u=++y,A={id:u,method:f,params:l,chain:d,siteUrl:g.siteUrl,scriptSource:g.scriptSource,version:g.version,requestTime:new Date().toISOString(),referrer:document.referrer,href:window.location.href,userAgent:navigator.userAgent,platform:navigator.platform,language:navigator.language};p.set(u,{callback:c,timestamp:Date.now(),method:f});let h={source:"keepkey-injected",type:"WALLET_REQUEST",requestId:u,requestInfo:A,timestamp:Date.now()};T?window.postMessage(h,window.location.origin):b(h)}catch(u){c(u)}}window.addEventListener("message",f=>{if(f.source!==window)return;let l=f.data;if(!(!l||typeof l!="object")){if(l.source==="keepkey-content"&&l.type==="INJECTION_CONFIRMED"){T=!0,C();return}if(l.source==="keepkey-content"&&l.type==="WALLET_RESPONSE"&&l.requestId){let d=p.get(l.requestId);d&&(l.error?d.callback(l.error):d.callback(null,l.result),p.delete(l.requestId))}}});class H{events=new Map;on(l,d){this.events.has(l)||this.events.set(l,new Set),this.events.get(l).add(d)}off(l,d){var c;(c=this.events.get(l))==null||c.delete(d)}removeListener(l,d){this.off(l,d)}removeAllListeners(l){l?this.events.delete(l):this.events.clear()}emit(l,...d){var c;(c=this.events.get(l))==null||c.forEach(u=>{try{u(...d)}catch{}})}once(l,d){let c=(...u)=>{d(...u),this.off(l,c)};this.on(l,c)}}function m(f){let l=new H,d={network:"mainnet",isKeepKey:!0,isMetaMask:o.enableMetaMaskMasking,isConnected:()=>T,request:({method:c,params:u=[]})=>new Promise((A,h)=>{k(c,u,f,(w,E)=>{if(w)console.log(`[HANDOFF] dApp \u2190 KeepKey (${f}/${c}) REJECT + params=${JSON.stringify(u)} + error=`,w),h(w);else{let P=typeof E,X=P==="string"?`len=${E.length} value=${E}`:`value=${JSON.stringify(E)}`;console.log(`[HANDOFF] dApp \u2190 KeepKey (${f}/${c}) RESOLVE + params=${JSON.stringify(u)} + type=${P} ${X}`),A(E)}})}),send:(c,u,A)=>{if(c.chain||(c.chain=f),typeof A=="function"){k(c.method,c.params||u,f,(h,w)=>{h?A(h):A(null,{id:c.id,jsonrpc:"2.0",result:w})});return}else return{id:c.id,jsonrpc:"2.0",result:null}},sendAsync:(c,u,A)=>{c.chain||(c.chain=f);let h=A||u;typeof h=="function"&&k(c.method,c.params||u,f,(w,E)=>{w?h(w):h(null,{id:c.id,jsonrpc:"2.0",result:E})})},on:(c,u)=>(l.on(c,u),d),off:(c,u)=>(l.off(c,u),d),removeListener:(c,u)=>(l.removeListener(c,u),d),removeAllListeners:c=>(l.removeAllListeners(c),d),emit:(c,...u)=>(l.emit(c,...u),d),once:(c,u)=>(l.once(c,u),d),enable:()=>d.request({method:"eth_requestAccounts"}),_metamask:{isUnlocked:()=>Promise.resolve(!0)}};return f==="ethereum"&&(d.chainId="0x1",d.networkVersion="1",d.selectedAddress=null,d._handleAccountsChanged=c=>{d.selectedAddress=c[0]||null,l.emit("accountsChanged",c)},d._handleChainChanged=c=>{d.chainId=c,l.emit("chainChanged",c)},d._handleConnect=c=>{l.emit("connect",c)},d._handleDisconnect=c=>{d.selectedAddress=null,l.emit("disconnect",c)}),d}let z="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAIKADAAQAAAABAAAAIAAAAACshmLzAAADUklEQVRYCb1XTUgUYRie3bXEWhVLQaUsgwVLoUtEQjUJiZX0A0GX7BIZXurkOTSvdo2kvETHAsOshFgqOqhlRD9C7SGS1JTCsj1krU7PM+w7zMzOzuzMqi88+73v9z7vz3zzzTeziuIgmqbFgG5gBPguFOgq4CXLIMwCo0AXEJN4zxHkEuA6kAIMkUBMqMZk7so/UG8AUcnjOIKwFXgHZIgEwKFmOHOfYO4aySVjmAoc7O4R0EB7lYS5h9K1jBJ6A7CuAfXG7OopbKLXkh4dccNZ7jlsi0gAJlWLI5jBPWFsTK5AGxCRImswFqDGWanDBo6IsYbjUanFbmrFWIHxD3IsmfJsgB4y2aJuF4UrUC5GnuNtxJeEQqEoAb3LJV+F4ctlHwkZXDULv8fEKQCHB4+rCJ9ngKcIGUTVRubT027y8yR9bOM4mhKTTwNJZD4miaDXAG8dqzlMShw3YRCZRVAr7vU4g5F/D4ZBoJK2H+Em9CsfEdBoKn4K9jPAd3G9sMPqZEzpRPzAwRfWJpN9EfZSRkAOE5LD7wrw8dkpwRh55VMm27fqt4FiVBjGBTaxEm4Db8d+4BPtIOK3AdbYCPC1qh/haGIS9gHgDeBbgjTAIkXAfTRxkgaamMNwCHgB+BMk4Decq0hGkFQbka/WMyZ/EeyHNo6TuSwx3Nn8gHQVIYOkOhB5Gp4zcdbBHiDvZ2pRuzozru2euKuDOucg/KliTAjKKMa9ksBpxBLrbzRwVfifOnB4RR2g3QSH3Cfx5FRdc2KoGstroUeQKh47vnAwWvUKjsPcA/wWdBUkjRAgZdsznO8D5xLGC/Opxc3NiQeV9uIsgkNDaUoMFpNDLleAn0cTQNBjGaFW6fn2Wrky/dI6abPOl9eN9deoWhjLloCv3+bPy7w3/9kzfvjX120g1cuSdsJ47xm1CgS9AaxCErlbV6qJ22W1nq22lG75AtIHWQEeJpOYaAT6gBQQWC5XNCjc7dkkHFKWe6v3FcLfbzRAMlcC6IC6C+gGxgCectZnCRMuopVG1v+Nx04sYINlxLH4wI6W52UFhT+Q41b2Nl0qeLnwZPGQucNHrXN6ZDG94RQuO688XbwNFzvjlSuwH03wEW8H+Bf/dxrUOWdc+H8mKXtEpGpY3AAAAABJRU5ErkJggg==",J="data:image/svg+xml;utf8,"+encodeURIComponent('MM');function R(f){let l={uuid:"350670db-19fa-4704-a166-e52e178b59d4",name:"KeepKey",icon:z,rdns:"com.keepkey.client"};if(window.dispatchEvent(new CustomEvent("eip6963:announceProvider",{detail:Object.freeze({info:l,provider:f})})),o.enableMetaMaskMasking){let d={uuid:"9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",name:"MetaMask",icon:J,rdns:"io.metamask"};window.dispatchEvent(new CustomEvent("eip6963:announceProvider",{detail:Object.freeze({info:d,provider:f})}))}}async function Q(){let f=m("ethereum"),l={binance:m("binance"),bitcoin:m("bitcoin"),bitcoincash:m("bitcoincash"),dogecoin:m("dogecoin"),dash:m("dash"),ethereum:f,keplr:m("keplr"),litecoin:m("litecoin"),thorchain:m("thorchain"),mayachain:m("mayachain")},d={binance:m("binance"),bitcoin:m("bitcoin"),bitcoincash:m("bitcoincash"),dogecoin:m("dogecoin"),dash:m("dash"),ethereum:f,osmosis:m("osmosis"),cosmos:m("cosmos"),litecoin:m("litecoin"),thorchain:m("thorchain"),mayachain:m("mayachain"),ripple:m("ripple")},c=(u,A,{force:h=!1}={})=>{if(!(a[u]&&!h))try{Object.defineProperty(a,u,{value:A,writable:!1,configurable:!0})}catch{r.lastError=`Failed to mount ${u}`}};o.enableMetaMaskMasking&&c("ethereum",f,{force:!0}),o.enableXfiMasking&&c("xfi",l,{force:!0}),c("keepkey",d,{force:!0}),window.addEventListener("eip6963:requestProvider",()=>{R(f)}),R(f),setTimeout(()=>{R(f)},100);try{let u=new U(k);L(u)}catch{}if(o.enablePhantomMasking)try{let u=new O(k);Object.defineProperty(a,"solana",{value:u,writable:!1,configurable:!0})}catch{}try{let u=new N(k);a.tronLink||Object.defineProperty(a,"tronLink",{value:u.tronLink,writable:!1,configurable:!0}),a.tronWeb||Object.defineProperty(a,"tronWeb",{value:u.tronWeb,writable:!1,configurable:!0})}catch{}try{a.hive_keychain||Object.defineProperty(a,"hive_keychain",{value:G(k),writable:!1,configurable:!0})}catch{}window.addEventListener("message",u=>{var A,h,w;((A=u.data)==null?void 0:A.type)==="CHAIN_CHANGED"&&f.emit("chainChanged",(h=u.data.provider)==null?void 0:h.chainId),((w=u.data)==null?void 0:w.type)==="ACCOUNTS_CHANGED"&&f._handleAccountsChanged&&f._handleAccountsChanged(u.data.accounts||[])}),_().then(u=>{u||(r.lastError="Injection not verified")})}Q(),document.readyState==="loading"&&document.addEventListener("DOMContentLoaded",()=>{if(a.ethereum&&typeof a.dispatchEvent=="function"){let f=a.ethereum;R(f)}})})();})(); diff --git a/chrome-extension/src/background/chainConfig.ts b/chrome-extension/src/background/chainConfig.ts index 54f835c..7c52b3a 100644 --- a/chrome-extension/src/background/chainConfig.ts +++ b/chrome-extension/src/background/chainConfig.ts @@ -42,6 +42,7 @@ export const shortListSymbolToCaip: Record = { SOL: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501', TON: 'ton:-239/slip44:607', TRX: 'tron:27Lqcw/slip44:195', + HIVE: 'hive:beeab0de/slip44:1275', }; export const shortListNameToCaip: Record = { @@ -65,6 +66,7 @@ export const shortListNameToCaip: Record = { solana: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501', ton: 'ton:-239/slip44:607', tron: 'tron:27Lqcw/slip44:195', + hive: 'hive:beeab0de/slip44:1275', }; // ---- bip32ToAddressNList ---- diff --git a/chrome-extension/src/background/chains/hiveHandler.ts b/chrome-extension/src/background/chains/hiveHandler.ts new file mode 100644 index 0000000..ad045cb --- /dev/null +++ b/chrome-extension/src/background/chains/hiveHandler.ts @@ -0,0 +1,263 @@ +import { requestStorage } from '@extension/storage'; +import { v4 as uuidv4 } from 'uuid'; +import * as wallet from '../wallet'; +import { createProviderRpcError, createTimeoutError } from '../utils'; +import { requireHiveFirmware } from '../firmware'; + +const TAG = ' | hiveHandler | '; + +// Vault REST API (device signing) + Pioneer (chain reads / broadcast — the +// RPC source of truth; no direct Hive-node URLs, same policy as the vault). +const VAULT_URL = 'http://localhost:1646'; +const PIONEER_URL = 'https://api.keepkey.info'; +const HIVE_NETWORK_ID = 'hive:beeab0de'; +const HIVE_DECIMALS = 3; + +// SLIP-0048: m/48'/13'/role'/account'/0' (all hardened). Must match vault + +// firmware (hive.h HIVE_SLIP48_*) and Ledger's Hive app. +const H = 0x80000000; +const HIVE_ROLES = { owner: 0, active: 1, memo: 3, posting: 4 } as const; +function hiveAddressN(role: keyof typeof HIVE_ROLES = 'active', accountIndex = 0): number[] { + return [H + 48, H + 13, H + HIVE_ROLES[role], H + accountIndex, H + 0]; +} + +// ponytail: single-account (index 0, active role) spike — multi-account and +// posting-key ops when a dApp actually needs them. +let cachedPubkey: string | null = null; +let cachedAccountName: string | null = null; + +export function resetHiveState() { + cachedPubkey = null; + cachedAccountName = null; +} + +/** Get the Bearer API key from the SDK for direct REST calls */ +function getApiKey(): string { + const sdk = wallet.getSdk(); + const key = sdk.getClient?.()?.getApiKey?.(); + if (!key) { + throw createProviderRpcError(-32603, 'API key not available — vault may not be connected'); + } + return key; +} + +/** Derive the active-role STM public key via the vault. */ +async function getHivePublicKey(): Promise { + if (cachedPubkey) return cachedPubkey; + const resp = await fetch(`${VAULT_URL}/addresses/hive`, { + method: 'POST', + headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${getApiKey()}` }, + body: JSON.stringify({ address_n: hiveAddressN('active', 0) }), + signal: AbortSignal.timeout(30_000), + }); + if (!resp.ok) { + const text = await resp.text().catch(() => ''); + if (resp.status === 403) + throw createProviderRpcError(4100, 'Hive is disabled in the vault — enable it in Vault settings'); + if (resp.status === 501) throw createProviderRpcError(4100, `Hive requires firmware 7.15.0+: ${text}`); + throw createProviderRpcError(-32603, `Vault /addresses/hive failed (${resp.status}): ${text}`); + } + const { address } = await resp.json(); + if (!address) throw createProviderRpcError(-32603, 'Vault returned no Hive public key'); + cachedPubkey = address; + return address; +} + +/** Resolve the on-chain account name owning our active key (Pioneer lookup). */ +async function getHiveAccount(): Promise<{ name: string; pubkey: string }> { + const pubkey = await getHivePublicKey(); + if (cachedAccountName) return { name: cachedAccountName, pubkey }; + const resp = await fetch(`${PIONEER_URL}/api/v1/hive/account/${encodeURIComponent(pubkey)}`, { + signal: AbortSignal.timeout(20_000), + }); + const data = await resp.json(); + if (!data.success || data.noAccount || !data.account?.name) { + throw createProviderRpcError( + 4100, + 'No Hive account exists for this KeepKey yet — create one in the KeepKey Vault (Hive onboarding)', + ); + } + cachedAccountName = data.account.name; + return { name: data.account.name, pubkey }; +} + +/** Build the event object for the side-panel approval flow */ +function buildEvent(requestInfo: any, method: string, params: any[]) { + if (!requestInfo.id) requestInfo.id = uuidv4(); + return { + id: requestInfo.id, + networkId: HIVE_NETWORK_ID, + chain: 'hive', + href: requestInfo.href, + language: requestInfo.language, + platform: requestInfo.platform, + referrer: requestInfo.referrer, + requestTime: requestInfo.requestTime, + scriptSource: requestInfo.scriptSource, + siteUrl: requestInfo.siteUrl, + userAgent: requestInfo.userAgent, + injectScriptVersion: requestInfo.version, + requestInfo, + type: method, + request: params, + status: 'request', + timestamp: new Date().toISOString(), + }; +} + +/** Save event to storage + open side panel + wait for user approval */ +async function requestUserApproval( + event: any, + requestInfo: any, + method: string, + params: any[], + requireApproval: (networkId: string, requestInfo: any, chain: any, method: string, params: any) => Promise, +) { + await requestStorage.addEvent(event); + chrome.runtime.sendMessage({ action: 'TRANSACTION_CONTEXT_UPDATED', id: event.id }).catch(() => {}); + const approval = await requireApproval(HIVE_NETWORK_ID, requestInfo, 'hive', method, params); + if (!approval?.success) { + throw createProviderRpcError(4001, 'User rejected the request'); + } +} + +/** + * Full transfer flow: build (Pioneer tx-params + account), approve + * (side panel), sign (vault → device), broadcast (Pioneer). + * + * params[0] mirrors Hive Keychain's requestTransfer: + * { to, amount: "1.000", memo?, currency: 'HIVE' } + */ +async function hiveTransfer( + params: any[], + requestInfo: any, + requireApproval: (networkId: string, requestInfo: any, chain: any, method: string, params: any) => Promise, +) { + const { to, amount, memo, currency } = params[0] || {}; + if (!to || !amount) throw createProviderRpcError(-32602, 'Hive transfer requires { to, amount }'); + // ponytail: HIVE only — HBD needs Pioneer broadcast support first + if (currency && currency !== 'HIVE') { + throw createProviderRpcError(4200, `Only HIVE transfers are supported (got ${currency})`); + } + const parsed = parseFloat(amount); + if (!isFinite(parsed) || parsed <= 0) throw createProviderRpcError(-32602, `Invalid amount: ${amount}`); + const amountBase = Math.round(parsed * 10 ** HIVE_DECIMALS); + const amountStr = (amountBase / 10 ** HIVE_DECIMALS).toFixed(HIVE_DECIMALS) + ' HIVE'; + + await requireHiveFirmware('Hive transfer'); + const from = await getHiveAccount(); + + const txParamsResp = await fetch(`${PIONEER_URL}/api/v1/hive/tx-params`, { + signal: AbortSignal.timeout(20_000), + }).then(r => r.json()); + if (!txParamsResp.success) { + throw createProviderRpcError(-32603, `Hive tx-params failed: ${txParamsResp.error || 'unknown'}`); + } + + // Approval first — the user sees resolved from/to/amount before the device round-trip + const event = buildEvent(requestInfo, 'transfer', params); + (event as any).unsignedTx = { + from: from.name, + to, + amount: amountStr, + memo: memo || '', + refBlockNum: txParamsResp.refBlockNum, + expiration: txParamsResp.expirationIso, + }; + await requestUserApproval(event, requestInfo, 'transfer', params, requireApproval); + + // Sign on-device via the vault (serialization happens in firmware) + let signResp: Response; + try { + signResp = await fetch(`${VAULT_URL}/hive/sign-transfer`, { + method: 'POST', + headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${getApiKey()}` }, + body: JSON.stringify({ + address_n: hiveAddressN('active', 0), + ref_block_num: txParamsResp.refBlockNum, + ref_block_prefix: txParamsResp.refBlockPrefix, + expiration: txParamsResp.expirationUnix, + from: from.name, + to, + amount: amountBase, + asset_symbol: 'HIVE', + memo: memo || undefined, + chain_id: txParamsResp.chainId, + }), + // Wait on the user holding the device button + signal: AbortSignal.timeout(300_000), + }); + } catch (e: any) { + if (e.name === 'TimeoutError' || e.name === 'AbortError') throw createTimeoutError('Vault signing timed out'); + throw createProviderRpcError(-32603, `Vault connection failed: ${e.message}`); + } + if (!signResp.ok) { + const text = await signResp.text().catch(() => ''); + throw createProviderRpcError(-32603, `Vault Hive sign failed (${signResp.status}): ${text}`); + } + const { signature } = await signResp.json(); + if (!signature) throw createProviderRpcError(-32603, 'Vault returned no Hive signature'); + + // Broadcast via Pioneer + const bResp = await fetch(`${PIONEER_URL}/api/v1/hive/broadcast`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + ref_block_num: txParamsResp.refBlockNum, + ref_block_prefix: txParamsResp.refBlockPrefix, + expiration: txParamsResp.expirationIso, + from: from.name, + to, + amount: amountStr, + memo: memo || '', + signature: signature.replace(/^0x/, ''), + }), + signal: AbortSignal.timeout(30_000), + }); + const bData = await bResp.json().catch(() => ({})); + if (!bData.success || !bData.txid) { + throw createProviderRpcError( + -32603, + `Hive broadcast failed: ${bData.error || JSON.stringify(bData).slice(0, 200)}`, + ); + } + + await requestStorage.updateEventById(event.id, { ...event, txid: bData.txid, status: 'broadcasted' } as any); + chrome.runtime + .sendMessage({ + action: 'transaction_complete', + eventId: requestInfo.id, + txHash: bData.txid, + explorerTxLink: 'https://hiveblocks.com/tx/', + networkId: HIVE_NETWORK_ID, + }) + .catch(() => {}); + + return { txid: bData.txid }; +} + +export const handleHiveRequest = async ( + method: string, + params: any[], + requestInfo: any, + _ADDRESS: string, + _KEEPKEY_WALLET: any, + requireApproval: (networkId: string, requestInfo: any, chain: any, method: string, params: any) => Promise, +): Promise => { + console.log(TAG, 'method:', method); + switch (method) { + case 'hive_handshake': { + // Keychain-compat presence check — succeed without touching the device + return true; + } + case 'hive_getAccount': { + const account = await getHiveAccount(); + return account; + } + case 'hive_transfer': { + return await hiveTransfer(params, requestInfo, requireApproval); + } + default: + throw createProviderRpcError(4200, `Hive method ${method} not supported`); + } +}; diff --git a/chrome-extension/src/background/firmware.ts b/chrome-extension/src/background/firmware.ts index a2bdb08..c947bf3 100644 --- a/chrome-extension/src/background/firmware.ts +++ b/chrome-extension/src/background/firmware.ts @@ -17,6 +17,9 @@ import * as wallet from './wallet'; import { createProviderRpcError } from './utils'; const REQUIRED = { major: 7, minor: 14, patch: 1 } as const; +// Hive (SLIP-0048 keys + Graphene transfer signing) shipped in 7.15.0; +// anything older returns Failure_UnknownMessage for the Hive message types. +const REQUIRED_HIVE = { major: 7, minor: 15, patch: 0 } as const; interface FirmwareVersion { major: number; @@ -34,10 +37,10 @@ function parseVersion(features: any): FirmwareVersion | null { return { major, minor, patch }; } -function meetsMin(v: FirmwareVersion): boolean { - if (v.major !== REQUIRED.major) return v.major > REQUIRED.major; - if (v.minor !== REQUIRED.minor) return v.minor > REQUIRED.minor; - return v.patch >= REQUIRED.patch; +function meetsMin(v: FirmwareVersion, min: FirmwareVersion = REQUIRED): boolean { + if (v.major !== min.major) return v.major > min.major; + if (v.minor !== min.minor) return v.minor > min.minor; + return v.patch >= min.patch; } async function readVersion(): Promise { @@ -68,3 +71,23 @@ export async function requireMessageSigningFirmware(label: string): Promise { + const v = await readVersion(); + if (!v) { + throw createProviderRpcError( + -32603, + `${label} requires firmware 7.15.0 — could not read device firmware version. Plug in your KeepKey and try again.`, + ); + } + if (!meetsMin(v, REQUIRED_HIVE)) { + throw createProviderRpcError( + 4200, + `${label} requires firmware 7.15.0 or later. Your KeepKey is on ${v.major}.${v.minor}.${v.patch}. Update via the KeepKey Vault desktop app and retry.`, + ); + } +} diff --git a/chrome-extension/src/background/methods.ts b/chrome-extension/src/background/methods.ts index 05e24d3..1d2fac1 100644 --- a/chrome-extension/src/background/methods.ts +++ b/chrome-extension/src/background/methods.ts @@ -16,6 +16,7 @@ import { handleRippleRequest } from './chains/rippleHandler'; import { handleSolanaRequest } from './chains/solanaHandler'; import { handleTronRequest } from './chains/tronHandler'; import { handleTonRequest } from './chains/tonHandler'; +import { handleHiveRequest } from './chains/hiveHandler'; import type { ProviderRpcError } from './utils'; import { createProviderRpcError, formatUserError } from './utils'; import { openSidePanel, setApprovalBadge } from './popup'; @@ -230,6 +231,10 @@ export const handleWalletRequest = async ( return await handleTonRequest(method, params, requestInfo, ADDRESS, __KEEPKEY_WALLET, requireApproval); break; } + case 'hive': { + return await handleHiveRequest(method, params, requestInfo, ADDRESS, __KEEPKEY_WALLET, requireApproval); + break; + } default: { console.log(tag, `Chain ${chain} not supported`); throw createProviderRpcError(4200, `Chain ${chain} not supported`); diff --git a/chrome-extension/src/injected/hive-provider.ts b/chrome-extension/src/injected/hive-provider.ts new file mode 100644 index 0000000..5130c40 --- /dev/null +++ b/chrome-extension/src/injected/hive-provider.ts @@ -0,0 +1,126 @@ +/** + * KeepKey Hive provider — injects a `window.hive_keychain` shim mirroring + * Hive Keychain's API surface (the de-facto standard Hive dApps call), + * routing signing to the extension background (and ultimately the KeepKey + * device via the vault REST API). Reads/broadcasts go through Pioneer. + * + * Scope of this MVP (firmware 7.15.0+): + * - requestHandshake — presence check + * - requestTransfer — native HIVE transfer (device-serialized + signed) + * + * Out of scope (returned as keychain-style failures so dApps degrade + * gracefully): posting-key ops (vote/post/custom_json), requestSignBuffer + * (firmware has no Hive message-sign message type), requestBroadcast + * (generic ops need per-op firmware serialization), encode/decode (memo-key + * crypto is on-device only). + */ + +import type { ChainType } from './types'; + +type WalletRequestFn = ( + method: string, + params: any[], + chain: ChainType, + callback: (error: any, result?: any) => void, +) => void; + +interface KeychainResponse { + success: boolean; + error: string | null; + message: string | null; + request_id: number; + result: any; + data: any; +} + +type KeychainCallback = (response: KeychainResponse) => void; + +export function createHiveKeychainShim(walletRequest: WalletRequestFn) { + let currentId = 1; + + function respond(callback: KeychainCallback | undefined, requestId: number, data: any, error: any, result?: any) { + if (typeof callback !== 'function') return; + const message = error ? error?.message || String(error) : null; + callback({ + success: !error, + error: error ? (error?.code === 4001 ? 'user_cancel' : message) : null, + message, + request_id: requestId, + result: error ? null : (result ?? null), + data, + }); + } + + function dispatch(method: string, data: any, callback?: KeychainCallback) { + const requestId = currentId++; + walletRequest(method, [data], 'hive', (error, result) => { + respond(callback, requestId, data, error, result); + }); + } + + function notSupported(type: string, data: any, callback?: KeychainCallback) { + respond(callback, currentId++, data, { + message: `KeepKey does not support Keychain request "${type}" yet`, + }); + } + + return { + current_id: 1, + + /** dApp presence check — Keychain calls back with no arguments. */ + requestHandshake: function (callback: () => void) { + dispatch('hive_handshake', {}, () => { + if (typeof callback === 'function') callback(); + }); + }, + + /** Native HIVE transfer, mirrors Keychain's signature exactly. */ + requestTransfer: function ( + account: string, + to: string, + amount: string, + memo: string, + currency: string, + callback: KeychainCallback, + enforce = false, + rpc?: string, + ) { + const data = { type: 'transfer', username: account, to, amount, memo, enforce, currency, rpc }; + dispatch('hive_transfer', data, callback); + }, + + // ── Unsupported Keychain surface — fail fast, keychain-style ────── + requestSignBuffer: function (_a: string, _m: string, _k: string, callback: KeychainCallback) { + notSupported('signBuffer', { type: 'signBuffer' }, callback); + }, + requestBroadcast: function (_a: string, _ops: any[], _k: string, callback: KeychainCallback) { + notSupported('broadcast', { type: 'broadcast' }, callback); + }, + requestSignTx: function (_a: string, _tx: any, _k: string, callback: KeychainCallback) { + notSupported('signTx', { type: 'signTx' }, callback); + }, + requestVote: function (_a: string, _p: string, _au: string, _w: number, callback: KeychainCallback) { + notSupported('vote', { type: 'vote' }, callback); + }, + requestCustomJson: function ( + _a: string, + _id: string, + _k: string, + _j: string, + _d: string, + callback: KeychainCallback, + ) { + notSupported('custom_json', { type: 'custom' }, callback); + }, + requestPost: function (...args: any[]) { + const callback = args.find(a => typeof a === 'function'); + notSupported('post', { type: 'post' }, callback); + }, + requestEncodeMessage: function (_u: string, _r: string, _m: string, _k: string, callback: KeychainCallback) { + notSupported('encode', { type: 'encode' }, callback); + }, + requestVerifyKey: function (_a: string, _m: string, _k: string, callback: KeychainCallback) { + notSupported('decode', { type: 'decode' }, callback); + }, + }; +} diff --git a/chrome-extension/src/injected/injected.ts b/chrome-extension/src/injected/injected.ts index ce1dc81..5023727 100644 --- a/chrome-extension/src/injected/injected.ts +++ b/chrome-extension/src/injected/injected.ts @@ -12,6 +12,7 @@ import { KeepKeySolanaWallet } from './solana-wallet-standard'; import { registerSolanaWallet } from './solana-wallet-register'; import { KeepKeySolanaProvider } from './solana-provider'; import { KeepKeyTronProvider } from './tron-provider'; +import { createHiveKeychainShim } from './hive-provider'; (function () { const VERSION = '2.1.0'; @@ -663,6 +664,20 @@ import { KeepKeyTronProvider } from './tron-provider'; // swallow; Tron registration is best-effort } + // Hive Keychain shim — window.hive_keychain is the de-facto discovery + // API for Hive dApps. Mount only if the real Keychain isn't installed. + try { + if (!(kWindow as any).hive_keychain) { + Object.defineProperty(kWindow, 'hive_keychain', { + value: createHiveKeychainShim(walletRequest), + writable: false, + configurable: true, + }); + } + } catch (_e) { + // swallow; Hive registration is best-effort + } + // Handle chain changes and other events window.addEventListener('message', (event: MessageEvent) => { if (event.data?.type === 'CHAIN_CHANGED') { diff --git a/chrome-extension/src/injected/types.ts b/chrome-extension/src/injected/types.ts index fd3f5e2..65b39e6 100644 --- a/chrome-extension/src/injected/types.ts +++ b/chrome-extension/src/injected/types.ts @@ -63,7 +63,8 @@ export type ChainType = | 'ripple' | 'keplr' | 'solana' - | 'tron'; + | 'tron' + | 'hive'; export interface WalletProvider { network: string; diff --git a/packages/shared/lib/utils/chainConfig.ts b/packages/shared/lib/utils/chainConfig.ts index 0928e7e..eb09ca6 100644 --- a/packages/shared/lib/utils/chainConfig.ts +++ b/packages/shared/lib/utils/chainConfig.ts @@ -26,6 +26,7 @@ export const Chain = { THORChain: 'THOR', Ton: 'TON', Tron: 'TRX', + Hive: 'HIVE', } as const; export type ChainValue = (typeof Chain)[keyof typeof Chain]; @@ -54,6 +55,7 @@ export const ChainToNetworkId: Record = { // Canonical CAIP-2 networkIds from keepkey-vault-v11/shared/chains.ts TON: 'ton:-239', TRX: 'tron:27Lqcw', + HIVE: 'hive:beeab0de', }; // ---- NetworkIdToChain (reverse map) ---- @@ -86,6 +88,7 @@ export const COIN_MAP_LONG: Record = { SOL: 'solana', TON: 'ton', TRX: 'tron', + HIVE: 'hive', }; // ---- availableChainsByWallet (replaces @pioneer-platform/pioneer-caip's version) ---- @@ -139,6 +142,7 @@ const NETWORK_SLIP44: Record = { 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp': 'slip44:501', 'tron:27Lqcw': 'slip44:195', 'ton:-239': 'slip44:607', + 'hive:beeab0de': 'slip44:1275', }; export function networkIdToIcon(networkId: string): string { diff --git a/pages/side-panel/src/approval/Transaction.tsx b/pages/side-panel/src/approval/Transaction.tsx index 0d83f2a..e48791b 100644 --- a/pages/side-panel/src/approval/Transaction.tsx +++ b/pages/side-panel/src/approval/Transaction.tsx @@ -234,6 +234,9 @@ const Transaction = ({ case 'tron': setTransactionType('other'); break; + case 'hive': + setTransactionType('other'); + break; default: setTransactionType('unknown'); } From 9d0db948cc78a64d08f549bbd73b3d1b4f604415 Mon Sep 17 00:00:00 2001 From: highlander Date: Tue, 14 Jul 2026 22:33:25 -0300 Subject: [PATCH 2/3] =?UTF-8?q?fix(hive):=20address=20review=20=E2=80=94?= =?UTF-8?q?=20keychain=20coexistence,=20memo/amount/account=20safety,=20ap?= =?UTF-8?q?proval=20render,=20state=20reset,=20full=20stub=20surface?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - injected: mount window.hive_keychain as a plain writable property so the real Hive Keychain (document_idle) can replace our shim; stub all 29 remaining Keychain request* methods so none throw TypeError - handler: reject encrypted ('#') memos instead of leaking plaintext on-chain; exact decimal-string amount validation (no parseFloat coercion or rounding); currency now required; reject username mismatch (enforce semantics — we control one account); unsignedTx.payment shaped for RequestDetailsCard (destination/amount/decimals/symbol + caip) - lifecycle: resetHiveState() wired into all three device reset sites Co-Authored-By: Claude Fable 5 --- chrome-extension/public/injected.js | 30 +++---- .../src/background/chains/hiveHandler.ts | 52 ++++++++++-- chrome-extension/src/background/index.ts | 4 + .../src/injected/hive-provider.ts | 79 +++++++++++-------- chrome-extension/src/injected/injected.ts | 11 ++- 5 files changed, 113 insertions(+), 63 deletions(-) diff --git a/chrome-extension/public/injected.js b/chrome-extension/public/injected.js index 09de9dc..f26da71 100644 --- a/chrome-extension/public/injected.js +++ b/chrome-extension/public/injected.js @@ -1,19 +1,19 @@ -"use strict";(()=>{var V="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";function x(i){let e=[0];for(let n of i){let s=V.indexOf(n);if(s===-1)throw new Error("Invalid base58 character");let t=s;for(let a=0;a>=8;for(;t>0;)e.push(t&255),t>>=8}for(let n of i){if(n!=="1")break;e.push(0)}return new Uint8Array(e.reverse())}var U=class i{#r;#e=[];#n=null;#a=new Set;version="1.0.0";name="KeepKey";icon="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAIKADAAQAAAABAAAAIAAAAACshmLzAAADUklEQVRYCb1XTUgUYRie3bXEWhVLQaUsgwVLoUtEQjUJiZX0A0GX7BIZXurkOTSvdo2kvETHAsOshFgqOqhlRD9C7SGS1JTCsj1krU7PM+w7zMzOzuzMqi88+73v9z7vz3zzzTeziuIgmqbFgG5gBPguFOgq4CXLIMwCo0AXEJN4zxHkEuA6kAIMkUBMqMZk7so/UG8AUcnjOIKwFXgHZIgEwKFmOHOfYO4aySVjmAoc7O4R0EB7lYS5h9K1jBJ6A7CuAfXG7OopbKLXkh4dccNZ7jlsi0gAJlWLI5jBPWFsTK5AGxCRImswFqDGWanDBo6IsYbjUanFbmrFWIHxD3IsmfJsgB4y2aJuF4UrUC5GnuNtxJeEQqEoAb3LJV+F4ctlHwkZXDULv8fEKQCHB4+rCJ9ngKcIGUTVRubT027y8yR9bOM4mhKTTwNJZD4miaDXAG8dqzlMShw3YRCZRVAr7vU4g5F/D4ZBoJK2H+Em9CsfEdBoKn4K9jPAd3G9sMPqZEzpRPzAwRfWJpN9EfZSRkAOE5LD7wrw8dkpwRh55VMm27fqt4FiVBjGBTaxEm4Db8d+4BPtIOK3AdbYCPC1qh/haGIS9gHgDeBbgjTAIkXAfTRxkgaamMNwCHgB+BMk4Decq0hGkFQbka/WMyZ/EeyHNo6TuSwx3Nn8gHQVIYOkOhB5Gp4zcdbBHiDvZ2pRuzozru2euKuDOucg/KliTAjKKMa9ksBpxBLrbzRwVfifOnB4RR2g3QSH3Cfx5FRdc2KoGstroUeQKh47vnAwWvUKjsPcA/wWdBUkjRAgZdsznO8D5xLGC/Opxc3NiQeV9uIsgkNDaUoMFpNDLleAn0cTQNBjGaFW6fn2Wrky/dI6abPOl9eN9deoWhjLloCv3+bPy7w3/9kzfvjX120g1cuSdsJ47xm1CgS9AaxCErlbV6qJ02W1nq22lG75AtIHWQEeJpOYaAT6gBQQWC5XNCjc7dkkHFKWe6v3FcLfbzRAMlcC6IC6C+gGxgCectZnCRMuopVG1v+Nx04sYINlxLH4wI6W52UFhT+Q41b2Nl0qeLnwZPGQucNHrXN6ZDG94RQuO688XbwNFzvjlSuwH03wEW8H+Bf/dxrUOWdc+H8mKXtEpGpY3AAAAABJRU5ErkJggg==";chains=["solana:mainnet"];static ACCOUNT_FEATURES=["solana:signTransaction","solana:signAndSendTransaction","solana:signMessage"];get accounts(){return this.#e}features={"standard:connect":{version:"1.0.0",connect:async()=>{if(this.#e.length>0)return{accounts:this.#e};let e=await this.#s("solana_getAccounts",[]).catch(()=>null);if(!Array.isArray(e)||e.length===0){let n=this.#n||await this.#s("solana_connect",[]);e=n?[{address:n}]:[]}return this.#o(e),{accounts:this.#e}}},"standard:disconnect":{version:"1.0.0",disconnect:async()=>{await this.#s("solana_disconnect",[]).catch(()=>{}),this.#e=[];try{localStorage.removeItem("keepkey-solana")}catch{}this.#t()}},"standard:events":{version:"1.0.0",on:(e,n)=>(e==="change"&&this.#a.add(n),()=>{this.#a.delete(n)})},"solana:signMessage":{version:"1.0.0",signMessage:async(...e)=>{let n=[];for(let{message:s,account:t}of e){let a=await this.#s("solana_signMessage",[Array.from(s),{accountAddress:t==null?void 0:t.address}]);n.push({signedMessage:s,signature:new Uint8Array(a)})}return n}},"solana:signTransaction":{version:"1.0.0",supportedTransactionVersions:new Set(["legacy",0]),signTransaction:async(...e)=>{let n=[];for(let{transaction:s,account:t}of e){let a=await this.#s("solana_signTransaction",[Array.from(s),{accountAddress:t==null?void 0:t.address}]);n.push({signedTransaction:new Uint8Array(a)})}return n}},"solana:signAndSendTransaction":{version:"1.0.0",supportedTransactionVersions:new Set(["legacy",0]),signAndSendTransaction:async(...e)=>{let n=[];for(let{transaction:s,account:t}of e){let a=await this.#s("solana_signAndSendTransaction",[Array.from(s),{accountAddress:t==null?void 0:t.address}]);n.push({signature:x(a)})}return n}},"keepkey:signOffchainMessage":{version:"1.0.0",signOffchainMessage:async e=>{let n=typeof e.message=="string"?Array.from(new TextEncoder().encode(e.message)):Array.from(e.message);return await this.#s("solana_signOffchainMessage",[{message:n,version:e.version,messageFormat:e.messageFormat}])}},"solana:signIn":{version:"1.0.0",signIn:async(...e)=>{var s;let n=[];for(let t of e){if(this.#e.length===0){let k=this.#n||await this.#s("solana_connect",[]);k&&this.#c(k)}let a=(t==null?void 0:t.address)&&this.#e.find(k=>k.address===t.address)||this.#e[0];if(!a)throw new Error("Not connected");let r=(t==null?void 0:t.domain)||location.host,o=(t==null?void 0:t.address)||a.address,g=(t==null?void 0:t.uri)||location.href,y=(t==null?void 0:t.version)||"1",p=(t==null?void 0:t.chainId)||"mainnet",v=(t==null?void 0:t.nonce)||Math.random().toString(36).substring(2),T=(t==null?void 0:t.issuedAt)||new Date().toISOString(),I=(t==null?void 0:t.statement)||"",b=`${r} wants you to sign in with your Solana account: -${o}`;if(I&&(b+=` +"use strict";(()=>{var V="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";function L(r){let e=[0];for(let t of r){let s=V.indexOf(t);if(s===-1)throw new Error("Invalid base58 character");let n=s;for(let a=0;a>=8;for(;n>0;)e.push(n&255),n>>=8}for(let t of r){if(t!=="1")break;e.push(0)}return new Uint8Array(e.reverse())}var U=class r{#a;#e=[];#t=null;#r=new Set;version="1.0.0";name="KeepKey";icon="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAIKADAAQAAAABAAAAIAAAAACshmLzAAADUklEQVRYCb1XTUgUYRie3bXEWhVLQaUsgwVLoUtEQjUJiZX0A0GX7BIZXurkOTSvdo2kvETHAsOshFgqOqhlRD9C7SGS1JTCsj1krU7PM+w7zMzOzuzMqi88+73v9z7vz3zzzTeziuIgmqbFgG5gBPguFOgq4CXLIMwCo0AXEJN4zxHkEuA6kAIMkUBMqMZk7so/UG8AUcnjOIKwFXgHZIgEwKFmOHOfYO4aySVjmAoc7O4R0EB7lYS5h9K1jBJ6A7CuAfXG7OopbKLXkh4dccNZ7jlsi0gAJlWLI5jBPWFsTK5AGxCRImswFqDGWanDBo6IsYbjUanFbmrFWIHxD3IsmfJsgB4y2aJuF4UrUC5GnuNtxJeEQqEoAb3LJV+F4ctlHwkZXDULv8fEKQCHB4+rCJ9ngKcIGUTVRubT027y8yR9bOM4mhKTTwNJZD4miaDXAG8dqzlMShw3YRCZRVAr7vU4g5F/D4ZBoJK2H+Em9CsfEdBoKn4K9jPAd3G9sMPqZEzpRPzAwRfWJpN9EfZSRkAOE5LD7wrw8dkpwRh55VMm27fqt4FiVBjGBTaxEm4Db8d+4BPtIOK3AdbYCPC1qh/haGIS9gHgDeBbgjTAIkXAfTRxkgaamMNwCHgB+BMk4Decq0hGkFQbka/WMyZ/EeyHNo6TuSwx3Nn8gHQVIYOkOhB5Gp4zcdbBHiDvZ2pRuzozru2euKuDOucg/KliTAjKKMa9ksBpxBLrbzRwVfifOnB4RR2g3QSH3Cfx5FRdc2KoGstroUeQKh47vnAwWvUKjsPcA/wWdBUkjRAgZdsznO8D5xLGC/Opxc3NiQeV9uIsgkNDaUoMFpNDLleAn0cTQNBjGaFW6fn2Wrky/dI6abPOl9eN9deoWhjLloCv3+bPy7w3/9kzfvjX120g1cuSdsJ47xm1CgS9AaxCErlbV6qJ02W1nq22lG75AtIHWQEeJpOYaAT6gBQQWC5XNCjc7dkkHFKWe6v3FcLfbzRAMlcC6IC6C+gGxgCectZnCRMuopVG1v+Nx04sYINlxLH4wI6W52UFhT+Q41b2Nl0qeLnwZPGQucNHrXN6ZDG94RQuO688XbwNFzvjlSuwH03wEW8H+Bf/dxrUOWdc+H8mKXtEpGpY3AAAAABJRU5ErkJggg==";chains=["solana:mainnet"];static ACCOUNT_FEATURES=["solana:signTransaction","solana:signAndSendTransaction","solana:signMessage"];get accounts(){return this.#e}features={"standard:connect":{version:"1.0.0",connect:async()=>{if(this.#e.length>0)return{accounts:this.#e};let e=await this.#s("solana_getAccounts",[]).catch(()=>null);if(!Array.isArray(e)||e.length===0){let t=this.#t||await this.#s("solana_connect",[]);e=t?[{address:t}]:[]}return this.#o(e),{accounts:this.#e}}},"standard:disconnect":{version:"1.0.0",disconnect:async()=>{await this.#s("solana_disconnect",[]).catch(()=>{}),this.#e=[];try{localStorage.removeItem("keepkey-solana")}catch{}this.#n()}},"standard:events":{version:"1.0.0",on:(e,t)=>(e==="change"&&this.#r.add(t),()=>{this.#r.delete(t)})},"solana:signMessage":{version:"1.0.0",signMessage:async(...e)=>{let t=[];for(let{message:s,account:n}of e){let a=await this.#s("solana_signMessage",[Array.from(s),{accountAddress:n==null?void 0:n.address}]);t.push({signedMessage:s,signature:new Uint8Array(a)})}return t}},"solana:signTransaction":{version:"1.0.0",supportedTransactionVersions:new Set(["legacy",0]),signTransaction:async(...e)=>{let t=[];for(let{transaction:s,account:n}of e){let a=await this.#s("solana_signTransaction",[Array.from(s),{accountAddress:n==null?void 0:n.address}]);t.push({signedTransaction:new Uint8Array(a)})}return t}},"solana:signAndSendTransaction":{version:"1.0.0",supportedTransactionVersions:new Set(["legacy",0]),signAndSendTransaction:async(...e)=>{let t=[];for(let{transaction:s,account:n}of e){let a=await this.#s("solana_signAndSendTransaction",[Array.from(s),{accountAddress:n==null?void 0:n.address}]);t.push({signature:L(a)})}return t}},"keepkey:signOffchainMessage":{version:"1.0.0",signOffchainMessage:async e=>{let t=typeof e.message=="string"?Array.from(new TextEncoder().encode(e.message)):Array.from(e.message);return await this.#s("solana_signOffchainMessage",[{message:t,version:e.version,messageFormat:e.messageFormat}])}},"solana:signIn":{version:"1.0.0",signIn:async(...e)=>{var s;let t=[];for(let n of e){if(this.#e.length===0){let E=this.#t||await this.#s("solana_connect",[]);E&&this.#c(E)}let a=(n==null?void 0:n.address)&&this.#e.find(E=>E.address===n.address)||this.#e[0];if(!a)throw new Error("Not connected");let i=(n==null?void 0:n.domain)||location.host,o=(n==null?void 0:n.address)||a.address,f=(n==null?void 0:n.uri)||location.href,A=(n==null?void 0:n.version)||"1",y=(n==null?void 0:n.chainId)||"mainnet",w=(n==null?void 0:n.nonce)||Math.random().toString(36).substring(2),v=(n==null?void 0:n.issuedAt)||new Date().toISOString(),C=(n==null?void 0:n.statement)||"",b=`${i} wants you to sign in with your Solana account: +${o}`;if(C&&(b+=` -${I}`),b+=` +${C}`),b+=` -URI: ${g}`,b+=` -Version: ${y}`,b+=` -Chain ID: ${p}`,b+=` -Nonce: ${v}`,b+=` -Issued At: ${T}`,t!=null&&t.expirationTime&&(b+=` -Expiration Time: ${t.expirationTime}`),t!=null&&t.notBefore&&(b+=` -Not Before: ${t.notBefore}`),t!=null&&t.requestId&&(b+=` -Request ID: ${t.requestId}`),(s=t==null?void 0:t.resources)!=null&&s.length){b+=` -Resources:`;for(let k of t.resources)b+=` -- ${k}`}let C=new TextEncoder().encode(b),_=await this.#s("solana_signMessage",[Array.from(C),{accountAddress:a.address}]);n.push({account:a,signedMessage:C,signature:new Uint8Array(_)})}return n}}};constructor(e){this.#r=e;try{let n=localStorage.getItem("keepkey-solana");if(n){let{address:s}=JSON.parse(n);s&&typeof s=="string"&&(this.#n=s)}}catch{}this.#l()}#i(e){return{address:e,publicKey:x(e),chains:["solana:mainnet"],features:[...i.ACCOUNT_FEATURES]}}#c(e){this.#e=[this.#i(e)];try{localStorage.setItem("keepkey-solana",JSON.stringify({address:e}))}catch{}this.#t()}#o(e){var n;this.#e=e.filter(s=>s==null?void 0:s.address).map(s=>this.#i(s.address));try{let s=(n=this.#e[0])==null?void 0:n.address;s&&localStorage.setItem("keepkey-solana",JSON.stringify({address:s}))}catch{}this.#t()}async#l(){try{let e=await this.#s("solana_connect",[]);if(e&&typeof e=="string"){this.#n=e;try{localStorage.setItem("keepkey-solana",JSON.stringify({address:e}))}catch{}}}catch{}}#t(){let e=this.#e,n=this.features;this.#a.forEach(s=>{try{s({accounts:e,features:n})}catch{}})}#s(e,n){return new Promise((s,t)=>{this.#r(e,n,"solana",(a,r)=>{a?t(a):s(r)})})}};function L(i){let e=({register:s})=>{s(i)};try{let s=window.navigator;s.wallets||(s.wallets=[]),Array.isArray(s.wallets)?s.wallets.push(e):typeof s.wallets.register=="function"&&s.wallets.register(i)}catch{}let n=()=>{try{window.dispatchEvent(new CustomEvent("wallet-standard:register-wallet",{detail:e}))}catch{}};n(),setTimeout(n,100),setTimeout(n,1e3),window.addEventListener("wallet-standard:app-ready",s=>{let t=s.detail;try{t&&typeof t.register=="function"?e(t):typeof t=="function"&&t(e)}catch{}})}var Y="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";function Z(i){let e=[0];for(let n of i){let s=Y.indexOf(n);if(s===-1)throw new Error("Invalid base58 character");let t=s;for(let a=0;a>=8;for(;t>0;)e.push(t&255),t>>=8}for(let n of i){if(n!=="1")break;e.push(0)}return new Uint8Array(e.reverse())}function $(i){let e=Z(i);return{toString:()=>i,toBase58:()=>i,toBytes:()=>e,toBuffer:()=>e,equals:n=>{var s,t;try{return((s=n==null?void 0:n.toBase58)==null?void 0:s.call(n))===i||((t=n==null?void 0:n.toString)==null?void 0:t.call(n))===i}catch{return!1}}}}function j(i){return i!=null&&typeof i=="object"&&"version"in i&&i.message!=null}function F(i){if(!i||typeof i.serialize!="function")throw new Error("Unsupported transaction: missing serialize()");return j(i)?new Uint8Array(i.serialize()):new Uint8Array(i.serialize({requireAllSignatures:!1,verifySignatures:!1}))}function ee(i){let e=i[0];if(!e)throw new Error("Signed transaction has no signatures");if(e&128)throw new Error("Unexpected multi-byte signature count");return i.slice(1,65)}function ne(i){var n,s;if(j(i)){let t=(n=i.message)==null?void 0:n.staticAccountKeys;if(t&&t.length)return t[0];throw new Error("Versioned transaction missing account keys")}if(i.feePayer)return i.feePayer;let e=(s=i.signatures)==null?void 0:s[0];if(e!=null&&e.publicKey)return e.publicKey;throw new Error("Legacy transaction missing fee payer")}var O=class{#r;#e=new Map;#n=null;#a=null;isPhantom=!0;isKeepKey=!0;constructor(e){this.#r=e;try{let n=localStorage.getItem("keepkey-solana");if(n){let{address:s}=JSON.parse(n);s&&typeof s=="string"&&(this.#a=s)}}catch{}this.#l()}get publicKey(){return this.#n}get isConnected(){return this.#n!==null}async connect(e){if(this.#n)return{publicKey:this.#n};let n=this.#a||await this.#t("solana_connect",[]);if(!n)throw new Error("Failed to connect to KeepKey");return this.#c(n),{publicKey:this.#n}}async disconnect(){await this.#t("solana_disconnect",[]).catch(()=>{}),this.#n=null;try{localStorage.removeItem("keepkey-solana")}catch{}this.#i("disconnect")}async signMessage(e,n){await this.#o();let s=await this.#t("solana_signMessage",[Array.from(e)]);return{signature:new Uint8Array(s),publicKey:this.#n}}async signTransaction(e){await this.#o();let n=F(e),s=await this.#t("solana_signTransaction",[Array.from(n)]),t=new Uint8Array(s),a=ee(t);return e.addSignature(ne(e),a),e}async signAllTransactions(e){let n=[];for(let s of e)n.push(await this.signTransaction(s));return n}async signAndSendTransaction(e,n){await this.#o();let s=F(e);return{signature:await this.#t("solana_signAndSendTransaction",[Array.from(s)]),publicKey:this.#n}}async request({method:e,params:n}){var t,a,r,o,g,y;let s=Array.isArray(n)?n:n!=null?[n]:[];switch(e){case"connect":return this.connect(s[0]);case"disconnect":return this.disconnect();case"signTransaction":return this.signTransaction(((t=s[0])==null?void 0:t.transaction)??s[0]);case"signAllTransactions":return this.signAllTransactions(((a=s[0])==null?void 0:a.transactions)??s[0]);case"signAndSendTransaction":return this.signAndSendTransaction(((r=s[0])==null?void 0:r.transaction)??s[0],((o=s[0])==null?void 0:o.options)??s[1]);case"signMessage":return this.signMessage(((g=s[0])==null?void 0:g.message)??s[0],(y=s[0])==null?void 0:y.display);default:throw new Error(`KeepKey (Solana): unsupported method "${e}"`)}}on(e,n){return this.#e.has(e)||this.#e.set(e,new Set),this.#e.get(e).add(n),this}off(e,n){var s;return(s=this.#e.get(e))==null||s.delete(n),this}removeListener(e,n){return this.off(e,n)}removeAllListeners(e){return e?this.#e.delete(e):this.#e.clear(),this}#i(e,...n){var s;(s=this.#e.get(e))==null||s.forEach(t=>{try{t(...n)}catch{}})}#c(e){this.#n=$(e),this.#a=e;try{localStorage.setItem("keepkey-solana",JSON.stringify({address:e}))}catch{}this.#i("connect",this.#n)}async#o(){this.#n||await this.connect()}async#l(){try{let e=await this.#t("solana_connect",[]);if(e&&typeof e=="string"){this.#a=e;try{localStorage.setItem("keepkey-solana",JSON.stringify({address:e}))}catch{}}}catch{}}#t(e,n){return new Promise((s,t)=>{this.#r(e,n,"solana",(a,r)=>{a?t(a):s(r)})})}};var B="https://api.trongrid.io",te="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";function se(i){let e=[0];for(let n of i){let s=te.indexOf(n);if(s===-1)throw new Error("Invalid base58 character");let t=s;for(let a=0;a>=8;for(;t>0;)e.push(t&255),t>>=8}for(let n of i){if(n!=="1")break;e.push(0)}return new Uint8Array(e.reverse())}function ae(i){let e="";for(let n of i)e+=n.toString(16).padStart(2,"0");return e}function W(i){let e=se(i);if(e.length!==25||e[0]!==65)throw new Error(`Invalid Tron address: ${i}`);return ae(e.slice(0,21))}function D(i){if(typeof i!="string"||i.length!==34||!i.startsWith("T"))return!1;try{return W(i),!0}catch{return!1}}var q=class{events=new Map;on(e,n){this.events.has(e)||this.events.set(e,new Set),this.events.get(e).add(n)}off(e,n){var s;(s=this.events.get(e))==null||s.delete(n)}emit(e,...n){var s;(s=this.events.get(e))==null||s.forEach(t=>{try{t(...n)}catch{}})}};function K(i,e,n){return new Promise((s,t)=>{i(e,n,"tron",(a,r)=>{a?t(a):s(r)})})}var re=8e3,ie=12e3,oe=i=>i>=500&&i<600;async function M(i,e){let s=i.includes("broadcasttransaction")?ie:re,t=2,a;for(let r=1;r<=t;r++)try{let o=await fetch(`${B}${i}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e),signal:AbortSignal.timeout(s)});if(!o.ok){if(oe(o.status)&&rsetTimeout(y,200*r));continue}let g=await o.text().catch(()=>"");throw new Error(`TronGrid ${i} failed (${o.status}): ${g}`)}return await o.json()}catch(o){if(a=o,rsetTimeout(g,200*r));continue}}throw a}var N=class{tronWeb;tronLink;address=null;hexAddress=null;emitter=new q;walletRequest;constructor(e){this.walletRequest=e,this.tronWeb=this.buildTronWeb(),this.tronLink=this.buildTronLink()}setAddress(e){!e||!D(e)||(this.address=e,this.hexAddress=W(e),this.tronWeb.ready=!0,this.tronWeb.defaultAddress={base58:e,hex:this.hexAddress,name:"KeepKey",type:1},this.tronLink.ready=!0,this.fireMessage("setAccount",{address:e,name:"KeepKey",type:1}),this.fireMessage("accountsChanged",{address:e}))}fireMessage(e,n){try{window.postMessage({message:{action:e,data:n},isTronLink:!0},window.location.origin)}catch{}this.emitter.emit(e,n)}buildTronLink(){return{isTronLink:!0,ready:!1,tronWeb:null,request:async({method:e,params:n})=>{switch(e){case"tron_requestAccounts":case"tron_accounts":{let s=await K(this.walletRequest,"tron_requestAccounts",[]);return!s||typeof s!="string"?{code:4001,message:"User denied account access"}:(this.setAddress(s),{code:200,message:"ok"})}default:return K(this.walletRequest,e,Array.isArray(n)?n:[n])}},on:(e,n)=>this.emitter.on(e,n),off:(e,n)=>this.emitter.off(e,n)}}buildTronWeb(){let e=this,n={sign:async(r,o,g,y)=>{if(typeof r=="string")throw new Error("tronWeb.trx.sign(message) not supported \u2014 use tronWeb.trx.signMessage()");if(!r||!r.raw_data_hex)throw new Error("tronWeb.trx.sign expects a built transaction with raw_data_hex");return await K(e.walletRequest,"tron_sign",[r])},signMessage:async(r,o)=>await K(e.walletRequest,"tron_signMessage",[r]),signMessageV2:async(r,o)=>await K(e.walletRequest,"signMessageV2",[r]),sendRawTransaction:async r=>M("/wallet/broadcasttransaction",r),broadcast:async r=>n.sendRawTransaction(r),getBalance:async r=>{let o=r||e.address;if(!o)throw new Error("No address \u2014 call tron_requestAccounts first");let g=await M("/wallet/getaccount",{address:o,visible:!0});return typeof(g==null?void 0:g.balance)=="number"?g.balance:0},getAccount:async r=>{let o=r||e.address;if(!o)throw new Error("No address \u2014 call tron_requestAccounts first");return M("/wallet/getaccount",{address:o,visible:!0})},getUnconfirmedAccount:async r=>{let o=r||e.address;if(!o)throw new Error("No address \u2014 call tron_requestAccounts first");return M("/wallet/getaccount",{address:o,visible:!0})},getTransaction:async r=>M("/wallet/gettransactionbyid",{value:r})},a={isTronLink:!0,ready:!1,defaultAddress:{base58:!1,hex:!1,name:!1,type:-1},fullNode:{host:B},solidityNode:{host:B},eventServer:{host:B},trx:n,transactionBuilder:{sendTrx:async(r,o,g)=>{let y=g||e.address;if(!y)throw new Error("No address \u2014 call tron_requestAccounts first");return M("/wallet/createtransaction",{owner_address:y,to_address:r,amount:o,visible:!0})},triggerSmartContract:async(r,o,g={},y=[],p)=>{let v=p||e.address;if(!v)throw new Error("No address \u2014 call tron_requestAccounts first");return M("/wallet/triggersmartcontract",{contract_address:r,function_selector:o,parameter:ce(y),fee_limit:g.feeLimit??1e8,call_value:g.callValue??0,owner_address:v,visible:!0})}},utils:{isAddress:r=>D(r),fromSun:r=>String(Number(r)/1e6),toSun:r=>String(Math.round(Number(r)*1e6)),toHex:r=>W(r)},on:(r,o)=>this.emitter.on(r,o),off:(r,o)=>this.emitter.off(r,o),setAddress:r=>{},isConnected:()=>this.address!==null};return queueMicrotask(()=>{this.tronLink&&(this.tronLink.tronWeb=a)}),a}};function ce(i){if(!Array.isArray(i)||i.length===0)return"";let e="";for(let n of i)if(n.type==="address"){let s=String(n.value),t=s.startsWith("T")?W(s).slice(2):s.replace(/^0x/,"").replace(/^41/,"");e+=t.padStart(64,"0")}else if(n.type==="uint256"||n.type==="uint"){let s=BigInt(n.value);e+=s.toString(16).padStart(64,"0")}else{let s=String(n.value).replace(/^0x/,"");e+=s.padStart(64,"0")}return e}function G(i){let e=1;function n(a,r,o,g,y){if(typeof a!="function")return;let p=g?(g==null?void 0:g.message)||String(g):null;a({success:!g,error:g?(g==null?void 0:g.code)===4001?"user_cancel":p:null,message:p,request_id:r,result:g?null:y??null,data:o})}function s(a,r,o){let g=e++;i(a,[r],"hive",(y,p)=>{n(o,g,r,y,p)})}function t(a,r,o){n(o,e++,r,{message:`KeepKey does not support Keychain request "${a}" yet`})}return{current_id:1,requestHandshake:function(a){s("hive_handshake",{},()=>{typeof a=="function"&&a()})},requestTransfer:function(a,r,o,g,y,p,v=!1,T){s("hive_transfer",{type:"transfer",username:a,to:r,amount:o,memo:g,enforce:v,currency:y,rpc:T},p)},requestSignBuffer:function(a,r,o,g){t("signBuffer",{type:"signBuffer"},g)},requestBroadcast:function(a,r,o,g){t("broadcast",{type:"broadcast"},g)},requestSignTx:function(a,r,o,g){t("signTx",{type:"signTx"},g)},requestVote:function(a,r,o,g,y){t("vote",{type:"vote"},y)},requestCustomJson:function(a,r,o,g,y,p){t("custom_json",{type:"custom"},p)},requestPost:function(...a){let r=a.find(o=>typeof o=="function");t("post",{type:"post"},r)},requestEncodeMessage:function(a,r,o,g,y){t("encode",{type:"encode"},y)},requestVerifyKey:function(a,r,o,g){t("decode",{type:"decode"},g)}}}(function(){let i="2.1.0",a=window,r={isInjected:!1,version:i,injectedAt:Date.now(),retryCount:0};if(a.keepkeyInjectionState&&a.keepkeyInjectionState.version>=i)return;a.keepkeyInjectionState=r;let o=(()=>{var l;let f={enableMetaMaskMasking:!1,enableXfiMasking:!1,enableKeplrMasking:!1,enablePhantomMasking:!1};try{let d=document.currentScript,c=document.getElementById("keepkey-injected-script"),u=(l=d==null?void 0:d.dataset)!=null&&l.masking?d:c,A=u==null?void 0:u.dataset.masking;if(!A)return f;let h=JSON.parse(A);return{enableMetaMaskMasking:h.enableMetaMaskMasking===!0,enableXfiMasking:h.enableXfiMasking===!0,enableKeplrMasking:h.enableKeplrMasking===!0,enablePhantomMasking:h.enablePhantomMasking===!0}}catch{return f}})();console.log(`[KeepKey] masking: metamask=${o.enableMetaMaskMasking?"on":"off"} xfi=${o.enableXfiMasking?"on":"off"} keplr=${o.enableKeplrMasking?"on":"off"} phantom=${o.enablePhantomMasking?"on":"off"}`);let g={siteUrl:window.location.href,scriptSource:"KeepKey Extension",version:i,injectedTime:new Date().toISOString(),origin:window.location.origin,protocol:window.location.protocol},y=0,p=new Map,v=[],T=!1;setInterval(()=>{let f=Date.now();p.forEach((l,d)=>{f-l.timestamp>3e5&&(l.callback(new Error("Request timeout")),p.delete(d))})},5e3);let b=f=>{v.length>=100&&v.shift(),v.push(f)},C=()=>{if(T)for(;v.length>0;){let f=v.shift();f&&window.postMessage(f,window.location.origin)}},_=(f=0)=>new Promise(l=>{let d=++y,c=setTimeout(()=>{f<3?setTimeout(()=>{_(f+1).then(l)},100*Math.pow(2,f)):(r.lastError="Failed to verify injection",l(!1))},1e3),u=A=>{var h,w,E;A.source===window&&((h=A.data)==null?void 0:h.source)==="keepkey-content"&&((w=A.data)==null?void 0:w.type)==="INJECTION_CONFIRMED"&&((E=A.data)==null?void 0:E.requestId)===d&&(clearTimeout(c),window.removeEventListener("message",u),T=!0,r.isInjected=!0,C(),l(!0))};window.addEventListener("message",u),window.postMessage({source:"keepkey-injected",type:"INJECTION_VERIFY",requestId:d,version:i,timestamp:Date.now()},window.location.origin)});function k(f,l=[],d,c){if(!f||typeof f!="string"){c(new Error("Invalid method"));return}Array.isArray(l)||(l=[l]);try{let u=++y,A={id:u,method:f,params:l,chain:d,siteUrl:g.siteUrl,scriptSource:g.scriptSource,version:g.version,requestTime:new Date().toISOString(),referrer:document.referrer,href:window.location.href,userAgent:navigator.userAgent,platform:navigator.platform,language:navigator.language};p.set(u,{callback:c,timestamp:Date.now(),method:f});let h={source:"keepkey-injected",type:"WALLET_REQUEST",requestId:u,requestInfo:A,timestamp:Date.now()};T?window.postMessage(h,window.location.origin):b(h)}catch(u){c(u)}}window.addEventListener("message",f=>{if(f.source!==window)return;let l=f.data;if(!(!l||typeof l!="object")){if(l.source==="keepkey-content"&&l.type==="INJECTION_CONFIRMED"){T=!0,C();return}if(l.source==="keepkey-content"&&l.type==="WALLET_RESPONSE"&&l.requestId){let d=p.get(l.requestId);d&&(l.error?d.callback(l.error):d.callback(null,l.result),p.delete(l.requestId))}}});class H{events=new Map;on(l,d){this.events.has(l)||this.events.set(l,new Set),this.events.get(l).add(d)}off(l,d){var c;(c=this.events.get(l))==null||c.delete(d)}removeListener(l,d){this.off(l,d)}removeAllListeners(l){l?this.events.delete(l):this.events.clear()}emit(l,...d){var c;(c=this.events.get(l))==null||c.forEach(u=>{try{u(...d)}catch{}})}once(l,d){let c=(...u)=>{d(...u),this.off(l,c)};this.on(l,c)}}function m(f){let l=new H,d={network:"mainnet",isKeepKey:!0,isMetaMask:o.enableMetaMaskMasking,isConnected:()=>T,request:({method:c,params:u=[]})=>new Promise((A,h)=>{k(c,u,f,(w,E)=>{if(w)console.log(`[HANDOFF] dApp \u2190 KeepKey (${f}/${c}) REJECT +URI: ${f}`,b+=` +Version: ${A}`,b+=` +Chain ID: ${y}`,b+=` +Nonce: ${w}`,b+=` +Issued At: ${v}`,n!=null&&n.expirationTime&&(b+=` +Expiration Time: ${n.expirationTime}`),n!=null&&n.notBefore&&(b+=` +Not Before: ${n.notBefore}`),n!=null&&n.requestId&&(b+=` +Request ID: ${n.requestId}`),(s=n==null?void 0:n.resources)!=null&&s.length){b+=` +Resources:`;for(let E of n.resources)b+=` +- ${E}`}let S=new TextEncoder().encode(b),R=await this.#s("solana_signMessage",[Array.from(S),{accountAddress:a.address}]);t.push({account:a,signedMessage:S,signature:new Uint8Array(R)})}return t}}};constructor(e){this.#a=e;try{let t=localStorage.getItem("keepkey-solana");if(t){let{address:s}=JSON.parse(t);s&&typeof s=="string"&&(this.#t=s)}}catch{}this.#l()}#i(e){return{address:e,publicKey:L(e),chains:["solana:mainnet"],features:[...r.ACCOUNT_FEATURES]}}#c(e){this.#e=[this.#i(e)];try{localStorage.setItem("keepkey-solana",JSON.stringify({address:e}))}catch{}this.#n()}#o(e){var t;this.#e=e.filter(s=>s==null?void 0:s.address).map(s=>this.#i(s.address));try{let s=(t=this.#e[0])==null?void 0:t.address;s&&localStorage.setItem("keepkey-solana",JSON.stringify({address:s}))}catch{}this.#n()}async#l(){try{let e=await this.#s("solana_connect",[]);if(e&&typeof e=="string"){this.#t=e;try{localStorage.setItem("keepkey-solana",JSON.stringify({address:e}))}catch{}}}catch{}}#n(){let e=this.#e,t=this.features;this.#r.forEach(s=>{try{s({accounts:e,features:t})}catch{}})}#s(e,t){return new Promise((s,n)=>{this.#a(e,t,"solana",(a,i)=>{a?n(a):s(i)})})}};function x(r){let e=({register:s})=>{s(r)};try{let s=window.navigator;s.wallets||(s.wallets=[]),Array.isArray(s.wallets)?s.wallets.push(e):typeof s.wallets.register=="function"&&s.wallets.register(r)}catch{}let t=()=>{try{window.dispatchEvent(new CustomEvent("wallet-standard:register-wallet",{detail:e}))}catch{}};t(),setTimeout(t,100),setTimeout(t,1e3),window.addEventListener("wallet-standard:app-ready",s=>{let n=s.detail;try{n&&typeof n.register=="function"?e(n):typeof n=="function"&&n(e)}catch{}})}var Y="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";function Z(r){let e=[0];for(let t of r){let s=Y.indexOf(t);if(s===-1)throw new Error("Invalid base58 character");let n=s;for(let a=0;a>=8;for(;n>0;)e.push(n&255),n>>=8}for(let t of r){if(t!=="1")break;e.push(0)}return new Uint8Array(e.reverse())}function $(r){let e=Z(r);return{toString:()=>r,toBase58:()=>r,toBytes:()=>e,toBuffer:()=>e,equals:t=>{var s,n;try{return((s=t==null?void 0:t.toBase58)==null?void 0:s.call(t))===r||((n=t==null?void 0:t.toString)==null?void 0:n.call(t))===r}catch{return!1}}}}function j(r){return r!=null&&typeof r=="object"&&"version"in r&&r.message!=null}function F(r){if(!r||typeof r.serialize!="function")throw new Error("Unsupported transaction: missing serialize()");return j(r)?new Uint8Array(r.serialize()):new Uint8Array(r.serialize({requireAllSignatures:!1,verifySignatures:!1}))}function ee(r){let e=r[0];if(!e)throw new Error("Signed transaction has no signatures");if(e&128)throw new Error("Unexpected multi-byte signature count");return r.slice(1,65)}function te(r){var t,s;if(j(r)){let n=(t=r.message)==null?void 0:t.staticAccountKeys;if(n&&n.length)return n[0];throw new Error("Versioned transaction missing account keys")}if(r.feePayer)return r.feePayer;let e=(s=r.signatures)==null?void 0:s[0];if(e!=null&&e.publicKey)return e.publicKey;throw new Error("Legacy transaction missing fee payer")}var O=class{#a;#e=new Map;#t=null;#r=null;isPhantom=!0;isKeepKey=!0;constructor(e){this.#a=e;try{let t=localStorage.getItem("keepkey-solana");if(t){let{address:s}=JSON.parse(t);s&&typeof s=="string"&&(this.#r=s)}}catch{}this.#l()}get publicKey(){return this.#t}get isConnected(){return this.#t!==null}async connect(e){if(this.#t)return{publicKey:this.#t};let t=this.#r||await this.#n("solana_connect",[]);if(!t)throw new Error("Failed to connect to KeepKey");return this.#c(t),{publicKey:this.#t}}async disconnect(){await this.#n("solana_disconnect",[]).catch(()=>{}),this.#t=null;try{localStorage.removeItem("keepkey-solana")}catch{}this.#i("disconnect")}async signMessage(e,t){await this.#o();let s=await this.#n("solana_signMessage",[Array.from(e)]);return{signature:new Uint8Array(s),publicKey:this.#t}}async signTransaction(e){await this.#o();let t=F(e),s=await this.#n("solana_signTransaction",[Array.from(t)]),n=new Uint8Array(s),a=ee(n);return e.addSignature(te(e),a),e}async signAllTransactions(e){let t=[];for(let s of e)t.push(await this.signTransaction(s));return t}async signAndSendTransaction(e,t){await this.#o();let s=F(e);return{signature:await this.#n("solana_signAndSendTransaction",[Array.from(s)]),publicKey:this.#t}}async request({method:e,params:t}){var n,a,i,o,f,A;let s=Array.isArray(t)?t:t!=null?[t]:[];switch(e){case"connect":return this.connect(s[0]);case"disconnect":return this.disconnect();case"signTransaction":return this.signTransaction(((n=s[0])==null?void 0:n.transaction)??s[0]);case"signAllTransactions":return this.signAllTransactions(((a=s[0])==null?void 0:a.transactions)??s[0]);case"signAndSendTransaction":return this.signAndSendTransaction(((i=s[0])==null?void 0:i.transaction)??s[0],((o=s[0])==null?void 0:o.options)??s[1]);case"signMessage":return this.signMessage(((f=s[0])==null?void 0:f.message)??s[0],(A=s[0])==null?void 0:A.display);default:throw new Error(`KeepKey (Solana): unsupported method "${e}"`)}}on(e,t){return this.#e.has(e)||this.#e.set(e,new Set),this.#e.get(e).add(t),this}off(e,t){var s;return(s=this.#e.get(e))==null||s.delete(t),this}removeListener(e,t){return this.off(e,t)}removeAllListeners(e){return e?this.#e.delete(e):this.#e.clear(),this}#i(e,...t){var s;(s=this.#e.get(e))==null||s.forEach(n=>{try{n(...t)}catch{}})}#c(e){this.#t=$(e),this.#r=e;try{localStorage.setItem("keepkey-solana",JSON.stringify({address:e}))}catch{}this.#i("connect",this.#t)}async#o(){this.#t||await this.connect()}async#l(){try{let e=await this.#n("solana_connect",[]);if(e&&typeof e=="string"){this.#r=e;try{localStorage.setItem("keepkey-solana",JSON.stringify({address:e}))}catch{}}}catch{}}#n(e,t){return new Promise((s,n)=>{this.#a(e,t,"solana",(a,i)=>{a?n(a):s(i)})})}};var P="https://api.trongrid.io",ne="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";function se(r){let e=[0];for(let t of r){let s=ne.indexOf(t);if(s===-1)throw new Error("Invalid base58 character");let n=s;for(let a=0;a>=8;for(;n>0;)e.push(n&255),n>>=8}for(let t of r){if(t!=="1")break;e.push(0)}return new Uint8Array(e.reverse())}function re(r){let e="";for(let t of r)e+=t.toString(16).padStart(2,"0");return e}function W(r){let e=se(r);if(e.length!==25||e[0]!==65)throw new Error(`Invalid Tron address: ${r}`);return re(e.slice(0,21))}function D(r){if(typeof r!="string"||r.length!==34||!r.startsWith("T"))return!1;try{return W(r),!0}catch{return!1}}var B=class{events=new Map;on(e,t){this.events.has(e)||this.events.set(e,new Set),this.events.get(e).add(t)}off(e,t){var s;(s=this.events.get(e))==null||s.delete(t)}emit(e,...t){var s;(s=this.events.get(e))==null||s.forEach(n=>{try{n(...t)}catch{}})}};function K(r,e,t){return new Promise((s,n)=>{r(e,t,"tron",(a,i)=>{a?n(a):s(i)})})}var ae=8e3,ie=12e3,oe=r=>r>=500&&r<600;async function M(r,e){let s=r.includes("broadcasttransaction")?ie:ae,n=2,a;for(let i=1;i<=n;i++)try{let o=await fetch(`${P}${r}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e),signal:AbortSignal.timeout(s)});if(!o.ok){if(oe(o.status)&&isetTimeout(A,200*i));continue}let f=await o.text().catch(()=>"");throw new Error(`TronGrid ${r} failed (${o.status}): ${f}`)}return await o.json()}catch(o){if(a=o,isetTimeout(f,200*i));continue}}throw a}var N=class{tronWeb;tronLink;address=null;hexAddress=null;emitter=new B;walletRequest;constructor(e){this.walletRequest=e,this.tronWeb=this.buildTronWeb(),this.tronLink=this.buildTronLink()}setAddress(e){!e||!D(e)||(this.address=e,this.hexAddress=W(e),this.tronWeb.ready=!0,this.tronWeb.defaultAddress={base58:e,hex:this.hexAddress,name:"KeepKey",type:1},this.tronLink.ready=!0,this.fireMessage("setAccount",{address:e,name:"KeepKey",type:1}),this.fireMessage("accountsChanged",{address:e}))}fireMessage(e,t){try{window.postMessage({message:{action:e,data:t},isTronLink:!0},window.location.origin)}catch{}this.emitter.emit(e,t)}buildTronLink(){return{isTronLink:!0,ready:!1,tronWeb:null,request:async({method:e,params:t})=>{switch(e){case"tron_requestAccounts":case"tron_accounts":{let s=await K(this.walletRequest,"tron_requestAccounts",[]);return!s||typeof s!="string"?{code:4001,message:"User denied account access"}:(this.setAddress(s),{code:200,message:"ok"})}default:return K(this.walletRequest,e,Array.isArray(t)?t:[t])}},on:(e,t)=>this.emitter.on(e,t),off:(e,t)=>this.emitter.off(e,t)}}buildTronWeb(){let e=this,t={sign:async(i,o,f,A)=>{if(typeof i=="string")throw new Error("tronWeb.trx.sign(message) not supported \u2014 use tronWeb.trx.signMessage()");if(!i||!i.raw_data_hex)throw new Error("tronWeb.trx.sign expects a built transaction with raw_data_hex");return await K(e.walletRequest,"tron_sign",[i])},signMessage:async(i,o)=>await K(e.walletRequest,"tron_signMessage",[i]),signMessageV2:async(i,o)=>await K(e.walletRequest,"signMessageV2",[i]),sendRawTransaction:async i=>M("/wallet/broadcasttransaction",i),broadcast:async i=>t.sendRawTransaction(i),getBalance:async i=>{let o=i||e.address;if(!o)throw new Error("No address \u2014 call tron_requestAccounts first");let f=await M("/wallet/getaccount",{address:o,visible:!0});return typeof(f==null?void 0:f.balance)=="number"?f.balance:0},getAccount:async i=>{let o=i||e.address;if(!o)throw new Error("No address \u2014 call tron_requestAccounts first");return M("/wallet/getaccount",{address:o,visible:!0})},getUnconfirmedAccount:async i=>{let o=i||e.address;if(!o)throw new Error("No address \u2014 call tron_requestAccounts first");return M("/wallet/getaccount",{address:o,visible:!0})},getTransaction:async i=>M("/wallet/gettransactionbyid",{value:i})},a={isTronLink:!0,ready:!1,defaultAddress:{base58:!1,hex:!1,name:!1,type:-1},fullNode:{host:P},solidityNode:{host:P},eventServer:{host:P},trx:t,transactionBuilder:{sendTrx:async(i,o,f)=>{let A=f||e.address;if(!A)throw new Error("No address \u2014 call tron_requestAccounts first");return M("/wallet/createtransaction",{owner_address:A,to_address:i,amount:o,visible:!0})},triggerSmartContract:async(i,o,f={},A=[],y)=>{let w=y||e.address;if(!w)throw new Error("No address \u2014 call tron_requestAccounts first");return M("/wallet/triggersmartcontract",{contract_address:i,function_selector:o,parameter:ce(A),fee_limit:f.feeLimit??1e8,call_value:f.callValue??0,owner_address:w,visible:!0})}},utils:{isAddress:i=>D(i),fromSun:i=>String(Number(i)/1e6),toSun:i=>String(Math.round(Number(i)*1e6)),toHex:i=>W(i)},on:(i,o)=>this.emitter.on(i,o),off:(i,o)=>this.emitter.off(i,o),setAddress:i=>{},isConnected:()=>this.address!==null};return queueMicrotask(()=>{this.tronLink&&(this.tronLink.tronWeb=a)}),a}};function ce(r){if(!Array.isArray(r)||r.length===0)return"";let e="";for(let t of r)if(t.type==="address"){let s=String(t.value),n=s.startsWith("T")?W(s).slice(2):s.replace(/^0x/,"").replace(/^41/,"");e+=n.padStart(64,"0")}else if(t.type==="uint256"||t.type==="uint"){let s=BigInt(t.value);e+=s.toString(16).padStart(64,"0")}else{let s=String(t.value).replace(/^0x/,"");e+=s.padStart(64,"0")}return e}function G(r){let e=1;function t(o,f,A,y,w){if(typeof o!="function")return;let v=y?(y==null?void 0:y.message)||String(y):null;o({success:!y,error:y?(y==null?void 0:y.code)===4001?"user_cancel":v:null,message:v,request_id:f,result:y?null:w??null,data:A})}function s(o,f,A){let y=e++;r(o,[f],"hive",(w,v)=>{t(A,y,f,w,v)})}function n(o,f,A){t(A,e++,f,{message:`KeepKey does not support Keychain request "${o}" yet`})}let a={current_id:1,requestHandshake:function(o){s("hive_handshake",{},()=>{typeof o=="function"&&o()})},requestTransfer:function(o,f,A,y,w,v,C=!1,b){s("hive_transfer",{type:"transfer",username:o,to:f,amount:A,memo:y,enforce:C,currency:w,rpc:b},v)}},i=["requestEncodeMessage","requestEncodeWithKeys","requestVerifyKey","requestSignBuffer","requestAddAccountAuthority","requestRemoveAccountAuthority","requestAddKeyAuthority","requestRemoveKeyAuthority","requestBroadcast","requestSignTx","requestSignedCall","requestPost","requestVote","requestCustomJson","requestSendToken","requestDelegation","requestWitnessVote","requestProxy","requestPowerUp","requestPowerDown","requestCreateClaimedAccount","requestCreateProposal","requestRemoveProposal","requestUpdateProposalVote","requestAddAccount","requestConversion","requestRecurrentTransfer","requestSavingsOperation","requestSwap"];for(let o of i)a[o]=(...f)=>{let A=f.find(y=>typeof y=="function");n(o.replace(/^request/,""),{type:o},A)};return a}(function(){let r="2.1.0",a=window,i={isInjected:!1,version:r,injectedAt:Date.now(),retryCount:0};if(a.keepkeyInjectionState&&a.keepkeyInjectionState.version>=r)return;a.keepkeyInjectionState=i;let o=(()=>{var l;let g={enableMetaMaskMasking:!1,enableXfiMasking:!1,enableKeplrMasking:!1,enablePhantomMasking:!1};try{let d=document.currentScript,c=document.getElementById("keepkey-injected-script"),u=(l=d==null?void 0:d.dataset)!=null&&l.masking?d:c,m=u==null?void 0:u.dataset.masking;if(!m)return g;let h=JSON.parse(m);return{enableMetaMaskMasking:h.enableMetaMaskMasking===!0,enableXfiMasking:h.enableXfiMasking===!0,enableKeplrMasking:h.enableKeplrMasking===!0,enablePhantomMasking:h.enablePhantomMasking===!0}}catch{return g}})();console.log(`[KeepKey] masking: metamask=${o.enableMetaMaskMasking?"on":"off"} xfi=${o.enableXfiMasking?"on":"off"} keplr=${o.enableKeplrMasking?"on":"off"} phantom=${o.enablePhantomMasking?"on":"off"}`);let f={siteUrl:window.location.href,scriptSource:"KeepKey Extension",version:r,injectedTime:new Date().toISOString(),origin:window.location.origin,protocol:window.location.protocol},A=0,y=new Map,w=[],v=!1;setInterval(()=>{let g=Date.now();y.forEach((l,d)=>{g-l.timestamp>3e5&&(l.callback(new Error("Request timeout")),y.delete(d))})},5e3);let b=g=>{w.length>=100&&w.shift(),w.push(g)},S=()=>{if(v)for(;w.length>0;){let g=w.shift();g&&window.postMessage(g,window.location.origin)}},R=(g=0)=>new Promise(l=>{let d=++A,c=setTimeout(()=>{g<3?setTimeout(()=>{R(g+1).then(l)},100*Math.pow(2,g)):(i.lastError="Failed to verify injection",l(!1))},1e3),u=m=>{var h,k,T;m.source===window&&((h=m.data)==null?void 0:h.source)==="keepkey-content"&&((k=m.data)==null?void 0:k.type)==="INJECTION_CONFIRMED"&&((T=m.data)==null?void 0:T.requestId)===d&&(clearTimeout(c),window.removeEventListener("message",u),v=!0,i.isInjected=!0,S(),l(!0))};window.addEventListener("message",u),window.postMessage({source:"keepkey-injected",type:"INJECTION_VERIFY",requestId:d,version:r,timestamp:Date.now()},window.location.origin)});function E(g,l=[],d,c){if(!g||typeof g!="string"){c(new Error("Invalid method"));return}Array.isArray(l)||(l=[l]);try{let u=++A,m={id:u,method:g,params:l,chain:d,siteUrl:f.siteUrl,scriptSource:f.scriptSource,version:f.version,requestTime:new Date().toISOString(),referrer:document.referrer,href:window.location.href,userAgent:navigator.userAgent,platform:navigator.platform,language:navigator.language};y.set(u,{callback:c,timestamp:Date.now(),method:g});let h={source:"keepkey-injected",type:"WALLET_REQUEST",requestId:u,requestInfo:m,timestamp:Date.now()};v?window.postMessage(h,window.location.origin):b(h)}catch(u){c(u)}}window.addEventListener("message",g=>{if(g.source!==window)return;let l=g.data;if(!(!l||typeof l!="object")){if(l.source==="keepkey-content"&&l.type==="INJECTION_CONFIRMED"){v=!0,S();return}if(l.source==="keepkey-content"&&l.type==="WALLET_RESPONSE"&&l.requestId){let d=y.get(l.requestId);d&&(l.error?d.callback(l.error):d.callback(null,l.result),y.delete(l.requestId))}}});class H{events=new Map;on(l,d){this.events.has(l)||this.events.set(l,new Set),this.events.get(l).add(d)}off(l,d){var c;(c=this.events.get(l))==null||c.delete(d)}removeListener(l,d){this.off(l,d)}removeAllListeners(l){l?this.events.delete(l):this.events.clear()}emit(l,...d){var c;(c=this.events.get(l))==null||c.forEach(u=>{try{u(...d)}catch{}})}once(l,d){let c=(...u)=>{d(...u),this.off(l,c)};this.on(l,c)}}function p(g){let l=new H,d={network:"mainnet",isKeepKey:!0,isMetaMask:o.enableMetaMaskMasking,isConnected:()=>v,request:({method:c,params:u=[]})=>new Promise((m,h)=>{E(c,u,g,(k,T)=>{if(k)console.log(`[HANDOFF] dApp \u2190 KeepKey (${g}/${c}) REJECT params=${JSON.stringify(u)} - error=`,w),h(w);else{let P=typeof E,X=P==="string"?`len=${E.length} value=${E}`:`value=${JSON.stringify(E)}`;console.log(`[HANDOFF] dApp \u2190 KeepKey (${f}/${c}) RESOLVE + error=`,k),h(k);else{let _=typeof T,X=_==="string"?`len=${T.length} value=${T}`:`value=${JSON.stringify(T)}`;console.log(`[HANDOFF] dApp \u2190 KeepKey (${g}/${c}) RESOLVE params=${JSON.stringify(u)} - type=${P} ${X}`),A(E)}})}),send:(c,u,A)=>{if(c.chain||(c.chain=f),typeof A=="function"){k(c.method,c.params||u,f,(h,w)=>{h?A(h):A(null,{id:c.id,jsonrpc:"2.0",result:w})});return}else return{id:c.id,jsonrpc:"2.0",result:null}},sendAsync:(c,u,A)=>{c.chain||(c.chain=f);let h=A||u;typeof h=="function"&&k(c.method,c.params||u,f,(w,E)=>{w?h(w):h(null,{id:c.id,jsonrpc:"2.0",result:E})})},on:(c,u)=>(l.on(c,u),d),off:(c,u)=>(l.off(c,u),d),removeListener:(c,u)=>(l.removeListener(c,u),d),removeAllListeners:c=>(l.removeAllListeners(c),d),emit:(c,...u)=>(l.emit(c,...u),d),once:(c,u)=>(l.once(c,u),d),enable:()=>d.request({method:"eth_requestAccounts"}),_metamask:{isUnlocked:()=>Promise.resolve(!0)}};return f==="ethereum"&&(d.chainId="0x1",d.networkVersion="1",d.selectedAddress=null,d._handleAccountsChanged=c=>{d.selectedAddress=c[0]||null,l.emit("accountsChanged",c)},d._handleChainChanged=c=>{d.chainId=c,l.emit("chainChanged",c)},d._handleConnect=c=>{l.emit("connect",c)},d._handleDisconnect=c=>{d.selectedAddress=null,l.emit("disconnect",c)}),d}let z="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAIKADAAQAAAABAAAAIAAAAACshmLzAAADUklEQVRYCb1XTUgUYRie3bXEWhVLQaUsgwVLoUtEQjUJiZX0A0GX7BIZXurkOTSvdo2kvETHAsOshFgqOqhlRD9C7SGS1JTCsj1krU7PM+w7zMzOzuzMqi88+73v9z7vz3zzzTeziuIgmqbFgG5gBPguFOgq4CXLIMwCo0AXEJN4zxHkEuA6kAIMkUBMqMZk7so/UG8AUcnjOIKwFXgHZIgEwKFmOHOfYO4aySVjmAoc7O4R0EB7lYS5h9K1jBJ6A7CuAfXG7OopbKLXkh4dccNZ7jlsi0gAJlWLI5jBPWFsTK5AGxCRImswFqDGWanDBo6IsYbjUanFbmrFWIHxD3IsmfJsgB4y2aJuF4UrUC5GnuNtxJeEQqEoAb3LJV+F4ctlHwkZXDULv8fEKQCHB4+rCJ9ngKcIGUTVRubT027y8yR9bOM4mhKTTwNJZD4miaDXAG8dqzlMShw3YRCZRVAr7vU4g5F/D4ZBoJK2H+Em9CsfEdBoKn4K9jPAd3G9sMPqZEzpRPzAwRfWJpN9EfZSRkAOE5LD7wrw8dkpwRh55VMm27fqt4FiVBjGBTaxEm4Db8d+4BPtIOK3AdbYCPC1qh/haGIS9gHgDeBbgjTAIkXAfTRxkgaamMNwCHgB+BMk4Decq0hGkFQbka/WMyZ/EeyHNo6TuSwx3Nn8gHQVIYOkOhB5Gp4zcdbBHiDvZ2pRuzozru2euKuDOucg/KliTAjKKMa9ksBpxBLrbzRwVfifOnB4RR2g3QSH3Cfx5FRdc2KoGstroUeQKh47vnAwWvUKjsPcA/wWdBUkjRAgZdsznO8D5xLGC/Opxc3NiQeV9uIsgkNDaUoMFpNDLleAn0cTQNBjGaFW6fn2Wrky/dI6abPOl9eN9deoWhjLloCv3+bPy7w3/9kzfvjX120g1cuSdsJ47xm1CgS9AaxCErlbV6qJ22W1nq22lG75AtIHWQEeJpOYaAT6gBQQWC5XNCjc7dkkHFKWe6v3FcLfbzRAMlcC6IC6C+gGxgCectZnCRMuopVG1v+Nx04sYINlxLH4wI6W52UFhT+Q41b2Nl0qeLnwZPGQucNHrXN6ZDG94RQuO688XbwNFzvjlSuwH03wEW8H+Bf/dxrUOWdc+H8mKXtEpGpY3AAAAABJRU5ErkJggg==",J="data:image/svg+xml;utf8,"+encodeURIComponent('MM');function R(f){let l={uuid:"350670db-19fa-4704-a166-e52e178b59d4",name:"KeepKey",icon:z,rdns:"com.keepkey.client"};if(window.dispatchEvent(new CustomEvent("eip6963:announceProvider",{detail:Object.freeze({info:l,provider:f})})),o.enableMetaMaskMasking){let d={uuid:"9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",name:"MetaMask",icon:J,rdns:"io.metamask"};window.dispatchEvent(new CustomEvent("eip6963:announceProvider",{detail:Object.freeze({info:d,provider:f})}))}}async function Q(){let f=m("ethereum"),l={binance:m("binance"),bitcoin:m("bitcoin"),bitcoincash:m("bitcoincash"),dogecoin:m("dogecoin"),dash:m("dash"),ethereum:f,keplr:m("keplr"),litecoin:m("litecoin"),thorchain:m("thorchain"),mayachain:m("mayachain")},d={binance:m("binance"),bitcoin:m("bitcoin"),bitcoincash:m("bitcoincash"),dogecoin:m("dogecoin"),dash:m("dash"),ethereum:f,osmosis:m("osmosis"),cosmos:m("cosmos"),litecoin:m("litecoin"),thorchain:m("thorchain"),mayachain:m("mayachain"),ripple:m("ripple")},c=(u,A,{force:h=!1}={})=>{if(!(a[u]&&!h))try{Object.defineProperty(a,u,{value:A,writable:!1,configurable:!0})}catch{r.lastError=`Failed to mount ${u}`}};o.enableMetaMaskMasking&&c("ethereum",f,{force:!0}),o.enableXfiMasking&&c("xfi",l,{force:!0}),c("keepkey",d,{force:!0}),window.addEventListener("eip6963:requestProvider",()=>{R(f)}),R(f),setTimeout(()=>{R(f)},100);try{let u=new U(k);L(u)}catch{}if(o.enablePhantomMasking)try{let u=new O(k);Object.defineProperty(a,"solana",{value:u,writable:!1,configurable:!0})}catch{}try{let u=new N(k);a.tronLink||Object.defineProperty(a,"tronLink",{value:u.tronLink,writable:!1,configurable:!0}),a.tronWeb||Object.defineProperty(a,"tronWeb",{value:u.tronWeb,writable:!1,configurable:!0})}catch{}try{a.hive_keychain||Object.defineProperty(a,"hive_keychain",{value:G(k),writable:!1,configurable:!0})}catch{}window.addEventListener("message",u=>{var A,h,w;((A=u.data)==null?void 0:A.type)==="CHAIN_CHANGED"&&f.emit("chainChanged",(h=u.data.provider)==null?void 0:h.chainId),((w=u.data)==null?void 0:w.type)==="ACCOUNTS_CHANGED"&&f._handleAccountsChanged&&f._handleAccountsChanged(u.data.accounts||[])}),_().then(u=>{u||(r.lastError="Injection not verified")})}Q(),document.readyState==="loading"&&document.addEventListener("DOMContentLoaded",()=>{if(a.ethereum&&typeof a.dispatchEvent=="function"){let f=a.ethereum;R(f)}})})();})(); + type=${_} ${X}`),m(T)}})}),send:(c,u,m)=>{if(c.chain||(c.chain=g),typeof m=="function"){E(c.method,c.params||u,g,(h,k)=>{h?m(h):m(null,{id:c.id,jsonrpc:"2.0",result:k})});return}else return{id:c.id,jsonrpc:"2.0",result:null}},sendAsync:(c,u,m)=>{c.chain||(c.chain=g);let h=m||u;typeof h=="function"&&E(c.method,c.params||u,g,(k,T)=>{k?h(k):h(null,{id:c.id,jsonrpc:"2.0",result:T})})},on:(c,u)=>(l.on(c,u),d),off:(c,u)=>(l.off(c,u),d),removeListener:(c,u)=>(l.removeListener(c,u),d),removeAllListeners:c=>(l.removeAllListeners(c),d),emit:(c,...u)=>(l.emit(c,...u),d),once:(c,u)=>(l.once(c,u),d),enable:()=>d.request({method:"eth_requestAccounts"}),_metamask:{isUnlocked:()=>Promise.resolve(!0)}};return g==="ethereum"&&(d.chainId="0x1",d.networkVersion="1",d.selectedAddress=null,d._handleAccountsChanged=c=>{d.selectedAddress=c[0]||null,l.emit("accountsChanged",c)},d._handleChainChanged=c=>{d.chainId=c,l.emit("chainChanged",c)},d._handleConnect=c=>{l.emit("connect",c)},d._handleDisconnect=c=>{d.selectedAddress=null,l.emit("disconnect",c)}),d}let z="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAIKADAAQAAAABAAAAIAAAAACshmLzAAADUklEQVRYCb1XTUgUYRie3bXEWhVLQaUsgwVLoUtEQjUJiZX0A0GX7BIZXurkOTSvdo2kvETHAsOshFgqOqhlRD9C7SGS1JTCsj1krU7PM+w7zMzOzuzMqi88+73v9z7vz3zzzTeziuIgmqbFgG5gBPguFOgq4CXLIMwCo0AXEJN4zxHkEuA6kAIMkUBMqMZk7so/UG8AUcnjOIKwFXgHZIgEwKFmOHOfYO4aySVjmAoc7O4R0EB7lYS5h9K1jBJ6A7CuAfXG7OopbKLXkh4dccNZ7jlsi0gAJlWLI5jBPWFsTK5AGxCRImswFqDGWanDBo6IsYbjUanFbmrFWIHxD3IsmfJsgB4y2aJuF4UrUC5GnuNtxJeEQqEoAb3LJV+F4ctlHwkZXDULv8fEKQCHB4+rCJ9ngKcIGUTVRubT027y8yR9bOM4mhKTTwNJZD4miaDXAG8dqzlMShw3YRCZRVAr7vU4g5F/D4ZBoJK2H+Em9CsfEdBoKn4K9jPAd3G9sMPqZEzpRPzAwRfWJpN9EfZSRkAOE5LD7wrw8dkpwRh55VMm27fqt4FiVBjGBTaxEm4Db8d+4BPtIOK3AdbYCPC1qh/haGIS9gHgDeBbgjTAIkXAfTRxkgaamMNwCHgB+BMk4Decq0hGkFQbka/WMyZ/EeyHNo6TuSwx3Nn8gHQVIYOkOhB5Gp4zcdbBHiDvZ2pRuzozru2euKuDOucg/KliTAjKKMa9ksBpxBLrbzRwVfifOnB4RR2g3QSH3Cfx5FRdc2KoGstroUeQKh47vnAwWvUKjsPcA/wWdBUkjRAgZdsznO8D5xLGC/Opxc3NiQeV9uIsgkNDaUoMFpNDLleAn0cTQNBjGaFW6fn2Wrky/dI6abPOl9eN9deoWhjLloCv3+bPy7w3/9kzfvjX120g1cuSdsJ47xm1CgS9AaxCErlbV6qJ22W1nq22lG75AtIHWQEeJpOYaAT6gBQQWC5XNCjc7dkkHFKWe6v3FcLfbzRAMlcC6IC6C+gGxgCectZnCRMuopVG1v+Nx04sYINlxLH4wI6W52UFhT+Q41b2Nl0qeLnwZPGQucNHrXN6ZDG94RQuO688XbwNFzvjlSuwH03wEW8H+Bf/dxrUOWdc+H8mKXtEpGpY3AAAAABJRU5ErkJggg==",J="data:image/svg+xml;utf8,"+encodeURIComponent('MM');function q(g){let l={uuid:"350670db-19fa-4704-a166-e52e178b59d4",name:"KeepKey",icon:z,rdns:"com.keepkey.client"};if(window.dispatchEvent(new CustomEvent("eip6963:announceProvider",{detail:Object.freeze({info:l,provider:g})})),o.enableMetaMaskMasking){let d={uuid:"9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",name:"MetaMask",icon:J,rdns:"io.metamask"};window.dispatchEvent(new CustomEvent("eip6963:announceProvider",{detail:Object.freeze({info:d,provider:g})}))}}async function Q(){let g=p("ethereum"),l={binance:p("binance"),bitcoin:p("bitcoin"),bitcoincash:p("bitcoincash"),dogecoin:p("dogecoin"),dash:p("dash"),ethereum:g,keplr:p("keplr"),litecoin:p("litecoin"),thorchain:p("thorchain"),mayachain:p("mayachain")},d={binance:p("binance"),bitcoin:p("bitcoin"),bitcoincash:p("bitcoincash"),dogecoin:p("dogecoin"),dash:p("dash"),ethereum:g,osmosis:p("osmosis"),cosmos:p("cosmos"),litecoin:p("litecoin"),thorchain:p("thorchain"),mayachain:p("mayachain"),ripple:p("ripple")},c=(u,m,{force:h=!1}={})=>{if(!(a[u]&&!h))try{Object.defineProperty(a,u,{value:m,writable:!1,configurable:!0})}catch{i.lastError=`Failed to mount ${u}`}};o.enableMetaMaskMasking&&c("ethereum",g,{force:!0}),o.enableXfiMasking&&c("xfi",l,{force:!0}),c("keepkey",d,{force:!0}),window.addEventListener("eip6963:requestProvider",()=>{q(g)}),q(g),setTimeout(()=>{q(g)},100);try{let u=new U(E);x(u)}catch{}if(o.enablePhantomMasking)try{let u=new O(E);Object.defineProperty(a,"solana",{value:u,writable:!1,configurable:!0})}catch{}try{let u=new N(E);a.tronLink||Object.defineProperty(a,"tronLink",{value:u.tronLink,writable:!1,configurable:!0}),a.tronWeb||Object.defineProperty(a,"tronWeb",{value:u.tronWeb,writable:!1,configurable:!0})}catch{}try{a.hive_keychain||(a.hive_keychain=G(E))}catch{}window.addEventListener("message",u=>{var m,h,k;((m=u.data)==null?void 0:m.type)==="CHAIN_CHANGED"&&g.emit("chainChanged",(h=u.data.provider)==null?void 0:h.chainId),((k=u.data)==null?void 0:k.type)==="ACCOUNTS_CHANGED"&&g._handleAccountsChanged&&g._handleAccountsChanged(u.data.accounts||[])}),R().then(u=>{u||(i.lastError="Injection not verified")})}Q(),document.readyState==="loading"&&document.addEventListener("DOMContentLoaded",()=>{if(a.ethereum&&typeof a.dispatchEvent=="function"){let g=a.ethereum;q(g)}})})();})(); diff --git a/chrome-extension/src/background/chains/hiveHandler.ts b/chrome-extension/src/background/chains/hiveHandler.ts index ad045cb..a0c733f 100644 --- a/chrome-extension/src/background/chains/hiveHandler.ts +++ b/chrome-extension/src/background/chains/hiveHandler.ts @@ -133,19 +133,48 @@ async function hiveTransfer( requestInfo: any, requireApproval: (networkId: string, requestInfo: any, chain: any, method: string, params: any) => Promise, ) { - const { to, amount, memo, currency } = params[0] || {}; + const { username, to, amount, memo, currency, enforce } = params[0] || {}; if (!to || !amount) throw createProviderRpcError(-32602, 'Hive transfer requires { to, amount }'); // ponytail: HIVE only — HBD needs Pioneer broadcast support first - if (currency && currency !== 'HIVE') { - throw createProviderRpcError(4200, `Only HIVE transfers are supported (got ${currency})`); + if (currency !== 'HIVE') { + throw createProviderRpcError(4200, `Only HIVE transfers are supported (got ${currency ?? 'no currency'})`); + } + // Keychain contract: a memo starting with '#' must be encrypted with the + // memo key. We can't do that yet — reject rather than leak it on-chain + // as plaintext forever. + if (typeof memo === 'string' && memo.startsWith('#')) { + throw createProviderRpcError( + 4200, + 'Encrypted memos (starting with "#") are not supported — the memo would be broadcast as public plaintext', + ); + } + // Exact decimal-string validation (Keychain requires 3 decimals); no + // parseFloat — "1foo" and rounding of "0.0006" must be rejected, not + // silently coerced into a different transfer. + if (typeof amount !== 'string' || !/^\d+(\.\d{1,3})?$/.test(amount)) { + throw createProviderRpcError( + -32602, + `Invalid amount "${amount}" — expected a decimal string with up to 3 decimals`, + ); + } + const [whole, frac = ''] = amount.split('.'); + const amountBase = Number(whole) * 10 ** HIVE_DECIMALS + Number(frac.padEnd(HIVE_DECIMALS, '0')); + if (!Number.isSafeInteger(amountBase) || amountBase <= 0) { + throw createProviderRpcError(-32602, `Invalid amount "${amount}" — must be greater than zero`); } - const parsed = parseFloat(amount); - if (!isFinite(parsed) || parsed <= 0) throw createProviderRpcError(-32602, `Invalid amount: ${amount}`); - const amountBase = Math.round(parsed * 10 ** HIVE_DECIMALS); const amountStr = (amountBase / 10 ** HIVE_DECIMALS).toFixed(HIVE_DECIMALS) + ' HIVE'; await requireHiveFirmware('Hive transfer'); const from = await getHiveAccount(); + // Keychain semantics: the dApp may name the sending account. We control + // exactly one account (index 0), so any mismatch is a hard reject — never + // report success for a payment from a different account than requested. + if (username && username !== from.name) { + throw createProviderRpcError( + 4100, + `This KeepKey controls @${from.name}, not @${username}${enforce ? ' (account enforced by the dApp)' : ''}`, + ); + } const txParamsResp = await fetch(`${PIONEER_URL}/api/v1/hive/tx-params`, { signal: AbortSignal.timeout(20_000), @@ -158,11 +187,18 @@ async function hiveTransfer( const event = buildEvent(requestInfo, 'transfer', params); (event as any).unsignedTx = { from: from.name, - to, - amount: amountStr, memo: memo || '', refBlockNum: txParamsResp.refBlockNum, expiration: txParamsResp.expirationIso, + caip: 'hive:beeab0de/slip44:1275', + // Shape the generic approval renderer reads (RequestDetailsCard): + // amount in base units, divided by 10^decimals for display. + payment: { + destination: to, + amount: amountBase, + decimals: HIVE_DECIMALS, + symbol: 'HIVE', + }, }; await requestUserApproval(event, requestInfo, 'transfer', params, requireApproval); diff --git a/chrome-extension/src/background/index.ts b/chrome-extension/src/background/index.ts index f554152..74c78e3 100644 --- a/chrome-extension/src/background/index.ts +++ b/chrome-extension/src/background/index.ts @@ -13,6 +13,7 @@ import { handleSwapMessage, resolveAddress } from './swapHandler'; import { startSwapEventStream, stopSwapEventStream } from './swapEventStream'; import { resetTonState, prefetchTonAddress } from './chains/tonHandler'; import { resetTronState, prefetchTronPubkey } from './chains/tronHandler'; +import { resetHiveState } from './chains/hiveHandler'; import { handleWalletRequest } from './methods'; import { setApprovalBadge } from './popup'; import { fetchJsonWithTimeout } from './fetchUtils'; @@ -162,6 +163,7 @@ async function handleDeviceSwitch(newDeviceInfo: any) { resetSolanaState(); resetTronState(); resetTonState(); + resetHiveState(); // Re-fetch against the new device. refreshPubkeys re-probes and pulls // a fresh pubkey batch, then updates state.initialized. @@ -266,6 +268,7 @@ async function checkKeepKey() { resetSolanaState(); resetTronState(); resetTonState(); + resetHiveState(); } KEEPKEY_STATE = 4; // Set state to errored updateIcon(); @@ -936,6 +939,7 @@ const onStart = async function () { resetSolanaState(); // clear stale cached address before re-init resetTronState(); resetTonState(); + resetHiveState(); await wallet.init(); console.log(tag, 'Wallet initialized'); diff --git a/chrome-extension/src/injected/hive-provider.ts b/chrome-extension/src/injected/hive-provider.ts index 5130c40..0d44e13 100644 --- a/chrome-extension/src/injected/hive-provider.ts +++ b/chrome-extension/src/injected/hive-provider.ts @@ -64,7 +64,7 @@ export function createHiveKeychainShim(walletRequest: WalletRequestFn) { }); } - return { + const shim: Record = { current_id: 1, /** dApp presence check — Keychain calls back with no arguments. */ @@ -88,39 +88,50 @@ export function createHiveKeychainShim(walletRequest: WalletRequestFn) { const data = { type: 'transfer', username: account, to, amount, memo, enforce, currency, rpc }; dispatch('hive_transfer', data, callback); }, + }; - // ── Unsupported Keychain surface — fail fast, keychain-style ────── - requestSignBuffer: function (_a: string, _m: string, _k: string, callback: KeychainCallback) { - notSupported('signBuffer', { type: 'signBuffer' }, callback); - }, - requestBroadcast: function (_a: string, _ops: any[], _k: string, callback: KeychainCallback) { - notSupported('broadcast', { type: 'broadcast' }, callback); - }, - requestSignTx: function (_a: string, _tx: any, _k: string, callback: KeychainCallback) { - notSupported('signTx', { type: 'signTx' }, callback); - }, - requestVote: function (_a: string, _p: string, _au: string, _w: number, callback: KeychainCallback) { - notSupported('vote', { type: 'vote' }, callback); - }, - requestCustomJson: function ( - _a: string, - _id: string, - _k: string, - _j: string, - _d: string, - callback: KeychainCallback, - ) { - notSupported('custom_json', { type: 'custom' }, callback); - }, - requestPost: function (...args: any[]) { + // The rest of Keychain's public surface (per public/hive_keychain.js). + // Every method must exist — dApps call them unconditionally and a missing + // function is a TypeError instead of a graceful keychain-style failure. + // The callback can sit at a different position per method (trailing + // optional rpc/params), so find it rather than fixing an arity. + const UNSUPPORTED = [ + 'requestEncodeMessage', + 'requestEncodeWithKeys', + 'requestVerifyKey', + 'requestSignBuffer', + 'requestAddAccountAuthority', + 'requestRemoveAccountAuthority', + 'requestAddKeyAuthority', + 'requestRemoveKeyAuthority', + 'requestBroadcast', + 'requestSignTx', + 'requestSignedCall', + 'requestPost', + 'requestVote', + 'requestCustomJson', + 'requestSendToken', + 'requestDelegation', + 'requestWitnessVote', + 'requestProxy', + 'requestPowerUp', + 'requestPowerDown', + 'requestCreateClaimedAccount', + 'requestCreateProposal', + 'requestRemoveProposal', + 'requestUpdateProposalVote', + 'requestAddAccount', + 'requestConversion', + 'requestRecurrentTransfer', + 'requestSavingsOperation', + 'requestSwap', + ]; + for (const name of UNSUPPORTED) { + shim[name] = (...args: any[]) => { const callback = args.find(a => typeof a === 'function'); - notSupported('post', { type: 'post' }, callback); - }, - requestEncodeMessage: function (_u: string, _r: string, _m: string, _k: string, callback: KeychainCallback) { - notSupported('encode', { type: 'encode' }, callback); - }, - requestVerifyKey: function (_a: string, _m: string, _k: string, callback: KeychainCallback) { - notSupported('decode', { type: 'decode' }, callback); - }, - }; + notSupported(name.replace(/^request/, ''), { type: name }, callback); + }; + } + + return shim; } diff --git a/chrome-extension/src/injected/injected.ts b/chrome-extension/src/injected/injected.ts index 5023727..e0f03de 100644 --- a/chrome-extension/src/injected/injected.ts +++ b/chrome-extension/src/injected/injected.ts @@ -665,14 +665,13 @@ import { createHiveKeychainShim } from './hive-provider'; } // Hive Keychain shim — window.hive_keychain is the de-facto discovery - // API for Hive dApps. Mount only if the real Keychain isn't installed. + // API for Hive dApps. Mount only if the real Keychain isn't installed, + // and keep the property writable: we inject at document_start but the + // real Keychain injects at document_idle — it must be able to replace + // our (partial) shim with its full implementation. try { if (!(kWindow as any).hive_keychain) { - Object.defineProperty(kWindow, 'hive_keychain', { - value: createHiveKeychainShim(walletRequest), - writable: false, - configurable: true, - }); + (kWindow as any).hive_keychain = createHiveKeychainShim(walletRequest); } } catch (_e) { // swallow; Hive registration is best-effort From 1734518b144ec898082e36f1190b097e931a73c5 Mon Sep 17 00:00:00 2001 From: highlander Date: Tue, 14 Jul 2026 23:01:54 -0300 Subject: [PATCH 3/3] fix(hive): Keychain-shaped transfer result + reject enforce without username MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - return { id, tx_id, confirmed } (matches hive-tx.utils.ts) instead of { txid } so dApps reading response.result.id / .tx_id work - enforce=true with no username is malformed — reject with -32602 Co-Authored-By: Claude Fable 5 --- chrome-extension/src/background/chains/hiveHandler.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/chrome-extension/src/background/chains/hiveHandler.ts b/chrome-extension/src/background/chains/hiveHandler.ts index a0c733f..8b4b1fe 100644 --- a/chrome-extension/src/background/chains/hiveHandler.ts +++ b/chrome-extension/src/background/chains/hiveHandler.ts @@ -169,6 +169,9 @@ async function hiveTransfer( // Keychain semantics: the dApp may name the sending account. We control // exactly one account (index 0), so any mismatch is a hard reject — never // report success for a payment from a different account than requested. + if (enforce === true && !username) { + throw createProviderRpcError(-32602, 'enforce=true requires a username'); + } if (username && username !== from.name) { throw createProviderRpcError( 4100, @@ -269,7 +272,13 @@ async function hiveTransfer( }) .catch(() => {}); - return { txid: bData.txid }; + // Keychain-shaped result — dApps read response.result.id / .tx_id + // (hive-tx.utils.ts returns { id, tx_id, confirmed }). + return { + id: bData.txid, + tx_id: bData.txid, + confirmed: Boolean(bData.blockNum ?? bData.block_num), + }; } export const handleHiveRequest = async (