UNPKG

@windoge98/pnp-coinbase

Version:

Coinbase wallet adapter for Plug-n-Play

2 lines (1 loc) 10.6 kB
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@windoge98/plug-n-play"),t=require("@dfinity/agent"),i=require("@solana/wallet-adapter-coinbase"),n=require("@solana/wallet-adapter-base"),a=require("@solana/web3.js"),s=require("@dfinity/identity"),r=require("bs58"),o=({IDL:e})=>{const t=e.Variant({IncludeUriInSeed:e.Null,DisablePrincipalToSolMapping:e.Null,DisableSolToPrincipalMapping:e.Null});e.Record({uri:e.Text,runtime_features:e.Opt(e.Vec(t)),domain:e.Text,statement:e.Opt(e.Text),scheme:e.Opt(e.Text),salt:e.Text,session_expires_in:e.Opt(e.Nat64),targets:e.Opt(e.Vec(e.Text)),chain_id:e.Opt(e.Text),sign_in_expires_in:e.Opt(e.Nat64)});const i=e.Vec(e.Nat8),n=e.Text,a=e.Variant({Ok:n,Err:e.Text}),s=e.Variant({Ok:i,Err:e.Text}),r=e.Vec(e.Nat8),o=r,c=e.Nat64,l=e.Record({pubkey:r,targets:e.Opt(e.Vec(e.Principal)),expiration:c}),d=e.Record({signature:e.Vec(e.Nat8),delegation:l}),h=e.Variant({Ok:d,Err:e.Text}),w=e.Text,p=e.Text,u=r,g=e.Record({user_canister_pubkey:u,expiration:c}),b=e.Variant({Ok:g,Err:e.Text}),A=e.Record({uri:e.Text,issued_at:e.Nat64,domain:e.Text,statement:e.Text,version:e.Nat32,chain_id:e.Text,address:n,nonce:e.Text,expiration_time:e.Nat64}),y=e.Variant({Ok:A,Err:e.Text});return e.Service({get_address:e.Func([i],[a],["query"]),get_caller_address:e.Func([],[a],["query"]),get_principal:e.Func([n],[s],["query"]),siws_get_delegation:e.Func([n,o,c],[h],["query"]),siws_login:e.Func([w,n,o,p],[b],[]),siws_prepare_login:e.Func([n],[y],[])})},c=class extends e.BaseSiwxAdapter{constructor(t){super(t),this.walletName="Coinbase Wallet",this.id="coinbase",this.coinbaseAdapter=null,this.solanaAddress=null,this.connectingPromise=null,this.handleCoinbaseConnect=e=>{this.solanaAddress=e.toBase58(),this.logger.debug("Coinbase wallet connected",{address:this.solanaAddress})},this.handleCoinbaseDisconnect=()=>{this.state!==e.Adapter.Status.DISCONNECTING&&this.state!==e.Adapter.Status.DISCONNECTED&&this.disconnect()},this.handleCoinbaseError=t=>{this.logger.error("Coinbase wallet error",t,{wallet:this.walletName}),this.setState(e.Adapter.Status.ERROR),this.disconnect()},this.id=t.adapter.id,this.walletName=t.adapter.walletName,this.logo=t.adapter.logo,this.config=t.config,this.initializeConnection(),this.isBrowser()&&this.initializeCoinbaseAdapter(),this.setState(e.Adapter.Status.READY)}resolveProviderCanisterId(){const e=this.config,t=e.providerCanisterId||e.siwsProviderCanisterId;if(!t)throw new Error("SIWS provider canister ID not configured.");return String(t)}isBrowser(){return"undefined"!=typeof window&&void 0!==window.document}initializeConnection(){const e=this.config.solanaNetwork||n.WalletAdapterNetwork.Mainnet,t=this.config.rpcEndpoint||(e===n.WalletAdapterNetwork.Mainnet?"https://api.mainnet-beta.solana.com":"https://api.devnet.solana.com");this.solanaConnection=new a.Connection(t)}initializeCoinbaseAdapter(){this.coinbaseAdapter=new i.CoinbaseWalletAdapter,this.setupWalletListeners()}setupWalletListeners(){this.coinbaseAdapter&&(this.coinbaseAdapter.on("connect",this.handleCoinbaseConnect),this.coinbaseAdapter.on("disconnect",this.handleCoinbaseDisconnect),this.coinbaseAdapter.on("error",this.handleCoinbaseError))}removeWalletListeners(){this.coinbaseAdapter&&(this.coinbaseAdapter.off("connect",this.handleCoinbaseConnect),this.coinbaseAdapter.off("disconnect",this.handleCoinbaseDisconnect),this.coinbaseAdapter.off("error",this.handleCoinbaseError))}destroy(){this.removeWalletListeners()}async isConnected(){return!!this.coinbaseAdapter&&(this.coinbaseAdapter.connected&&null!==this.identity&&!this.identity.getPrincipal().isAnonymous())}async connect(){return this.connectingPromise||(this.connectingPromise=(async()=>{if(!this.isBrowser())throw new Error("Cannot connect to Coinbase wallet in non-browser environment");if(this.resolveProviderCanisterId(),this.identity&&this.state===e.Adapter.Status.CONNECTED){const t=this.identity.getPrincipal();return{owner:t.toText(),subaccount:e.deriveAccountId(t)}}this.setState(e.Adapter.Status.CONNECTING);try{if(this.coinbaseAdapter||this.initializeCoinbaseAdapter(),!this.coinbaseAdapter)throw new Error("Failed to initialize Coinbase adapter");if(!this.coinbaseAdapter.connected){if(this.logger.debug("Connecting to Coinbase wallet..."),this.coinbaseAdapter.readyState===n.WalletReadyState.NotDetected)throw new Error("Coinbase wallet is not installed. Please install the Coinbase Wallet browser extension.");try{await this.coinbaseAdapter.connect()}catch(t){if(this.logger.error("Coinbase connection error",t,{wallet:this.walletName}),"WalletWindowClosedError"===t.name||t.message?.includes("User rejected the request")||t.message?.includes("Wallet closed"))throw this.setState(e.Adapter.Status.DISCONNECTED),new Error("Connection cancelled by user");throw this.setState(e.Adapter.Status.ERROR),t}}if(!this.coinbaseAdapter.publicKey)throw new Error("Coinbase wallet connected but no public key available");if(!("signMessage"in this.coinbaseAdapter))throw new Error("Coinbase wallet does not support message signing required for SIWS");const i=this.coinbaseAdapter.publicKey.toBase58();this.solanaAddress=i,await this.storeExternalAddress(`${this.id}-solana-address`,i);const{identity:a,sessionKey:s}=await this.performSiwsLogin(i,this.coinbaseAdapter);this.identity=a,this.sessionKey=s;const r=a.getPrincipal();return this.setState(e.Adapter.Status.CONNECTED),{owner:r.toText(),subaccount:e.deriveAccountId(r)}}catch(t){throw this.logger.error("Connect process failed",t,{wallet:this.walletName}),this.setState(e.Adapter.Status.ERROR),t}finally{this.connectingPromise=null}})()),this.connectingPromise}async createSiwsProviderActor(e){const i=e??new t.AnonymousIdentity;return this.createProviderActor(o,i)}async signSiwsMessage(t,i){const n=e.formatSiwsMessage(t),a=(new TextEncoder).encode(n);if(!("signMessage"in i))throw new Error("Coinbase wallet does not support signMessage.");const s=await i.signMessage(a);if(s instanceof Uint8Array)return r.encode(s);if(s instanceof ArrayBuffer)return r.encode(new Uint8Array(s));try{const e=Array.isArray(s)?s:Object.values(s);return r.encode(new Uint8Array(e))}catch(o){throw this.logger.error("Error encoding signature",o,{wallet:this.walletName}),new Error(`Failed to encode signature from Coinbase: ${o.message}`)}}async performSiwsLogin(e,t){const i=await this.createSiwsProviderActor(),n=await i.siws_prepare_login(e);if("Err"in n)throw new Error(`SIWS Prepare Login failed: ${n.Err}`);const a=await this.signSiwsMessage(n.Ok,t),r=s.Ed25519KeyIdentity.generate(),o=r.getPublicKey().toDer(),c=await i.siws_login(a,e,new Uint8Array(o),n.Ok.nonce);if("Err"in c)throw new Error(`SIWS Login failed: ${c.Err}`);const l=await i.siws_get_delegation(e,new Uint8Array(o),c.Ok.expiration);if("Err"in l)throw new Error(`SIWS Get Delegation failed: ${l.Err}`);return{identity:this.createDelegationIdentity(l.Ok,r,new Uint8Array(c.Ok.user_canister_pubkey).buffer),sessionKey:r}}async disconnectInternal(){if(await super.disconnectInternal(),this.coinbaseAdapter)try{this.coinbaseAdapter.connected&&(this.removeWalletListeners(),await this.coinbaseAdapter.disconnect(),this.setupWalletListeners())}catch(e){this.logger.warn("Error during Coinbase disconnect",{error:e,wallet:this.walletName})}this.identity=null,this.solanaAddress=null}async getPrincipal(){if(!this.identity)throw new Error("Not connected or SIWS flow not completed.");return this.identity.getPrincipal().toText()}async getAccountId(){const t=await this.getPrincipal();if(!t)throw new Error("Principal not available to derive account ID");return e.deriveAccountId(t)}async getSolanaAddress(){if(!this.solanaAddress)throw new Error("Not connected or Solana address not available.");return this.solanaAddress}async getAddresses(){const t=this.identity?.getPrincipal();return{sol:{address:this.solanaAddress,network:this.config.solanaNetwork},icp:{address:t?.toText(),subaccount:t?e.deriveAccountId(t):void 0}}}createActorInternal(e,t,i){if((i?.requiresSigning??!0)&&!this.identity)throw new Error("Cannot create signed actor: Not connected or SIWS flow not completed.");const n=this.buildHttpAgentSync({identity:this.identity??void 0});return this.createActorWithAgent(n,e,t)}async getSolBalance(){if(!this.coinbaseAdapter?.publicKey)throw new Error("Wallet not connected");try{const e=await this.solanaConnection.getBalance(this.coinbaseAdapter.publicKey);return{amount:e/a.LAMPORTS_PER_SOL}}catch(e){throw this.logger.error("Failed to get SOL balance",e,{wallet:this.walletName}),e}}async estimateTransactionFee(e){if(!this.coinbaseAdapter?.publicKey)throw new Error("Wallet not connected");try{const{blockhash:t}=await this.solanaConnection.getLatestBlockhash();e.recentBlockhash=t,e.feePayer=this.coinbaseAdapter.publicKey;const i=e.compileMessage(),n=await this.solanaConnection.getFeeForMessage(i,"confirmed");if(null===n.value)throw new Error("Unable to estimate fee");return n.value/a.LAMPORTS_PER_SOL}catch(t){throw this.logger.error("Failed to estimate transaction fee",t,{wallet:this.walletName}),t}}async getTransactionStatus(e){try{const t=await this.solanaConnection.getSignatureStatus(e);return t.value?{confirmed:"confirmed"===t.value.confirmationStatus||"finalized"===t.value.confirmationStatus,slot:t.value.slot,err:t.value.err}:{confirmed:!1}}catch(t){throw this.logger.error("Failed to get transaction status",t,{wallet:this.walletName,signature:e}),t}}async onStorageRestored(e,t){const i=await this.readExternalAddress(`${this.id}-solana-address`);i&&(this.solanaAddress=i)}async onClearStoredSession(){this.solanaAddress=null,await this.storage.remove(`${this.id}-solana-address`)}};c.supportedChains=[e.Adapter.Chain.ICP,e.Adapter.Chain.SOL];let l=c;const d=e.createAdapterExtension({coinbase:{id:"coinbase",enabled:!1,walletName:"Coinbase Wallet",logo:"data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%201024%201024'%3e%3crect%20width='1024'%20height='1024'%20rx='512'%20fill='%230052FF'/%3e%3cpath%20fill='%23FFF'%20d='M512%20692c-99.4%200-180-80.6-180-180s80.6-180%20180-180c89.1%200%20163.2%2065%20177.5%20150h-90c-12.6-34.4-45.6-60-85.5-60-49.7%200-90%2040.3-90%2090s40.3%2090%2090%2090c39.9%200%2072.9-25.6%2085.5-60h90C675.2%20627%20602.1%20692%20512%20692z'/%3e%3c/svg%3e",website:"https://www.coinbase.com/wallet",chain:"SOL",adapter:l,config:{enabled:!1,solanaNetwork:n.WalletAdapterNetwork.Mainnet,siwsProviderCanisterId:""}}});Object.defineProperty(exports,"formatSiwsMessage",{enumerable:!0,get:()=>e.formatSiwsMessage}),exports.CoinbaseAdapter=l,exports.CoinbaseExtension=d;