@naculus/connector-coinbase 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +257 -0
- package/dist/index.d.ts +257 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +29 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Naculus
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var g=Object.defineProperty;var y=Object.getOwnPropertyDescriptor;var _=Object.getOwnPropertyNames;var I=Object.prototype.hasOwnProperty;var b=(d,e)=>{for(var n in e)g(d,n,{get:e[n],enumerable:!0})},P=(d,e,n,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of _(e))!I.call(d,o)&&o!==n&&g(d,o,{get:()=>e[o],enumerable:!(s=y(e,o))||s.enumerable});return d};var A=d=>P(g({},"__esModule",{value:!0}),d);var x={};b(x,{CoinbaseConnector:()=>f,CoinbaseProviderAdapter:()=>l,createCoinbaseConnector:()=>S});module.exports=A(x);var C=require("@coinbase/wallet-sdk"),t=require("@naculus/connect-core");var l=class{rawProvider;listeners=new Map;constructor(e){this.rawProvider=e}getProvider(){return this.rawProvider}on(e,n){let s=this.listeners.get(e);if(s)s.has(n)||(s.add(n),this.rawProvider.on(e,n));else{let o=new Set;o.add(n),this.listeners.set(e,o),this.rawProvider.on(e,n)}}off(e,n){let s=this.listeners.get(e);s&&(s.delete(n),this.rawProvider.off(e,n),s.size===0&&this.listeners.delete(e))}removeAllListeners(){for(let[e,n]of this.listeners.entries())for(let s of n)this.rawProvider.off(e,s);this.listeners.clear()}cleanup(){this.removeAllListeners()}};var f=class{id="coinbase";name="Coinbase Wallet";kind="eip6963";namespaces=["eip155"];supports={desktop:!0,mobile:!0,deepLink:!0,qr:!0,trustedReconnect:!1};config;sdk;providerAdapter;lastSession;sessionExpiryHandler;connectionMode;constructor(e){if(!e.appName)throw new t.WalletError("invalid_input","CoinbaseConnector requires appName.");this.config={appName:e.appName,appLogoUrl:e.appLogoUrl,appChainIds:e.appChainIds??[1],preference:e.preference??"all",onQRCodeResponse:e.onQRCodeResponse,overrideRpcUrl:e.overrideRpcUrl}}getSDK(){return this.sdk?this.sdk:(this.sdk=new C.CoinbaseWalletSDK({appName:this.config.appName,appLogoUrl:this.config.appLogoUrl??null,appChainIds:this.config.appChainIds}),this.sdk)}getProvider(){let e=this.getSDK(),n={options:this.config.preference},s=e.makeWeb3Provider(n);return this.providerAdapter=new l(s),s}async connect(e){let n=this.getProvider(),s=e&&typeof e=="object"&&!Array.isArray(e)?e:void 0;this.setupEventListeners(n);try{let o=await n.request({method:"eth_requestAccounts"});if(!o||o.length===0)throw new t.WalletError("user_rejected","No accounts returned from Coinbase Wallet.");let r=await n.request({method:"eth_chainId"}),i=Number.parseInt(r,16);this.connectionMode=this.detectConnectionMode(n);let a=`eip155:${i}`,c=(0,t.createEmptySession)({id:crypto.randomUUID(),topic:void 0,walletId:"coinbase-wallet",walletType:"eip6963",namespaces:{eip155:{chains:[a],accounts:o.map(p=>`${a}:${p}`),methods:["eth_sendTransaction","eth_signTransaction","personal_sign","eth_signTypedData_v4","wallet_switchEthereumChain","eth_requestAccounts","eth_accounts","eth_chainId"],events:["accountsChanged","chainChanged","disconnect"]}},platform:(0,t.detectPlatform)(),createdAt:new Date().toISOString(),updatedAt:new Date().toISOString()});return this.lastSession=c,c}catch(o){this.cleanup();let r=o instanceof Error?o.message:"Coinbase Wallet connect failed.";throw r.toLowerCase().includes("rejected")||r.toLowerCase().includes("user denied")?new t.WalletError("user_rejected",r,o):new t.WalletError("wallet_unavailable",r,o)}}async reconnect(e){throw new t.WalletError("session_expired","Coinbase Wallet does not support silent reconnection. Please call connect() again.")}async disconnect(e){try{this.providerAdapter&&await this.providerAdapter.getProvider().disconnect()}catch{}finally{this.cleanup()}}async getAccounts(e){if(this.providerAdapter)try{let s=await this.providerAdapter.getProvider().request({method:"eth_accounts"});if(s&&s.length>0)return s}catch{}return(0,t.extractAccounts)(e.namespaces)}async signMessage(e,n){let s=this.requireProvider();if(!n||typeof n!="object")throw new t.WalletError("method_not_allowed",t.CONNECTOR_ERROR_MESSAGES.INVALID_INPUT);let o=n,r=typeof o.message=="string"?o.message:void 0,i=typeof o.address=="string"?o.address:void 0;if(!r||!i)throw new t.WalletError("method_not_allowed",t.CONNECTOR_ERROR_MESSAGES.MISSING_MESSAGE);let a=i.includes(":")?i.split(":").pop():i,p=r.startsWith("{")?["eth_signTypedData_v4"]:["personal_sign","eth_sign"],h;for(let m of p){let w;m==="personal_sign"?w=[this.hexEncode(r),a]:m==="eth_sign"?w=[a,this.hexEncode(r)]:w=[a,r];try{return await s.request({method:m,params:w})}catch(v){h=v;let u=v instanceof Error?v.message.toLowerCase():"";if(!(u.includes("not been authorized")||u.includes("not authorized")||u.includes("not approved")||u.includes("method not found")||u.includes("method_not_allowed")))throw new t.WalletError("signature_rejected","Coinbase signature rejected.",v)}}throw h instanceof t.WalletError?h:new t.WalletError("signature_rejected","Coinbase signature rejected.",h)}async signTransaction(e,n){let s=this.requireProvider();if(!n||typeof n!="object")throw new t.WalletError("method_not_allowed",t.CONNECTOR_ERROR_MESSAGES.INVALID_INPUT);let r=n.transaction;if(!r)throw new t.WalletError("method_not_allowed",t.CONNECTOR_ERROR_MESSAGES.MISSING_TX);try{return await s.request({method:"eth_signTransaction",params:[r]})}catch(i){throw i instanceof t.WalletError?i:new t.WalletError("signature_rejected","Coinbase sign transaction rejected.",i)}}async sendTransaction(e,n){let s=this.requireProvider();if(!n||typeof n!="object")throw new t.WalletError("method_not_allowed",t.CONNECTOR_ERROR_MESSAGES.INVALID_INPUT);let r=n.transaction;if(!r)throw new t.WalletError("method_not_allowed",t.CONNECTOR_ERROR_MESSAGES.MISSING_TX);try{return await s.request({method:"eth_sendTransaction",params:[r]})}catch(i){throw i instanceof t.WalletError?i:new t.WalletError("tx_failed","Coinbase transaction failed.",i)}}async switchChain(e,n){let s=this.requireProvider();if(!n.startsWith("eip155:"))throw new t.WalletError("chain_unsupported","Coinbase connector only supports EVM chains.");let o=n.split(":")[1],r=`0x${Number(o).toString(16)}`;try{await s.request({method:"wallet_switchEthereumChain",params:[{chainId:r}]})}catch(i){let a=i instanceof Error?i.message.toLowerCase():"";throw a.includes("unrecognized")||a.includes("4902")?new t.WalletError("chain_unsupported",`Coinbase Wallet does not support chain ${n}.`,i):i instanceof t.WalletError?i:new t.WalletError("chain_unsupported","Coinbase switch chain failed.",i)}}async sendCalls(e,n,s){let o=this.requireProvider(),r=s??this.getDefaultChainId(e);try{return await o.request({method:"wallet_sendCalls",params:[{calls:n,chainId:r}]})}catch{let a=(e.namespaces.eip155?.accounts??[])[0]?.split(":").pop();if(!a)throw new t.WalletError("session_expired",t.CONNECTOR_ERROR_MESSAGES.NO_ACCOUNT_TX);let c=[];for(let p of n){let h=await o.request({method:"eth_sendTransaction",params:[{from:a,to:p.to,value:p.value,data:p.data}]});c.push(h)}return c.length===1?c[0]:c.join(",")}}async getCapabilities(e){let n={};for(let s of Object.values(e.namespaces)){let o=s.capabilities??{},r=!!o.atomicBatch;for(let i of s.chains)n[i]={atomicBatch:r?{supported:!0,maxBatchSize:5}:{supported:!1},paymasterService:o.paymasterService?{supported:!0}:void 0}}return n}async request(e){return this.requireProvider().request({method:e.method,params:e.params})}async getBalance(e){let n=this.lastSession;if(!n)throw new t.WalletError("session_expired",t.CONNECTOR_ERROR_MESSAGES.SESSION_EXPIRED);let s=e??this.getDefaultChainId(n),o=this.config.overrideRpcUrl?.[s]??t.DEFAULT_RPC_URLS[s];if(!o)throw new t.WalletError("chain_unsupported","No RPC URL for chain "+s);let i=(0,t.extractAccounts)(n.namespaces)[0]?.split(":").pop();if(!i)throw new t.WalletError("method_not_allowed",t.CONNECTOR_ERROR_MESSAGES.NO_ACCOUNTS);return(await(await fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({jsonrpc:"2.0",id:1,method:"eth_getBalance",params:[i,"latest"]})})).json()).result}getConnectionMode(){return this.connectionMode}isExtensionMode(){return this.connectionMode==="extension"}updateChainIds(e){this.config.appChainIds=e,console.warn("[CoinbaseConnector] updateChainIds: Coinbase SDK does not support dynamic chain ID updates. New chain IDs stored in config but will only apply on next connect().")}onSessionExpiry(e){this.sessionExpiryHandler=e}requireProvider(){if(!this.providerAdapter)throw new t.WalletError("session_expired",t.CONNECTOR_ERROR_MESSAGES.SESSION_EXPIRED);return this.providerAdapter.getProvider()}detectConnectionMode(e){if(typeof window<"u"){let s=window.coinbaseWalletExtension;if(s&&s===e)return"extension"}return this.config.preference==="smartWalletOnly"?"smart-wallet":"walletlink"}setupEventListeners(e){this.providerAdapter||(this.providerAdapter=new l(e)),this.providerAdapter.on("accountsChanged",n=>{let s=n;if(s.length===0)this.sessionExpiryHandler?.();else if(this.lastSession){let o=this.getDefaultChainId(this.lastSession);this.lastSession.namespaces.eip155={...this.lastSession.namespaces.eip155,accounts:s.map(r=>`${o}:${r}`)},this.lastSession.updatedAt=new Date().toISOString()}}),this.providerAdapter.on("chainChanged",n=>{if(this.lastSession){let s=n,r=`eip155:${Number.parseInt(s,16)}`,i=this.lastSession.namespaces.eip155?.accounts??[];this.lastSession.namespaces.eip155={...this.lastSession.namespaces.eip155,chains:[r],accounts:i.map(a=>{let c=a.split(":");return c.length>=3?`${r}:${c[2]}`:`${r}:${c[0]}`})},this.lastSession.updatedAt=new Date().toISOString()}}),this.providerAdapter.on("disconnect",()=>{this.sessionExpiryHandler?.(),this.cleanup()})}cleanup(){this.providerAdapter&&(this.providerAdapter.cleanup(),this.providerAdapter=void 0),this.sdk=void 0,this.lastSession=void 0,this.connectionMode=void 0}getDefaultChainId(e){let n=e.namespaces.eip155;return n&&n.chains.length>0?n.chains[0]:t.EIP155_MAINNET}hexEncode(e){if(/^0x[0-9a-fA-F]*$/.test(e))return e;let n=new TextEncoder().encode(e),s="0x";for(let o=0;o<n.length;o++)s+=n[o].toString(16).padStart(2,"0");return s}};function S(d){return new f(d)}0&&(module.exports={CoinbaseConnector,CoinbaseProviderAdapter,createCoinbaseConnector});
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/connector.ts","../src/provider.ts"],"sourcesContent":["export { CoinbaseConnector, createCoinbaseConnector } from \"./connector\";\nexport { CoinbaseProviderAdapter } from \"./provider\";\nexport type {\n CoinbaseConnectionMode,\n CoinbaseConnectorConfig,\n CoinbaseSession,\n} from \"./types\";\n","import type { Preference, ProviderInterface } from \"@coinbase/wallet-sdk\";\nimport { CoinbaseWalletSDK } from \"@coinbase/wallet-sdk\";\nimport type {\n BatchCall,\n ConnectorSupport,\n UniversalConnector,\n UniversalWalletSession,\n WalletCapabilities,\n} from \"@naculus/connect-core\";\nimport {\n CONNECTOR_ERROR_MESSAGES,\n createEmptySession,\n DEFAULT_RPC_URLS,\n detectPlatform,\n EIP155_MAINNET,\n extractAccounts,\n WalletError,\n} from \"@naculus/connect-core\";\nimport { CoinbaseProviderAdapter } from \"./provider\";\nimport type {\n CoinbaseConnectionMode,\n CoinbaseConnectorConfig,\n CoinbaseSession,\n} from \"./types\";\n\n/**\n * Coinbase Wallet connector for connect SDK.\n *\n * Supports:\n * - Coinbase Wallet browser extension detection\n * - WalletLink mode (QR code / deep link for mobile app)\n * - Smart Wallet (via `smartWalletOnly` preference)\n * - EIP-1193 provider for all EVM interactions\n *\n * Coexists with the EIP-6963 injected connector: the extension path\n * duplicates what EIP-6963 provides, but this connector adds WalletLink\n * and Smart Wallet modes that EIP-6963 cannot cover.\n *\n * @example\n * ```typescript\n * const connector = new CoinbaseConnector({\n * appName: \"My DApp\",\n * appChainIds: [1, 137],\n * });\n *\n * const session = await connector.connect();\n * ```\n */\nexport class CoinbaseConnector implements UniversalConnector {\n /** Unique connector identifier */\n readonly id = \"coinbase\";\n\n /** Display name for UI */\n readonly name = \"Coinbase Wallet\";\n\n /** Connector type identifier (shared with EIP-6963 for wallet-type parity) */\n readonly kind = \"eip6963\";\n\n /** Supported chain namespaces (EVM only) */\n readonly namespaces = [\"eip155\"];\n\n /** Feature support flags */\n readonly supports: ConnectorSupport = {\n desktop: true,\n mobile: true,\n deepLink: true,\n qr: true,\n trustedReconnect: false, // CB SDK does not support silent reconnection\n };\n\n /** Connector configuration */\n readonly config: {\n appName: string;\n appLogoUrl?: string;\n appChainIds: number[];\n preference: Preference[\"options\"];\n onQRCodeResponse?: (url: string) => void;\n overrideRpcUrl?: Record<string, string>;\n };\n\n private sdk?: CoinbaseWalletSDK;\n private providerAdapter?: CoinbaseProviderAdapter;\n private lastSession?: UniversalWalletSession;\n private sessionExpiryHandler?: () => void;\n\n /** Connected mode determined during connect */\n private connectionMode?: CoinbaseConnectionMode;\n\n constructor(config: CoinbaseConnectorConfig) {\n if (!config.appName) {\n throw new WalletError(\n \"invalid_input\",\n \"CoinbaseConnector requires appName.\",\n );\n }\n\n this.config = {\n appName: config.appName,\n appLogoUrl: config.appLogoUrl,\n appChainIds: config.appChainIds ?? [1],\n preference: config.preference ?? \"all\",\n onQRCodeResponse: config.onQRCodeResponse,\n overrideRpcUrl: config.overrideRpcUrl,\n };\n }\n\n /**\n * Get or initialize the Coinbase Wallet SDK instance.\n */\n private getSDK(): CoinbaseWalletSDK {\n if (this.sdk) {\n return this.sdk;\n }\n\n this.sdk = new CoinbaseWalletSDK({\n appName: this.config.appName,\n appLogoUrl: this.config.appLogoUrl ?? null,\n appChainIds: this.config.appChainIds,\n });\n\n return this.sdk;\n }\n\n /**\n * Get the provider from the SDK with the configured preference.\n */\n private getProvider(): ProviderInterface {\n const sdk = this.getSDK();\n\n const preference: Preference = {\n options: this.config.preference,\n };\n\n const provider = sdk.makeWeb3Provider(preference);\n\n this.providerAdapter = new CoinbaseProviderAdapter(provider);\n\n return provider;\n }\n\n /**\n * Connect to Coinbase Wallet.\n *\n * If the browser extension is installed, it will be used automatically.\n * Otherwise, WalletLink mode will show a QR code or redirect to the\n * Coinbase Wallet mobile app.\n *\n * @param input - Optional input with chainId override\n * @returns UniversalWalletSession\n */\n async connect(input?: unknown): Promise<UniversalWalletSession> {\n const provider = this.getProvider();\n const connectInput =\n input && typeof input === \"object\" && !Array.isArray(input)\n ? (input as Record<string, unknown>)\n : undefined;\n\n // Subscribe to events so we can react to wallet-side changes\n this.setupEventListeners(provider);\n\n try {\n // Request accounts — this triggers the connection flow\n const accounts = (await provider.request({\n method: \"eth_requestAccounts\",\n })) as `0x${string}`[];\n\n if (!accounts || accounts.length === 0) {\n throw new WalletError(\n \"user_rejected\",\n \"No accounts returned from Coinbase Wallet.\",\n );\n }\n\n const chainIdHex = (await provider.request({\n method: \"eth_chainId\",\n })) as string;\n\n const chainIdNum = Number.parseInt(chainIdHex, 16);\n\n // Detect connection mode\n this.connectionMode = this.detectConnectionMode(provider);\n\n // Build the session\n const caip2ChainId = `eip155:${chainIdNum}`;\n const walletSession = createEmptySession({\n id: crypto.randomUUID(),\n topic: undefined, // Coinbase SDK does not use topics\n walletId: \"coinbase-wallet\",\n walletType: \"eip6963\",\n namespaces: {\n eip155: {\n chains: [caip2ChainId],\n accounts: accounts.map((a) => `${caip2ChainId}:${a}`),\n // Standard EIP-1193 methods supported by Coinbase Wallet\n // These are the minimal set required for core dApp interactions.\n // Extend as needed for app-specific requirements.\n methods: [\n \"eth_sendTransaction\",\n \"eth_signTransaction\",\n \"personal_sign\",\n \"eth_signTypedData_v4\",\n \"wallet_switchEthereumChain\",\n \"eth_requestAccounts\",\n \"eth_accounts\",\n \"eth_chainId\",\n ],\n events: [\"accountsChanged\", \"chainChanged\", \"disconnect\"],\n },\n },\n platform: detectPlatform(),\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n });\n\n // On-chain balance check is optional; just include a basic session\n this.lastSession = walletSession;\n return walletSession;\n } catch (error) {\n this.cleanup();\n const message =\n error instanceof Error\n ? error.message\n : \"Coinbase Wallet connect failed.\";\n if (\n message.toLowerCase().includes(\"rejected\") ||\n message.toLowerCase().includes(\"user denied\")\n ) {\n throw new WalletError(\"user_rejected\", message, error);\n }\n\n throw new WalletError(\"wallet_unavailable\", message, error);\n }\n }\n\n /**\n * Reconnect — Coinbase SDK does not support silent reconnection.\n * @throws WalletError always (trustedReconnect is false)\n */\n async reconnect(\n _session: UniversalWalletSession,\n ): Promise<UniversalWalletSession> {\n throw new WalletError(\n \"session_expired\",\n \"Coinbase Wallet does not support silent reconnection. Please call connect() again.\",\n );\n }\n\n /**\n * Disconnect from Coinbase Wallet.\n * Cleans up the provider and SDK state.\n */\n async disconnect(_session: UniversalWalletSession): Promise<void> {\n try {\n if (this.providerAdapter) {\n const provider = this.providerAdapter.getProvider();\n await provider.disconnect();\n }\n } catch {\n // Swallow disconnect errors — provider may be in an invalid state\n } finally {\n this.cleanup();\n }\n }\n\n /**\n * Get accounts from the current session.\n */\n async getAccounts(session: UniversalWalletSession): Promise<string[]> {\n if (this.providerAdapter) {\n try {\n const provider = this.providerAdapter.getProvider();\n const accounts = (await provider.request({\n method: \"eth_accounts\",\n })) as `0x${string}`[];\n if (accounts && accounts.length > 0) {\n return accounts;\n }\n } catch {\n // Fall back to session data\n }\n }\n\n return extractAccounts(session.namespaces);\n }\n\n /**\n * Sign a message using personal_sign or eth_signTypedData_v4.\n */\n async signMessage(\n session: UniversalWalletSession,\n input: unknown,\n ): Promise<unknown> {\n const provider = this.requireProvider();\n if (!input || typeof input !== \"object\")\n throw new WalletError(\n \"method_not_allowed\",\n CONNECTOR_ERROR_MESSAGES.INVALID_INPUT,\n );\n\n const inputObj = input as Record<string, unknown>;\n const message =\n typeof inputObj.message === \"string\" ? inputObj.message : undefined;\n const rawAddress =\n typeof inputObj.address === \"string\" ? inputObj.address : undefined;\n if (!message || !rawAddress)\n throw new WalletError(\n \"method_not_allowed\",\n CONNECTOR_ERROR_MESSAGES.MISSING_MESSAGE,\n );\n\n const address = rawAddress.includes(\":\")\n ? rawAddress.split(\":\").pop()!\n : rawAddress;\n\n // Determine signing method based on message content\n const isStructured = message.startsWith(\"{\");\n const tryMethods = isStructured\n ? [\"eth_signTypedData_v4\"]\n : [\"personal_sign\", \"eth_sign\"];\n\n let lastError: unknown;\n for (const tryMethod of tryMethods) {\n let params: unknown[];\n if (tryMethod === \"personal_sign\") {\n params = [this.hexEncode(message), address];\n } else if (tryMethod === \"eth_sign\") {\n params = [address, this.hexEncode(message)];\n } else {\n // eth_signTypedData_v4\n params = [address, message];\n }\n\n try {\n return await provider.request({\n method: tryMethod,\n params,\n });\n } catch (error) {\n lastError = error;\n const errMsg =\n error instanceof Error ? error.message.toLowerCase() : \"\";\n const isMethodRejection =\n errMsg.includes(\"not been authorized\") ||\n errMsg.includes(\"not authorized\") ||\n errMsg.includes(\"not approved\") ||\n errMsg.includes(\"method not found\") ||\n errMsg.includes(\"method_not_allowed\");\n\n if (!isMethodRejection) {\n // Unrecoverable error\n throw new WalletError(\n \"signature_rejected\",\n \"Coinbase signature rejected.\",\n error,\n );\n }\n // Fall through to next method\n }\n }\n\n if (lastError instanceof WalletError) throw lastError;\n throw new WalletError(\n \"signature_rejected\",\n \"Coinbase signature rejected.\",\n lastError,\n );\n }\n\n /**\n * Sign a transaction without broadcasting it.\n *\n * Uses eth_signTransaction to request the connected wallet to sign the\n * given transaction parameters. The signed transaction is returned\n * but NOT submitted to the network. Callers may inspect or relay\n * the signed payload as needed.\n *\n * @param session - Active wallet session\n * @param input - Object with a `transaction` field containing standard EVM tx params\n * @returns The signed transaction (RLP-encoded hex string)\n * @throws WalletError if session is expired or input is invalid\n */\n async signTransaction(\n session: UniversalWalletSession,\n input: unknown,\n ): Promise<unknown> {\n const provider = this.requireProvider();\n if (!input || typeof input !== \"object\")\n throw new WalletError(\n \"method_not_allowed\",\n CONNECTOR_ERROR_MESSAGES.INVALID_INPUT,\n );\n\n const inputObj = input as Record<string, unknown>;\n const transaction = inputObj.transaction as\n | Record<string, unknown>\n | undefined;\n if (!transaction)\n throw new WalletError(\n \"method_not_allowed\",\n CONNECTOR_ERROR_MESSAGES.MISSING_TX,\n );\n\n try {\n return await provider.request({\n method: \"eth_signTransaction\",\n params: [transaction],\n });\n } catch (error) {\n if (error instanceof WalletError) throw error;\n throw new WalletError(\n \"signature_rejected\",\n \"Coinbase sign transaction rejected.\",\n error,\n );\n }\n }\n\n /**\n * Send a transaction (eth_sendTransaction).\n */\n async sendTransaction(\n session: UniversalWalletSession,\n input: unknown,\n ): Promise<unknown> {\n const provider = this.requireProvider();\n if (!input || typeof input !== \"object\")\n throw new WalletError(\n \"method_not_allowed\",\n CONNECTOR_ERROR_MESSAGES.INVALID_INPUT,\n );\n\n const inputObj = input as Record<string, unknown>;\n const transaction = inputObj.transaction as\n | Record<string, unknown>\n | undefined;\n if (!transaction)\n throw new WalletError(\n \"method_not_allowed\",\n CONNECTOR_ERROR_MESSAGES.MISSING_TX,\n );\n\n try {\n return await provider.request({\n method: \"eth_sendTransaction\",\n params: [transaction],\n });\n } catch (error) {\n if (error instanceof WalletError) throw error;\n throw new WalletError(\"tx_failed\", \"Coinbase transaction failed.\", error);\n }\n }\n\n /**\n * Switch the connected chain.\n */\n async switchChain(\n session: UniversalWalletSession,\n chainId: string,\n ): Promise<void> {\n const provider = this.requireProvider();\n if (!chainId.startsWith(\"eip155:\")) {\n throw new WalletError(\n \"chain_unsupported\",\n \"Coinbase connector only supports EVM chains.\",\n );\n }\n\n const numericChainId = chainId.split(\":\")[1];\n const hexChainId = `0x${Number(numericChainId).toString(16)}`;\n\n try {\n await provider.request({\n method: \"wallet_switchEthereumChain\",\n params: [{ chainId: hexChainId }],\n });\n } catch (error) {\n const errMsg = error instanceof Error ? error.message.toLowerCase() : \"\";\n // Wallet might not have the chain — try wallet_addEthereumChain\n if (errMsg.includes(\"unrecognized\") || errMsg.includes(\"4902\")) {\n throw new WalletError(\n \"chain_unsupported\",\n `Coinbase Wallet does not support chain ${chainId}.`,\n error,\n );\n }\n if (error instanceof WalletError) throw error;\n throw new WalletError(\n \"chain_unsupported\",\n \"Coinbase switch chain failed.\",\n error,\n );\n }\n }\n\n /**\n * Send batched calls (wallet_sendCalls) with fallback.\n */\n async sendCalls(\n session: UniversalWalletSession,\n calls: BatchCall[],\n chainId?: string,\n ): Promise<string> {\n const provider = this.requireProvider();\n const resolvedChainId = chainId ?? this.getDefaultChainId(session);\n\n try {\n return (await provider.request({\n method: \"wallet_sendCalls\",\n params: [{ calls, chainId: resolvedChainId }],\n })) as string;\n } catch {\n // Fallback: send each call individually\n const accounts = session.namespaces.eip155?.accounts ?? [];\n const fromAccount = accounts[0]?.split(\":\").pop();\n if (!fromAccount) {\n throw new WalletError(\n \"session_expired\",\n CONNECTOR_ERROR_MESSAGES.NO_ACCOUNT_TX,\n );\n }\n\n const txHashes: string[] = [];\n for (const call of calls) {\n const hash = (await provider.request({\n method: \"eth_sendTransaction\",\n params: [\n {\n from: fromAccount,\n to: call.to,\n value: call.value,\n data: call.data,\n },\n ],\n })) as string;\n txHashes.push(hash);\n }\n\n return txHashes.length === 1 ? txHashes[0] : txHashes.join(\",\");\n }\n }\n\n /**\n * Get wallet capabilities for the connected session.\n */\n async getCapabilities(\n session: UniversalWalletSession,\n ): Promise<Record<string, WalletCapabilities>> {\n const capabilities: Record<string, WalletCapabilities> = {};\n\n for (const ns of Object.values(session.namespaces)) {\n const nsCaps = (ns.capabilities ?? {}) as Record<string, unknown>;\n const atomicBatchSupported = Boolean(nsCaps.atomicBatch);\n\n for (const chain of ns.chains) {\n capabilities[chain] = {\n atomicBatch: atomicBatchSupported\n ? { supported: true, maxBatchSize: 5 }\n : { supported: false },\n paymasterService: nsCaps.paymasterService\n ? { supported: true }\n : undefined,\n };\n }\n }\n\n return capabilities;\n }\n\n /**\n * Make a raw JSON-RPC request through the Coinbase provider.\n */\n async request(request: {\n method: string;\n params: unknown[];\n }): Promise<unknown> {\n const provider = this.requireProvider();\n return provider.request({\n method: request.method,\n params: request.params,\n });\n }\n\n /**\n * Get the ETH balance of the first connected account.\n */\n async getBalance(chainId?: string): Promise<string> {\n const session = this.lastSession;\n if (!session)\n throw new WalletError(\n \"session_expired\",\n CONNECTOR_ERROR_MESSAGES.SESSION_EXPIRED,\n );\n\n const cId = chainId ?? this.getDefaultChainId(session);\n const rpcUrl = this.config.overrideRpcUrl?.[cId] ?? DEFAULT_RPC_URLS[cId];\n if (!rpcUrl)\n throw new WalletError(\"chain_unsupported\", \"No RPC URL for chain \" + cId);\n\n const allAccounts = extractAccounts(session.namespaces);\n const address = allAccounts[0]?.split(\":\").pop();\n if (!address)\n throw new WalletError(\n \"method_not_allowed\",\n CONNECTOR_ERROR_MESSAGES.NO_ACCOUNTS,\n );\n\n const response = await fetch(rpcUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n jsonrpc: \"2.0\",\n id: 1,\n method: \"eth_getBalance\",\n params: [address, \"latest\"],\n }),\n });\n const data = await response.json();\n return data.result;\n }\n\n // ── Coinbase-specific methods ──\n\n /**\n * Get the connection mode detected during connect.\n */\n getConnectionMode(): CoinbaseConnectionMode | undefined {\n return this.connectionMode;\n }\n\n /**\n * Whether the connector is currently using extension mode.\n */\n isExtensionMode(): boolean {\n return this.connectionMode === \"extension\";\n }\n\n /**\n * Update the supported chain IDs on the SDK instance.\n */\n updateChainIds(chainIds: number[]): void {\n this.config.appChainIds = chainIds;\n console.warn(\n \"[CoinbaseConnector] updateChainIds: Coinbase SDK does not support dynamic chain ID updates. \" +\n \"New chain IDs stored in config but will only apply on next connect().\",\n );\n }\n\n /**\n * Register a handler to be called when the session expires or the wallet disconnects.\n */\n onSessionExpiry(handler: () => void): void {\n this.sessionExpiryHandler = handler;\n }\n\n // ── Private helpers ──\n\n /**\n * Get the provider or throw if not connected.\n */\n private requireProvider(): ProviderInterface {\n if (!this.providerAdapter) {\n throw new WalletError(\n \"session_expired\",\n CONNECTOR_ERROR_MESSAGES.SESSION_EXPIRED,\n );\n }\n return this.providerAdapter.getProvider();\n }\n\n /**\n * Detect the connection mode based on the provider.\n * Extension mode: provider is Coinbase browser extension\n * Smart Wallet: provider is Coinbase Smart Wallet\n * Default: WalletLink (mobile/QR)\n */\n private detectConnectionMode(\n provider: ProviderInterface,\n ): CoinbaseConnectionMode {\n // Check if the extension is installed and matches\n if (typeof window !== \"undefined\") {\n const win = window as unknown as Record<string, unknown>;\n const extProvider = win.coinbaseWalletExtension as\n | ProviderInterface\n | undefined;\n if (extProvider && extProvider === provider) {\n return \"extension\";\n }\n }\n\n // Check for Smart Wallet\n if (this.config.preference === \"smartWalletOnly\") {\n return \"smart-wallet\";\n }\n\n return \"walletlink\";\n }\n\n /**\n * Set up event listeners on the provider to handle wallet-side changes.\n */\n private setupEventListeners(provider: ProviderInterface): void {\n if (!this.providerAdapter) {\n this.providerAdapter = new CoinbaseProviderAdapter(provider);\n }\n\n this.providerAdapter.on(\"accountsChanged\", (accounts: unknown) => {\n const accs = accounts as string[];\n if (accs.length === 0) {\n // All accounts disconnected\n this.sessionExpiryHandler?.();\n } else if (this.lastSession) {\n // Update accounts in the session\n const chainId = this.getDefaultChainId(this.lastSession);\n this.lastSession.namespaces.eip155 = {\n ...this.lastSession.namespaces.eip155,\n accounts: accs.map((a) => `${chainId}:${a}`),\n };\n this.lastSession.updatedAt = new Date().toISOString();\n }\n });\n\n this.providerAdapter.on(\"chainChanged\", (chainId: unknown) => {\n if (this.lastSession) {\n const hexChainId = chainId as string;\n const chainIdNum = Number.parseInt(hexChainId, 16);\n const caip2ChainId = `eip155:${chainIdNum}`;\n\n // Update the session namespaces with the new chain\n const existingAccounts =\n this.lastSession.namespaces.eip155?.accounts ?? [];\n\n this.lastSession.namespaces.eip155 = {\n ...this.lastSession.namespaces.eip155,\n chains: [caip2ChainId],\n accounts: existingAccounts.map((a) => {\n const parts = a.split(\":\");\n return parts.length >= 3\n ? `${caip2ChainId}:${parts[2]}`\n : `${caip2ChainId}:${parts[0]}`;\n }),\n };\n\n this.lastSession.updatedAt = new Date().toISOString();\n }\n });\n\n this.providerAdapter.on(\"disconnect\", () => {\n this.sessionExpiryHandler?.();\n this.cleanup();\n });\n }\n\n /**\n * Clean up provider adapter and SDK state.\n */\n private cleanup(): void {\n if (this.providerAdapter) {\n this.providerAdapter.cleanup();\n this.providerAdapter = undefined;\n }\n this.sdk = undefined;\n this.lastSession = undefined;\n this.connectionMode = undefined;\n }\n\n /**\n * Get the default chain ID from the session.\n */\n private getDefaultChainId(session: UniversalWalletSession): string {\n const evmNamespace = session.namespaces[\"eip155\"];\n if (evmNamespace && evmNamespace.chains.length > 0) {\n return evmNamespace.chains[0];\n }\n\n return EIP155_MAINNET;\n }\n\n /**\n * Hex-encode a string for Ethereum JSON-RPC methods.\n */\n private hexEncode(message: string): `0x${string}` {\n if (/^0x[0-9a-fA-F]*$/.test(message)) return message as `0x${string}`;\n\n const bytes = new TextEncoder().encode(message);\n let hex = \"0x\";\n for (let i = 0; i < bytes.length; i++) {\n hex += bytes[i].toString(16).padStart(2, \"0\");\n }\n return hex as `0x${string}`;\n }\n}\n\n/**\n * Create a CoinbaseConnector instance.\n */\nexport function createCoinbaseConnector(\n config: CoinbaseConnectorConfig,\n): CoinbaseConnector {\n return new CoinbaseConnector(config);\n}\n","import type { ProviderInterface } from \"@coinbase/wallet-sdk\";\n\n/**\n * Adapter that bridges the Coinbase wallet-sdk ProviderInterface\n * (EIP-1193 compatible) to Naculus's internal EIP-6963 provider type.\n *\n * Handles provider event subscriptions and cleanup so the connector\n * can react to accountsChanged, chainChanged, and disconnect events\n * without leaking listeners.\n */\nexport class CoinbaseProviderAdapter {\n private readonly rawProvider: ProviderInterface;\n private listeners: Map<string, Set<(...args: unknown[]) => void>> = new Map();\n\n constructor(provider: ProviderInterface) {\n this.rawProvider = provider;\n }\n\n /**\n * Get the underlying provider instance.\n */\n getProvider(): ProviderInterface {\n return this.rawProvider;\n }\n\n /**\n * Subscribe to a provider event.\n * Avoids duplicate handler registration for the same event+handler pair.\n */\n on(event: string, handler: (...args: unknown[]) => void): void {\n const existing = this.listeners.get(event);\n if (!existing) {\n const set = new Set<(...args: unknown[]) => void>();\n set.add(handler);\n this.listeners.set(event, set);\n this.rawProvider.on(event as any, handler as any);\n } else if (!existing.has(handler)) {\n existing.add(handler);\n this.rawProvider.on(event as any, handler as any);\n }\n }\n\n /**\n * Unsubscribe from a provider event.\n */\n off(event: string, handler: (...args: unknown[]) => void): void {\n const existing = this.listeners.get(event);\n if (existing) {\n existing.delete(handler);\n this.rawProvider.off(event as any, handler as any);\n if (existing.size === 0) {\n this.listeners.delete(event);\n }\n }\n }\n\n /**\n * Remove all event listeners registered through this adapter.\n */\n removeAllListeners(): void {\n for (const [event, handlers] of this.listeners.entries()) {\n for (const handler of handlers) {\n this.rawProvider.off(event as any, handler as any);\n }\n }\n this.listeners.clear();\n }\n\n /**\n * Clean up all subscriptions and prepare for garbage collection.\n */\n cleanup(): void {\n this.removeAllListeners();\n }\n}\n"],"mappings":"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","names":["index_exports","__export","CoinbaseConnector","CoinbaseProviderAdapter","createCoinbaseConnector","__toCommonJS","import_wallet_sdk","import_connect_core","CoinbaseProviderAdapter","provider","event","handler","existing","set","handlers","CoinbaseConnector","config","sdk","preference","provider","CoinbaseProviderAdapter","input","connectInput","accounts","chainIdHex","chainIdNum","caip2ChainId","walletSession","a","error","message","_session","session","inputObj","rawAddress","address","tryMethods","lastError","tryMethod","params","errMsg","transaction","chainId","numericChainId","hexChainId","calls","resolvedChainId","fromAccount","txHashes","call","hash","capabilities","ns","nsCaps","atomicBatchSupported","chain","request","cId","rpcUrl","chainIds","handler","extProvider","accs","existingAccounts","parts","evmNamespace","bytes","hex","i","createCoinbaseConnector"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { Preference, ProviderInterface, CoinbaseWalletSDK } from '@coinbase/wallet-sdk';
|
|
2
|
+
import { UniversalConnector, ConnectorSupport, UniversalWalletSession, BatchCall, WalletCapabilities } from '@naculus/connect-core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Configuration for the Coinbase Wallet connector.
|
|
6
|
+
*/
|
|
7
|
+
interface CoinbaseConnectorConfig {
|
|
8
|
+
/** Application name (displayed in Coinbase authorization screen) */
|
|
9
|
+
appName: string;
|
|
10
|
+
/** Application logo URL (optional) */
|
|
11
|
+
appLogoUrl?: string;
|
|
12
|
+
/** Initial supported chain IDs (defaults to [1] — Ethereum mainnet) */
|
|
13
|
+
appChainIds?: number[];
|
|
14
|
+
/**
|
|
15
|
+
* Preference for wallet mode.
|
|
16
|
+
* - "all": Show all options (extension + WalletLink)
|
|
17
|
+
* - "smartWalletOnly": Only Smart Wallet
|
|
18
|
+
* - "eoaOnly": Only EOA (externally owned account)
|
|
19
|
+
* @default "all"
|
|
20
|
+
*/
|
|
21
|
+
preference?: Preference["options"];
|
|
22
|
+
/** QR code response URL callback (WalletLink mode) */
|
|
23
|
+
onQRCodeResponse?: (url: string) => void;
|
|
24
|
+
/** Override default RPC URLs by CAIP-2 chain ID */
|
|
25
|
+
overrideRpcUrl?: Record<string, string>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Internal Coinbase session state.
|
|
29
|
+
*/
|
|
30
|
+
interface CoinbaseSession {
|
|
31
|
+
/** The Coinbase Wallet SDK provider instance */
|
|
32
|
+
provider: ProviderInterface;
|
|
33
|
+
/** The Coinbase Wallet SDK instance (for cleanup) */
|
|
34
|
+
sdk: CoinbaseWalletSDK;
|
|
35
|
+
/** Connected accounts */
|
|
36
|
+
accounts: `0x${string}`[];
|
|
37
|
+
/** Connected chain ID (CAIP-2 format, e.g. "eip155:1") */
|
|
38
|
+
chainId: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Connection mode determined at connect time.
|
|
42
|
+
*/
|
|
43
|
+
type CoinbaseConnectionMode = "extension" | "walletlink" | "smart-wallet";
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Coinbase Wallet connector for connect SDK.
|
|
47
|
+
*
|
|
48
|
+
* Supports:
|
|
49
|
+
* - Coinbase Wallet browser extension detection
|
|
50
|
+
* - WalletLink mode (QR code / deep link for mobile app)
|
|
51
|
+
* - Smart Wallet (via `smartWalletOnly` preference)
|
|
52
|
+
* - EIP-1193 provider for all EVM interactions
|
|
53
|
+
*
|
|
54
|
+
* Coexists with the EIP-6963 injected connector: the extension path
|
|
55
|
+
* duplicates what EIP-6963 provides, but this connector adds WalletLink
|
|
56
|
+
* and Smart Wallet modes that EIP-6963 cannot cover.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* const connector = new CoinbaseConnector({
|
|
61
|
+
* appName: "My DApp",
|
|
62
|
+
* appChainIds: [1, 137],
|
|
63
|
+
* });
|
|
64
|
+
*
|
|
65
|
+
* const session = await connector.connect();
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
declare class CoinbaseConnector implements UniversalConnector {
|
|
69
|
+
/** Unique connector identifier */
|
|
70
|
+
readonly id = "coinbase";
|
|
71
|
+
/** Display name for UI */
|
|
72
|
+
readonly name = "Coinbase Wallet";
|
|
73
|
+
/** Connector type identifier (shared with EIP-6963 for wallet-type parity) */
|
|
74
|
+
readonly kind = "eip6963";
|
|
75
|
+
/** Supported chain namespaces (EVM only) */
|
|
76
|
+
readonly namespaces: string[];
|
|
77
|
+
/** Feature support flags */
|
|
78
|
+
readonly supports: ConnectorSupport;
|
|
79
|
+
/** Connector configuration */
|
|
80
|
+
readonly config: {
|
|
81
|
+
appName: string;
|
|
82
|
+
appLogoUrl?: string;
|
|
83
|
+
appChainIds: number[];
|
|
84
|
+
preference: Preference["options"];
|
|
85
|
+
onQRCodeResponse?: (url: string) => void;
|
|
86
|
+
overrideRpcUrl?: Record<string, string>;
|
|
87
|
+
};
|
|
88
|
+
private sdk?;
|
|
89
|
+
private providerAdapter?;
|
|
90
|
+
private lastSession?;
|
|
91
|
+
private sessionExpiryHandler?;
|
|
92
|
+
/** Connected mode determined during connect */
|
|
93
|
+
private connectionMode?;
|
|
94
|
+
constructor(config: CoinbaseConnectorConfig);
|
|
95
|
+
/**
|
|
96
|
+
* Get or initialize the Coinbase Wallet SDK instance.
|
|
97
|
+
*/
|
|
98
|
+
private getSDK;
|
|
99
|
+
/**
|
|
100
|
+
* Get the provider from the SDK with the configured preference.
|
|
101
|
+
*/
|
|
102
|
+
private getProvider;
|
|
103
|
+
/**
|
|
104
|
+
* Connect to Coinbase Wallet.
|
|
105
|
+
*
|
|
106
|
+
* If the browser extension is installed, it will be used automatically.
|
|
107
|
+
* Otherwise, WalletLink mode will show a QR code or redirect to the
|
|
108
|
+
* Coinbase Wallet mobile app.
|
|
109
|
+
*
|
|
110
|
+
* @param input - Optional input with chainId override
|
|
111
|
+
* @returns UniversalWalletSession
|
|
112
|
+
*/
|
|
113
|
+
connect(input?: unknown): Promise<UniversalWalletSession>;
|
|
114
|
+
/**
|
|
115
|
+
* Reconnect — Coinbase SDK does not support silent reconnection.
|
|
116
|
+
* @throws WalletError always (trustedReconnect is false)
|
|
117
|
+
*/
|
|
118
|
+
reconnect(_session: UniversalWalletSession): Promise<UniversalWalletSession>;
|
|
119
|
+
/**
|
|
120
|
+
* Disconnect from Coinbase Wallet.
|
|
121
|
+
* Cleans up the provider and SDK state.
|
|
122
|
+
*/
|
|
123
|
+
disconnect(_session: UniversalWalletSession): Promise<void>;
|
|
124
|
+
/**
|
|
125
|
+
* Get accounts from the current session.
|
|
126
|
+
*/
|
|
127
|
+
getAccounts(session: UniversalWalletSession): Promise<string[]>;
|
|
128
|
+
/**
|
|
129
|
+
* Sign a message using personal_sign or eth_signTypedData_v4.
|
|
130
|
+
*/
|
|
131
|
+
signMessage(session: UniversalWalletSession, input: unknown): Promise<unknown>;
|
|
132
|
+
/**
|
|
133
|
+
* Sign a transaction without broadcasting it.
|
|
134
|
+
*
|
|
135
|
+
* Uses eth_signTransaction to request the connected wallet to sign the
|
|
136
|
+
* given transaction parameters. The signed transaction is returned
|
|
137
|
+
* but NOT submitted to the network. Callers may inspect or relay
|
|
138
|
+
* the signed payload as needed.
|
|
139
|
+
*
|
|
140
|
+
* @param session - Active wallet session
|
|
141
|
+
* @param input - Object with a `transaction` field containing standard EVM tx params
|
|
142
|
+
* @returns The signed transaction (RLP-encoded hex string)
|
|
143
|
+
* @throws WalletError if session is expired or input is invalid
|
|
144
|
+
*/
|
|
145
|
+
signTransaction(session: UniversalWalletSession, input: unknown): Promise<unknown>;
|
|
146
|
+
/**
|
|
147
|
+
* Send a transaction (eth_sendTransaction).
|
|
148
|
+
*/
|
|
149
|
+
sendTransaction(session: UniversalWalletSession, input: unknown): Promise<unknown>;
|
|
150
|
+
/**
|
|
151
|
+
* Switch the connected chain.
|
|
152
|
+
*/
|
|
153
|
+
switchChain(session: UniversalWalletSession, chainId: string): Promise<void>;
|
|
154
|
+
/**
|
|
155
|
+
* Send batched calls (wallet_sendCalls) with fallback.
|
|
156
|
+
*/
|
|
157
|
+
sendCalls(session: UniversalWalletSession, calls: BatchCall[], chainId?: string): Promise<string>;
|
|
158
|
+
/**
|
|
159
|
+
* Get wallet capabilities for the connected session.
|
|
160
|
+
*/
|
|
161
|
+
getCapabilities(session: UniversalWalletSession): Promise<Record<string, WalletCapabilities>>;
|
|
162
|
+
/**
|
|
163
|
+
* Make a raw JSON-RPC request through the Coinbase provider.
|
|
164
|
+
*/
|
|
165
|
+
request(request: {
|
|
166
|
+
method: string;
|
|
167
|
+
params: unknown[];
|
|
168
|
+
}): Promise<unknown>;
|
|
169
|
+
/**
|
|
170
|
+
* Get the ETH balance of the first connected account.
|
|
171
|
+
*/
|
|
172
|
+
getBalance(chainId?: string): Promise<string>;
|
|
173
|
+
/**
|
|
174
|
+
* Get the connection mode detected during connect.
|
|
175
|
+
*/
|
|
176
|
+
getConnectionMode(): CoinbaseConnectionMode | undefined;
|
|
177
|
+
/**
|
|
178
|
+
* Whether the connector is currently using extension mode.
|
|
179
|
+
*/
|
|
180
|
+
isExtensionMode(): boolean;
|
|
181
|
+
/**
|
|
182
|
+
* Update the supported chain IDs on the SDK instance.
|
|
183
|
+
*/
|
|
184
|
+
updateChainIds(chainIds: number[]): void;
|
|
185
|
+
/**
|
|
186
|
+
* Register a handler to be called when the session expires or the wallet disconnects.
|
|
187
|
+
*/
|
|
188
|
+
onSessionExpiry(handler: () => void): void;
|
|
189
|
+
/**
|
|
190
|
+
* Get the provider or throw if not connected.
|
|
191
|
+
*/
|
|
192
|
+
private requireProvider;
|
|
193
|
+
/**
|
|
194
|
+
* Detect the connection mode based on the provider.
|
|
195
|
+
* Extension mode: provider is Coinbase browser extension
|
|
196
|
+
* Smart Wallet: provider is Coinbase Smart Wallet
|
|
197
|
+
* Default: WalletLink (mobile/QR)
|
|
198
|
+
*/
|
|
199
|
+
private detectConnectionMode;
|
|
200
|
+
/**
|
|
201
|
+
* Set up event listeners on the provider to handle wallet-side changes.
|
|
202
|
+
*/
|
|
203
|
+
private setupEventListeners;
|
|
204
|
+
/**
|
|
205
|
+
* Clean up provider adapter and SDK state.
|
|
206
|
+
*/
|
|
207
|
+
private cleanup;
|
|
208
|
+
/**
|
|
209
|
+
* Get the default chain ID from the session.
|
|
210
|
+
*/
|
|
211
|
+
private getDefaultChainId;
|
|
212
|
+
/**
|
|
213
|
+
* Hex-encode a string for Ethereum JSON-RPC methods.
|
|
214
|
+
*/
|
|
215
|
+
private hexEncode;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Create a CoinbaseConnector instance.
|
|
219
|
+
*/
|
|
220
|
+
declare function createCoinbaseConnector(config: CoinbaseConnectorConfig): CoinbaseConnector;
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Adapter that bridges the Coinbase wallet-sdk ProviderInterface
|
|
224
|
+
* (EIP-1193 compatible) to Naculus's internal EIP-6963 provider type.
|
|
225
|
+
*
|
|
226
|
+
* Handles provider event subscriptions and cleanup so the connector
|
|
227
|
+
* can react to accountsChanged, chainChanged, and disconnect events
|
|
228
|
+
* without leaking listeners.
|
|
229
|
+
*/
|
|
230
|
+
declare class CoinbaseProviderAdapter {
|
|
231
|
+
private readonly rawProvider;
|
|
232
|
+
private listeners;
|
|
233
|
+
constructor(provider: ProviderInterface);
|
|
234
|
+
/**
|
|
235
|
+
* Get the underlying provider instance.
|
|
236
|
+
*/
|
|
237
|
+
getProvider(): ProviderInterface;
|
|
238
|
+
/**
|
|
239
|
+
* Subscribe to a provider event.
|
|
240
|
+
* Avoids duplicate handler registration for the same event+handler pair.
|
|
241
|
+
*/
|
|
242
|
+
on(event: string, handler: (...args: unknown[]) => void): void;
|
|
243
|
+
/**
|
|
244
|
+
* Unsubscribe from a provider event.
|
|
245
|
+
*/
|
|
246
|
+
off(event: string, handler: (...args: unknown[]) => void): void;
|
|
247
|
+
/**
|
|
248
|
+
* Remove all event listeners registered through this adapter.
|
|
249
|
+
*/
|
|
250
|
+
removeAllListeners(): void;
|
|
251
|
+
/**
|
|
252
|
+
* Clean up all subscriptions and prepare for garbage collection.
|
|
253
|
+
*/
|
|
254
|
+
cleanup(): void;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export { type CoinbaseConnectionMode, CoinbaseConnector, type CoinbaseConnectorConfig, CoinbaseProviderAdapter, type CoinbaseSession, createCoinbaseConnector };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { Preference, ProviderInterface, CoinbaseWalletSDK } from '@coinbase/wallet-sdk';
|
|
2
|
+
import { UniversalConnector, ConnectorSupport, UniversalWalletSession, BatchCall, WalletCapabilities } from '@naculus/connect-core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Configuration for the Coinbase Wallet connector.
|
|
6
|
+
*/
|
|
7
|
+
interface CoinbaseConnectorConfig {
|
|
8
|
+
/** Application name (displayed in Coinbase authorization screen) */
|
|
9
|
+
appName: string;
|
|
10
|
+
/** Application logo URL (optional) */
|
|
11
|
+
appLogoUrl?: string;
|
|
12
|
+
/** Initial supported chain IDs (defaults to [1] — Ethereum mainnet) */
|
|
13
|
+
appChainIds?: number[];
|
|
14
|
+
/**
|
|
15
|
+
* Preference for wallet mode.
|
|
16
|
+
* - "all": Show all options (extension + WalletLink)
|
|
17
|
+
* - "smartWalletOnly": Only Smart Wallet
|
|
18
|
+
* - "eoaOnly": Only EOA (externally owned account)
|
|
19
|
+
* @default "all"
|
|
20
|
+
*/
|
|
21
|
+
preference?: Preference["options"];
|
|
22
|
+
/** QR code response URL callback (WalletLink mode) */
|
|
23
|
+
onQRCodeResponse?: (url: string) => void;
|
|
24
|
+
/** Override default RPC URLs by CAIP-2 chain ID */
|
|
25
|
+
overrideRpcUrl?: Record<string, string>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Internal Coinbase session state.
|
|
29
|
+
*/
|
|
30
|
+
interface CoinbaseSession {
|
|
31
|
+
/** The Coinbase Wallet SDK provider instance */
|
|
32
|
+
provider: ProviderInterface;
|
|
33
|
+
/** The Coinbase Wallet SDK instance (for cleanup) */
|
|
34
|
+
sdk: CoinbaseWalletSDK;
|
|
35
|
+
/** Connected accounts */
|
|
36
|
+
accounts: `0x${string}`[];
|
|
37
|
+
/** Connected chain ID (CAIP-2 format, e.g. "eip155:1") */
|
|
38
|
+
chainId: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Connection mode determined at connect time.
|
|
42
|
+
*/
|
|
43
|
+
type CoinbaseConnectionMode = "extension" | "walletlink" | "smart-wallet";
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Coinbase Wallet connector for connect SDK.
|
|
47
|
+
*
|
|
48
|
+
* Supports:
|
|
49
|
+
* - Coinbase Wallet browser extension detection
|
|
50
|
+
* - WalletLink mode (QR code / deep link for mobile app)
|
|
51
|
+
* - Smart Wallet (via `smartWalletOnly` preference)
|
|
52
|
+
* - EIP-1193 provider for all EVM interactions
|
|
53
|
+
*
|
|
54
|
+
* Coexists with the EIP-6963 injected connector: the extension path
|
|
55
|
+
* duplicates what EIP-6963 provides, but this connector adds WalletLink
|
|
56
|
+
* and Smart Wallet modes that EIP-6963 cannot cover.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* const connector = new CoinbaseConnector({
|
|
61
|
+
* appName: "My DApp",
|
|
62
|
+
* appChainIds: [1, 137],
|
|
63
|
+
* });
|
|
64
|
+
*
|
|
65
|
+
* const session = await connector.connect();
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
declare class CoinbaseConnector implements UniversalConnector {
|
|
69
|
+
/** Unique connector identifier */
|
|
70
|
+
readonly id = "coinbase";
|
|
71
|
+
/** Display name for UI */
|
|
72
|
+
readonly name = "Coinbase Wallet";
|
|
73
|
+
/** Connector type identifier (shared with EIP-6963 for wallet-type parity) */
|
|
74
|
+
readonly kind = "eip6963";
|
|
75
|
+
/** Supported chain namespaces (EVM only) */
|
|
76
|
+
readonly namespaces: string[];
|
|
77
|
+
/** Feature support flags */
|
|
78
|
+
readonly supports: ConnectorSupport;
|
|
79
|
+
/** Connector configuration */
|
|
80
|
+
readonly config: {
|
|
81
|
+
appName: string;
|
|
82
|
+
appLogoUrl?: string;
|
|
83
|
+
appChainIds: number[];
|
|
84
|
+
preference: Preference["options"];
|
|
85
|
+
onQRCodeResponse?: (url: string) => void;
|
|
86
|
+
overrideRpcUrl?: Record<string, string>;
|
|
87
|
+
};
|
|
88
|
+
private sdk?;
|
|
89
|
+
private providerAdapter?;
|
|
90
|
+
private lastSession?;
|
|
91
|
+
private sessionExpiryHandler?;
|
|
92
|
+
/** Connected mode determined during connect */
|
|
93
|
+
private connectionMode?;
|
|
94
|
+
constructor(config: CoinbaseConnectorConfig);
|
|
95
|
+
/**
|
|
96
|
+
* Get or initialize the Coinbase Wallet SDK instance.
|
|
97
|
+
*/
|
|
98
|
+
private getSDK;
|
|
99
|
+
/**
|
|
100
|
+
* Get the provider from the SDK with the configured preference.
|
|
101
|
+
*/
|
|
102
|
+
private getProvider;
|
|
103
|
+
/**
|
|
104
|
+
* Connect to Coinbase Wallet.
|
|
105
|
+
*
|
|
106
|
+
* If the browser extension is installed, it will be used automatically.
|
|
107
|
+
* Otherwise, WalletLink mode will show a QR code or redirect to the
|
|
108
|
+
* Coinbase Wallet mobile app.
|
|
109
|
+
*
|
|
110
|
+
* @param input - Optional input with chainId override
|
|
111
|
+
* @returns UniversalWalletSession
|
|
112
|
+
*/
|
|
113
|
+
connect(input?: unknown): Promise<UniversalWalletSession>;
|
|
114
|
+
/**
|
|
115
|
+
* Reconnect — Coinbase SDK does not support silent reconnection.
|
|
116
|
+
* @throws WalletError always (trustedReconnect is false)
|
|
117
|
+
*/
|
|
118
|
+
reconnect(_session: UniversalWalletSession): Promise<UniversalWalletSession>;
|
|
119
|
+
/**
|
|
120
|
+
* Disconnect from Coinbase Wallet.
|
|
121
|
+
* Cleans up the provider and SDK state.
|
|
122
|
+
*/
|
|
123
|
+
disconnect(_session: UniversalWalletSession): Promise<void>;
|
|
124
|
+
/**
|
|
125
|
+
* Get accounts from the current session.
|
|
126
|
+
*/
|
|
127
|
+
getAccounts(session: UniversalWalletSession): Promise<string[]>;
|
|
128
|
+
/**
|
|
129
|
+
* Sign a message using personal_sign or eth_signTypedData_v4.
|
|
130
|
+
*/
|
|
131
|
+
signMessage(session: UniversalWalletSession, input: unknown): Promise<unknown>;
|
|
132
|
+
/**
|
|
133
|
+
* Sign a transaction without broadcasting it.
|
|
134
|
+
*
|
|
135
|
+
* Uses eth_signTransaction to request the connected wallet to sign the
|
|
136
|
+
* given transaction parameters. The signed transaction is returned
|
|
137
|
+
* but NOT submitted to the network. Callers may inspect or relay
|
|
138
|
+
* the signed payload as needed.
|
|
139
|
+
*
|
|
140
|
+
* @param session - Active wallet session
|
|
141
|
+
* @param input - Object with a `transaction` field containing standard EVM tx params
|
|
142
|
+
* @returns The signed transaction (RLP-encoded hex string)
|
|
143
|
+
* @throws WalletError if session is expired or input is invalid
|
|
144
|
+
*/
|
|
145
|
+
signTransaction(session: UniversalWalletSession, input: unknown): Promise<unknown>;
|
|
146
|
+
/**
|
|
147
|
+
* Send a transaction (eth_sendTransaction).
|
|
148
|
+
*/
|
|
149
|
+
sendTransaction(session: UniversalWalletSession, input: unknown): Promise<unknown>;
|
|
150
|
+
/**
|
|
151
|
+
* Switch the connected chain.
|
|
152
|
+
*/
|
|
153
|
+
switchChain(session: UniversalWalletSession, chainId: string): Promise<void>;
|
|
154
|
+
/**
|
|
155
|
+
* Send batched calls (wallet_sendCalls) with fallback.
|
|
156
|
+
*/
|
|
157
|
+
sendCalls(session: UniversalWalletSession, calls: BatchCall[], chainId?: string): Promise<string>;
|
|
158
|
+
/**
|
|
159
|
+
* Get wallet capabilities for the connected session.
|
|
160
|
+
*/
|
|
161
|
+
getCapabilities(session: UniversalWalletSession): Promise<Record<string, WalletCapabilities>>;
|
|
162
|
+
/**
|
|
163
|
+
* Make a raw JSON-RPC request through the Coinbase provider.
|
|
164
|
+
*/
|
|
165
|
+
request(request: {
|
|
166
|
+
method: string;
|
|
167
|
+
params: unknown[];
|
|
168
|
+
}): Promise<unknown>;
|
|
169
|
+
/**
|
|
170
|
+
* Get the ETH balance of the first connected account.
|
|
171
|
+
*/
|
|
172
|
+
getBalance(chainId?: string): Promise<string>;
|
|
173
|
+
/**
|
|
174
|
+
* Get the connection mode detected during connect.
|
|
175
|
+
*/
|
|
176
|
+
getConnectionMode(): CoinbaseConnectionMode | undefined;
|
|
177
|
+
/**
|
|
178
|
+
* Whether the connector is currently using extension mode.
|
|
179
|
+
*/
|
|
180
|
+
isExtensionMode(): boolean;
|
|
181
|
+
/**
|
|
182
|
+
* Update the supported chain IDs on the SDK instance.
|
|
183
|
+
*/
|
|
184
|
+
updateChainIds(chainIds: number[]): void;
|
|
185
|
+
/**
|
|
186
|
+
* Register a handler to be called when the session expires or the wallet disconnects.
|
|
187
|
+
*/
|
|
188
|
+
onSessionExpiry(handler: () => void): void;
|
|
189
|
+
/**
|
|
190
|
+
* Get the provider or throw if not connected.
|
|
191
|
+
*/
|
|
192
|
+
private requireProvider;
|
|
193
|
+
/**
|
|
194
|
+
* Detect the connection mode based on the provider.
|
|
195
|
+
* Extension mode: provider is Coinbase browser extension
|
|
196
|
+
* Smart Wallet: provider is Coinbase Smart Wallet
|
|
197
|
+
* Default: WalletLink (mobile/QR)
|
|
198
|
+
*/
|
|
199
|
+
private detectConnectionMode;
|
|
200
|
+
/**
|
|
201
|
+
* Set up event listeners on the provider to handle wallet-side changes.
|
|
202
|
+
*/
|
|
203
|
+
private setupEventListeners;
|
|
204
|
+
/**
|
|
205
|
+
* Clean up provider adapter and SDK state.
|
|
206
|
+
*/
|
|
207
|
+
private cleanup;
|
|
208
|
+
/**
|
|
209
|
+
* Get the default chain ID from the session.
|
|
210
|
+
*/
|
|
211
|
+
private getDefaultChainId;
|
|
212
|
+
/**
|
|
213
|
+
* Hex-encode a string for Ethereum JSON-RPC methods.
|
|
214
|
+
*/
|
|
215
|
+
private hexEncode;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Create a CoinbaseConnector instance.
|
|
219
|
+
*/
|
|
220
|
+
declare function createCoinbaseConnector(config: CoinbaseConnectorConfig): CoinbaseConnector;
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Adapter that bridges the Coinbase wallet-sdk ProviderInterface
|
|
224
|
+
* (EIP-1193 compatible) to Naculus's internal EIP-6963 provider type.
|
|
225
|
+
*
|
|
226
|
+
* Handles provider event subscriptions and cleanup so the connector
|
|
227
|
+
* can react to accountsChanged, chainChanged, and disconnect events
|
|
228
|
+
* without leaking listeners.
|
|
229
|
+
*/
|
|
230
|
+
declare class CoinbaseProviderAdapter {
|
|
231
|
+
private readonly rawProvider;
|
|
232
|
+
private listeners;
|
|
233
|
+
constructor(provider: ProviderInterface);
|
|
234
|
+
/**
|
|
235
|
+
* Get the underlying provider instance.
|
|
236
|
+
*/
|
|
237
|
+
getProvider(): ProviderInterface;
|
|
238
|
+
/**
|
|
239
|
+
* Subscribe to a provider event.
|
|
240
|
+
* Avoids duplicate handler registration for the same event+handler pair.
|
|
241
|
+
*/
|
|
242
|
+
on(event: string, handler: (...args: unknown[]) => void): void;
|
|
243
|
+
/**
|
|
244
|
+
* Unsubscribe from a provider event.
|
|
245
|
+
*/
|
|
246
|
+
off(event: string, handler: (...args: unknown[]) => void): void;
|
|
247
|
+
/**
|
|
248
|
+
* Remove all event listeners registered through this adapter.
|
|
249
|
+
*/
|
|
250
|
+
removeAllListeners(): void;
|
|
251
|
+
/**
|
|
252
|
+
* Clean up all subscriptions and prepare for garbage collection.
|
|
253
|
+
*/
|
|
254
|
+
cleanup(): void;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export { type CoinbaseConnectionMode, CoinbaseConnector, type CoinbaseConnectorConfig, CoinbaseProviderAdapter, type CoinbaseSession, createCoinbaseConnector };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{CoinbaseWalletSDK as S}from"@coinbase/wallet-sdk";import{CONNECTOR_ERROR_MESSAGES as d,createEmptySession as y,DEFAULT_RPC_URLS as _,detectPlatform as I,EIP155_MAINNET as b,extractAccounts as C,WalletError as i}from"@naculus/connect-core";var h=class{rawProvider;listeners=new Map;constructor(e){this.rawProvider=e}getProvider(){return this.rawProvider}on(e,n){let t=this.listeners.get(e);if(t)t.has(n)||(t.add(n),this.rawProvider.on(e,n));else{let s=new Set;s.add(n),this.listeners.set(e,s),this.rawProvider.on(e,n)}}off(e,n){let t=this.listeners.get(e);t&&(t.delete(n),this.rawProvider.off(e,n),t.size===0&&this.listeners.delete(e))}removeAllListeners(){for(let[e,n]of this.listeners.entries())for(let t of n)this.rawProvider.off(e,t);this.listeners.clear()}cleanup(){this.removeAllListeners()}};var v=class{id="coinbase";name="Coinbase Wallet";kind="eip6963";namespaces=["eip155"];supports={desktop:!0,mobile:!0,deepLink:!0,qr:!0,trustedReconnect:!1};config;sdk;providerAdapter;lastSession;sessionExpiryHandler;connectionMode;constructor(e){if(!e.appName)throw new i("invalid_input","CoinbaseConnector requires appName.");this.config={appName:e.appName,appLogoUrl:e.appLogoUrl,appChainIds:e.appChainIds??[1],preference:e.preference??"all",onQRCodeResponse:e.onQRCodeResponse,overrideRpcUrl:e.overrideRpcUrl}}getSDK(){return this.sdk?this.sdk:(this.sdk=new S({appName:this.config.appName,appLogoUrl:this.config.appLogoUrl??null,appChainIds:this.config.appChainIds}),this.sdk)}getProvider(){let e=this.getSDK(),n={options:this.config.preference},t=e.makeWeb3Provider(n);return this.providerAdapter=new h(t),t}async connect(e){let n=this.getProvider(),t=e&&typeof e=="object"&&!Array.isArray(e)?e:void 0;this.setupEventListeners(n);try{let s=await n.request({method:"eth_requestAccounts"});if(!s||s.length===0)throw new i("user_rejected","No accounts returned from Coinbase Wallet.");let o=await n.request({method:"eth_chainId"}),r=Number.parseInt(o,16);this.connectionMode=this.detectConnectionMode(n);let a=`eip155:${r}`,c=y({id:crypto.randomUUID(),topic:void 0,walletId:"coinbase-wallet",walletType:"eip6963",namespaces:{eip155:{chains:[a],accounts:s.map(p=>`${a}:${p}`),methods:["eth_sendTransaction","eth_signTransaction","personal_sign","eth_signTypedData_v4","wallet_switchEthereumChain","eth_requestAccounts","eth_accounts","eth_chainId"],events:["accountsChanged","chainChanged","disconnect"]}},platform:I(),createdAt:new Date().toISOString(),updatedAt:new Date().toISOString()});return this.lastSession=c,c}catch(s){this.cleanup();let o=s instanceof Error?s.message:"Coinbase Wallet connect failed.";throw o.toLowerCase().includes("rejected")||o.toLowerCase().includes("user denied")?new i("user_rejected",o,s):new i("wallet_unavailable",o,s)}}async reconnect(e){throw new i("session_expired","Coinbase Wallet does not support silent reconnection. Please call connect() again.")}async disconnect(e){try{this.providerAdapter&&await this.providerAdapter.getProvider().disconnect()}catch{}finally{this.cleanup()}}async getAccounts(e){if(this.providerAdapter)try{let t=await this.providerAdapter.getProvider().request({method:"eth_accounts"});if(t&&t.length>0)return t}catch{}return C(e.namespaces)}async signMessage(e,n){let t=this.requireProvider();if(!n||typeof n!="object")throw new i("method_not_allowed",d.INVALID_INPUT);let s=n,o=typeof s.message=="string"?s.message:void 0,r=typeof s.address=="string"?s.address:void 0;if(!o||!r)throw new i("method_not_allowed",d.MISSING_MESSAGE);let a=r.includes(":")?r.split(":").pop():r,p=o.startsWith("{")?["eth_signTypedData_v4"]:["personal_sign","eth_sign"],l;for(let g of p){let f;g==="personal_sign"?f=[this.hexEncode(o),a]:g==="eth_sign"?f=[a,this.hexEncode(o)]:f=[a,o];try{return await t.request({method:g,params:f})}catch(w){l=w;let u=w instanceof Error?w.message.toLowerCase():"";if(!(u.includes("not been authorized")||u.includes("not authorized")||u.includes("not approved")||u.includes("method not found")||u.includes("method_not_allowed")))throw new i("signature_rejected","Coinbase signature rejected.",w)}}throw l instanceof i?l:new i("signature_rejected","Coinbase signature rejected.",l)}async signTransaction(e,n){let t=this.requireProvider();if(!n||typeof n!="object")throw new i("method_not_allowed",d.INVALID_INPUT);let o=n.transaction;if(!o)throw new i("method_not_allowed",d.MISSING_TX);try{return await t.request({method:"eth_signTransaction",params:[o]})}catch(r){throw r instanceof i?r:new i("signature_rejected","Coinbase sign transaction rejected.",r)}}async sendTransaction(e,n){let t=this.requireProvider();if(!n||typeof n!="object")throw new i("method_not_allowed",d.INVALID_INPUT);let o=n.transaction;if(!o)throw new i("method_not_allowed",d.MISSING_TX);try{return await t.request({method:"eth_sendTransaction",params:[o]})}catch(r){throw r instanceof i?r:new i("tx_failed","Coinbase transaction failed.",r)}}async switchChain(e,n){let t=this.requireProvider();if(!n.startsWith("eip155:"))throw new i("chain_unsupported","Coinbase connector only supports EVM chains.");let s=n.split(":")[1],o=`0x${Number(s).toString(16)}`;try{await t.request({method:"wallet_switchEthereumChain",params:[{chainId:o}]})}catch(r){let a=r instanceof Error?r.message.toLowerCase():"";throw a.includes("unrecognized")||a.includes("4902")?new i("chain_unsupported",`Coinbase Wallet does not support chain ${n}.`,r):r instanceof i?r:new i("chain_unsupported","Coinbase switch chain failed.",r)}}async sendCalls(e,n,t){let s=this.requireProvider(),o=t??this.getDefaultChainId(e);try{return await s.request({method:"wallet_sendCalls",params:[{calls:n,chainId:o}]})}catch{let a=(e.namespaces.eip155?.accounts??[])[0]?.split(":").pop();if(!a)throw new i("session_expired",d.NO_ACCOUNT_TX);let c=[];for(let p of n){let l=await s.request({method:"eth_sendTransaction",params:[{from:a,to:p.to,value:p.value,data:p.data}]});c.push(l)}return c.length===1?c[0]:c.join(",")}}async getCapabilities(e){let n={};for(let t of Object.values(e.namespaces)){let s=t.capabilities??{},o=!!s.atomicBatch;for(let r of t.chains)n[r]={atomicBatch:o?{supported:!0,maxBatchSize:5}:{supported:!1},paymasterService:s.paymasterService?{supported:!0}:void 0}}return n}async request(e){return this.requireProvider().request({method:e.method,params:e.params})}async getBalance(e){let n=this.lastSession;if(!n)throw new i("session_expired",d.SESSION_EXPIRED);let t=e??this.getDefaultChainId(n),s=this.config.overrideRpcUrl?.[t]??_[t];if(!s)throw new i("chain_unsupported","No RPC URL for chain "+t);let r=C(n.namespaces)[0]?.split(":").pop();if(!r)throw new i("method_not_allowed",d.NO_ACCOUNTS);return(await(await fetch(s,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({jsonrpc:"2.0",id:1,method:"eth_getBalance",params:[r,"latest"]})})).json()).result}getConnectionMode(){return this.connectionMode}isExtensionMode(){return this.connectionMode==="extension"}updateChainIds(e){this.config.appChainIds=e,console.warn("[CoinbaseConnector] updateChainIds: Coinbase SDK does not support dynamic chain ID updates. New chain IDs stored in config but will only apply on next connect().")}onSessionExpiry(e){this.sessionExpiryHandler=e}requireProvider(){if(!this.providerAdapter)throw new i("session_expired",d.SESSION_EXPIRED);return this.providerAdapter.getProvider()}detectConnectionMode(e){if(typeof window<"u"){let t=window.coinbaseWalletExtension;if(t&&t===e)return"extension"}return this.config.preference==="smartWalletOnly"?"smart-wallet":"walletlink"}setupEventListeners(e){this.providerAdapter||(this.providerAdapter=new h(e)),this.providerAdapter.on("accountsChanged",n=>{let t=n;if(t.length===0)this.sessionExpiryHandler?.();else if(this.lastSession){let s=this.getDefaultChainId(this.lastSession);this.lastSession.namespaces.eip155={...this.lastSession.namespaces.eip155,accounts:t.map(o=>`${s}:${o}`)},this.lastSession.updatedAt=new Date().toISOString()}}),this.providerAdapter.on("chainChanged",n=>{if(this.lastSession){let t=n,o=`eip155:${Number.parseInt(t,16)}`,r=this.lastSession.namespaces.eip155?.accounts??[];this.lastSession.namespaces.eip155={...this.lastSession.namespaces.eip155,chains:[o],accounts:r.map(a=>{let c=a.split(":");return c.length>=3?`${o}:${c[2]}`:`${o}:${c[0]}`})},this.lastSession.updatedAt=new Date().toISOString()}}),this.providerAdapter.on("disconnect",()=>{this.sessionExpiryHandler?.(),this.cleanup()})}cleanup(){this.providerAdapter&&(this.providerAdapter.cleanup(),this.providerAdapter=void 0),this.sdk=void 0,this.lastSession=void 0,this.connectionMode=void 0}getDefaultChainId(e){let n=e.namespaces.eip155;return n&&n.chains.length>0?n.chains[0]:b}hexEncode(e){if(/^0x[0-9a-fA-F]*$/.test(e))return e;let n=new TextEncoder().encode(e),t="0x";for(let s=0;s<n.length;s++)t+=n[s].toString(16).padStart(2,"0");return t}};function P(m){return new v(m)}export{v as CoinbaseConnector,h as CoinbaseProviderAdapter,P as createCoinbaseConnector};
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/connector.ts","../src/provider.ts"],"sourcesContent":["import type { Preference, ProviderInterface } from \"@coinbase/wallet-sdk\";\nimport { CoinbaseWalletSDK } from \"@coinbase/wallet-sdk\";\nimport type {\n BatchCall,\n ConnectorSupport,\n UniversalConnector,\n UniversalWalletSession,\n WalletCapabilities,\n} from \"@naculus/connect-core\";\nimport {\n CONNECTOR_ERROR_MESSAGES,\n createEmptySession,\n DEFAULT_RPC_URLS,\n detectPlatform,\n EIP155_MAINNET,\n extractAccounts,\n WalletError,\n} from \"@naculus/connect-core\";\nimport { CoinbaseProviderAdapter } from \"./provider\";\nimport type {\n CoinbaseConnectionMode,\n CoinbaseConnectorConfig,\n CoinbaseSession,\n} from \"./types\";\n\n/**\n * Coinbase Wallet connector for connect SDK.\n *\n * Supports:\n * - Coinbase Wallet browser extension detection\n * - WalletLink mode (QR code / deep link for mobile app)\n * - Smart Wallet (via `smartWalletOnly` preference)\n * - EIP-1193 provider for all EVM interactions\n *\n * Coexists with the EIP-6963 injected connector: the extension path\n * duplicates what EIP-6963 provides, but this connector adds WalletLink\n * and Smart Wallet modes that EIP-6963 cannot cover.\n *\n * @example\n * ```typescript\n * const connector = new CoinbaseConnector({\n * appName: \"My DApp\",\n * appChainIds: [1, 137],\n * });\n *\n * const session = await connector.connect();\n * ```\n */\nexport class CoinbaseConnector implements UniversalConnector {\n /** Unique connector identifier */\n readonly id = \"coinbase\";\n\n /** Display name for UI */\n readonly name = \"Coinbase Wallet\";\n\n /** Connector type identifier (shared with EIP-6963 for wallet-type parity) */\n readonly kind = \"eip6963\";\n\n /** Supported chain namespaces (EVM only) */\n readonly namespaces = [\"eip155\"];\n\n /** Feature support flags */\n readonly supports: ConnectorSupport = {\n desktop: true,\n mobile: true,\n deepLink: true,\n qr: true,\n trustedReconnect: false, // CB SDK does not support silent reconnection\n };\n\n /** Connector configuration */\n readonly config: {\n appName: string;\n appLogoUrl?: string;\n appChainIds: number[];\n preference: Preference[\"options\"];\n onQRCodeResponse?: (url: string) => void;\n overrideRpcUrl?: Record<string, string>;\n };\n\n private sdk?: CoinbaseWalletSDK;\n private providerAdapter?: CoinbaseProviderAdapter;\n private lastSession?: UniversalWalletSession;\n private sessionExpiryHandler?: () => void;\n\n /** Connected mode determined during connect */\n private connectionMode?: CoinbaseConnectionMode;\n\n constructor(config: CoinbaseConnectorConfig) {\n if (!config.appName) {\n throw new WalletError(\n \"invalid_input\",\n \"CoinbaseConnector requires appName.\",\n );\n }\n\n this.config = {\n appName: config.appName,\n appLogoUrl: config.appLogoUrl,\n appChainIds: config.appChainIds ?? [1],\n preference: config.preference ?? \"all\",\n onQRCodeResponse: config.onQRCodeResponse,\n overrideRpcUrl: config.overrideRpcUrl,\n };\n }\n\n /**\n * Get or initialize the Coinbase Wallet SDK instance.\n */\n private getSDK(): CoinbaseWalletSDK {\n if (this.sdk) {\n return this.sdk;\n }\n\n this.sdk = new CoinbaseWalletSDK({\n appName: this.config.appName,\n appLogoUrl: this.config.appLogoUrl ?? null,\n appChainIds: this.config.appChainIds,\n });\n\n return this.sdk;\n }\n\n /**\n * Get the provider from the SDK with the configured preference.\n */\n private getProvider(): ProviderInterface {\n const sdk = this.getSDK();\n\n const preference: Preference = {\n options: this.config.preference,\n };\n\n const provider = sdk.makeWeb3Provider(preference);\n\n this.providerAdapter = new CoinbaseProviderAdapter(provider);\n\n return provider;\n }\n\n /**\n * Connect to Coinbase Wallet.\n *\n * If the browser extension is installed, it will be used automatically.\n * Otherwise, WalletLink mode will show a QR code or redirect to the\n * Coinbase Wallet mobile app.\n *\n * @param input - Optional input with chainId override\n * @returns UniversalWalletSession\n */\n async connect(input?: unknown): Promise<UniversalWalletSession> {\n const provider = this.getProvider();\n const connectInput =\n input && typeof input === \"object\" && !Array.isArray(input)\n ? (input as Record<string, unknown>)\n : undefined;\n\n // Subscribe to events so we can react to wallet-side changes\n this.setupEventListeners(provider);\n\n try {\n // Request accounts — this triggers the connection flow\n const accounts = (await provider.request({\n method: \"eth_requestAccounts\",\n })) as `0x${string}`[];\n\n if (!accounts || accounts.length === 0) {\n throw new WalletError(\n \"user_rejected\",\n \"No accounts returned from Coinbase Wallet.\",\n );\n }\n\n const chainIdHex = (await provider.request({\n method: \"eth_chainId\",\n })) as string;\n\n const chainIdNum = Number.parseInt(chainIdHex, 16);\n\n // Detect connection mode\n this.connectionMode = this.detectConnectionMode(provider);\n\n // Build the session\n const caip2ChainId = `eip155:${chainIdNum}`;\n const walletSession = createEmptySession({\n id: crypto.randomUUID(),\n topic: undefined, // Coinbase SDK does not use topics\n walletId: \"coinbase-wallet\",\n walletType: \"eip6963\",\n namespaces: {\n eip155: {\n chains: [caip2ChainId],\n accounts: accounts.map((a) => `${caip2ChainId}:${a}`),\n // Standard EIP-1193 methods supported by Coinbase Wallet\n // These are the minimal set required for core dApp interactions.\n // Extend as needed for app-specific requirements.\n methods: [\n \"eth_sendTransaction\",\n \"eth_signTransaction\",\n \"personal_sign\",\n \"eth_signTypedData_v4\",\n \"wallet_switchEthereumChain\",\n \"eth_requestAccounts\",\n \"eth_accounts\",\n \"eth_chainId\",\n ],\n events: [\"accountsChanged\", \"chainChanged\", \"disconnect\"],\n },\n },\n platform: detectPlatform(),\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n });\n\n // On-chain balance check is optional; just include a basic session\n this.lastSession = walletSession;\n return walletSession;\n } catch (error) {\n this.cleanup();\n const message =\n error instanceof Error\n ? error.message\n : \"Coinbase Wallet connect failed.\";\n if (\n message.toLowerCase().includes(\"rejected\") ||\n message.toLowerCase().includes(\"user denied\")\n ) {\n throw new WalletError(\"user_rejected\", message, error);\n }\n\n throw new WalletError(\"wallet_unavailable\", message, error);\n }\n }\n\n /**\n * Reconnect — Coinbase SDK does not support silent reconnection.\n * @throws WalletError always (trustedReconnect is false)\n */\n async reconnect(\n _session: UniversalWalletSession,\n ): Promise<UniversalWalletSession> {\n throw new WalletError(\n \"session_expired\",\n \"Coinbase Wallet does not support silent reconnection. Please call connect() again.\",\n );\n }\n\n /**\n * Disconnect from Coinbase Wallet.\n * Cleans up the provider and SDK state.\n */\n async disconnect(_session: UniversalWalletSession): Promise<void> {\n try {\n if (this.providerAdapter) {\n const provider = this.providerAdapter.getProvider();\n await provider.disconnect();\n }\n } catch {\n // Swallow disconnect errors — provider may be in an invalid state\n } finally {\n this.cleanup();\n }\n }\n\n /**\n * Get accounts from the current session.\n */\n async getAccounts(session: UniversalWalletSession): Promise<string[]> {\n if (this.providerAdapter) {\n try {\n const provider = this.providerAdapter.getProvider();\n const accounts = (await provider.request({\n method: \"eth_accounts\",\n })) as `0x${string}`[];\n if (accounts && accounts.length > 0) {\n return accounts;\n }\n } catch {\n // Fall back to session data\n }\n }\n\n return extractAccounts(session.namespaces);\n }\n\n /**\n * Sign a message using personal_sign or eth_signTypedData_v4.\n */\n async signMessage(\n session: UniversalWalletSession,\n input: unknown,\n ): Promise<unknown> {\n const provider = this.requireProvider();\n if (!input || typeof input !== \"object\")\n throw new WalletError(\n \"method_not_allowed\",\n CONNECTOR_ERROR_MESSAGES.INVALID_INPUT,\n );\n\n const inputObj = input as Record<string, unknown>;\n const message =\n typeof inputObj.message === \"string\" ? inputObj.message : undefined;\n const rawAddress =\n typeof inputObj.address === \"string\" ? inputObj.address : undefined;\n if (!message || !rawAddress)\n throw new WalletError(\n \"method_not_allowed\",\n CONNECTOR_ERROR_MESSAGES.MISSING_MESSAGE,\n );\n\n const address = rawAddress.includes(\":\")\n ? rawAddress.split(\":\").pop()!\n : rawAddress;\n\n // Determine signing method based on message content\n const isStructured = message.startsWith(\"{\");\n const tryMethods = isStructured\n ? [\"eth_signTypedData_v4\"]\n : [\"personal_sign\", \"eth_sign\"];\n\n let lastError: unknown;\n for (const tryMethod of tryMethods) {\n let params: unknown[];\n if (tryMethod === \"personal_sign\") {\n params = [this.hexEncode(message), address];\n } else if (tryMethod === \"eth_sign\") {\n params = [address, this.hexEncode(message)];\n } else {\n // eth_signTypedData_v4\n params = [address, message];\n }\n\n try {\n return await provider.request({\n method: tryMethod,\n params,\n });\n } catch (error) {\n lastError = error;\n const errMsg =\n error instanceof Error ? error.message.toLowerCase() : \"\";\n const isMethodRejection =\n errMsg.includes(\"not been authorized\") ||\n errMsg.includes(\"not authorized\") ||\n errMsg.includes(\"not approved\") ||\n errMsg.includes(\"method not found\") ||\n errMsg.includes(\"method_not_allowed\");\n\n if (!isMethodRejection) {\n // Unrecoverable error\n throw new WalletError(\n \"signature_rejected\",\n \"Coinbase signature rejected.\",\n error,\n );\n }\n // Fall through to next method\n }\n }\n\n if (lastError instanceof WalletError) throw lastError;\n throw new WalletError(\n \"signature_rejected\",\n \"Coinbase signature rejected.\",\n lastError,\n );\n }\n\n /**\n * Sign a transaction without broadcasting it.\n *\n * Uses eth_signTransaction to request the connected wallet to sign the\n * given transaction parameters. The signed transaction is returned\n * but NOT submitted to the network. Callers may inspect or relay\n * the signed payload as needed.\n *\n * @param session - Active wallet session\n * @param input - Object with a `transaction` field containing standard EVM tx params\n * @returns The signed transaction (RLP-encoded hex string)\n * @throws WalletError if session is expired or input is invalid\n */\n async signTransaction(\n session: UniversalWalletSession,\n input: unknown,\n ): Promise<unknown> {\n const provider = this.requireProvider();\n if (!input || typeof input !== \"object\")\n throw new WalletError(\n \"method_not_allowed\",\n CONNECTOR_ERROR_MESSAGES.INVALID_INPUT,\n );\n\n const inputObj = input as Record<string, unknown>;\n const transaction = inputObj.transaction as\n | Record<string, unknown>\n | undefined;\n if (!transaction)\n throw new WalletError(\n \"method_not_allowed\",\n CONNECTOR_ERROR_MESSAGES.MISSING_TX,\n );\n\n try {\n return await provider.request({\n method: \"eth_signTransaction\",\n params: [transaction],\n });\n } catch (error) {\n if (error instanceof WalletError) throw error;\n throw new WalletError(\n \"signature_rejected\",\n \"Coinbase sign transaction rejected.\",\n error,\n );\n }\n }\n\n /**\n * Send a transaction (eth_sendTransaction).\n */\n async sendTransaction(\n session: UniversalWalletSession,\n input: unknown,\n ): Promise<unknown> {\n const provider = this.requireProvider();\n if (!input || typeof input !== \"object\")\n throw new WalletError(\n \"method_not_allowed\",\n CONNECTOR_ERROR_MESSAGES.INVALID_INPUT,\n );\n\n const inputObj = input as Record<string, unknown>;\n const transaction = inputObj.transaction as\n | Record<string, unknown>\n | undefined;\n if (!transaction)\n throw new WalletError(\n \"method_not_allowed\",\n CONNECTOR_ERROR_MESSAGES.MISSING_TX,\n );\n\n try {\n return await provider.request({\n method: \"eth_sendTransaction\",\n params: [transaction],\n });\n } catch (error) {\n if (error instanceof WalletError) throw error;\n throw new WalletError(\"tx_failed\", \"Coinbase transaction failed.\", error);\n }\n }\n\n /**\n * Switch the connected chain.\n */\n async switchChain(\n session: UniversalWalletSession,\n chainId: string,\n ): Promise<void> {\n const provider = this.requireProvider();\n if (!chainId.startsWith(\"eip155:\")) {\n throw new WalletError(\n \"chain_unsupported\",\n \"Coinbase connector only supports EVM chains.\",\n );\n }\n\n const numericChainId = chainId.split(\":\")[1];\n const hexChainId = `0x${Number(numericChainId).toString(16)}`;\n\n try {\n await provider.request({\n method: \"wallet_switchEthereumChain\",\n params: [{ chainId: hexChainId }],\n });\n } catch (error) {\n const errMsg = error instanceof Error ? error.message.toLowerCase() : \"\";\n // Wallet might not have the chain — try wallet_addEthereumChain\n if (errMsg.includes(\"unrecognized\") || errMsg.includes(\"4902\")) {\n throw new WalletError(\n \"chain_unsupported\",\n `Coinbase Wallet does not support chain ${chainId}.`,\n error,\n );\n }\n if (error instanceof WalletError) throw error;\n throw new WalletError(\n \"chain_unsupported\",\n \"Coinbase switch chain failed.\",\n error,\n );\n }\n }\n\n /**\n * Send batched calls (wallet_sendCalls) with fallback.\n */\n async sendCalls(\n session: UniversalWalletSession,\n calls: BatchCall[],\n chainId?: string,\n ): Promise<string> {\n const provider = this.requireProvider();\n const resolvedChainId = chainId ?? this.getDefaultChainId(session);\n\n try {\n return (await provider.request({\n method: \"wallet_sendCalls\",\n params: [{ calls, chainId: resolvedChainId }],\n })) as string;\n } catch {\n // Fallback: send each call individually\n const accounts = session.namespaces.eip155?.accounts ?? [];\n const fromAccount = accounts[0]?.split(\":\").pop();\n if (!fromAccount) {\n throw new WalletError(\n \"session_expired\",\n CONNECTOR_ERROR_MESSAGES.NO_ACCOUNT_TX,\n );\n }\n\n const txHashes: string[] = [];\n for (const call of calls) {\n const hash = (await provider.request({\n method: \"eth_sendTransaction\",\n params: [\n {\n from: fromAccount,\n to: call.to,\n value: call.value,\n data: call.data,\n },\n ],\n })) as string;\n txHashes.push(hash);\n }\n\n return txHashes.length === 1 ? txHashes[0] : txHashes.join(\",\");\n }\n }\n\n /**\n * Get wallet capabilities for the connected session.\n */\n async getCapabilities(\n session: UniversalWalletSession,\n ): Promise<Record<string, WalletCapabilities>> {\n const capabilities: Record<string, WalletCapabilities> = {};\n\n for (const ns of Object.values(session.namespaces)) {\n const nsCaps = (ns.capabilities ?? {}) as Record<string, unknown>;\n const atomicBatchSupported = Boolean(nsCaps.atomicBatch);\n\n for (const chain of ns.chains) {\n capabilities[chain] = {\n atomicBatch: atomicBatchSupported\n ? { supported: true, maxBatchSize: 5 }\n : { supported: false },\n paymasterService: nsCaps.paymasterService\n ? { supported: true }\n : undefined,\n };\n }\n }\n\n return capabilities;\n }\n\n /**\n * Make a raw JSON-RPC request through the Coinbase provider.\n */\n async request(request: {\n method: string;\n params: unknown[];\n }): Promise<unknown> {\n const provider = this.requireProvider();\n return provider.request({\n method: request.method,\n params: request.params,\n });\n }\n\n /**\n * Get the ETH balance of the first connected account.\n */\n async getBalance(chainId?: string): Promise<string> {\n const session = this.lastSession;\n if (!session)\n throw new WalletError(\n \"session_expired\",\n CONNECTOR_ERROR_MESSAGES.SESSION_EXPIRED,\n );\n\n const cId = chainId ?? this.getDefaultChainId(session);\n const rpcUrl = this.config.overrideRpcUrl?.[cId] ?? DEFAULT_RPC_URLS[cId];\n if (!rpcUrl)\n throw new WalletError(\"chain_unsupported\", \"No RPC URL for chain \" + cId);\n\n const allAccounts = extractAccounts(session.namespaces);\n const address = allAccounts[0]?.split(\":\").pop();\n if (!address)\n throw new WalletError(\n \"method_not_allowed\",\n CONNECTOR_ERROR_MESSAGES.NO_ACCOUNTS,\n );\n\n const response = await fetch(rpcUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n jsonrpc: \"2.0\",\n id: 1,\n method: \"eth_getBalance\",\n params: [address, \"latest\"],\n }),\n });\n const data = await response.json();\n return data.result;\n }\n\n // ── Coinbase-specific methods ──\n\n /**\n * Get the connection mode detected during connect.\n */\n getConnectionMode(): CoinbaseConnectionMode | undefined {\n return this.connectionMode;\n }\n\n /**\n * Whether the connector is currently using extension mode.\n */\n isExtensionMode(): boolean {\n return this.connectionMode === \"extension\";\n }\n\n /**\n * Update the supported chain IDs on the SDK instance.\n */\n updateChainIds(chainIds: number[]): void {\n this.config.appChainIds = chainIds;\n console.warn(\n \"[CoinbaseConnector] updateChainIds: Coinbase SDK does not support dynamic chain ID updates. \" +\n \"New chain IDs stored in config but will only apply on next connect().\",\n );\n }\n\n /**\n * Register a handler to be called when the session expires or the wallet disconnects.\n */\n onSessionExpiry(handler: () => void): void {\n this.sessionExpiryHandler = handler;\n }\n\n // ── Private helpers ──\n\n /**\n * Get the provider or throw if not connected.\n */\n private requireProvider(): ProviderInterface {\n if (!this.providerAdapter) {\n throw new WalletError(\n \"session_expired\",\n CONNECTOR_ERROR_MESSAGES.SESSION_EXPIRED,\n );\n }\n return this.providerAdapter.getProvider();\n }\n\n /**\n * Detect the connection mode based on the provider.\n * Extension mode: provider is Coinbase browser extension\n * Smart Wallet: provider is Coinbase Smart Wallet\n * Default: WalletLink (mobile/QR)\n */\n private detectConnectionMode(\n provider: ProviderInterface,\n ): CoinbaseConnectionMode {\n // Check if the extension is installed and matches\n if (typeof window !== \"undefined\") {\n const win = window as unknown as Record<string, unknown>;\n const extProvider = win.coinbaseWalletExtension as\n | ProviderInterface\n | undefined;\n if (extProvider && extProvider === provider) {\n return \"extension\";\n }\n }\n\n // Check for Smart Wallet\n if (this.config.preference === \"smartWalletOnly\") {\n return \"smart-wallet\";\n }\n\n return \"walletlink\";\n }\n\n /**\n * Set up event listeners on the provider to handle wallet-side changes.\n */\n private setupEventListeners(provider: ProviderInterface): void {\n if (!this.providerAdapter) {\n this.providerAdapter = new CoinbaseProviderAdapter(provider);\n }\n\n this.providerAdapter.on(\"accountsChanged\", (accounts: unknown) => {\n const accs = accounts as string[];\n if (accs.length === 0) {\n // All accounts disconnected\n this.sessionExpiryHandler?.();\n } else if (this.lastSession) {\n // Update accounts in the session\n const chainId = this.getDefaultChainId(this.lastSession);\n this.lastSession.namespaces.eip155 = {\n ...this.lastSession.namespaces.eip155,\n accounts: accs.map((a) => `${chainId}:${a}`),\n };\n this.lastSession.updatedAt = new Date().toISOString();\n }\n });\n\n this.providerAdapter.on(\"chainChanged\", (chainId: unknown) => {\n if (this.lastSession) {\n const hexChainId = chainId as string;\n const chainIdNum = Number.parseInt(hexChainId, 16);\n const caip2ChainId = `eip155:${chainIdNum}`;\n\n // Update the session namespaces with the new chain\n const existingAccounts =\n this.lastSession.namespaces.eip155?.accounts ?? [];\n\n this.lastSession.namespaces.eip155 = {\n ...this.lastSession.namespaces.eip155,\n chains: [caip2ChainId],\n accounts: existingAccounts.map((a) => {\n const parts = a.split(\":\");\n return parts.length >= 3\n ? `${caip2ChainId}:${parts[2]}`\n : `${caip2ChainId}:${parts[0]}`;\n }),\n };\n\n this.lastSession.updatedAt = new Date().toISOString();\n }\n });\n\n this.providerAdapter.on(\"disconnect\", () => {\n this.sessionExpiryHandler?.();\n this.cleanup();\n });\n }\n\n /**\n * Clean up provider adapter and SDK state.\n */\n private cleanup(): void {\n if (this.providerAdapter) {\n this.providerAdapter.cleanup();\n this.providerAdapter = undefined;\n }\n this.sdk = undefined;\n this.lastSession = undefined;\n this.connectionMode = undefined;\n }\n\n /**\n * Get the default chain ID from the session.\n */\n private getDefaultChainId(session: UniversalWalletSession): string {\n const evmNamespace = session.namespaces[\"eip155\"];\n if (evmNamespace && evmNamespace.chains.length > 0) {\n return evmNamespace.chains[0];\n }\n\n return EIP155_MAINNET;\n }\n\n /**\n * Hex-encode a string for Ethereum JSON-RPC methods.\n */\n private hexEncode(message: string): `0x${string}` {\n if (/^0x[0-9a-fA-F]*$/.test(message)) return message as `0x${string}`;\n\n const bytes = new TextEncoder().encode(message);\n let hex = \"0x\";\n for (let i = 0; i < bytes.length; i++) {\n hex += bytes[i].toString(16).padStart(2, \"0\");\n }\n return hex as `0x${string}`;\n }\n}\n\n/**\n * Create a CoinbaseConnector instance.\n */\nexport function createCoinbaseConnector(\n config: CoinbaseConnectorConfig,\n): CoinbaseConnector {\n return new CoinbaseConnector(config);\n}\n","import type { ProviderInterface } from \"@coinbase/wallet-sdk\";\n\n/**\n * Adapter that bridges the Coinbase wallet-sdk ProviderInterface\n * (EIP-1193 compatible) to Naculus's internal EIP-6963 provider type.\n *\n * Handles provider event subscriptions and cleanup so the connector\n * can react to accountsChanged, chainChanged, and disconnect events\n * without leaking listeners.\n */\nexport class CoinbaseProviderAdapter {\n private readonly rawProvider: ProviderInterface;\n private listeners: Map<string, Set<(...args: unknown[]) => void>> = new Map();\n\n constructor(provider: ProviderInterface) {\n this.rawProvider = provider;\n }\n\n /**\n * Get the underlying provider instance.\n */\n getProvider(): ProviderInterface {\n return this.rawProvider;\n }\n\n /**\n * Subscribe to a provider event.\n * Avoids duplicate handler registration for the same event+handler pair.\n */\n on(event: string, handler: (...args: unknown[]) => void): void {\n const existing = this.listeners.get(event);\n if (!existing) {\n const set = new Set<(...args: unknown[]) => void>();\n set.add(handler);\n this.listeners.set(event, set);\n this.rawProvider.on(event as any, handler as any);\n } else if (!existing.has(handler)) {\n existing.add(handler);\n this.rawProvider.on(event as any, handler as any);\n }\n }\n\n /**\n * Unsubscribe from a provider event.\n */\n off(event: string, handler: (...args: unknown[]) => void): void {\n const existing = this.listeners.get(event);\n if (existing) {\n existing.delete(handler);\n this.rawProvider.off(event as any, handler as any);\n if (existing.size === 0) {\n this.listeners.delete(event);\n }\n }\n }\n\n /**\n * Remove all event listeners registered through this adapter.\n */\n removeAllListeners(): void {\n for (const [event, handlers] of this.listeners.entries()) {\n for (const handler of handlers) {\n this.rawProvider.off(event as any, handler as any);\n }\n }\n this.listeners.clear();\n }\n\n /**\n * Clean up all subscriptions and prepare for garbage collection.\n */\n cleanup(): void {\n this.removeAllListeners();\n }\n}\n"],"mappings":"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","names":["CoinbaseWalletSDK","CONNECTOR_ERROR_MESSAGES","createEmptySession","DEFAULT_RPC_URLS","detectPlatform","EIP155_MAINNET","extractAccounts","WalletError","CoinbaseProviderAdapter","provider","event","handler","existing","set","handlers","CoinbaseConnector","config","WalletError","CoinbaseWalletSDK","sdk","preference","provider","CoinbaseProviderAdapter","input","connectInput","accounts","chainIdHex","chainIdNum","caip2ChainId","walletSession","createEmptySession","a","detectPlatform","error","message","_session","session","extractAccounts","CONNECTOR_ERROR_MESSAGES","inputObj","rawAddress","address","tryMethods","lastError","tryMethod","params","errMsg","transaction","chainId","numericChainId","hexChainId","calls","resolvedChainId","fromAccount","txHashes","call","hash","capabilities","ns","nsCaps","atomicBatchSupported","chain","request","cId","rpcUrl","DEFAULT_RPC_URLS","chainIds","handler","extProvider","accs","existingAccounts","parts","evmNamespace","EIP155_MAINNET","bytes","hex","i","createCoinbaseConnector"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@naculus/connector-coinbase",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@coinbase/wallet-sdk": "^4.3.7",
|
|
18
|
+
"@naculus/connect-core": "0.1.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"tsup": "^8.3.6",
|
|
22
|
+
"typescript": "^5.9.3"
|
|
23
|
+
},
|
|
24
|
+
"sideEffects": false,
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsup",
|
|
27
|
+
"clean": "rm -rf dist"
|
|
28
|
+
}
|
|
29
|
+
}
|