ARC-6: Algorand Wallet Address Discovery API
API function, enable, which allows the discovery of accounts
Author | DanBurton |
---|---|
Discussions-To | https://github.com/algorandfoundation/ARCs/issues/52 |
Status | Deprecated |
Type | Standards Track |
Category | Interface |
Created | 2021-08-09 |
Table of Contents
Algorand Wallet Address Discovery API
Abstract
A function, enable
, which allows the discovery of accounts.
Optional functions, enableNetwork
and enableAccounts
, which handle the multiple capabilities of enable
separately.
This document requires nothing else, but further semantic meaning is prescribed to these functions in ARC-0010 which builds off of this one and a few others.
The caller of this function is usually a dApp.
Specification
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC-2119.
Comments like this are non-normative.
Interface EnableFunction
export type AlgorandAddress = string;
export type GenesisHash = string;
export type EnableNetworkFunction = (
opts?: EnableNetworkOpts
) => Promise<EnableNetworkResult>;
export type EnableAccountsFunction = (
opts?: EnableAccountsOpts
) => Promise<EnableAccountsResult>;
export type EnableFunction = (
opts?: EnableOpts
) => Promise<EnableResult>;
export type EnableOpts = (
EnableNetworkOpts & EnableAccountsOpts
);
export interface EnableNetworkOpts {
genesisID?: string;
genesisHash?: GenesisHash;
};
export interface EnableAccountsOpts {
accounts?: AlgorandAddress[];
};
export type EnableResult = (
EnableNetworkResult & EnableAccountsResult
);
export interface EnableNetworkResult {
genesisID: string;
genesisHash: GenesisHash;
}
export interface EnableAccountsResult {
accounts: AlgorandAddress[];
}
export interface EnableError extends Error {
code: number;
data?: any;
}
An EnableFunction
with optional input argument opts:EnableOpts
MUST return a value ret:EnableResult
or MUST throw an exception object of type EnableError
.
String specification: GenesisID
and GenesisHash
A GenesisID
is an ascii string
A GenesisHash
is base64 string representing a 32-byte genesis hash.
String specification: AlgorandAddress
Defined as in ARC-0001:
An Algorand address is represented by a 58-character base32 string. It includes includes the checksum.
Error Standards
EnableError
follows the same rules as SignTxnsError
from ARC-0001 and uses the same status error codes.
Interface WalletAccountManager
export interface WalletAccountManager {
switchAccount: (addr: AlgorandAddress) => Promise<void>
switchNetwork: (genesisID: string) => Promise<void>
onAccountSwitch: (hook: (addr: AlgorandAddress) => void)
onNetworkSwitch: (hook: (genesisID: string, genesisHash: GenesisHash) => void)
}
Wallets SHOULD expose switchAccount
function to allow an app to switch an account to another one managed by the wallet. The switchAccount
function should return a promise which will be fulfilled when the wallet will effectively switch an account.
The function must thrown an Error
exception when the wallet can’t execute the switch (for example, the provided address is not managed by the wallet or when the address is not a valid Algorand address).
Similarly, wallets SHOULD expose switchNetwork
function to instrument a wallet to switch to another network.
The function must thrown an Error
exception when the wallet can’t execute the switch (for example, when the provided genesis ID is not recognized by the wallet).
Very often, webapp have their own state with information about the user (provided by the account address) and a network. For example, a webapp can list all compatible Smart Contracts for a given network.
For descent integration with a wallet, we must be able to react in a webapp on the account and network switch from the wallet interface. For that we define 2 functions which MUST be exposed by wallets: onAccountSwitch
and onNetworkSwitch
. These function will register a hook and will call it whenever a user switches respectively an account or network from the wallet interface.
Semantic requirements
This ARC uses interchangeably the terms “throw an error” and “reject a promise with an error”.
First call to enable
Regarding a first call by a caller to enable(opts)
or enable()
(where opts
is undefined
), with potential promised return value ret
:
When genesisID
and/or genesisHash
is specified in opts
:
- The call
enable(opts)
MUST either throw an error or return an objectret
whereret.genesisID
andret.genesisHash
matchopts.genesisID
andopts.genesisHash
(i.e.,ret.genesisID
is identical toopts.genesisID
ifopts.genesisID
is specified, andret.genesisHash
is identical toopts.genesisHash
ifopts.genesisHash
is specified). - The user SHOULD be prompted for permission to acknowledge control of accounts on that specific network (defined by
ret.genesisID
andret.genesisHash
). - In the case only
opts.genesisID
is provided, several networks may match this ID and the user SHOULD be prompted to select the network they wish to use.
When neither genesisID
nor genesisHash
is specified in opts
:
- The user SHOULD be prompted to select the network they wish to use.
- The call
enable(opts)
MUST either throw an error or return an objectret
whereret.genesisID
andret.genesisHash
SHOULD represent the user’s selection of network. - The function MAY throw an error if it does not support user selection of network.
When accounts
is specified in opts
:
- The call
enable(opts)
MUST either throw an error or return an objectret
whereret.accounts
is an array that starts with all the same elements asopts.accounts
, in the same order. - The user SHOULD be prompted for permission to acknowledge their control of the specified accounts. The wallet MAY allow the user to provide more accounts than those listed. The wallet MAY allow the user to select fewer accounts than those listed, in which the wallet MUST return an error which SHOULD be a user rejected error and contain the rejected accounts in
data.accounts
.
When accounts
is not specified in opts
:
- The user SHOULD be prompted to select the accounts they wish to reveal on the selected network.
- The call
enable(opts)
MUST either throw an error or return an objectret
whereret.accounts
is a empty or non-empty array. - If
ret.accounts
is not empty, the caller MAY assume thatret.accounts[0]
is the user’s “currently-selected” or “default” account, for DApps that only require access to one account.
Empty
ret.accounts
array are used to allow a DApp to get access to an Algorand node but not to signing capabilities.
Network
In addition to the above rules, in all cases, if ret.genesisID
is one of the official network mainnet-v1.0
, testnet-v1.0
, or betanet-v1.0
, ret.genesisHash
MUST match the genesis hash of those networks
Genesis ID | Genesis Hash |
---|---|
mainnet-v1.0 |
wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8= |
testnet-v1.0 |
SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI= |
betanet-v1.0 |
mFgazF+2uRS1tMiL9dsj01hJGySEmPN28B/TjjvpVW0= |
When using a genesis ID that is not one of the above, the caller SHOULD always provide a genesisHash
.
This is because a genesisID
does not uniquely define a network in that case.
If a caller does not provide a genesisHash
, multiple calls to enable
may return a different network with the same genesisID
but a different genesisHash
.
Identification of the caller
The enable
function MAY remember the choices of the user made by a specific caller and use them everytime the same caller calls the function.
The function MUST ensure that the caller can be securely identified.
In particular, by default, the function MUST NOT allow webapps on the http protocol to call it, as such webapps can easily be modified by a man-in-the-middle attacker.
In the case of callers that are https websites, the caller SHOULD be identified by its fully qualified domain name.
The function MAY offer the user some “developer mode” or “advanced” options to allow calls from insecure dApps. In that case, the fact that the caller is insecure and/or the fact that the wallet in “developer mode” MUST be clearly displayed by the wallet.
Multiple calls to enable
The same caller MAY call multiple time the enable
function.
When the caller is a dApp, every time a dApp is refreshed, it actually SHOULD call the enable()
function.
The enable
function MAY NOT return the same value every time it is called, even when called with the exact same argument opts
.
The caller MUST NOT assume that the enable
function will always return the same value, and MUST properly handle changes of available accounts and/or changes of network.
For example, a user may want to change network or accounts for a dApp. That is why, upon refresh, the dApp SHOULD automatically switch network and perform all required changes. Examples of required changes include but are not limited to change of the list of accounts, change of statuses of the account (e.g., opted in or not), change of the balances of the accounts.
enableNetwork
and enableAccounts
It may be desirable for a dapp to perform network queries prior to requesting that the user enable an account for use with the dapp. Wallets may provide the functionality of enable
in two parts: enableNetwork
for network discovery, and enableAccounts
for account discovery, which together are the equivalent of calling enable
.
Rationale
This API puts power in the user’s hands to choose a preferred network and account to use when interacting with a dApp.
It also allows dApp developers to suggest a specific network, or specific accounts, as appropriate.
The user still maintains the ability to reject the dApp’s suggestions, which corresponds to rejecting the promise returned by enable()
.
Security Considerations
None.
Copyright
Copyright and related rights waived via CCO.
Citation
Please cite this document as:
DanBurton, "ARC-6: Algorand Wallet Address Discovery API," Algorand Requests for Comments, no. 6, August 2021. [Online serial]. Available: https://github.com/algorandfoundation/ARCs/blob/main/ARCs/arc-0006.md.