Skip to content

feat: initial support for stacks connect#585

Draft
r1n04h wants to merge 4 commits into
masterfrom
feat-stacks-connect
Draft

feat: initial support for stacks connect#585
r1n04h wants to merge 4 commits into
masterfrom
feat-stacks-connect

Conversation

@r1n04h
Copy link
Copy Markdown
Contributor

@r1n04h r1n04h commented Jan 20, 2026

Note

Initial integration of Stacks Connect and provider injection.

  • Injects LayerzWalletProvider exposing request() that proxies to window.ethereum.request
  • Registers Layerz Wallet metadata in window.wbip_providers and window.btc_providers
  • Adds @stacks/connect and related AppKit/WalletConnect dependencies (lockfile updates)
  • Pins create-hash to 1.2.0

Written by Cursor Bugbot for commit 5b029f1. This will update automatically on new commits. Configure here.

@r1n04h r1n04h added the WIP label Jan 20, 2026
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

Comment thread mobile/assets/js/inpage-bridge.jstxt Outdated
@@ -1,2 +1,2 @@
(()=>{"use strict";class e{_chainId="0x1";_eventSubscriptions={};_requestId=0;async backgroundRPC(e){const n=this._requestId++;return new Promise(((t,i)=>{let o=!1;document.addEventListener("LayerzWalletExtension",(function(e){if(o)return;const c=JSON.parse(e.detail);if("webpage"===c.for&&Number(c.id)===n){if(o=!0,c.error)return console.log("rejecting promise.................. error=",c.error),void i(c.error);console.log("resolving promise.................. response=",c.response),t(c.response)}}));const c={...e,for:"contentScript",id:n,from:window.location.hostname};document.dispatchEvent(new CustomEvent("LayerzWalletExtension",{detail:JSON.stringify(c)}))}))}request=e=>this.backgroundRPC(e);constructor(){const e=this;console.log("setting up a listener for callback events::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::"),document.addEventListener("LayerzWalletExtension",(function(n){const t=JSON.parse(n.detail);if("eventCallback"!==t.type)return;console.log("got event callback!",t);const i=e._eventSubscriptions[t.event]??[];console.log("we have",i.length,"to notify");for(const e of i)console.log("actually triggering the callback with the event payload"),e(t.arg)}))}on(e,n){return console.log("Dapp subscribes to an event",e),"connect"===e?(setTimeout((()=>n({chainId:this._chainId})),1e3),this):(this._eventSubscriptions[e]=this._eventSubscriptions[e]||[],this._eventSubscriptions[e].push(n),this)}removeListener(){}send(e,n){return e.method?this.request(e):"string"==typeof e?this.request({method:e,params:n}):void 0}sendAsync(e,n){}_metamask={isUnlocked:function(){return Promise.resolve(!0)}};get chainId(){return this._chainId}isMetaMask=!0;networkVersion="?";selectedAddress="???";enable(){return alert("enable"),Promise.resolve([])}isConnected(){return!0}_events={};_eventsCount=0;_maxListeners=0;_log={};_state={};_handleAccountsChanged=()=>{};_handleConnect=()=>{};_handleChainChanged=()=>{};_handleDisconnect=()=>{};_handleUnlockStateChanged=()=>{};_rpcRequest=()=>{};_rpcEngine={};_handleStreamDisconnect=()=>{};_jsonRpcConnection={};_sentWarnings={};_sendSync=()=>{};_warnOfDeprecation=()=>{}}window.ethereum=new e;const n="6c617965-727a-7761-6C6C-657400000000";document.addEventListener("LayerzWalletExtension",(async function(e){"contentScript"===JSON.parse(e.detail).for&&window.ReactNativeWebView.postMessage(e.detail)})),window.addEventListener("load",(function(){const t=new e;function i(){console.log("announceProvider()");const e={uuid:n,name:"Layerz Wallet",icon:"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTg2IiBoZWlnaHQ9IjE2NiIgdmlld0JveD0iMCAwIDE4NiAxNjYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxODYiIGhlaWdodD0iMTY2IiBmaWxsPSIjMDAwMDAwIi8+CjxwYXRoIGQ9Ik05Ni42NTU0IDEwMC42NDJIMjMuNTIyM0MxNy4zODg4IDEwMC42NDIgMTQuMjg5MSA5My4yNTkgMTguNTcyMyA4OC44ODJMNjMuMjA3MyA0My4yNTEyQzY3LjU2NTYgMzguNzk5IDczLjUzMDEgMzYuMjkxIDc5Ljc1NzYgMzYuMjkxSDE1Mi44OTFDMTU5LjAyNCAzNi4yOTEgMTYyLjEyNCA0My42NzM5IDE1Ny44NDEgNDguMDUwOUwxMTMuMjA2IDkzLjY4MTdDMTA4Ljg0NyA5OC4xMzM5IDEwMi44ODMgMTAwLjY0MiA5Ni42NTU0IDEwMC42NDJaIiBmaWxsPSJ1cmwoI3BhaW50MF9saW5lYXJfMl8zMzMyKSIvPgo8cGF0aCBkPSJNODkuNDY5NSA2NS4zNTc3SDE2Mi42MDNDMTY4LjczNiA2NS4zNTc3IDE3MS44MzYgNzIuNzQwNSAxNjcuNTUzIDc3LjExNzZMMTIyLjkxOCAxMjIuNzQ4QzExOC41NTkgMTI3LjIgMTEyLjU5NSAxMjkuNzA4IDEwNi4zNjcgMTI5LjcwOEgzMy4yMzQzQzI3LjEwMDcgMTI5LjcwOCAyNC4wMDEgMTIyLjMyNiAyOC4yODQyIDExNy45NDhMNzIuOTE5MiA3Mi4zMTc4Qzc3LjI3NzUgNjcuODY1NiA4My4yNDIgNjUuMzU3NyA4OS40Njk1IDY1LjM1NzdaIiBmaWxsPSJ1cmwoI3BhaW50MV9saW5lYXJfMl8zMzMyKSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzJfMzMzMiIgeDE9IjE2LjY2MzgiIHkxPSIxMDAiIHgyPSIxNjAuMjA1IiB5Mj0iNDAuOTYzMiIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSJ3aGl0ZSIgc3RvcC1vcGFjaXR5PSIwIi8+CjxzdG9wIG9mZnNldD0iMC44NSIgc3RvcC1jb2xvcj0id2hpdGUiLz4KPC9saW5lYXJHcmFkaWVudD4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDFfbGluZWFyXzJfMzMzMiIgeDE9IjE2OS45MDciIHkxPSI2NS44MzEiIHgyPSIyNi4zNjY0IiB5Mj0iMTI0Ljg2OCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSJ3aGl0ZSIgc3RvcC1vcGFjaXR5PSIwIi8+CjxzdG9wIG9mZnNldD0iMC44NSIgc3RvcC1jb2xvcj0id2hpdGUiLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4K",rdns:"com.layerzwallet.layerzwallet"},i=Object.freeze({info:e,provider:t});window.dispatchEvent(new CustomEvent("eip6963:announceProvider",{detail:i}))}t.signer={uri:n},window.addEventListener("eip6963:requestProvider",(e=>{console.log("Received EIP-6963 provider request"),i()})),i(),console.log("announceProvider() done")}))})();
(()=>{"use strict";class e{_chainId="0x1";_eventSubscriptions={};_requestId=0;async backgroundRPC(e){const i=this._requestId++;return new Promise((t,n)=>{let o=!1;document.addEventListener("LayerzWalletExtension",function(e){if(o)return;const c=JSON.parse(e.detail);if("webpage"===c.for&&Number(c.id)===i){if(o=!0,c.error)return console.log("rejecting promise.................. error=",c.error),void n(c.error);console.log("resolving promise.................. response=",c.response),t(c.response)}});const c={...e,for:"contentScript",id:i,from:window.location.hostname};document.dispatchEvent(new CustomEvent("LayerzWalletExtension",{detail:JSON.stringify(c)}))})}request=e=>this.backgroundRPC(e);constructor(){const e=this;console.log("setting up a listener for callback events::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::"),document.addEventListener("LayerzWalletExtension",function(i){const t=JSON.parse(i.detail);if("eventCallback"!==t.type)return;console.log("got event callback!",t);const n=e._eventSubscriptions[t.event]??[];console.log("we have",n.length,"to notify");for(const e of n)console.log("actually triggering the callback with the event payload"),e(t.arg)})}on(e,i){return console.log("Dapp subscribes to an event",e),"connect"===e?(setTimeout(()=>i({chainId:this._chainId}),1e3),this):(this._eventSubscriptions[e]=this._eventSubscriptions[e]||[],this._eventSubscriptions[e].push(i),this)}removeListener(){}send(e,i){return e.method?this.request(e):"string"==typeof e?this.request({method:e,params:i}):void 0}sendAsync(e,i){}_metamask={isUnlocked:function(){return Promise.resolve(!0)}};get chainId(){return this._chainId}isMetaMask=!0;networkVersion="?";selectedAddress="???";enable(){return alert("enable"),Promise.resolve([])}isConnected(){return!0}_events={};_eventsCount=0;_maxListeners=0;_log={};_state={};_handleAccountsChanged=()=>{};_handleConnect=()=>{};_handleChainChanged=()=>{};_handleDisconnect=()=>{};_handleUnlockStateChanged=()=>{};_rpcRequest=()=>{};_rpcEngine={};_handleStreamDisconnect=()=>{};_jsonRpcConnection={};_sentWarnings={};_sendSync=()=>{};_warnOfDeprecation=()=>{}}window.ethereum=new e,window.LayerzWalletProvider={async request(e,i){alert("request() method="+e+" params="+JSON.stringify(i));try{const t=await window.ethereum.request({method:e,params:i});return alert("replying with result="+JSON.stringify(t)),{jsonrpc:"2.0",result:t}}catch(e){return{jsonrpc:"2.0",error:e}}}},window.wbip_providers=window.wbip_providers||[],window.btc_providers=window.btc_providers||[];const i={id:"LayerzWalletProvider",name:"Layerz Wallet",icon:"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTg2IiBoZWlnaHQ9IjE2NiIgdmlld0JveD0iMCAwIDE4NiAxNjYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxODYiIGhlaWdodD0iMTY2IiBmaWxsPSIjMDAwMDAwIi8+CjxwYXRoIGQ9Ik05Ni42NTU0IDEwMC42NDJIMjMuNTIyM0MxNy4zODg4IDEwMC42NDIgMTQuMjg5MSA5My4yNTkgMTguNTcyMyA4OC44ODJMNjMuMjA3MyA0My4yNTEyQzY3LjU2NTYgMzguNzk5IDczLjUzMDEgMzYuMjkxIDc5Ljc1NzYgMzYuMjkxSDE1Mi44OTFDMTU5LjAyNCAzNi4yOTEgMTYyLjEyNCA0My42NzM5IDE1Ny44NDEgNDguMDUwOUwxMTMuMjA2IDkzLjY4MTdDMTA4Ljg0NyA5OC4xMzM5IDEwMi44ODMgMTAwLjY0MiA5Ni42NTU0IDEwMC42NDJaIiBmaWxsPSJ1cmwoI3BhaW50MF9saW5lYXJfMl8zMzMyKSIvPgo8cGF0aCBkPSJNODkuNDY5NSA2NS4zNTc3SDE2Mi42MDNDMTY4LjczNiA2NS4zNTc3IDE3MS44MzYgNzIuNzQwNSAxNjcuNTUzIDc3LjExNzZMMTIyLjkxOCAxMjIuNzQ4QzExOC41NTkgMTI3LjIgMTEyLjU5NSAxMjkuNzA4IDEwNi4zNjcgMTI5LjcwOEgzMy4yMzQzQzI3LjEwMDcgMTI5LjcwOCAyNC4wMDEgMTIyLjMyNiAyOC4yODQyIDExNy45NDhMNzIuOTE5MiA3Mi4zMTc4Qzc3LjI3NzUgNjcuODY1NiA4My4yNDIgNjUuMzU3NyA4OS40Njk1IDY1LjM1NzdaIiBmaWxsPSJ1cmwoI3BhaW50MV9saW5lYXJfMl8zMzMyKSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzJfMzMzMiIgeDE9IjE2LjY2MzgiIHkxPSIxMDAiIHgyPSIxNjAuMjA1IiB5Mj0iNDAuOTYzMiIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSJ3aGl0ZSIgc3RvcC1vcGFjaXR5PSIwIi8+CjxzdG9wIG9mZnNldD0iMC44NSIgc3RvcC1jb2xvcj0id2hpdGUiLz4KPC9saW5lYXJHcmFkaWVudD4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDFfbGluZWFyXzJfMzMzMiIgeDE9IjE2OS45MDciIHkxPSI2NS44MzEiIHgyPSIyNi4zNjY0IiB5Mj0iMTI0Ljg2OCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSJ3aGl0ZSIgc3RvcC1vcGFjaXR5PSIwIi8+CjxzdG9wIG9mZnNldD0iMC44NSIgc3RvcC1jb2xvcj0id2hpdGUiLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4K",webUrl:"https://layerzwallet.com",chromeWebStoreUrl:"https://play.google.com/store/apps/details?id=com.layerzwallet.mobile&pli=1",mozillaAddOnsUrl:"https://play.google.com/store/apps/details?id=com.layerzwallet.mobile&pli=1",googlePlayStoreUrl:"https://play.google.com/store/apps/details?id=com.layerzwallet.mobile&pli=1",iOSAppStoreUrl:"https://apps.apple.com/us/app/layerz-wallet/id6745974808"};window.wbip_providers.push(i),window.btc_providers.push(i);const t="6c617965-727a-7761-6C6C-657400000000";document.addEventListener("LayerzWalletExtension",async function(e){"contentScript"===JSON.parse(e.detail).for&&window.ReactNativeWebView.postMessage(e.detail)}),window.addEventListener("load",function(){const i=new e;function n(){console.log("announceProvider()");const e={uuid:t,name:"LZ WalletTTTTTTTTTTTTTTTTtttttttttttttttt",icon:"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTg2IiBoZWlnaHQ9IjE2NiIgdmlld0JveD0iMCAwIDE4NiAxNjYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxODYiIGhlaWdodD0iMTY2IiBmaWxsPSIjMDAwMDAwIi8+CjxwYXRoIGQ9Ik05Ni42NTU0IDEwMC42NDJIMjMuNTIyM0MxNy4zODg4IDEwMC42NDIgMTQuMjg5MSA5My4yNTkgMTguNTcyMyA4OC44ODJMNjMuMjA3MyA0My4yNTEyQzY3LjU2NTYgMzguNzk5IDczLjUzMDEgMzYuMjkxIDc5Ljc1NzYgMzYuMjkxSDE1Mi44OTFDMTU5LjAyNCAzNi4yOTEgMTYyLjEyNCA0My42NzM5IDE1Ny44NDEgNDguMDUwOUwxMTMuMjA2IDkzLjY4MTdDMTA4Ljg0NyA5OC4xMzM5IDEwMi44ODMgMTAwLjY0MiA5Ni42NTU0IDEwMC42NDJaIiBmaWxsPSJ1cmwoI3BhaW50MF9saW5lYXJfMl8zMzMyKSIvPgo8cGF0aCBkPSJNODkuNDY5NSA2NS4zNTc3SDE2Mi42MDNDMTY4LjczNiA2NS4zNTc3IDE3MS44MzYgNzIuNzQwNSAxNjcuNTUzIDc3LjExNzZMMTIyLjkxOCAxMjIuNzQ4QzExOC41NTkgMTI3LjIgMTEyLjU5NSAxMjkuNzA4IDEwNi4zNjcgMTI5LjcwOEgzMy4yMzQzQzI3LjEwMDcgMTI5LjcwOCAyNC4wMDEgMTIyLjMyNiAyOC4yODQyIDExNy45NDhMNzIuOTE5MiA3Mi4zMTc4Qzc3LjI3NzUgNjcuODY1NiA4My4yNDIgNjUuMzU3NyA4OS40Njk1IDY1LjM1NzdaIiBmaWxsPSJ1cmwoI3BhaW50MV9saW5lYXJfMl8zMzMyKSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzJfMzMzMiIgeDE9IjE2LjY2MzgiIHkxPSIxMDAiIHgyPSIxNjAuMjA1IiB5Mj0iNDAuOTYzMiIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSJ3aGl0ZSIgc3RvcC1vcGFjaXR5PSIwIi8+CjxzdG9wIG9mZnNldD0iMC44NSIgc3RvcC1jb2xvcj0id2hpdGUiLz4KPC9saW5lYXJHcmFkaWVudD4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDFfbGluZWFyXzJfMzMzMiIgeDE9IjE2OS45MDciIHkxPSI2NS44MzEiIHgyPSIyNi4zNjY0IiB5Mj0iMTI0Ljg2OCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSJ3aGl0ZSIgc3RvcC1vcGFjaXR5PSIwIi8+CjxzdG9wIG9mZnNldD0iMC44NSIgc3RvcC1jb2xvcj0id2hpdGUiLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4K",rdns:"com.layerzwallet.layerzwallet"},n=Object.freeze({info:e,provider:i});window.dispatchEvent(new CustomEvent("eip6963:announceProvider",{detail:n}))}i.signer={uri:t},window.addEventListener("eip6963:requestProvider",e=>{console.log("Received EIP-6963 provider request"),n()}),n(),console.log("announceProvider() done")})})();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test wallet name in EIP-6963 provider announcement

High Severity

The EIP-6963 provider info contains a test/debug wallet name "LZ WalletTTTTTTTTTTTTTTTTtttttttttttttttt" instead of "Layerz Wallet". This garbled name will be displayed to users in wallet selection interfaces across all dApps, making the wallet look unprofessional or broken.

Fix in Cursor Fix in Web


try {
const result = await window.ethereum.request({ method: method, params });
alert('replying with result=' + JSON.stringify(result));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug alert() dialogs will interrupt users

High Severity

The window.LayerzWalletProvider.request() function contains alert() calls that will display modal dialogs to users every time a Stacks dApp makes a request. These debugging statements will severely disrupt user experience by requiring manual dismissal of alert boxes during normal wallet operations.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant