aip | title | author | discussions-to (*optional) | Status | last-call-end-date (*optional) | type | created | updated | requires (*optional) |
---|---|---|---|---|---|---|---|---|---|
40 |
Address Standard v1 |
banool |
Draft |
07/10/2023 |
Standard |
06/18/2023 |
08/11/2023 |
This standard defines the following:
- What format APIs / tools should return addresses in.
- What formats APIs / tools should accept addresses in.
- How addresses should be displayed.
- How addresses should be stored.
Prior discussion has centered around ways to differentiate between addresses, public / private keys, and other representations of data as hex. There has also been discussion around a more compact representation of addresses. This standard is not related to these discussions (which are akin to a v2 standard), it only aims to standardize our existing approach (v1).
Each account / object on the Aptos blockchain is identified by a 32 byte account address. For internal uses, addresses are represented as just that, using a 32 byte sequence. However when transmitting and displaying addresses it is common to use a hex representation. As it stands today there is no standard that defines how these addresses should be represented in each context. Since there are multiple technically correct ways to represent an address, this leads to a fractured ecosystem in which different APIs, tools, and products represent and accept addresses differently. This can lead to issues with querying on-chain data and submitting correct transactions.
This AIP only defines a standard. Our intent is that existing APIs will continue to return addresses as they do today so as not to break any downstream users. New products however must conform to this standard.
As such, those who need to take action based on this AIP are generally platform developers, e.g. of APIs, tooling, wallets, etc.
Instead of defining a v1 standard we could immediately skip to a v2 standard. In this standard we could / would overhaul all identifiers (addresses, public keys, private keys, how we represent resources, bytes, etc). We could do this, but I suspect the discussion, implementation, and migration would take a very long time. So it is best to define a standard for what we have now and look into a v2 standard later.
In this alternative world we don't display special (see definition below) addresses differently, we just use the LONG form. This would make for a simpler standard but the UX would be poor, people are used to seeing special addresses (defined below) represented in short form, e.g. 0x1. In fact, there are benefits to allowing short form representation for special addresses, e.g. to avoid the potential for malicious actors displaying addresses that look like special addresses but are not, e.g. 0x0{62}1
vs 0x0{63}1
.
Representation:
0x<64 hex characters>
Examples:
0x0000000000000000000000000000000000000000000000000000000000000001
0x14b6041b77304fe9354aba2e0b1a0ae51d816d0513332ef651a039fac90339cb
0x043ec2cb158e3569842d537740fd53403e992b9e7349cc5d3dfaa5aff8faaef2
Representation:
0x<hex characters, leading zeroes trimmed>
Examples:
0x1
0x14b6041b77304fe9354aba2e0b1a0ae51d816d0513332ef651a039fac90339cb
0x43ec2cb158e3569842d537740fd53403e992b9e7349cc5d3dfaa5aff8faaef2
Same as LONG
, but without the 0x
prefix.
Same as SHORT
, but without the 0x
prefix.
Addresses are considered special if the first 63 characters of the hex string after the 0x prefix are zero. In other words, an address is special if the first 31 bytes are zero and the last byte is smaller than than 0b10000
(16). In other words, special is defined as an address that matches the following regex: ^0x0{63}[0-9a-f]$
. In short form this means the addresses in the range from 0x0
to 0xf
(inclusive) are special.
This is explained in greater detail in the reference implementation.
Functions that parse / validate strings representing account addresses MUST accept the following formats (in which the leading 0x is mandatory):
- LONG
- SHORT for special addresses
Additionally, functions that parse / validate account addresses MAY accept the following:
- LONG with or without leading 0x
- SHORT with or without leading 0x for all addresses (not just special)
The following correspond to representations actively in circulation. Implementations MAY accept these formats for the sake of backwards compatibility with the existing ecosystem. This is necessary since some tools return account addresses in these latter formats and cannot be updated without breaking backwards compatibility.
Where possible, developers should avoid accepting these formats (e.g. when building APIs, wallets, tools, etc) but it is allowed in order to maintain compatibility.
A reference implementation of this principle can be found in the v2 TypeScript SDK:
- fromString: This stricter function only accepts addresses in formats
1
and2
. - fromStringRelaxed: This more relaxed function accepts addresses in formats
1
,2
,3
, and4
.
This describes how addresses should be displayed. Display here refers to any time an address is shown to a user, including in web UIs, logs, compiler output, etc.
- Special addresses SHOULD be represented using
SHORT
format. - All other addresses MUST be represented using
LONG
format.
This section defines how to represent addresses in responses from APIs (such as those exposed by nodes and the indexer) and any other programming interface when using a hex representation.
- Addresses MUST be formatted using the same rules as for Display Format.
Some systems, such as the indexer processors when writing to storage, store addresses using a string representation.
- Addresses SHOULD be formatted using the same rules as for Display Format.
Note: Binary representation of addresses at rest is preferred.
When using a binary representation, addresses MUST be encoded as BCS in the canonical format.
The implementation of toString in the v2 Typescript SDK returns account addresses as strings in a way that conforms to the standard.
The fromString
and fromStringRelaxed
functions implement the parsing / validation side of the standard as described above.
Given different tools, sites, etc. represent / accept addresses in different ways already, this standard should not further fracture the ecosystem, but rather bring it together. Additionally, we are not planning on making breaking changes to existing APIs. So the risks should be minimal.
In this section I outline the order we must implement changes. I leave determining specific dates as a later exercise.
In the main languages we support (TypeScript, Rust, and Python), we must ensure that there are AccountAddress classes that conform to the standard. We should also strive to support other important languages like C#. This essentially means the following functions must exist:
- A function that outputs an address as a string that conforms to the standard.
- A function that parses an address from a string in a way that conforms to the standard (strict and relaxed).
- A function that checks for equality of addresses (effectively combining the two prior functions).
The reference implementation implements this for Rust. TypeScript and Python will be fast follows given the changes are minor.
Largely speaking this guide would be a condensed, code-focused version of this AIP, referring to the libraries we added and the migration timeline suggested below.
We will reach out to builders in the ecosystem with a list of clear action items. Odds are good that many tools are already standard compliant in some / all ways, so the changes needed should be minor. In other cases it will be a simple matter of updating dependencies to use newer versions of the SDKs.
Prior to any changes to how addresses are display / returned / stored, we must update everywhere that accepts addresses as input to be standards compliant. In all cases what addresses are accepted should be permissive than what they had before, as the goal of the standard is to be permissive on the input side.
Examples of things that might need to change:
- APIs
- Node APIs
- Indexer APIs
- Any other APIs run by ecosystem players
- CLIs
- The
aptos
CLI
- The
- Wallets
- Dapps
- Explorers
- Marketplaces
- Analytics platforms
- SDKs
- TypeScript
- Rust
- Python
- Configuration
- Node configs
- CLI configs
- Processor configs
In the general case if one tool accepts an address in only certain formats and another submits addresses in a different format then that might cause problems. This is why it is essential that anything that accepts addresses as input is made more permissive first, throughout the whole ecosystem, before we change output formats.
One of the key motivators for this AIP is problems that arise from comparing addresses in different formats. Any product (see the list above) that does this must migrate to the new equality functions, which will handle addresses in any of the acceptable input formats described above.
At this point it should be safe to update all products to display (in the case of graphical tools) or return (in the case of programmatic tools that return addresses as strings) addresses in a standard compliant way.
By disallowing accepting SHORT addresses for non-special addresses, we make phishing / mistakes harder (e.g. by attempting to trick users by omitting leading zeroes). I'm not aware of any notable security drawbacks to this AIP.
See the test plan of the reference implementation.