Tronwallet Adapter

概述

Tronwallet Adapter 是包含了一系列用于 Tron DApp 的钱包适配器和组件。通过开箱即用的组件和统一的方法,开发人员可以轻松地与多种钱包进行交互,包括选择/连接/断开 钱包以及签名消息或交易。

适配器

钱包适配器可以让你通过统一的 API 访问 TRON 钱包。

目前支持 TRON 网络的钱包有很多,比如TronLink、Ledger等等。 不同的钱包,甚至同一个钱包的不同版本可能有不同的接口。 Adapters 相关包的目的就是屏蔽这些差异并为 DApp 开发人员提供统一的接口。 如果 DApps 使用了 Tronwallet Adapter 代码,他们就不需要频繁的更改其代码。

例如,如果你想连接到不同的钱包,你必须使用不同的方法:

// TronLink
window.tronLink.request({ method: 'tron_requestAccounts' });

// Ledger
const transport = await TransportWebHID.create();
const app = new Trx(transport);

// WalletConnect
const wallet = new WalletConnectWallet({
   network: this._config.network,
   options: this._config.options,
});

使用适配器,您可以为不同的钱包使用统一的 API:

// TronLink
const tronlinkAdapter = new TronLinkAdapter();
await tronlinkAdapter.connect();
await tronlinkAdapter.signMessage(message);

// Ledger
const ledgerAdapter = new LedgerAdapter();
await ledgerAdapter.connect();
await ledgerAdapter.signMessage(message);

// WalletConnect
const walletconnectAdapter = new WalletConnectAdapter();
await walletconnectAdapter.connect();
await walletconnectAdapter.signMessage(message);

React Hooks

Adapter wallet hooks 会导出一个 useWallet() hook 用于管理钱包的全局状态,包括当前选择的钱包、连接状态、地址等。 同时它也提供了一些与钱包交互的方法。

当你的 dapp 支持多个钱包时,借助 useWallet() hook 你可以轻松地:

  • 选择要使用的钱包
  • 连接到选定的钱包
  • 断开与所选钱包的连接
  • 调用所选钱包的signMessage或者signTransaction

例子:

function Comp() {
   const { wallet, address, connected, select, connect, disconnect, signMessage, signTransaction } = useWallet();
   return (
       <div>
           <button onClick={() => select('TronLink')}>Select Wallet</button>
           <button onClick={connect}>Connect</button>
           <button onClick={disconnect}>Disconnect</button>
           <button onClick={() => signMessage('Hello World')}>Sign Message</button>
       </div>
   );
}

React UI 组件

useWallet() 只管理钱包的状态。 除此之外,我们也提供了一组开箱即用的组件来帮助您与钱包进行交互:

  • WalletSelectButton:选择钱包的钱包对话框
  • WalletConnectButton:连接到选定的钱包。
  • WalletDisconnectButton:断开与所选钱包的连接。
  • WalletActionButton:具有多种功能的按钮,包括选择/连接/断开连接

React 的演示参考这里

演示图片如下:

demo.png

Wallet Adapters

@tronweb3/tronwallet-adapters 提供了多个钱包适配器,以帮助开发人员使用统一的 API 连接到 Tron 钱包,例如 TronLink

支持的钱包

@tronweb3/tronwallet-adapters 会导出每个钱包的适配器,你可以选择使用这个包或者使用你想用的个人钱包适配器。

安装

npm install @tronweb3/tronwallet-abstract-adapter @tronweb3/tronwallet-adapters
# or pnpm install @tronweb3/tronwallet-abstract-adapter @tronweb3/tronwallet-adapters
# or yarn install @tronweb3/tronwallet-abstract-adapter @tronweb3/tronwallet-adapters

用法

React

你可以在组件中使用 @tronweb3/tronwallet-adapters,也可以使用 useMemo 缓存 adapter对象来提高性能。

import { TronLinkAdapter } from '@tronweb3/tronwallet-adapters';

function App() {
   const [connectState, setConnectState] = useState(AdapterState.NotFound);
   const [account, setAccount] = useState('');
   const [netwok, setNetwork] = useState({});
   const [signedMessage, setSignedMessage] = useState('');

   const adapter = useMemo(() => new TronLinkAdapter(), []);
   useEffect(() => {
       setConnectState(adapter.state);
       setAccount(adapter.address!);

       adapter.on('connect', () => {
           setAccount(adapter.address!);
       });

       adapter.on('stateChanged', (state) => {
           setConnectState(state);
       });

       adapter.on('accountsChanged', (data) => {
           setAccount(data);
       });

       adapter.on('chainChanged', (data) => {
           setNetwork(data);
       });

       adapter.on('disconnect', () => {
           // when disconnect from wallet
       });
       return () => {
           // remove all listeners when components is destroyed
           adapter.removeAllListeners();
       };
   }, []);

   async function sign() {
       const res = await adapter!.signMessage('helloworld');
       setSignedMessage(res);
   }

   return (
       <div className="App">
           <div>connectState: {connectState}</div>
           <div>current address: {account}</div>
           <div>current network: {JSON.stringify(netwok)}</div>
           <button disabled={adapter.connected} onClick={() => adapter.connect()}>
               Connect to TronLink
           </button>
           <button onClick={sign}>sign message</button>
           <br />
           SignedMessage: {signedMessage}
       </div>
   );
}

Vue

在 Vue 中,由于 created/mounted hook 只执行一次,你可以在 mountedcreated hook 中初始化适配器。

// vue2.x
export default {
   created() {
       this.adapter = new TronLinkAdapter();
       this.adapter.on('connect', () => {
           // here you can do something
       });
   },
   beforeDestroy() {
       this.adapter.removeAllListeners();
   }
}

// vue3
export default {
   setup() {
       onMounted(function() {
           const adapter = new TronLinkAdapter();
           adapter.on('connect', () => {
               // here you can do something
           });
       });
       onBeforeUnmount(function() {
           // remove all listeners when components is destroyed
           adapter.removeAllListeners();
       });
       return {};
   }
}

API参考

适配器接口

Adapter 类为指定钱包的所有适配器定义了公共接口。

构造函数

  • constructor(config): 适配器构造函数。 详细的config,参考适配器部分

属性

  • name:适配器的名称。
  • url :适配器钱包的网址。
  • icon: 适配器钱包的图标。
  • readyState:适配器对应钱包的三种状态:
    • Loading:正在循环检测中。
    • NotFound:检测完毕后发现钱包未安装。
    • Found:检测完毕后发现钱包已安装。
  • address: 适配器连接的当前账户地址。
  • connecting:适配器是否正在尝试连接到钱包。
  • connected:适配器是否连接到了钱包。

方法

  • connect(): Promise<void>:连接到钱包。
  • disconnect(): Promise<void>:断开与钱包的连接。
  • signMessage(message): Promise<string>:对消息进行签名,返回签名结果。
  • signTransaction(transaction):对交易进行签名,返回签名结果。
  • multiSign(transaction, privateKey: string | null, permissionId?): 对多签交易进行签名.
    • 如果 privateKey 不是 null, 则使用该 privateKey来签名而不是使用 TronLink 签名。.
    • 如果 permissionId没有提供, 默认使用 0(OwnerPerssion).
    • 关于多签的更多信息请查阅 文档.
  • switchChain(chainId: string): Promise<void>;: 请求 TronLink 切换当前使用的网络,需要传入 chainId.

事件

Adapter 继承了 eventemitter3 包中的 EventEmitter 类,可以通过 adapter.on('connect', function() {}) 来监听事件。

事件如下:

  • connect(address): 当适配器连接到钱包时触发,参数为当前账户的地址。
  • disconnect():当适配器与钱包断开连接时触发。
  • stateChanged(state: AdapteraState): 当适配器的状态改变时触发。参数是适配器的状态:
  enum AdapterState {
      NotFound = 'NotFound',
      Disconnect = 'Disconnected',
      Connected = 'Connected',
  }
  • accountsChanged(address: string):当用户更改钱包中当前选择的帐户时触发, 参数为新账户地址。
  • chainChanged(chainInfo: ChainInfo):当用户更改钱包中当前选定的链时触发, 参数为新的网络配置:
   interface ChainInfo {
       node: {
           chain: string;
           fullNode: string;
           solidityNode: string;
           eventServer: string;
       };
   }
  • error(ConnectionError):当调用适配器的方法时出现错误时触发。 WalletError 类型定义如下。

WalletError

WalletError 是一个超类,它定义了适配器出错时的错误类型,所有的错误类型都是从这个类继承而来。
开发者可以根据错误实例查看错误类型。

try {
   // do something here
} catch (error: WalletError) {
   if (error instanceof WalletNotFoundError) {
       console.log('Wallet is not found');
   }
}

所有错误如下:

  • WalletNotFoundError:未安装钱包时发生。
  • WalletNotSelectedError:连接但没有选择钱包时发生。
  • WalletDisconnectedError:钱包断开连接时发生。 当调用 signMessage() 或 signTransaction() 时不会自动连接钱包。
  • WalletConnectionError:尝试连接钱包时发生。
  • WalletDisconnectionError:尝试断开钱包连接时发生。
  • WalletSignMessageError:在调用 signMessage() 时发生。
  • WalletSignTransactionError:调用signTransaction() 时发生。
  • WalletWalletLoadError:WalletConnectAdapter 加载钱包时发生。
  • WalletWindowClosedError:WalletConnectAdapter 的 walletconnect QR 窗口关闭时发生。
  • WalletSwitchChainError: 调用 switchChain() 时发生. 目前只支持 TronLink。
  • WalletGetNetworkError: 调用 network() 获取网络信息时发生。

TronLinkAdapter

Constructor(config: TronLinkAdapterConfig)

interface TronLinkAdapterConfig {
    /**
    * The icon of your dapp. Used when open TronLink app in mobile device browsers.
    */
    dappIcon?: string;
    /**
    * The name of your dapp. Used when open TronLink app in mobile device browsers.
    */
    dappName?: string;
}
  • network() 方法可用于获取当前所使用的网络节点信息。 返回的 Network 类型如下所示:

    export enum NetworkType {
        Mainnet = 'Mainnet',
        Shasta = 'Shasta',
        Nile = 'Nile',
        /**
         * When use custom node
         */
        Unknown = 'Unknown',
    }
    
    export type Network = {
        networkType: NetworkType;
        chainId: string;
        fullNode: string;
        solidityNode: string;
        eventServer: string;
    };
    
  • DApp 不支持的disconnect。 由于 TronLinkAdapter 不支持通过 DApp 网站断开连接,因此调用 adapter.disconnect() 不会真正断开与 TronLink 的连接。

  • 在移动浏览器中自动打开 TronLink 应用程序。 如果开发者在手机浏览器中调用connect()方法,会在TronLink app 中打开 DApp 获取 TronLink 钱包。

其它钱包适配器
其它钱包适配器的构造函数参数配置可以参考它们的 README 文档.

Adapter React Hooks

@tronweb3/tronwallet-adapter-react-hooks 提供了 useWallet() hook, 可以让开发者方便地连接钱包并监听钱包连接状态的变化。

安装

npm install @tronweb3/tronwallet-adapter-react-hooks @tronweb3/tronwallet-abstract-adapter @tronweb3/tronwallet-adapters
# or pnpm install @tronweb3/tronwallet-adapter-react-hooks @tronweb3/tronwallet-abstract-adapter @tronweb3/tronwallet-adapters
# or yarn install @tronweb3/tronwallet-adapter-react-hooks @tronweb3/tronwallet-abstract-adapter @tronweb3/tronwallet-adapters

用法

@tronweb3/tronwallet-adapter-react-hooks 使用 React 的 Context 来维护共享数据。 因此,开发人员需要将 App 内容包装在 WalletProvider 中。

你可以使用 onError 回调函数来处理各种错误,例如 WalletNotFoundErrorWalletConnectionError

import { useWallet, WalletProvider } from '@tronweb3/tronwallet-adapter-react-hooks';
import { WalletDisconnectedError, WalletError, WalletNotFoundError } from '@tronweb3/tronwallet-abstract-adapter';
import toast, { Toaster } from 'react-hot-toast';

function App() {
   // use `react-hot-toast` npm package to notify user what happened here
   function onError(e: WalletError) {
       if (e instanceof WalletNotFoundError) {
           toast.error(e.message);
       } else if (e instanceof WalletDisconnectedError) {
           toast.error(e.message);
       } else toast.error(e.message);
   }
   return (
       <WalletProvider onError={onError}>
           <ConnectComponent></ConnectComponent>
           <Profile></Profile>
       </WalletProvider>
   );
}
function ConnectComponent() {
   const { connect, disconnect, select, connected } = useWallet();
   return (<div>
     <button type="button" onClick={() => select('TronLink' as any)}> Select TronLink</button>
     <button type="button" disabled={connected} onClick={connect}>Connect</button><br>
     <button type="button" disabled={!connected} onClick={disconnect}>Disconnect</button>
   </div>);
}
function Profile() {
   const { address, connected, wallet } = useWallet();
   return (<div>
       <p> <span>Connection Status:</span> {connected ? 'Connected' : 'Disconnected'}</p>
       <p> <span>Your selected Wallet:</span> {wallet?.adapter.name} </p>
       <p> <span>Your Address:</span> {address} </p>
   </div>);
}

API 参考

WalletProvideruseWalletContext.ProvideruseContext() 一样协同工作。 底层 WalletProviderContext 通过 useWallet 获取,它的主要功能是维护一些状态。 所以开发者需要用 WalletProvider 包装应用组件。

function App() {
   return (
       <div>
           <WalletProvider>/* here is application components */</WalletProvider>
       </div>
   );
}

WalletProvider Props

adapters

  • 是否必需:false
  • 类型:Adapter[]
  • 默认值:[ new TronLinkAdapter() ]

用于指定支持哪些钱包适配器。 所有钱包适配器都可以从@tronweb3/tronwallet-adapters包或它们的独立包中导入。

  • 例子
import { TronLinkAdapter } from '@tronweb3/tronwallet-adapters';
function App() {
    const adapters = useMemo(() => [new TronLinkAdapter()]);
    return (
        <div>
            <WalletProvider adapters={adapters}>/* here is application components */</WalletProvider>
        </div>
    );
}

onError

  • 是否必需:false
  • 类型:(error:WalletError):void
  • 默认值:function(error) { console.error(error); }

用于处理使用钱包时出现的错误。 开发人员可以使用回调函数,根据error类型告诉用户发生了什么。所有错误类型都可以在 这里 查找。

  • 例子
   functon onError(e) {
   if (e instanceof WalletNotFoundError) {
           console.error(e.message);
       } else if (e instanceof WalletDisconnectedError) {
           console.error(e.message);
       } else console.error(e.message);
   }

autoConnect

  • 是否必需:false
  • 类型:boolean
  • 默认值:true

加载页面并选择钱包时是否自动连接到指定钱包。

localStorageKey

  • 是否必需:false
  • 类型:string
  • 默认值:tronAdapterName

指定用于在 localStorage 中缓存钱包名称的键名。 当用户选择钱包时,应用程序会将所选钱包存储到 localStorage 中。

useWallet()

useWallet 是一个 React Hook,它提供了一组属性和方法用于选择和连接钱包、获取钱包状态等。

useWallet() 必须在 WalletProvider 的子组件中使用!

useWallet 返回参数

autoConnect

  • 类型:boolean
    与传递给 WalletProviderautoConnect 属性同步。

wallet

  • 类型: Wallet | null
    当前选择的钱包。如果没有选择钱包,则该值为null

Wallet 定义如下:

interface Wallet {
   adapter: Adapter; // wallet adapter
   state: AdapterState;
}
enum AdapterState {
   NotFound = 'NotFound',
   Disconnect = 'Disconnected',
   Connected = 'Connected',
}

address

  • 类型:string | null
    当前选择的钱包地址。 如果没有选择钱包,则值为null

wallets

  • 类型:Wallet[]
    初始化 WalletProvider 时传入的 adapters 相对应的 wallet 数组

connecting

  • 类型:boolean
    指示是否正在连接到钱包。

connected

  • 类型:boolean
    表明是否与钱包连接。

disconnecting

  • 类型:boolean
    指示是否正在断开与钱包的连接。

select

  • 输入:(walletAdapterName: AdapterName) => void
    通过 walletAdapterName 选择一个钱包。 可以在 这里 找到有效的适配器

connect

  • 类型:() => Promise<void>
    连接到当前选择的钱包。

disconnect

  • 类型:() => Promise<void>
    断开与当前所选钱包的连接。

signTransaction

  • 类型:(transaction: Transaction) => Promise<SignedTransaction>
    签名未签名的交易。 此方法与 TronWeb API 相同。

signMessage

  • 输入:(message: string) => Promise<string>
    签名消息。

例子

import { useWallet } from '@tronweb3/tronwallet-adapter-react-hooks';

function Content() {
   const { connect, disconnect, select, connected } = useWallet();
   return (
       <div>
           <button type="button" onClick={() => select('TronLink Adapter')}>
               Select TronLink
           </button>
           <button type="button" disabled={connected} onClick={connect}>
               Connect
           </button>
           <button type="button" disabled={!connected} onClick={disconnect}>
               Disconnect
           </button>
       </div>
   );
}

Adapter React UI 组件

@tronweb3/tronwallet-adapter-react-ui 提供了一组开箱即用的组件,可以轻松选择、更改、连接和断开钱包。

这个包依赖于@tronweb3/tronwallet-adapter-react-hooks来工作。 因此,开发人员必须将 App 内容包装在 WalletProvider 中。

安装

npm install @tronweb3/tronwallet-adapter-react-ui @tronweb3/tronwallet-adapter-react-hooks @tronweb3/tronwallet-abstract-adapter @tronweb3/tronwallet-adapters

# or pnpm install @tronweb3/tronwallet-adapter-react-ui @tronweb3/tronwallet-adapter-react-hooks @tronweb3/tronwallet-abstract-adapter @tronweb3/tronwallet-adapters

# or yarn install @tronweb3/tronwallet-adapter-react-ui @tronweb3/tronwallet-adapter-react-hooks @tronweb3/tronwallet-abstract-adapter @tronweb3/tronwallet-adapters

用法

@tronweb3/tronwallet-adapter-react-ui 通过 Context.Provider 提供Select Wallet Modal。 开发人员必须将 App 内容包装在 WalletProviderWalletModalProvider 中。

import { useWallet, WalletProvider } from '@tronweb3/tronwallet-adapter-react-hooks';
import { WalletModalProvider, WalletActionButton } from '@tronweb3/tronwallet-adapter-react-ui';
import '@tronweb3/tronwallet-adapter-react-ui/style.css';
import { WalletDisconnectedError, WalletError, WalletNotFoundError } from '@tronweb3/tronwallet-abstract-adapter';
import toast, { Toaster } from 'react-hot-toast';

function App() {
   // here use `react-hot-toast` npm package to notify user what happened
   function onError(e: WalletError) {
       if (e instanceof WalletNotFoundError) {
           toast.error(e.message);
       } else if (e instanceof WalletDisconnectedError) {
           toast.error(e.message);
       } else toast.error(e.message);
   }
   return (
       <WalletProvider onError={onError}>
           <WalletModalProvider>
               <ConnectComponent></ConnectComponent>
               <Profile></Profile>
           </WalletModalProvider>
       </WalletProvider>
   );
}
function ConnectComponent() {
   const { connect, disconnect, select, connected } = useWallet();
   return <WalletActionButton></WalletActionButton>;
}
function Profile() {
   const { address, connected, wallet } = useWallet();
   return (
       <div>
           <p>
               <span>Connection Status:</span> {connected ? 'Connected' : 'Disconnected'}
           </p>
           <p>
               <span>Your selected Wallet:</span> {wallet?.adapter.name}
           </p>
           <p>
               <span>Your Address:</span> {address}
           </p>
       </div>
   );
}

API参考

WalletModalProvider 和 useWalletModal

WalletModalProvider 通过 Context.Provider 提供 Select Wallet Modal。 模式可以通过useWalletModal 控制。

function App() {
   const { visible, setVisible } = useWalletModal();
   function toggle() {
       setVisible((visible) => !visible);
   }
   return (
       <div>
           <button onClick={toggle}>{visible ? 'Close Modal' : 'Open Modal'}</button>
       </div>
   );
}

WalletConnectButton

连接到所选钱包的按钮。 该按钮在以下情况下被禁用:

  • 没有选择钱包
  • 正在连接到钱包
  • 已连接
  • 被 props 禁用

Props

type ButtonProps = PropsWithChildren<{
   className?: string,
   disabled?: boolean,
   onClick?: (e: MouseEvent<HTMLButtonElement>) => void,
   style?: CSSProperties,
   tabIndex?: number,
   icon?: string,
}>;

WalletDisconnectButton

连接到所选钱包的按钮。 该按钮在以下情况下被禁用:

  • 没有选择钱包
  • 正在连接到钱包
  • 被 props 禁用

Props

WalletConnectButton 相同。

WalletSelectButton

打开Select Wallet Modal的按钮。

Props

WalletConnectButton 相同。

WalletActionButton

具有多种功能的按钮,包括:

  • 选择钱包
  • 连接到钱包
  • 断开与钱包的连接
  • 显示当前选择的钱包和地址
  • 复制地址

推荐使用该组件连接钱包。
演示如下:
示例

Props

WalletConnectButton 相同。