Description
tip: 6963
title: Multi Injected Provider Discovery
author: Zorro <zorro11639@gmail.com>
discussions to: https://github.com/tronprotocol/tips/issues/737
category: Interface
status: Draft
created: 2025-03-19
Table of Contents
Summary
This protocol specifies an alternative discovery mechanism to window.tron
for TIP-1193 providers which supports discovering multiple injected Wallet Providers in a web page using Javascript’s window
events.
Abstract
Currently, Tron wallets is recommended to inject their Wallet providers into the same window object window.tron
according to TIP-1193. However, this creates conflicts for users that may install more than one browser extension.
Browser extensions are loaded in the web page in an unpredictable order and the final Provider is usually provided by the last wallet resulting in that users cannot select the specified wallet if the wallet is not loaded last.
This results not only in a degraded user experience but also increases the barrier to entry for new browser extensions as users are forced to only install one browser extension at a time.
In this proposal, we present a solution that focuses on optimizing the interoperability of multiple Wallet Providers. This solution aims to foster fairer competition by reducing the barriers to entry for new Wallet Providers, along with enhancing the user experience on Tron networks.
This is achieved by introducing a set of window events to provide a two-way communication protocol between Tron libraries and injected scripts provided by browser extensions thus enabling users to select their wallet of choice.
Specification
Definitions
Wallet Provider: A user agent that manages keys and facilitates transactions with Tron.
Decentralized Application (DApp): A web page that relies upon one or many Web3 platform APIs which are exposed to the web page via the Wallet.
Provider Discovery Library: A library or piece of software that assists a DApp to interact with the Wallet.
Provider Info
Each Wallet Provider will be announced with the following interface TIP6963ProviderInfo
. The values in the TIP6963ProviderInfo
must be included within the TIP6963ProviderInfo
object. The TIP6963ProviderInfo
may also include extra extensible properties within the object. If a DApp does not recognize the additional properties, it should ignore them.
uuid
- a globally unique identifier the Wallet Provider that must be (UUIDv4 compliant) to uniquely distinguish different TIP-1193 provider sessions that have matching properties defined below during the lifetime of the page. The cryptographic uniqueness provided by UUIDv4 guarantees that two independentTIP6963ProviderInfo
objects can be separately identified.name
- a human-readable local alias of the Wallet Provider to be displayed to the user on the DApp. (e.g.Example Wallet Extension
orAwesome Example Wallet
)icon
- a URI pointing to an image. The image should be a square with 96x96px minimum resolution. See the Images/Icons below for further requirements of this property.rdns
- The Wallet must supply therdns
property which is intended to be a domain name from the Domain Name System in reverse syntax ordering such ascom.example.subdomain
. It's up to the Wallet to determine the domain name they wish to use, but it's generally expected the identifier will remain the same throughout the development of the Wallet. It's also worth noting that similar to a user agent string in browsers, there are times where the supplied value could be unknown, invalid, incorrect, or attempt to imitate a different Wallet. Therefore, the DApp should be able to handle these failure cases with minimal degradation to the functionality of the DApp.
/**
* Represents the assets needed to display a wallet
*/
interface TIP6963ProviderInfo {
uuid: string;
name: string;
icon: string;
rdns: string;
}
Images/Icons
A URI-encoded image was chosen to enable flexibility for multiple protocols for fetching and rendering icons, for example:
# svg (data uri)
data:image/svg+xml,<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32px" height="32px" viewBox="0 0 32 32"><circle fill="red" cx="16" cy="16" r="12"/></svg>
# png (data uri)
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==
The icon
string must be a data URI as defined in RFC-2397. The image should be a square with 96x96px minimum resolution. The image format is recommended to be either lossless or vector based such as PNG, WebP or SVG to make the image easy to render on the DApp. Since SVG images can execute Javascript, applications and libraries must render SVG images using the <img>
tag to ensure no untrusted Javascript execution can occur.
RDNS
The rdns
(Reverse-DNS) property serves to provide an identifier which DApps can rely on to be stable between sessions. The Reverse Domain Name Notation is chosen to prevent namespace collisions.
The Reverse-DNS convention implies that the value should start with a reversed DNS domain name controlled by the Provider. The domain name should be followed by a subdomain or a product name. Example: com.example.MyBrowserWallet
.
- The
rdns
value must be a valid RFC-1034 Domain Name; - The DNS part of the
rdns
value should be an active domain controlled by the Provider; - DApps may reject the Providers which do not follow the Reverse-DNS convention correctly;
- DApps should not use the
rnds
value for feature detection as these are self-attested and prone to impersonation or bad incentives without an additional verification mechanism; feature-discovery and verification are both out of scope of this interface specification.
Provider Detail
The TIP6963ProviderDetail
is used as a composition interface to announce a Wallet Provider and related metadata about the Wallet Provider. The TIP6963ProviderDetail
must contain an info
property of type TIP6963ProviderInfo
and a provider
property of type TIP1193Provider
defined by TIP-1193.
interface TIP6963ProviderDetail {
info: TIP6963ProviderInfo;
provider: TIP1193Provider;
}
Window Events
In order to prevent provider collisions, the DApp and the Wallet are expected to emit an event and instantiate an eventListener to discover the various Wallets. This forms an Event concurrency loop.
Since the DApp code and Wallet code aren't guaranteed to run in a particular order, the events are designed to handle such race conditions.
To emit events, both DApps and Wallets must use the window.dispatchEvent
function to emit events and must use the window.addEventListener
function to observe events. There are two Event interfaces used for the DApp and Wallet to discover each other.
Announce and Request Events
The TIP6963AnnounceProviderEvent
interface must be a CustomEvent
object with a type
property containing a string value of TIP6963:announceProvider
and a detail
property with an object value of type TIP6963ProviderDetail
. The TIP6963ProviderDetail
object should be frozen by calling Object.freeze()
on the value of the detail
property.
// Announce Event dispatched by a Wallet
interface TIP6963AnnounceProviderEvent extends CustomEvent {
type: "TIP6963:announceProvider";
detail: TIP6963ProviderDetail;
}
The TIP6963RequestProviderEvent
interface must be an Event
object with a type
property containing a string value of TIP6963:requestProvider
.
// Request Event dispatched by a DApp
interface TIP6963RequestProviderEvent extends Event {
type: "TIP6963:requestProvider";
}
The Wallet must announce the TIP6963AnnounceProviderEvent
to the DApp via a window.dispatchEvent()
function call. The Wallet must add an EventListener to catch an TIP6963RequestProviderEvent
dispatched from the DApp. This EventListener must use a handler that will re-dispatch an TIP6963AnnounceProviderEvent
. This re-announcement by the Wallet is useful for when a Wallet's initial Event announcement may have been delayed or fired before the DApp had initialized its EventListener. This allows the various Wallet Providers to react to the DApp without the need to pollute the window.tron
namespace which can produce non-deterministic wallet behavior such as different wallets connecting each time.
The Wallet dispatches the "TIP6963:announceProvider"
event with immutable contents and listens to the "TIP6963:requestProvider"
event:
let info: TIP6963ProviderInfo;
let provider: TIP1193Provider;
const announceEvent: TIP6963AnnounceProviderEvent = new CustomEvent(
"TIP6963:announceProvider",
{ detail: Object.freeze({ info, provider }) }
);
// The Wallet dispatches an announce event which is heard by
// the DApp code that had run earlier
window.dispatchEvent(announceEvent);
// The Wallet listens to the request events which may be
// dispatched later and re-dispatches the `TIP6963AnnounceProviderEvent`
window.addEventListener("TIP6963:requestProvider", () => {
window.dispatchEvent(announceEvent);
});
The DApp must listen for the TIP6963AnnounceProviderEvent
dispatched by the Wallet via a window.addEventListener()
method and must not remove the Event Listener for the lifetime of the page so that the DApp can continue to handle Events beyond the initial page load interaction. The DApp must dispatch the TIP6963RequestProviderEvent
via a window.dispatchEvent()
function call after the TIP6963AnnounceProviderEvent
handler has been initialized.
// The DApp listens to announced providers
window.addEventListener(
"TIP6963:announceProvider",
(event: TIP6963AnnounceProviderEvent) => {}
);
// The DApp dispatches a request event which will be heard by
// Wallets' code that had run earlier
window.dispatchEvent(new Event("TIP6963:requestProvider"));
The DApp may elect to persist various TIP6963ProviderDetail
objects contained in the announcement events sent by multiple wallets. Thus, if the user wishes to utilize a different Wallet over time, the user can express this within the DApp's interface and the DApp can immediately elect to send transactions to that new Wallet. Otherwise, the DApp may re-initiate the wallet discovery flow via dispatching a new TIP6963RequestProviderEvent
, potentially discovering a different set of wallets.
The described orchestration of events guarantees that the DApp is able to discover the Wallet, regardless of which code executes first, the Wallet code or the DApp code.
Backwards Compatibility
This TIP doesn't require supplanting window.tron
, so it doesn't directly break existing applications that cannot update to this method of Wallet discovery. However, it is recommended DApps implement this TIP to ensure discovery of multiple Wallet Providers and should disable window.tron
usage except as a fail-over when discovery fails. Similarly, Wallets should keep compatibility of window.tron
to ensure backwards compatibility for DApps that have not implemented this TIP. In order to prevent the previous issues of namespace collisions, it's also recommended that wallets inject their provider object under a wallet specific namespace then proxy the object into the window.tron
namespace.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status