跳转至

主动请求TronLink插件功能

连接网站 TIP-1102

简介

TronLink 可用于管理钱包私钥,DApp 在进行一些需签名的操作前,需要连接 TronLink,并通过 TronLink 获取用户签名授权。此协议在于显式地告知用户 DApp 主动连接 TronLink 的行为,并获取用户的授权同意。 此方法遵循以太坊 EIP-1102 协议。

技术规范

代码示例

try {
  await window.tron.request({method: 'eth_requestAccounts'});
} catch (e) {}

返回值

如果成功,返回一个数组,数组只有一个元素,为同意连接的当前 TronLink 账户。如: ['TMVQGm1qAQYVdetCeGRRkTWYYrLXuHK2HC']。

如果失败,则会返回错误码及报错信息。详见“错误码”部分。

错误码

错误码 名称 描述
4001 用户拒绝请求 用户通过点击“拒绝”按钮,或关闭弹窗,都会触发该错误码
-32002 处于其他流程中 当前 DApp 存在其他流程,无法执行当前请求
-32602 参数不合规 传入的参数不合规,或者传入了额外的参数
4200 不支持此方法 不支持此方法

交互流程

触发 eth_requestAccounts 之后,如果 TronLink 处于锁屏状态,会弹出锁屏弹窗:

TronLink 解锁弹窗,输入钱包密码

解锁后,或者 TronLink 已经提前解锁,会打开连接确认的弹窗:

TronLink 连接授权弹窗,显示请求方域名以及"确认"/"拒绝"按钮

旧版用法(不推荐): 兼容用法:tron_requestAccounts

获取TronLink的provider TIP-6963

简介

当多个钱包同时存在,会出现对 window.tron 对象的抢占行为。为了保证 DApp 可以获取到特定钱包的 provider,所以实现 TIP-6963 规范。

技术规范

代码示例

interface TIP1193Provider {
  request: (args: RequestArguments) => Promise<unknown>;
  on(event: string, listener: (...args: any[]) => void): this;
  removeListener(event: string, listener: (...args: any[]) => void): this;
  tronWeb: TronWeb;
  [key: `is${string}`]: boolean;
}

/**
 * Represents the assets needed to display a wallet
 */
interface TIP6963ProviderInfo {
  uuid: string;
  name: string;
  icon: string;
  rdns: string;
}

interface TIP6963ProviderDetail {
  info: TIP6963ProviderInfo;
  provider: TIP1193Provider;
}

// Announce Event dispatched by a Wallet
interface TIP6963AnnounceProviderEvent extends CustomEvent {
  type: "TIP6963:announceProvider";
  detail: TIP6963ProviderDetail;
}

// The DApp listens to announced providers
window.addEventListener(
  "TIP6963:announceProvider",
  (event: TIP6963AnnounceProviderEvent) => {

    // Confirm if it is a Tronlink UUID
    if (event.detail.info.rdns !== 'org.tronlink.www' || event.detail.info.name !== 'TronLink') {
      console.error('it is NOT TronLink provider');
      return;
    }

    // event.detail.provider === window.tron
    const tronProvider = event.detail.provider;

    tronProvider.on('accountsChanged', (accountArray) => {
      console.log('tip-6963 accountsChanged', accountArray);
    })
  }
);

// The DApp dispatches a request event which will be heard by 
// Wallets' code that had run earlier
window.dispatchEvent(new Event("TIP6963:requestProvider"));
DApp 按照上述代码实现后,可以精准获取到 TronLink 提供的 provider。 TronLink 的 rdns 是 org.tronlink.www,name 是 TronLink。

普通转账 sendTrx

前提条件: 已通过 eth_requestAccounts 完成 DApp 连接授权(参见上方 连接网站 TIP-1102)。

简介

DApp 需要用户发起一笔 TRX 转账。

波场网络上发起转账需要 3 个步骤:

  1. 构造转账交易
  2. 对交易进行签名
  3. 对签名后的交易进行广播

在这里,TronLink 介入的是第 2 步签名的部分,1、3 两步需要开发者使用 tronWeb 完成。

技术规范

代码示例

const tronweb = window.tron.tronWeb;
const fromAddress = tronweb.defaultAddress.base58;
const toAddress = "TDvSsdrNM5eeXNL3czpa6AxLDHZA9nwe9K";
const tx = await tronweb.transactionBuilder.sendTrx(toAddress, 10, fromAddress); // 步骤1
try {
  const signedTx = await tronweb.trx.sign(tx); // 步骤2
  await tronweb.trx.sendRawTransaction(signedTx); // 步骤3
} catch (e) {}

当代码执行到 await tronweb.trx.sign(tx); 时,TronLink 钱包会提示弹窗,需要用户进行确认,如下图:

TronLink 交易审批弹窗,显示收款地址、TRX 金额以及"拒绝"/"签名"按钮

如果用户在弹窗中选择【拒绝】,则会抛出异常,开发者可捕获此异常进行业务处理。

如果用户在弹窗中选择【签名】,DApp 可以拿到签名后的交易,继续进行广播。

旧版用法(不推荐): 兼容用法:sendTrx(window.tronLink)

多签转账 multiSign

前提条件: 已通过 eth_requestAccounts 完成 DApp 连接授权(参见上方 连接网站 TIP-1102)。

简介

此处可参考 普通转账 sendTrx。

技术规范

代码示例

const tronweb = window.tron.tronWeb;
const toAddress = "TDvSsdrNM5eeXNL3czpa6AxLDHZA9nwe9K";
const activePermissionId = 2;
const tx = await tronweb.transactionBuilder.sendTrx(
    toAddress, 10, 
    { permissionId: activePermissionId}
); // 步骤1
try {
  const signedTx = await tronweb.trx.multiSign(tx, undefined, activePermissionId); // 步骤2
  await tronweb.trx.sendRawTransaction(signedTx); // 步骤3
} catch (e) {}

如果用户在弹窗中选择【拒绝】,则会抛出异常,开发者可捕获此异常进行业务处理。

如果用户在弹窗中选择【签名】,DApp 可以拿到签名后的交易,继续进行广播。

旧版用法(不推荐): 兼容用法:multiSign(window.tronLink)

消息签名 signMessageV2

前提条件: 已通过 eth_requestAccounts 完成 DApp 连接授权(参见上方 连接网站 TIP-1102)。

简介

DApp 可以请求用户对消息签名,再将消息与签名转发给后端验签,以此验证用户登录。

技术规范

代码示例

const tronweb = window.tron.tronWeb;
const message = "Sign in to Example DApp\nNonce: 8f3a2c"; // 普通 UTF-8 文本
try {
  const signedString = await tronweb.trx.signMessageV2(message);
  // 用同一个字符串验签,返回签名者的 base58 地址
  const recovered = await tronweb.trx.verifyMessageV2(message, signedString);
  const ok = recovered === tronweb.defaultAddress.base58;
} catch (e) {}

参数

消息须为非空字符串。TronLink 对该字符串的 UTF-8 字节签名,并加上 TIP-191 前缀 "\x19TRON Signed Message:\n" + <字节长度>。外观类似 hex 的字符串不会被解码:"0x1234" 按六个文本字符(字节为 30 78 31 32 33 34)签名,而不是两个原始字节 12 34。

通过 TronLink 注入的 tronWeb 只支持字符串(TronLink 插件 4.0.0 起)。TronWeb 本身还接受 Uint8Array / number[] 来签原始字节,但 TronLink 会拒绝这些类型,因此 DApp 无法通过 signMessageV2 让 TronLink 对任意原始字节签名。

验签时把同一个字符串传给 verifyMessageV2(见上方示例)。它返回恢复出的 base58 地址,地址不匹配时不会抛错——需要自行比对。

参见 TronWeb 消息签名与验签说明。

返回值

如果用户在弹窗中选择签名,DApp 可以得到签名后的十六进制字符串,比如:

0xaa302ca153b10dff25b5f00a7e2f603c5916b8f6d78cdaf2122e24cab56ad39a79f60ff3916dde9761baaadea439b567475dde183ee3f8530b4cc76082b29c341c
如果报错,则会返回如下信息:
Uncaught (in promise) Invalid transaction provided

交互流程

当代码执行到 await tronweb.trx.signMessageV2(message); 时,TronLink 钱包会提示弹窗,需要用户进行确认,如下图。弹窗以文本形式展示消息内容:

TronLink 消息签名审批弹窗,展示待签消息内容以及"拒绝"/"签名"按钮

如果用户在弹窗中选择【拒绝】,则会抛出异常,开发者可捕获此异常进行业务处理。

旧版用法(不推荐): 兼容用法:signMessageV2(window.tronLink)

添加资产 wallet_watchAsset

前提条件: 已通过 eth_requestAccounts 完成 DApp 连接授权(参见上方 连接网站 TIP-1102)。

简介

DApp 提供按钮给用户,直接将指定的 Token 添加到用户插件的资产展示列表中。

技术规范

代码示例

const res = await window.tron.request({
  method: 'wallet_watchAsset',
  params: {
    type: 'trc20',
    options: {
        address: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t'
    }
  } as WatchAssetParams,
});

参数

interface WatchAssetParams {
  type: 'trc10' | 'trc20' | 'trc721';
  options: {
    address: string;
    symbol?: string;
    decimals?: number;
    image?: string;
  }
}
* method: wallet_watchAsset,固定字符串 * params: WatchAssetParams,具体参数如下 * type: 目前只支持 trc10 / trc20 / trc721 三种 * options: * address: token 的合约地址 或者 token id,必传 * symbol: 占位(目前未使用),可选 * decimals: 占位(目前未使用),可选 * image: 占位(目前未使用),可选

返回值

此方法没有返回值。

交互流程

添加 TRC10 资产

await window.tron.request({
  method: 'wallet_watchAsset',
  params: {
    type: 'trc10',
    options: {
      address: '1002000'
    },
  },
});

代码执行时,TronLink 会弹出添加窗口,用户点击确定添加 TRC10 资产,或者取消添加。

TronLink 添加 TRC10 资产弹窗,显示代币 ID、名称及"添加"/"取消"按钮

点击”添加”按钮,资产被添加到资产列表,如下图所示。

资产列表添加 TRC10 后界面,新代币显示在列表顶部

添加 TRC20 资产

await window.tron.request({
  method: 'wallet_watchAsset',
  params: {
    type: 'trc20',
    options: {
      address: 'TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9'
    },
  },
});

代码执行时,TronLink 会弹出添加窗口,用户点击确定添加 TRC20 资产,或者取消添加。

TronLink 添加 TRC20 资产弹窗,显示合约地址、符号及"添加"/"取消"按钮

点击”添加”按钮,资产被添加到资产列表,如下图所示。

资产列表添加 TRC20 后界面,新代币显示在列表顶部

添加 TRC721 资产

await window.tron.request({
  method: 'wallet_watchAsset',
  params: {
    type: 'trc721',
    options: {
      address: 'TVtaUnsgKXhTfqSFRnHCsSXzPiXmm53nZt'
    },
  },
});

代码执行时,TronLink 会弹出添加窗口,用户点击确定添加 TRC721 资产,或者取消添加。

TronLink 添加 TRC721 资产弹窗,显示 NFT 合约地址及"添加"/"取消"按钮

点击”添加”按钮,资产被添加到资产列表,如下图所示。

资产列表添加 TRC721 后界面,新藏品集合显示在列表顶部

旧版用法(不推荐): 兼容用法:wallet_watchAsset(window.tronLink)

切换网络 TIP-3326

简介

大部分 DApp 都会在特定的链上提供服务。DApp 可以通过调用本协议,告诉 TronLink 期望使用的链,TronLink 会弹出弹窗告知用户即将切换的链,用户可以选择是否同意切换。 在用户同意切换后,DApp 可以基于目标链,提供正常的 DApp 服务。 本协议遵循以太坊 EIP-3326 协议。

技术规范

代码示例

try {
  await window.tron.request({
    method: 'wallet_switchEthereumChain',
    params: [{chainId: '0x2b6653dc'}]
  });
} catch (e) {}

参数

params 接受一个只有单一元素的数组,单一元素为 SwitchTronChainParameter 类型:

interface SwitchTronChainParameter {
  chainId: string;
}
- 目前只支持如下 chainId: - mainnet(主网):0x2b6653dc - shasta(shasta 测试网):0x94a9059e - nile(nile 测试网):0xcd8690dc - chainId 的值大小写敏感。

返回值

如果成功,返回 null。 如果失败,则会返回错误码及报错信息。详见“错误码”部分。

错误码

错误码 名称 描述
4001 用户拒绝请求 用户通过点击“拒绝”按钮,或关闭弹窗,都会触发该错误码
4902 不合法 chainId 目前只支持部分 chainId,详见“目前支持的 chainId”部分
-32002 处于其他流程中 当前 DApp 存在其他流程,无法执行当前请求
-32602 参数不合规 传入的参数不合规,或者传入了额外的参数
4200 不支持此方法 不支持此方法

交互流程

触发 wallet_switchEthereumChain 之后,如果 TronLink 处于锁屏状态,会弹出锁屏弹窗:

TronLink 解锁弹窗,输入钱包密码

解锁后,或者 TronLink 已经提前解锁,会打开连接确认的弹窗:

TronLink 网络切换确认弹窗,显示目标网络名称、chainId 与"确认"/"拒绝"按钮

旧版用法(不推荐): 兼容用法:wallet_switchEthereumChain(tronLink.request)


JSON Schema 参考

下方 RPC 风格的方法都通过 window.tron.request({ method, params? }) 调用;tronWeb 系列方法(sign、multiSign、signMessageV2)接收一个位置参数并返回 Promise。Schema 遵循 JSON Schema Draft 7。request 方法按各方法列出的数字 code 判断;tronWeb 签名方法失败的返回形式因原因而异,按各方法下的说明处理。

eth_requestAccounts(TIP-1102)

请求——params 必须不传或传 []:

{
  "type": "object",
  "required": ["method"],
  "properties": {
    "method": { "const": "eth_requestAccounts" },
    "params": { "type": "array", "maxItems": 0 }
  }
}

响应——长度为 1 的数组,元素是已授权的 base58 地址:

{
  "type": "array",
  "minItems": 1,
  "maxItems": 1,
  "items": { "type": "string", "description": "TRON 地址(base58,T 开头)" }
}

错误码——4001 用户拒绝 · -32002 另有请求进行中 · -32602 参数非法 · 4200 不支持此方法。

wallet_watchAsset(添加资产)

请求:

{
  "type": "object",
  "required": ["method", "params"],
  "properties": {
    "method": { "const": "wallet_watchAsset" },
    "params": {
      "type": "object",
      "required": ["type", "options"],
      "properties": {
        "type": { "type": "string", "enum": ["trc10", "trc20", "trc721"] },
        "options": {
          "type": "object",
          "required": ["address"],
          "properties": {
            "address":  { "type": "string", "description": "代币合约地址(TRC20 / TRC721)或 token id(TRC10)" },
            "symbol":   { "type": "string", "description": "可选显示符号" },
            "decimals": { "type": "integer", "minimum": 0, "description": "可选显示精度" },
            "image":    { "type": "string", "format": "uri", "description": "可选图标 URI" }
          }
        }
      }
    }
  }
}

响应——无返回值(undefined)。用户点击「添加」时 Promise resolve;点击「取消」时 reject,错误码 4001。

wallet_switchEthereumChain(TIP-3326)

请求:

{
  "type": "object",
  "required": ["method", "params"],
  "properties": {
    "method": { "const": "wallet_switchEthereumChain" },
    "params": {
      "type": "array",
      "minItems": 1,
      "maxItems": 1,
      "items": {
        "type": "object",
        "required": ["chainId"],
        "properties": {
          "chainId": { "type": "string", "enum": ["0x2b6653dc", "0x94a9059e", "0xcd8690dc"], "description": "主网 / Shasta / Nile(大小写敏感)" }
        }
      }
    }
  }
}

响应——成功时返回 null。

错误码——4001 用户拒绝 · 4902 chainId 非法 · -32002 另有请求进行中 · -32602 参数非法 · 4200 不支持此方法。

tronweb.trx.sign(transaction) —— 对 TRON 交易签名

参数——一个未签名的 TronWeb 交易对象(与 tronweb.transactionBuilder.* 的返回值同结构):

{
  "type": "object",
  "required": ["txID", "raw_data", "raw_data_hex"],
  "properties": {
    "visible":      { "type": "boolean" },
    "txID":         { "type": "string", "description": "64 字符十六进制交易 id" },
    "raw_data":     { "type": "object", "description": "协议层交易体(ref_block_*、expiration、contract[]、timestamp、fee_limit?)" },
    "raw_data_hex": { "type": "string" }
  }
}

返回——同结构加上 signature 数组:

{
  "type": "object",
  "required": ["txID", "raw_data", "raw_data_hex", "signature"],
  "properties": {
    "signature": { "type": "array", "items": { "type": "string", "description": "65 字节十六进制签名(r||s||v)" } }
  }
}

用户拒绝时,Promise 以纯字符串 'Confirmation declined by user' reject(不是 Error,没有数字 code)。参见处理拒签。

tronweb.trx.multiSign(transaction, privateKey?, permissionId) —— 多签签名

输入输出结构同 sign。permissionId 是整数,2 通常对应首个 active 权限。每次调用向 signature 数组追加一个签名——收集到阈值数量后通过 sendRawTransaction 广播。

tronweb.trx.signMessageV2(message) —— TIP-191 消息签名

参数——非空字符串,按 UTF-8 文本签名。0x 前缀不会使其变成原始字节,TronLink 会拒绝非字符串输入;详见消息签名。

{ "type": "string" }

返回——带 0x 前缀的 65 字节十六进制签名:

{ "type": "string", "pattern": "^0x[0-9a-fA-F]{130}$" }

错误结构: 在 TronLink 注入的 tronWeb 上(插件 4.0.0 起),用户拒绝 trx.sign、trx.multiSign 或 trx.signMessageV2 时,Promise 以纯字符串 'Confirmation declined by user' reject——不是 Error 对象,也没有数字 code。{ code: 4001, message: 'User rejected the request.' } 对象只由 window.tron.request(...) 方法(如 eth_requestAccounts)返回。其他失败(钱包未解锁、载荷非法、观察钱包等)返回其他值——参见处理拒签。不要自动重试签名。

其他 reject 字符串包括:'Invalid transaction provided'(消息为空或签名失败)、'Transaction integrity check failed'(非字符串消息;4.11.1 起,更早版本返回 'Invalid transaction provided')、'User has not unlocked wallet'。这些都不是用户拒绝。

TIP-6963(provider 发现)

事件机制,不是 request 方法。结构定义见上方 获取TronLink的provider TIP-6963 的 TypeScript 接口;线上格式是两个 CustomEvent:

  • TIP6963:requestProvider —— 由 DApp 派发,无 payload。
  • TIP6963:announceProvider —— 由已安装的每个钱包派发,detail = { info: { uuid, name, icon, rdns }, provider }。TronLink 的 rdns = "org.tronlink.www"、name = "TronLink"。

兼容用法 tron_requestAccounts

为兼容性保留;新接入请使用 eth_requestAccounts。

请求:

{
  "type": "object",
  "required": ["method"],
  "properties": {
    "method": { "const": "tron_requestAccounts" },
    "params": {
      "type": "object",
      "properties": {
        "websiteIcon": { "type": "string", "format": "uri" },
        "websiteName": { "type": "string" }
      }
    }
  }
}

响应:

{
  "type": "object",
  "required": ["code", "message"],
  "properties": {
    "code":    { "type": "integer", "enum": [200, 4000, 4001] },
    "message": { "type": "string" }
  }
}

code:200 站点已授权 或 用户批准 · 4000 重复授权请求待处理 · 4001 用户拒绝。钱包被锁的状态用空字符串响应表达,而非数字码。


旧版用法(不推荐)

下列接口作为兼容别名保留,新接入请使用上方推荐用法。window.tronLink 与 window.tron 在功能上等价,但前者将逐步不再维护。

兼容用法:tron_requestAccounts

简介

TronLink 提供外部发起 TRX 转账、合约签名、授权等功能。基于安全的考虑,需要用户在关键操作前先对发起请求的 DApp 进行【连接网站】授权,在授权成功后才允许操作。 所以 DApp 要先进行【连接网站】操作,等待用户允许后,方能发起需要授权的请求。

技术规范

代码示例

const res = await tronWeb.request(
  {
    method: 'tron_requestAccounts',
    params: {
      websiteIcon: '<WEBSITE ICON URI>',
      websiteName: '<WEBSITE NAME>',
    } as RequestAccountParams,
  }
);

参数

interface RequestAccountsParams {
  websiteIcon?: string;
  websiteName?: string;
}
* method: tron_requestAccounts 固定的字符串 * params: RequestAccountParams 类型,具体参数如下 * websiteIcon: DApp 网站的 Icon 的 URI,具体会展示在用户已连接网站列表中 * websiteName: DApp 网站名称

返回值

类型说明:

interface ReqestAccountsResponse {
  code: 200 | 4000 | 4001,
  message: string
}

返回码 描述 返回消息
无 钱包处于锁定状态 空字符串
200 网站此前已被用户允许连接 The site is already in the whitelist
200 用户同意连接 User allowed the request.
4000 当前请求前已经有同一个 DApp 发起了连接网站请求,并且弹窗仍未关闭 Authorization requests are being processed, please do not resubmit
4001 用户拒绝连接 User rejected the request
if (window.tronLink.ready) {
  const tronweb = tronLink.tronWeb;
  const fromAddress = tronweb.defaultAddress.base58;
  const toAddress = "TDvSsdrNM5eeXNL3czpa6AxLDHZA9nwe9K";
  const tx = await tronweb.transactionBuilder.sendTrx(toAddress, 10, fromAddress); // 步骤1
  try {
    const signedTx = await tronweb.trx.sign(tx); // 步骤2
    await tronweb.trx.sendRawTransaction(signedTx); // 步骤3
  } catch (e) {}
}
if (window.tronLink.ready) {
  const tronweb = tronLink.tronWeb;
  const toAddress = "TDvSsdrNM5eeXNL3czpa6AxLDHZA9nwe9K";
  const activePermissionId = 2;
  const tx = await tronweb.transactionBuilder.sendTrx(
      toAddress, 10, 
      { permissionId: activePermissionId}
  ); // 步骤1
  try {
    const signedTx = await tronweb.trx.multiSign(tx, undefined, activePermissionId); // 步骤2
    await tronweb.trx.sendRawTransaction(signedTx); // 步骤3
  } catch (e) {}
}
if (window.tronLink.ready) {
  const tronweb = tronLink.tronWeb;
  try {
    const message = "Sign in to Example DApp\nNonce: 8f3a2c"; // 普通 UTF-8 文本
    const signedString = await tronweb.trx.signMessageV2(message);
  } catch (e) {}
}
// 添加 TRC10
if (window.tronLink.ready) {
  const tronweb = tronLink.tronWeb;
  try {
    tronweb.request({
      method: 'wallet_watchAsset',
      params: {
        type: 'trc10',
        options: { address: '1002000' },
      },
    });
  } catch (e) {}
}

// 添加 TRC20
if (window.tronLink.ready) {
  const tronweb = tronLink.tronWeb;
  try {
    tronweb.request({
      method: 'wallet_watchAsset',
      params: {
        type: 'trc20',
        options: { address: 'TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9' },
      },
    });
  } catch (e) {}
}

// 添加 TRC721
if (window.tronLink.ready) {
  const tronweb = tronLink.tronWeb;
  try {
    tronweb.request({
      method: 'wallet_watchAsset',
      params: {
        type: 'trc721',
        options: { address: 'TVtaUnsgKXhTfqSFRnHCsSXzPiXmm53nZt' },
      },
    });
  } catch (e) {}
}

兼容用法:wallet_switchEthereumChain(tronLink.request)

try {
  await tronLink.request({
    method: 'wallet_switchEthereumChain',
    params: [{chainId: '0x2b6653dc'}]
  });
} catch (e) {}