unich-web3-sdk
Version:
Web3 SDK for DEX applications with multi-chain support
6 lines (5 loc) • 17.9 kB
JavaScript
"use client";
"use strict";
import {createPublicClient,http,defineChain,createWalletClient,custom}from'viem';import {mainnet,goerli,sepolia,polygon,polygonMumbai,optimism,optimismGoerli,arbitrum,arbitrumGoerli,base,baseGoerli}from'viem/chains';import {Connection,PublicKey}from'@solana/web3.js';import fe from'eventemitter3';import {createContext,useState,useRef,useEffect,useContext}from'react';import {create}from'zustand';import {persist}from'zustand/middleware';import {jsx}from'react/jsx-runtime';var V=(t=>(t.EVM="evm",t.SVM="svm",t))(V||{}),L=(o=>(o.MAINNET="mainnet",o.TESTNET="testnet",o.DEVNET="devnet",o))(L||{});var H=(r=>(r.DISCONNECTED="disconnected",r.CONNECTING="connecting",r.CONNECTED="connected",r.ERROR="error",r))(H||{}),G=(i=>(i.CONNECT="connect",i.DISCONNECT="disconnect",i.CHAIN_CHANGED="chainChanged",i.ACCOUNT_CHANGED="accountChanged",i.ERROR="error",i))(G||{});function P(n){return [mainnet,goerli,sepolia,polygon,polygonMumbai,optimism,optimismGoerli,arbitrum,arbitrumGoerli,base,baseGoerli].find(t=>t.id===n)}var f=class{constructor(){this.clientCache=new Map;}getProvider(e){let t=e.id;if(this.clientCache.has(t))return this.clientCache.get(t);let o=P(t),r;if(o)r=createPublicClient({chain:o,transport:http()});else {let i=defineChain({id:Number(t),name:e.name,nativeCurrency:{name:e.nativeCurrency.name,symbol:e.nativeCurrency.symbol,decimals:e.nativeCurrency.decimals},rpcUrls:{default:{http:e.rpcUrls}},blockExplorers:e.blockExplorerUrls?{default:{name:e.name,url:e.blockExplorerUrls[0]}}:void 0});r=createPublicClient({chain:i,transport:http()});}return this.clientCache.set(t,r),r}async getBalance(e,t){return await this.getProvider(e).getBalance({address:t})}async getBlockNumber(e){return await this.getProvider(e).getBlockNumber()}getWalletClient(e){if(typeof window<"u"&&window.ethereum){let t=P(e.id);if(t)return createWalletClient({chain:t,transport:custom(window.ethereum)})}return null}};var E=class{constructor(){this.connection=null;}async getProvider(e){return this.connection||(this.connection=new Connection(e.rpcUrls[0])),this.connection}async getBalance(e,t){let o=await this.getProvider(e),r=new PublicKey(t),i=await o.getBalance(r);return BigInt(i)}async getBlockNumber(e){let o=await(await this.getProvider(e)).getBlockHeight();return BigInt(o)}};var ae={id:1,name:"Ethereum",type:"evm",networkType:"mainnet",rpcUrls:["https://eth-mainnet.g.alchemy.com/v2/your-api-key","https://mainnet.infura.io/v3/your-api-key"],nativeCurrency:{name:"Ether",symbol:"ETH",decimals:18},blockExplorerUrls:["https://etherscan.io"],iconUrl:"https://cryptologos.cc/logos/ethereum-eth-logo.png"},se={id:137,name:"Polygon",type:"evm",networkType:"mainnet",rpcUrls:["https://polygon-rpc.com","https://polygon-mainnet.g.alchemy.com/v2/your-api-key"],nativeCurrency:{name:"MATIC",symbol:"MATIC",decimals:18},blockExplorerUrls:["https://polygonscan.com"],iconUrl:"https://cryptologos.cc/logos/polygon-matic-logo.png"},ce={id:42161,name:"Arbitrum",type:"evm",networkType:"mainnet",rpcUrls:["https://arb1.arbitrum.io/rpc","https://arb-mainnet.g.alchemy.com/v2/your-api-key"],nativeCurrency:{name:"Ether",symbol:"ETH",decimals:18},blockExplorerUrls:["https://arbiscan.io"],iconUrl:"https://cryptologos.cc/logos/arbitrum-arb-logo.png"},le={id:10,name:"Optimism",type:"evm",networkType:"mainnet",rpcUrls:["https://mainnet.optimism.io","https://opt-mainnet.g.alchemy.com/v2/your-api-key"],nativeCurrency:{name:"Ether",symbol:"ETH",decimals:18},blockExplorerUrls:["https://optimistic.etherscan.io"],iconUrl:"https://cryptologos.cc/logos/optimism-op-logo.png"},de={id:56,name:"BNB Smart Chain",type:"evm",networkType:"mainnet",rpcUrls:["https://bsc-dataseed.binance.org","https://bsc-dataseed1.defibit.io"],nativeCurrency:{name:"BNB",symbol:"BNB",decimals:18},blockExplorerUrls:["https://bscscan.com"],iconUrl:"https://cryptologos.cc/logos/bnb-bnb-logo.png"},k=[ae,se,ce,le,de];var ue={id:"mainnet-beta",name:"Solana",type:"svm",networkType:"mainnet",rpcUrls:["https://api.mainnet-beta.solana.com","https://solana-api.projectserum.com"],nativeCurrency:{name:"Solana",symbol:"SOL",decimals:9},blockExplorerUrls:["https://explorer.solana.com"],iconUrl:"https://cryptologos.cc/logos/solana-sol-logo.png"},he={id:"devnet",name:"Solana Devnet",type:"svm",networkType:"devnet",rpcUrls:["https://api.devnet.solana.com"],nativeCurrency:{name:"Solana",symbol:"SOL",decimals:9},blockExplorerUrls:["https://explorer.solana.com/?cluster=devnet"],iconUrl:"https://cryptologos.cc/logos/solana-sol-logo.png"},Ce={id:"testnet",name:"Solana Testnet",type:"svm",networkType:"testnet",rpcUrls:["https://api.testnet.solana.com"],nativeCurrency:{name:"Solana",symbol:"SOL",decimals:9},blockExplorerUrls:["https://explorer.solana.com/?cluster=testnet"],iconUrl:"https://cryptologos.cc/logos/solana-sol-logo.png"},_=[ue,he,Ce];var me={evm:new f,svm:new E};function pe(n){let e=me[n];if(!e)throw new Error(`Chain adapter not found for chain type: ${n}`);return e}function je(n){return pe(n.type)}function ge(){return [...k,..._]}function h(n){return ge().find(e=>e.id===n)||null}var C=class{constructor(e,t,o,r,i){this._status="disconnected";this._error=null;this._account=null;this._chainId=null;this._eventEmitter=new fe,this.id=e,this.name=t,this.type=o,this.icon=r,this.supportedChains=i;}isConnected(){return this._status==="connected"}getStatus(){return this._status}getError(){return this._error}getAccount(){return this._account}getChainId(){return this._chainId}on(e,t){this._eventEmitter.on(e,t);}off(e,t){this._eventEmitter.off(e,t);}removeAllListeners(e){e?this._eventEmitter.removeAllListeners(e):this._eventEmitter.removeAllListeners();}updateStatus(e){this._status=e;}updateError(e){this._error=e,e&&this._eventEmitter.emit("error",e);}updateAccount(e){let t=this._account;this._account=e,e&&e!==t&&this._eventEmitter.emit("accountChanged",e);}updateChainId(e){let t=this._chainId;this._chainId=e,e&&e!==t&&this._eventEmitter.emit("chainChanged",e);}};var s=false,a=null,v=class extends C{constructor(){super("metamask","MetaMask","injected","https://metamask.io/images/metamask-fox.svg",["evm"]),this.boundAccountsChanged=this.handleAccountsChanged.bind(this),this.boundChainChanged=this.handleChainChanged.bind(this),this.boundDisconnect=this.handleDisconnect.bind(this);}isAvailable(){return typeof window<"u"&&!!window.ethereum}async connect(e){if(!this.isAvailable()){let o={code:"wallet_unavailable",message:"MetaMask is not installed"};throw this.updateError(o),this.updateStatus("error"),o}if(localStorage.getItem("metamask_disconnected")==="true"){let o={code:"user_disconnected",message:"User has explicitly disconnected. Please connect manually."};throw this.updateError(o),this.updateStatus("error"),o}if(s){let o={code:"request_pending",message:"A wallet connection request is already pending. Please check your wallet extension."};throw this.updateError(o),this.updateStatus("error"),o}try{this.updateStatus("connecting"),s=!0,a&&clearTimeout(a),a=setTimeout(()=>{s=!1;},3e4);let o=await window.ethereum.request({method:"eth_requestAccounts"});a&&(clearTimeout(a),a=null),s=!1;let r=o[0],i=await window.ethereum.request({method:"eth_chainId"}),c=parseInt(i,16);return e&&c!==e&&await this.switchChain(e),this.setupEventListeners(),this.updateAccount(r),this.updateChainId(c),this.updateStatus("connected"),this.updateError(null),localStorage.removeItem("metamask_disconnected"),this._eventEmitter.emit("connect",r),r}catch(o){if(a&&(clearTimeout(a),a=null),s=false,o.code===-32002){let i={code:"request_already_pending",message:"A wallet connection request is already pending. Please check your MetaMask extension and approve or reject the request.",details:o};throw this.updateError(i),this.updateStatus("error"),i}let r={code:o.code||"connection_error",message:o.message||"Failed to connect to MetaMask",details:o};throw this.updateError(r),this.updateStatus("error"),r}}async disconnect(){this._status!=="disconnected"&&(localStorage.setItem("metamask_disconnected","true"),this.removeAllListeners(),this.removeEventListeners(),this.updateAccount(null),this.updateChainId(null),this.updateStatus("disconnected"),this.updateError(null),this._eventEmitter.emit("disconnect"));}async switchChain(e){if(!this.isAvailable()||!this.isConnected()){let t={code:"not_connected",message:"Not connected to MetaMask"};throw this.updateError(t),t}if(s){let t={code:"request_pending",message:"A wallet request is already pending. Please check your wallet extension."};throw this.updateError(t),t}try{s=!0,a&&clearTimeout(a),a=setTimeout(()=>{s=!1;},3e4);let t=typeof e=="number"?`0x${e.toString(16)}`:e;await window.ethereum.request({method:"wallet_switchEthereumChain",params:[{chainId:t}]}),a&&(clearTimeout(a),a=null),s=!1,this.updateChainId(e);}catch(t){if(a&&(clearTimeout(a),a=null),s=false,t.code===-32002){let o={code:"request_already_pending",message:"A wallet request is already pending. Please check your MetaMask extension and approve or reject the request.",details:t};throw this.updateError(o),o}if(t.code===4902){let o=h(e);if(o)await this.addChain(o);else throw {code:"chain_not_found",message:`Chain with ID ${e} not found`}}else {let o={code:t.code||"chain_switch_error",message:t.message||"Failed to switch chain",details:t};throw this.updateError(o),o}}}async addChain(e){if(s){let t={code:"request_pending",message:"A wallet request is already pending. Please check your wallet extension."};throw this.updateError(t),t}try{s=!0,a&&clearTimeout(a),a=setTimeout(()=>{s=!1;},3e4),await window.ethereum.request({method:"wallet_addEthereumChain",params:[{chainId:typeof e.id=="number"?`0x${e.id.toString(16)}`:e.id,chainName:e.name,nativeCurrency:e.nativeCurrency,rpcUrls:e.rpcUrls,blockExplorerUrls:e.blockExplorerUrls}]}),a&&(clearTimeout(a),a=null),s=!1;}catch(t){if(a&&(clearTimeout(a),a=null),s=false,t.code===-32002){let r={code:"request_already_pending",message:"A wallet request is already pending. Please check your MetaMask extension and approve or reject the request.",details:t};throw this.updateError(r),r}let o={code:t.code||"add_chain_error",message:t.message||"Failed to add chain",details:t};throw this.updateError(o),o}}setupEventListeners(){window.ethereum&&(window.ethereum.on("accountsChanged",this.boundAccountsChanged),window.ethereum.on("chainChanged",this.boundChainChanged),window.ethereum.on("disconnect",this.boundDisconnect));}removeEventListeners(){window.ethereum&&(window.ethereum.removeListener("accountsChanged",this.boundAccountsChanged),window.ethereum.removeListener("chainChanged",this.boundChainChanged),window.ethereum.removeListener("disconnect",this.boundDisconnect));}handleAccountsChanged(e){e.length===0?this.disconnect():this.updateAccount(e[0]);}handleChainChanged(e){let t=parseInt(e,16);this.updateChainId(t);}handleDisconnect(){this.updateStatus("disconnected"),this.updateAccount(null),this.updateChainId(null),this.updateError(null),this.removeEventListeners();}};var b=class extends C{constructor(){super("phantom","Phantom","injected","https://phantom.app/img/logo.png",["svm"]);}isAvailable(){return typeof window<"u"&&!!window.phantom?.solana?.isPhantom}async connect(e){if(!this.isAvailable()){let t={code:"wallet_unavailable",message:"Phantom is not installed"};throw this.updateError(t),this.updateStatus("error"),t}try{this.updateStatus("connecting");let t=window.phantom?.solana;if(!t)throw new Error("Phantom solana provider not found");let r=(await t.connect()).publicKey.toString();this.setupEventListeners(),this.updateAccount(r);let i=e||"mainnet-beta";return this.updateChainId(i),this.updateStatus("connected"),this.updateError(null),this._eventEmitter.emit("connect",r),r}catch(t){let o={code:t.code||"connection_error",message:t.message||"Failed to connect to Phantom",details:t};throw this.updateError(o),this.updateStatus("error"),o}}async disconnect(){if(this.isAvailable())try{let e=window.phantom?.solana;e&&await e.disconnect();}catch(e){console.error("Error disconnecting from Phantom:",e);}this.removeEventListeners(),this.updateAccount(null),this.updateChainId(null),this.updateStatus("disconnected"),this.updateError(null),this._eventEmitter.emit("disconnect");}async switchChain(e){if(!this.isAvailable()||!this.isConnected()){let t={code:"not_connected",message:"Not connected to Phantom"};throw this.updateError(t),t}try{this.updateChainId(e),console.warn("Please switch to the desired network manually in your Phantom wallet.");}catch(t){let o={code:t.code||"chain_switch_error",message:t.message||"Failed to switch chain",details:t};throw this.updateError(o),o}}setupEventListeners(){let e=window.phantom?.solana;e&&(e.on("accountChanged",this.handleAccountChanged.bind(this)),e.on("disconnect",this.handleDisconnect.bind(this)));}removeEventListeners(){let e=window.phantom?.solana;e&&(e.off("accountChanged",this.handleAccountChanged.bind(this)),e.off("disconnect",this.handleDisconnect.bind(this)));}handleAccountChanged(e){e?this.updateAccount(e.toString()):this.disconnect();}handleDisconnect(){this.disconnect();}};var D=[new v,new b];function g(){return typeof window>"u"?[]:D.filter(n=>"isAvailable"in n&&typeof n.isAvailable=="function"?n.isAvailable():true)}function R(n){return D.find(e=>e.id===n)}var y=false,be=typeof window<"u",ye=()=>be?{getItem:n=>localStorage.getItem(n)??null,setItem:(n,e)=>{localStorage.setItem(n,e);},removeItem:n=>{localStorage.removeItem(n);}}:{getItem:n=>null,setItem:(n,e)=>{},removeItem:n=>{}},w=create()(persist((n,e)=>({status:"disconnected",account:null,chainId:null,connector:null,error:null,chain:null,connect:async(t,o)=>{try{e().connector&&await e().disconnect(),n({status:"connecting",error:null});let i=R(t);if(!i)throw new Error(`Connector with ID ${t} not found`);let c=await i.connect(o),d=i.getChainId(),m=d?h(d):null;i.on("accountChanged",l=>{n({account:l});}),i.on("chainChanged",l=>{let p=h(l);n({chainId:l,chain:p||null});}),i.on("disconnect",()=>{y||e().disconnect();}),i.on("error",l=>{n({error:l,status:l?"error":e().status});}),n({connector:i,account:c,chainId:d,chain:m,status:"connected"});}catch(r){throw n({status:"error",error:r}),r}},disconnect:async()=>{if(!y){y=true;try{let{connector:t}=e();if(t)try{t.removeAllListeners(),await t.disconnect();}catch(o){console.error("Error disconnecting:",o);}localStorage.removeItem("web3-storage"),n({status:"disconnected",account:null,chainId:null,connector:null,chain:null,error:null});}finally{y=false;}}},switchChain:async t=>{let{connector:o}=e();if(!o)throw new Error("Not connected to any wallet");try{await o.switchChain(t);let r=h(t);n({chainId:t,chain:r||null});}catch(r){throw n({error:r}),r}},setStatus:t=>n({status:t}),setAccount:t=>n({account:t}),setChainId:t=>n({chainId:t,chain:t&&h(t)||null}),setConnector:t=>n({connector:t}),setError:t=>n({error:t})}),{name:"web3-storage",storage:ye(),partialize:n=>({account:n.account,chainId:n.chainId,connector:n.connector?n.connector.id:null})}));var M=typeof window<"u",Ne={status:"disconnected",account:null,chainId:null,chain:null,error:null,isConnected:false,isConnecting:false,isDisconnected:true,isError:false,connect:async()=>{console.warn("Web3 not initialized");},disconnect:async()=>{console.warn("Web3 not initialized");},switchChain:async()=>{console.warn("Web3 not initialized");},getAvailableConnectors:()=>[]};function I(n){let e=w(),[t,o]=useState(Ne),r=useRef(false);return useEffect(()=>{M&&o({status:e.status,account:e.account,chainId:e.chainId,chain:e.chain,error:e.error,isConnected:e.status==="connected",isConnecting:e.status==="connecting",isDisconnected:e.status==="disconnected",isError:e.status==="error",connect:e.connect,disconnect:e.disconnect,switchChain:e.switchChain,getAvailableConnectors:g});},[e.status,e.account,e.chainId,e.chain,e.error,e.connect,e.disconnect,e.switchChain]),useEffect(()=>{if(!M||!n?.autoConnect||r.current)return;(async()=>{try{r.current=!0;let c=localStorage.getItem("web3-storage");if(!c)return;let d=JSON.parse(c);if(!d?.state?.connector)return;let m=g(),l=d.state.connector,p=m.find(u=>u.id===l);p&&await e.connect(p.id,n.defaultChain);}catch(c){console.error("Auto-connect failed:",c);}})();},[n?.autoConnect,n?.defaultChain,e.connect]),t}var U=createContext(null),Ae={autoConnect:false};function xe({children:n,config:e}){let t=I(e);return jsx(U.Provider,{value:t,children:n})}function Pe({children:n,config:e}){let t={...Ae,...e},[o,r]=useState(false);return useEffect(()=>{r(true);},[]),o?jsx(xe,{config:t,children:n}):null}function B(){let n=useContext(U);if(!n)throw new Error("useWeb3Context must be used within a Web3Provider");return n}function ke({onConnect:n,onError:e,chainId:t,className:o,children:r}){let{connect:i,account:c,status:d}=B(),[m,l]=useState(false);return useEffect(()=>{d!=="connecting"&&l(false);},[d]),useEffect(()=>{c&&n&&n(c);},[c,n]),jsx("button",{onClick:async()=>{try{l(!0);let u=g();if(u.length>0){let T=u.find(q=>q.id==="metamask");T?await i(T.id,t):u.length>0&&await i(u[0].id,t);}else throw new Error("No wallet connectors available")}catch(u){l(false),e&&u instanceof Error&&e(u),console.error("Failed to connect wallet:",u);}},disabled:m||d==="connected",className:o,style:{padding:"0.5rem 1rem",backgroundColor:"#4CAF50",color:"white",border:"none",borderRadius:"4px",cursor:"pointer",...!o&&{}},children:m?"Connecting...":d==="connected"?"Connected":r||"Connect Wallet"})}
export{C as BaseConnector,V as ChainType,ke as ConnectWalletButton,G as ConnectorEvent,H as ConnectorStatus,f as EVMChainAdapter,v as MetaMaskConnector,L as NetworkType,b as PhantomConnector,E as SVMChainAdapter,Pe as Web3Provider,ge as getAllChains,g as getAvailableConnectors,pe as getChainAdapter,je as getChainAdapterForChain,h as getChainById,R as getConnectorById,k as popularEVMChains,_ as popularSVMChains,I as useWeb3,B as useWeb3Context,w as useWeb3Store};//# sourceMappingURL=index.mjs.map
//# sourceMappingURL=index.mjs.map