Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
This is the suggested template for new AEXs.
Note that an AEX number will be assigned by an editor. When opening a pull
request to submit your AEX, please use an abbreviated title in the filename,aex-draft_title_abbrev.md.
The title should be 44 characters or less.
"If you can't explain it simply, you don't understand it well enough." Provide a simplified and layman-accessible explanation of the AEX.
A short (~200 word) description of the technical issue being addressed.
The motivation should clearly explain why existing specifications are inadequate to address the problem that the AEX solves. AEX submissions without sufficient motivation may be rejected outright.
The technical specification should describe the syntax and semantics of any new or changed feature. The specification should be detailed enough to allow competing, interoperable implementations.
The rationale fleshes out the specification by describing what motivated the design and why particular design decisions were made. It should describe alternate designs that were considered and related work, e.g. how the feature is supported in other languages. The rationale may also provide evidence of consensus within the community, and should discuss important objections or concerns raised during discussion.
All AEXs that introduce backwards incompatibilities must include a section describing these incompatibilities and their severity. The AEX must explain how the author proposes to deal with these incompatibilities. AEX submissions without a sufficient backwards compatibility treatise may be rejected outright.
The implementations, if applicable, must be completed before any AEX is given status "Last Call", and it needs be completed before the AEX is accepted. There is merit to the approach of reaching consensus on the specification and rationale before writing code, but the principle of "rough consensus and running code" is still useful when it comes to resolving many discussions of API details.
Copyright and related rights go here if the License and License-Code
fields don't cover the copyright requirements.
AEX: <to be assigned by editors>
Title: <AEX title>
Author: <a list of the author's or authors' name(s) and/or username(s), or name(s) and email(s), e.g. (use with the parentheses or triangular brackets): FirstName LastName (@GitHubUsername), FirstName LastName <[email protected]>, FirstName (@GitHubUsername) and GitHubUsername (@GitHubUsername)>
License: <license names, abbreviated>
License-Code (*optional): <license names, abbreviated>
Discussions-To: <URL>
Status: <Draft | Review | Last Call (yyyy-mm-dd to yyyy-mm-dd) | Final | Active | Updated | Superseded | Rejected | Withdrawn>
Type: <Interface | Informational | Meta>
Created: <date created on, in ISO 8601 (yyyy-mm-dd) format>
Requires (*optional): <AEX number(s)>
Replaces (*optional): <AEX number(s)>
Updates (*optional): <AEX number(s)>This aexpansion specify the derivation path used in the Aeternity blockchain for deterministic wallets
The aexpansion is meant to avoid incompatibility with deterministic wallet implementations across the Aeternity landscape and to make it easy for the derivation path to be found.
Accounts derivation path is taken from , except that in all path segments hardened derivation is used since does not support public derivation.
The derivation path (m/purpose'/coin_type'/account_index'/change'/address_index') is therefore:
m/44H/457H/${account_index}H/0H/${address_index}H
where account_index and address_index are integer starting from 0.
The coin type value for Aeternity, 457, has been generated on random.org and is registered in .
AEX: 10
Title: Derivation path for deterministic wallets
Author: Andrea Giacobino (@noandrea), Denis Davidyuk (@davidyuk)
License: BSD-3-Clause
Discussions-To: https://forum.aeternity.com/t/aex-10-derivation-path-for-deterministic-wallets/3586
License-Code: ISC
Status: Final
Type: Informational
Created: 2019-03-04When opening a pull request to submit a new AEX, please use the suggested template: https://github.com/aeternity/AEXs/blob/master/aex-X.md
The Aeternity Expansions (AEX), or aexpansions, are standards proposed by the community at large, i.e. everyone. Some of them can be mandatory in a specific context, e.g. AEX-1 describes the set of rules governing this repository, but are restricted to the application layer.
The purpose of this repository is to provide a high quality and accessible set of specifications; ideally together with implementations ready to be used if applicable.
If you want to contribute please follow these steps:
Read
Fork this repository
Add your proposal to your fork, using the template provided
Submit a pull request to the AEX repository
Each proposal should go into the AEXS directory. If you need to embed images
or other assets add a subdirectory in the assets directory with the number
of your proposal once assigned, e.g. assets/aex-1/image.png.
Everything beyond step 4 is governed by .
Read for more details.
Draft - an AEX that is open for consideration and is undergoing rapid iteration and changes.
Review - an AEX that is done with its initial iteration and in review by a wide audience.
Last Call - In review by a wide audience - last call for accepting changes.
Final - an AEX that has been in Last Call for at least 2 weeks and all technical changes that were requested have been addressed by the author.
This README is released under the license.
Active - proposal can be in constant flux. No unaddressed substantiated objections are left, crucial or cosmetic updates only.
Updated - signalling that an updating standard might have to be considered
Superseded - an AEX that is deprecated because other AEX supersedes it.
Rejected - editors deemed this proposal to be unworkable
Withdrawn - signalling that the proposal is no longer relevant.
Withdrawn
Inter-Wallet Communication
Withdrawn
Data Serialization
Draft
Message Signing
Review
Fungible Token Standard
Final
Derivation path for deterministic wallets
Draft
æpps Meta Information Format
Review
Non-Fungible Token Standard
Active
AEX process
Withdrawn
Third-party Wallet Provider Support
Active
Secret storage format
Withdrawn
æternity wallet deep linking specification
The motivation behind this aexpansion is to improve the user experience of browsing æpps inside æpps browsers, wallets, and listing services. Our goal is to make it easy for users to recognize the purpose of an æpp/what the æpp allows its users to do and whether users would like to open the æpp and give it access to the account data it requests.
Name of aepp
aepp icons
Networks the aepp is available on [mainnet, testnet]
aepp description
Category (select from predermined list)
Author
Author URL
Related applications
Age restrictions/rating
Reference visuals for the result of implementing such metadata format are included below.
We are leveraging the webmanifest format and augmenting it to fit the needs of the blockchain context. The premise is that this format can be universal (used by multiple protocols, with only one field difference).
Consider if aeppsmanifest format applies to both web and non-web contexts.
This proposal does not address whether metadata provided by an aepp is accurate/truthful.
This proposal does not articulate a stance on storage concerns (as aepp metadata can be rendered dynamically or stored locally).
This proposal does not address the concern of aepps sharing aepps data between themselves.
Documentation: https://www.w3.org/TR/appmanifest/#name-member
Documentation: https://www.w3.org/TR/appmanifest/#icons-member
An array of AeternityNetworkIdType items, each item represents id of the network that aepp is compatible with. The persistence of this member means that aepp supports aeternity protocol.
AeternityNetworkIdType is a string, allowed values: ae_mainnet, ae_uat (testnet).
Documentation: https://www.w3.org/TR/appmanifest/#description-member
Documentation: https://www.w3.org/TR/appmanifest/#categories-member List of known values: https://github.com/w3c/manifest/wiki/Categories
The author member is a string that represents the name of author.
The author_url member is a string that represents the URL of the author's website.
Could be used to set age restrictions/ratings. Documentation: https://www.w3.org/TR/appmanifest/#iarc_rating_id-member
http://aeternity.com/aepp-base-example/webmanifest.json
Web App Manifest standard: https://www.w3.org/TR/appmanifest/ Extensions Registry: https://github.com/w3c/manifest/wiki/Extensions-Registry
MDN documentation says
PWA manifests include its name, author, icon(s), version, description, and list of all the necessary resources (among other things).
DD: I can't find a specific way to add author info except for adding it as a part of another member (for example, at the end of description member). Because of this, I am defining the author member in this document.
The document describes and defines the secret storage approach used by æternity.
The motivation for the AEX is to describe a standard way that is being used by æternity for secret storage.
The secret storage specification, although is being currently used for secret-key storage, should not be limited to it and should be used as a standard way of storing secret text/plain text (esp. user related or user-owned) in an encrypted format.
Having a standard way for encryption and storage of data enables
Interoperability, not only between æternity and æpps but also between the æpps.
Easy migration from an aeternity-supported wallet to another.
The data should always be stored in a .json file.
Each file should have a minimum of 1 JSON object with the following required fields
secret_type specifies the type of the encrypted data.
Specifying an appropriate secret_type helps consumers to decide the proper way
of handling the decrypted data without having to store additional metadata.
The following secret_type values have been proposed:
ed25519-bip39-mnemonic
ed25519-slip0010-masterkey
This list should be expanded with adoption.
It is not advised to use this format as a store for arbitrary binary data.
https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition
The document describes and defines the deep linking specification that every æternity supported wallet can implement and follow to redirect users to a specific activity or page inside the application.
URI based deep linking enables websites or applications to interact with native applications registered to listen for aeternity URI scheme and trigger wallet actions like open wallet accounts directly from third-party applications.
This also enables users to sign transaction using desktop or mobile based wallets without the need to copy or remember long addresses. This is especially beneficial for mobile wallet users as they can simply click on the wallet deep link to open the wallet app from the vendor app, confirm the transaction and return back to the vendor app through the callback url.
ae:
aeternity:
View/Open Account in the wallet
URI: aeternity:<wallet_address>
Associated wallet app will:
Open the wallet app
Check if the address exists or not
https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki
https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki
https://github.com/bitcoin/bips/blob/master/bip-0072.mediawiki
https://hackmd.io/s/BJObJntjQ
AEX: 130
Title: æpps Meta Information Format
Author: Stoyan Vasilev (@j28), Denis Davidyuk (@davidyuk)
License: BSD-3-Clause
Discussions-To: https://forum.aeternity.com/t/aexpansion-proposal-aepps-metadata-format-specification/3987/9
Status: Draft
Type: Meta
Created: 2019-07-09AEX: 3
Title: Secret storage format
Author: Sascha Hanse <[email protected]>, Shubhendu Shekhar (@shekhar-shubhendu)
License: BSD-3-Clause
Discussions-To: https://forum.aeternity.com/t/aex-3-secure-storage-format/3220
Status: Active
Type: Informational
Created: 2019-04-03AEX: 4
Title: æternity wallet deep linking specification
Author: Shubhendu Shekhar (@shekhar-shubhendu), Andrea Giacobino (@noandrea)
License: BSD-3-Clause
Discussions-To: https://forum.aeternity.com/t/aex-4-aeternity-wallet-deep-linking-specification/3231
Status: Withdrawn
Type: Standards Track
Created: 2019-04-03If the account is found with the wallet, then the user is redirected to the account information page of the provided address.
If the account is not found then prompt the user with an account not found message.
Example
aeternity:ak_2i2fioFMoEffBPeT3EBZEsxK1w579BuCgYE8WiMiADEQqUguU2
Payment
URI: aeternity:<receiver_address>/<amount>/<vendor_identifier>?callback=<url>
This URI scheme enables vendors to generate direct payment request URIs to user wallet.
Vendor generates a address for receiving payment.
Creates the URI substituting receiver_address with newly created address, amount with amount requested, vendor_identifier with a human readable identifier that can be used to correctly identify vendor and requested payment, and url with a callback URL that accepts a transaction id under txId query param.
Example
aeternity:ak_2gUJrd11cy65yqZ7mg1ULUG4kZ5r6v6vNqVtmA8HqUGKCf6kNf/125.12/myaeshop-ref9834?callback=https://myaeshop.com/verify
Sign and Broadcast
URI: aeternity:<transaction>/<network_id>/<label>?return=<txId/tx>&callback=<url>
This URI scheme enables users to sign (and broadcast) the transaction using deep linking enabled user wallet.
The user generates the transaction to be signed
Creates the URI substituting transaction and network_id with their respective value, label a human-readable text regarding the transaction and provides the query param return which indicates the return value type in the callback URL. It can contain only two possible values of string type:
txId: which indicates that the wallet needs to broadcast the transaction and return the transaction id
tx: means that the callback URL expects a signed transaction back.
And at last, substitutes the query param url which contains a callback URL that accepts a transaction id under txId query param and signed transaction output under tx query param.
Example
aeternity:tx_+E0MAaEBK4bq9XiZ/0QVdOa8Hs9V18v6dGZYIa8XXNYFpQh6yq6hAR8To7CL8AFABmKmi2nYdfeAPOxMCGR/btXYTHiXvVCjCoJOIAADgFcJyZ8=/ae_uat/sample_tx?return=tx&callback=https://myaeshop.com/verify
(optional) secret_format specifies the format of the encrypted data,
if not specified then a consumer should assume raw bytes
symmetric_alg specifies the algorithm used for symmetric encryption of the secret. This should be authenticated encryption and the only option is xsalsa20-poly1305 currently.
The ciphertext is the output of symmectric_alg , i.e. the output of libsodiums crypto_secretbox_easy, which is MAC + CIPHER with an upper-limit of 512 bytes.
cipher_params params used for successful decryption of the ciphertext
kdf specifies the methods used for key derivation.
kdf_params are the params used by the kdf
id is a Version 4 UUID
version currently 1. Defines the version of secret storage format.
Each JSON Object can also have an optional name field, which can be a human-readable name for the secret.
Authentication / Login (Proving you are the owner of an address/pubkey)
Authorization (Giving a user priviliges based on the coins / tokens he owns)
Prove Data Integrity
Voting
This technique has been around for a long time, but with the rise in crypto adoption it becomes more accessible to the average user.
DApp / Website / Wallet prepares a signing request containing a message (challenge) and other parameters (see below)
Signing request is sent to the Wallet / Signer containing the Private Key
User can select an identity (= keypair)
Message will be signed
User is either being redirected to the provided callback URL, or the signed message is displayed (eg. QR code)
The signature should be encoded in hex: Buffer.from(signature).toString("hex");
The response can contain an array of signatures and publicKeys, which allows a single message to be signed by multiple identities at once.
Serialization and Transport Layer are not part of this propaosal.
The Serialization is being discussed as part of AEX-7 Data Serialization
In order to prevent malicious DApps from sending a seeminly random message to be signed that actually contains data like a valid transaction, we should add a prefix to the message so this can't be exploited.
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign
{
"crypto": {
"secret_type": "ed25519-slip0010-masterkey",
"symmetric_alg": "xsalsa20-poly1305",
"ciphertext":"66891af8a59e83f0c600435a0681413644588f296240ab922ee357fa5ffa857f2709f8753b2b70d35625203adc6bf6e8",
"cipher_params": {
"nonce": "b085597ac8351330b469e9845fc9fb8cefa07e51cb7736a"
},
"kdf": "argon2id",
"kdf_params": {
"memlimit_kib": 65536,
"opslimit": 3,
"salt": "somesaltyness",
"parallelism": 1,
}
},
"id": "b1ff1e48-4e3e-4caf-818e-c8a7c3559d97",
"name": "main",
"version": 1
}AEX: 8
Title: Message Signing
Author: Andreas Gassmann (@AndreasGassmann), Alessandro De Carli (@dcale)
License: BSD-3-Clause
Discussions-To: TBD
Status: Draft
Type: Standards Track
Created: 2019-05-13
const signingRequest = {
message: string // Message to be signed
publicKey?: string // OPTIONAL: allows wallet to pre-select signing identity
ttl?: string // OPTIONAL: Blockheight or timestamp to prevent replay attacks
origin?: string // OPTIONAL: eg. aeternity.com
callbackURL?: string // OPTIONAL: eg. https://aeternity.com/?signedMessage=
}
const signingResponse = {
message: string // Message to be signed
signature: string[] // Signature of the message
publicKey: string[] // allows wallet to pre-select signing identity
ttl?: string // Blockheight or timestamp to prevent replay attacks
origin?: string // OPTIONAL: eg. aeternity.com
}// TODO: How do we fit TTL and hostname into the message?
import * as sodium from "libsodium-wrappers";
const signMessage = async (
message: string,
privateKey: Buffer
): Promise<string> => {
await sodium.ready;
const signature = sodium.crypto_sign_detached(
sodium.from_string(message),
privateKey
);
const hexSignature = Buffer.from(signature).toString("hex");
return hexSignature;
};import * as sodium from "libsodium-wrappers";
const verifyMessage = async (
message: string,
hexSignature: string,
publicKey: Buffer
): Promise<boolean> => {
await sodium.ready;
const signature = new Uint8Array(Buffer.from(hexSignature, "hex"));
const isValidSignature = sodium.crypto_sign_verify_detached(
signature,
message,
publicKey
);
return isValidSignature;
};// risk monitor path sick coconut cube ecology brief table adapt evil oven
const privateKey = Buffer.from(
"7f192bc4b5d6e828b6aeed3958f791f2d4d0f69e9b34a164df41f0f325c48ceb7d29631b2cc36eb4931a2ebe8b0a1bd95a29825ec21430b9e472146c851d2cfd",
"hex"
);
const publicKey = Buffer.from(
"7d29631b2cc36eb4931a2ebe8b0a1bd95a29825ec21430b9e472146c851d2cfd",
"hex"
);
const message = "This message will be signed.";
const signature = await signMessage(message, publicKey);
// e650110d48b42cf07f577b886f852b36945da4b175cb1629528b705799d1565799802c7fbb7d07685f8b22185db7ce5bd03f7e0e754b904b80b4fe4fda4f1802
const isValidSignature = await verifyMessage(message, signature, publicKey);
// trueThis document describes the process a proposal has to go through in order to be accepted into the AEX repository.
The purpose of AEXs is to provide high quality and accessible specifications to be used when developing applications on top of Aeternity. We intend AEXs to be the primary mechanisms for proposing new standards, for collecting community technical input on an issue, and for documenting the design decisions. Because the AEXs are maintained as text files in a versioned repository, their revision history is the historical record of the feature proposal.
There are three types of AEXs:
A Standards Track AEX describes any change or addition that affects the interoperability of applications using Aeternity. Standards Track AEXs consist of two parts: a design document and a reference implementation.
An Informational AEX describes a design issue, or provides general guidelines or information to the Aeternity community, but does not propose a new feature. Informational AEXs do not necessarily represent an Aeternity community consensus or recommendation, so users and implementors are free to ignore Informational AEXs or follow their advice.
A Meta AEX describes a process surrounding AEXs or proposes a change to (or an event in) a process. They often require community consensus; unlike Informational AEXs, they are more than recommendations, and users are typically not free to ignore them. Examples include procedures, guidelines, changes to the decision-making process, and changes to the tools or environment used in Aeternity development.
Parties involved in the process are you, the champion or AEX author and the AEX editors.
⚠️ Before you begin, vet your idea, this will save you time. Ask the Aeternity community first if an idea is original to avoid wasting time on something that will be be rejected based on prior research (searching the Internet does not always do the trick). It also helps to make sure the idea is applicable to the entire community and not just the author. Just because an idea sounds good to the author does not mean it will work for most people in most areas where Aeternity is used. Examples of appropriate public forums to gauge interest around your AEX include the and the issues section of this repository. In particular, the issues section of this repository is an excellent place to discuss your proposal with the community and start creating more formalised language around your AEX.
Your role as the champion is to write the AEX using the style and format described below, shepherd the discussions in the appropriate forums, and build community consensus around the idea.
Each status change is requested by the AEX author and reviewed by the AEX editors. Use a pull request to update the status.
AEXs should be changed from Draft or Review status, to Rejected status,
upon request by any person, if they have not made progress in three years. Such
a AEX may be changed to Draft status if the champion provides revisions that
meaningfully address public criticism of the proposal, or to Review status if
it meets the criteria required as described in the previous paragraph.
The transition from Last Call to either Final or Active happens
automatically if during the last call period, typically 14 days, no
substantiated objections are voiced or left unaddressed.
A Last Call which results in material changes or substantial unaddressed
technical complaints will cause the AEX to revert to Review.
It occasionally becomes necessary to transfer ownership of AEXs to a new champion. In general, we'd like to retain the original author as a co-author of the transferred AEX, but that's really up to the original author. A good reason to transfer ownership is because the original author no longer has the time or interest in updating it or following through with the AEX process, or has fallen off the face of the 'net (i.e. is unreachable or isn't responding to email). A bad reason to transfer ownership is because you don't agree with the direction of the AEX. We try to build consensus around an AEX, but if that's not possible, you can always submit a competing AEX.
If you are interested in assuming ownership of an AEX, send a message asking to take over, addressed to both the original author and the AEX editor. If the original author doesn't respond to email in a timely manner, the AEX editor will make a unilateral decision (it's not like such decisions can't be reversed :).
For each new AEX that comes in, an editor does the following:
Read the AEX to check if it is ready: sound and complete. The ideas must make technical sense, even if they don't seem likely to get to final status.
The title should accurately describe the content.
Check the AEX for language (spelling, grammar, sentence structure, etc.), markup (Github flavoured Markdown), code style
If the AEX isn't ready, the editor will send it back to the author for revision, with specific instructions.
Once the AEX is ready for the repository, the editor will:
Assign an AEX number (generally the PR number or, if preferred by the author, the issue # if there was discussion in the issues section of this repository about this AEX)
Merge the corresponding pull request
Send a message back to the AEX author with the next step.
In general, the editors:
Don't pass judgment on AEXs.
Are intended to fulfil administrative and editorial responsibilities.
Monitor AEX changes, and update AEX headers as appropriate.
Sascha Hanse (@knarz)
Philipp Piwowarsky (@thepiwo)
Successful AEXs should contain most of the following sections:
Preamble: RFC 822 style headers containing metadata about the AEX
Simple Summary: a simplified and layman-accessible explanation of the AEX
Abstract: a short (~200 word) description of the (technical) issue being addressed
Motivation: clearly explaining why the existing standards are inadequate to address the problem that the AEX solves
A AEX template can be found under AEX-X.
Each AEX must begin with an RFC 822 style header preamble. The headers must appear in the following order. Headers marked with "*" are optional and are described below. All other headers are required.
Header fields permitting lists must separate elements with commas.
The Discussions-To field should point to a discussion of the proposed
standard. Most of the initial work should happen in small, focused working
groups and only posted once it is in a reasonably stable state.
Please try to refrain from having WIP documents or very early drafts in this
repository as they tend orphan.
Each proposal must start in the Draft state and will move through the
phases in accordance to the criteria defined in this document.
If a proposal depends on another AEX, the Requires field should indicate
so.
Superseded-By, Replaces, Updates and Updated-By fields are required
since standards are in constant flux. Editors and authors should make sure
that old AEXs are update where appropriate.
The following recommended licenses should be used both for code and the specification:
Each submitted proposal needs to list at least one license. If code is
included, a separate license for it can be specified. The licenses should
be included in the header via the License and License-Code fields. If
those fields do not cover the requirements, include a copyright section in
your proposal.
Or with multiple licenses:
Other acceptable licenses:
ISC:
Apache-2.0:
MIT:
AGPL-3.0+:
Many parts of this document are inspired by or adapted with only minor modifications from many of the already existing standardisation processes.
AEX: 1
Title: AEX process
Author: Sascha Hanse <[email protected]>
License: CC0-1.0
Discussions-To: https://forum.aeternity.com/t/aeternity-best-current-practices-aexpansions/2461
Status: Active
Type: Meta
Created: 2019-02-01Final
proposal is considered to be completed
no unaddressed substantiated objections are left
crucial or cosmetic updates only
Active
proposal can be in constant flux
no unaddressed substantiated objections are left
crucial or cosmetic updates only
Updated
signalling that an updating standard might have to be considered
an updating proposal entered the Final state
crucial or cosmetic updates only
Superseded
standard should no longer be used
community consensus around a superseding proposal
none
Rejected
editors deemed this proposal to be unworkable
proposal was rejected before, authors were unwilling to respond to feedback, too unfocused, too broad, duplication of effort, being technically unsound, not providing proper motivation or addressing backwards compatibility
none
Withdrawn
signalling that the proposal is no longer relevant
withdrawn by original authors
none
Specification: should describe the syntax and semantics of any new feature and should be detailed enough to allow competing, interoperable implementations
Rationale: fleshes out the specification by describing what motivated the design and why particular design decisions were made. It should describe alternate designs that were considered and related work, e.g. how the feature is supported in other languages. The rationale may also provide evidence of consensus within the community, and should discuss important objections or concerns raised during discussion.
Backwards Compatibility: if a proposal is supposed to supersede another proposal without providing backwards compatibility then it should contain a section describing these incompatibilities and their severity. Further, it should explain how the author proposes to deal with these incompatibilities.
Test Cases: links to test cases if applicable
Implementations: implementation or link to implementations, if applicable
Draft
rapid iteration in small/focused working group
none
major
Review
review and feedback from editor and other interested parties
initial specification, editor assigned, template filled, authors and editor consider document to be in a state where the general public can give constructive feedback
no major TODOs left
Last call (yyyy-mm-dd to yyyy-mm-dd)
indicate that authors and editors consider document to be complete; solicit last round of feedback
at least one working implementation (if applicable), authors and editors deem the proposal complete
minor
Data needs to be transferred between different apps and devices. Those apps may or may not be located on the same device. The transport layer used by those apps is unknown and will not be part of this AEX. However, since many of the transport layers can only transfer a limited amount of data (eg. QR codes, Bluetooth, NFC, URLs), the data should always be encoded in a standardized and space efficient way.
The data serialization protocol in this AEX aims to provide a solution with the following goals in mind:
Easy understand and implement
Space efficient
Make sure to not transmit redundant information, send only what is necessary
Allow airgapped Apps such as AirGap Vault to properly display all necessary information when signing a transaction
Basic description of RLP.
We use RLP for the following reasons:
RLP is being used throughout the crypto space, with Aeternity adapting RLP as well after Ethereum
It is able to serialize and deserialize data in a predictable way
Easy to understand and implement
Encoded Data as Binary, size-efficient
The resulting string will be encoded with base58 because it is URL-safe, which is important for some transport layers.
The different transport layers are not part of this AEX, but we should keep the following transport layers in mind:
QR Codes
Deeplinking / URLs
Clipboard copy/paste
Push Notifications
WebSockets
Webhooks
Bluetooth
NFC
This example would result in the following, base58 encoded, string:
The payload can be another RLP array, depending on the MessageType.
While the transport layer is not part of this AEX, we have to think about how we can split up a request if it doesn't fit into one "packet", depending on the transport layer.
The protocol should offer a "chunking" functionality where the user can pass a maximum size per chunk.
This standard allows for new message types to be added by every application. But ideally, we should provide a set of commonly used types so they can be re-used between different apps. Some of the default types could be:
MetadataRequest (Information about the app, like name, version, url)
MetadataResponse
AccountShareRequest
AccountShareResponse
TransactionSignRequest
TransactionSignResponse
MessageSignRequest
MessageSignResponse
The numbers 0 to 999 of the message type are reserved for official/default messages, other numbers can be used for user defined types (for which they have to provide their own serialization / deserialization methods.)
There are 2 different kinds of versions:
Protocol Version: Defines the version of the sync protocol. If this does not match, then the request most likely cannot be processed.
Message Version: This version is specific to a currency and message type. This allows us to add additional functionality (new message types), without breaking existing (unchanged) message types.
Looking at the MessageVersion, MessageType and ProtcolIdentifier together, we can determine if we can handle that specific request or not.
We use RLP over more commonly used standards like JSON or XML because it's more space efficient.
We encode the resulting string with base58 so it will be compatible with URLs.
In some situations it makes sense to include context to a request, which is then also sent back with the response.
This could for example be used to attach IDs to requests to track their state.
The context should be used with caution because:
It will increase the size of both the request and response QR.
You cannot be sure that the response is received on the same device that created the original request. (eg. prepare in chrome extension > signing on offline device > broadcasting with mobile phone)
This context will be added as an optional parameter to all the default message types.
There is currently one working implementation inside the airgap-coin-lib, which uses this scheme to facilitate communication between online and offline devices.
+---------------+ +---------------+ +---------------+
| Draft |------------>| Review |<----------->| Last call |
+---------------+ +---------------+ +---------------+
^ | | ^ | | | |
| v | | | | | |
| +---------------+ | | | | | |
| | Withdrawn |<----+-----|-----------------------+ | | |
| +---------------+ | | | |
v | | | |
+---------------+ | | | |
| Rejected |<--------------------+---------------------------+ | |
+---------------+ -----------------+ |
| |
v v
+---------------+ +---------------+ +---------------+
| Updated |<----------->| Final | | Active |
+---------------+ +---------------+ +---------------+
| |
+----------+ +----------+
| |
v v
+---------------+
| Superseded |
+---------------+AEX: <to be assigned by editors>
Title: <AEX title>
Author: <a list of the author's or authors' name(s) and/or username(s), or name(s) and email(s), e.g. (use with the parentheses or triangular brackets): FirstName LastName (@GitHubUsername), FirstName LastName <[email protected]>, FirstName (@GitHubUsername) and GitHubUsername (@GitHubUsername)>
License: <license names, abbreviated>
License-Code (*optional): <license names, abbreviated>
Discussions-To: <URL>
Status: <Draft | Active Review | Last Call (yyyy-mm-dd to yyyy-mm-dd) | Final | Updated | Superseded | Rejected | Withdrawn>
Type: <Standards Track | Informational | Meta>
Created: <date created on, in ISO 8601 (yyyy-mm-dd) format>
Requires (*optional): <AEX number(s)>
Replaces (*optional): <AEX number(s)>
Superseded-By (*optional): <AEX number(s)>
Updates (*optional): <AEX number(s)>
Updated-By (*optional): <AEX number(s)>License: CC0-1.0
License-Code: Apache-2.0License: CC0-1.0
BSD-3-Clause
License-Code: Apache-2.0AEX: 7
Title: Data Serialization
Author: Andreas Gassmann (@AndreasGassmann), Alessandro De Carli (@dcale)
License: BSD-3-Clause
Discussions-To: TBD
Status: Withdrawn
Type: Standards Track
Created: 2019-05-13[
1, // ProtocolVersion
1, // SerializationType: Full or Chunked
[
// Array of Messages
[
1, // MessageVersion
2, // MessageType
"ae", // Protocol
"payload" // Payload depending on the MessageType
],
[
1, // MessageVersion
3, // MessageType
"ae", // Protocol
"payload" // Payload depending on the MessageType
]
]
];2hDLW1FiwvQs5ofPUgi5CgAJKDWiNncCoETXf7DGdkkDmrhN3zfunction serialize(rlp, maxChunkSizeInByte): string[] {}
function deserialize(chunks): any[] {}
const chunks = serialize(rlp, 1024);
const deserializedRlp = deserialize(chunks);[
1, // Protocol Version
1, // SerializationType: Full or Chunked
[
1, // Current Page
4, // Number of pages
"payload of current chunk as string"
]
];Currently, if a wallet wants to talk to another wallet then each wallet has to implement its own set of messages that the wallet on the other end will understand and this gets limited to communication between the wallets of a single vendor. Although, this single vendor implementation eases the transaction and message signing part when having multiple devices but it binds users to a single wallet and single experience.
By standardization of the set of messages that should be used for inter-wallet communication this AEX will to open the communication between different types of wallet (mobile, web, extensions) from different vendors.
SDK connects with a wallet
Wallet shares the account address with the SDK
When the SDK needs the wallet to sign the transaction it asks the wallet
Wallet signs and returns the signed transaction.
SDK connects with a wallet
Wallet returns its current address and other account addresses it may help the SDK in getting signature for.
When the SDK needs the wallet to sign the transaction it asks the wallet
Wallet performs steps in the below order
checks if it can sign the transaction.
else it checks if it holds a direct link to the wallet that can sign the transaction and forwards the request to it.
and at last it checks if there is an indirect link to the signing address i.e. via another connected wallet and forwards the request to it.
Once the transaction is signed, it is returned back to the requesting SDK for further processing.
error: Used to communicate any error occurred. Error code 1 to 9 are reserved and reused here from AEX-2.
ping/pong: general ping/pong messages to check liveness. Implementation of this method is not mandatory for wallets communicating over a transport layer that have native support for liveness check.
Parameters
Object
id - A unique identifier, must conform to the
Returns
Object
id - same id as in the corresponding request
data - Value will always be equal to pong
wallet.channel.initiate: Initiate request to open a communication channel. The request contains an identifier that will be used by either party to recognize and process incoming messages. The id should match the subsequent incoming or outgoing messages between the two wallets. The generated identifier must be unique and must conform to the UUID v4 standards.
Parameters
Object
id - A unique identifier, must conform to the
name - human readable wallet name
version - protocol version. Currently defaults to 1.
Returns
Object
id - same id as in the corresponding request
name - human readable wallet name
wallet.channel.close: Close the channel
Parameters
Object
id - A unique identifier, must conform to the
Returns
Object
wallet.channel.close_incoming: Close channel only for signing requests i.e. closing wallet can still ask the wallet on another end to sign or forward the transactions.
Parameters
Object
id - A unique identifier, must conform to the
Returns
wallet.get.address: Ask the connected wallet for its address
Parameters
Object
id - A unique identifier, must conform to the UUID v4 standards
wallet.update.address: Issued by connected wallets for returning addresses incl. from the connected wallets. The connected field is optional and contains the list of address of the wallets it is further connected to.
Parameters
Object
id - A unique identifier, must conform to the
address - Object containing two objects named current and connected.
current
wallet.sign.tx: Ask the connected wallet to sign the transaction.
Parameters
Object
id - A unique identifier, must conform to the UUID v4 standards
tx - raw unsigned transaction
Returns
Object
id - same id as in the corresponding request
tx - signed tx returned by the wallet
wallet.request.broadcast: Ask connected wallet to broadcast the transaction. The connected wallet can try to broadcast the transaction itself or forward it to the SDK. If the wallet is unable to broadcast it returns the error with code 3.
Parameters
Object
id - A unique identifier, must conform to the
[WIP]
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.
This document describes the technical specification and methods that the wallet provider (ex. Base æpp, Wællet, MetaMask) MUST use to interact with Aeternity based applications (hereinafter referred to as 'aepp' or 'aepps').
Currently, there exists no standard way for wallets and aepps to communicate and everything developed on the SDK side is developed only keeping the Base æpp in mind and rest of the wallet providers need to follow the same path. By defining the standard way of communication between aepps and Wallet we will not only reduce the dependency that other wallet providers have on the Base æpp as well as we'll have a clear standard method of communication on the SDK side that can be further extended upon whenever required.
Wherever possible, this document tries to closely follow the who.what.how naming convention for JSON-RPC methods.
JSON-RPC 2.0 response error object that is used to communicate any error occurred while processing the request.
Types of errors
Aepp Invokable Methods
This section defines the methods that the aepps MUST invoke to either get information from the wallet or request the wallet to perform an action.
connection.open: connection request sent by the aepp to the wallet.
Parameters
Object
version: protocol version. Currently defaults to 1.
Returns
Wallet Invokable Notifications
connection.announcePresence: MAY be used by the wallets to announce their presence wherever required (e.g. postMessage API). This message SHOULD NOT be used by the wallets where a 1-to-1 connection with the aepp is already established but instead wait for the aepp to initiate the connection using connection.open message.
Note: This is a helper message for the aepp to identify the presence of a wallet.
Parameters
Object
networkId
Invokable by Wallet and Aepp
connection.close: SHOULD be used by Aepp or Wallet for informing the other party that it is disconnecting and further requests will either be denied or not acknowledged.
Aepp: https://github.com/aeternity/aepp-sdk-js/tree/feature/aex-2/examples/browser/vuejs/connect-two-ae
Extension Wallet: https://github.com/aeternity/aepp-sdk-js/tree/feature/aex-2/examples/browser/extension
Transaction Encoding and Serialization https://github.com/aeternity/protocol/blob/master/node/api/api_encoding.md https://github.com/aeternity/protocol/blob/master/serializations.md
JSON-RPC 2.0 Specification https://www.jsonrpc.org/specification
AEX: 5
Title: Inter-Wallet Communication
Author: Shubhendu Shekhar <@shekhar-shubhendu>
License: BSD-3-Clause
Discussions-To: https://forum.aeternity.com/t/aex-5-inter-wallet-communication/3371
Status: Withdrawn
Type: Standards Track
Created: 2019-04-29AEX: 2
Title: Third-party Wallet Provider Support
Author: Shubhendu Shekhar (@shekhar-shubhendu), Andrea Giacobino (@noandrea), Enrico Icardi (@ricricucit) & Nazar Duchak (@nduchak)
License: BSD-3-Clause
Discussions-To: https://forum.aeternity.com/t/aex-2-js-sdk-interfaces-for-wallets/2715
License-Code: Apache-2.0
Status: Withdrawn
Type: Standards Track
Created: 2019-03-04id - same id as in the corresponding request
status - Value will always be equal to ack
Objectid - same id as in the corresponding request
status - Value will always be equal to ack
connected(optional) - Object containing multiple connected accounts.
Example Account Format
tx - signed transaction to be broadcasted
verify - Boolean. Perform verification before broadcasting or not.
Returns
Object
id - same id as in the corresponding request
tx_id - transaction id of the broadcasted transaction
{
"current": {
"ak_2iBPH7HUz3cSDVEUWiHg76MZJ6tZooVNBmmxcgVK6VV8KAE688": {
"name": "this property is optional"
}
}
}networkId: Network id used by the wallet
Returns errors
Unsupported protocol version
Unsupported network
address.subscribe: request the wallet to get or subscribe to address changes. This method MUST return only if the aepp is successfully subscribed else it MUST throw the appropriate error.
Parameters
Object
type: payload indicating the type of update i.e. subscription or un-subscription. This supports two options:
subscribe (datatype: string): MUST be used by the aepp to request a subscription.
unsubscribe (datatype: string): MUST be used by the aepp to request an un-subscription.
value: indicating the subscription/un-subscription that needs to be handled Currently supported options:
current (datatype: string): MUST be used to for current user account
connected (datatype: string): MUST be used for connected wallet accounts.
Returns
Object
subscription: Array of string indicating the current subscriptions. Example: ['current', 'connected']
address: This is a nested JSON Object containing the subscribed addresses in the below defined format. Same as address.update notification, please refer for more details.
This field MUST be included in the response when the wallet receives a subscription request i.e. when type == 'subscribe'.
This field is OPTIONAL in the response when the wallet receives an un-subscription request i.e. when type == 'unsubscribe'.
Returns errors
Rejected by user
Account Format:
transaction.sign: request wallet for signature
Parameters
Object
tx: unsigned encoded transaction (Datatype: String).
return: Boolean (DEFAULT: false).
true: the aepp is indicating that it is expecting a signed transaction back in return and do not want the wallet to perform a transaction broadcast.
false: the aepp wants the wallet to sign and broadcast the transaction and return only the transaction id.
Returns
Object
result: this can be either of two values depending on the request (as mentioned in the above description of return):
signed transaction: signed encoded transaction (Datatype: String).
transaction hash: encoded transaction hash (Datatype: String).
Returns errors
Invalid transaction
Rejected by user
Broadcast failed
networkId.update: MUST be used by Wallet for informing Aepp about the change of network id.
Parameters
Object
networkId: Updated network id.
address.update: MUST be used by the wallet to notify subscribed aepps about the address change.
Parameters
Object
address: JSON Object. This MAY contain 1 or more keys but only of the below types. The values in the object completely depend on the aepp's subscription.
Subscription Type:
current: Object containing only a single account currently in use by the wallet. The account MAY also have an embedded name key which is a human-readable name for the account.
connected: Object containing multiple connected accounts. The accounts MAY also have an embedded name key which is a human-readable name for the account.
Account Format:
Code
Message
Meaning
1
Invalid transaction
MUST be returned whenever the transaction validity check fails and the node returns a similar error
2
Broadcast failed
MUST be returned by the aepp or wallet if it has been unable to broadcast the transaction.
3
Rejected by user
MUST be returned by the wallet when user denies the action request by aepp.
4
Unsupported protocol version
MUST be returned by wallet when it does not support protocol version the aepp wants to connect through.
Paste here the link to the topic on forum.aeternity.com
Paste here the url where the content of the AEX can be found
If you would like to submit an AEX and it has already been written as a draft (see the for an example), please submit it as a .
If you are considering a proposal but would like to get some feedback on the idea before submitting a draft, then continue opening an issue as a thread for discussion. Note that the more clearly and completely you state your idea the higher the quality of the feedback you are likely to receive.
Keep in mind the following guidelines from :
Each AEX must have a champion - someone who writes the AEX using the style and format described below, shepherds the discussions in the appropriate forums, and attempts to build community consensus around the idea. The AEX champion (a.k.a. Author) should first attempt to ascertain whether the idea is AEX- able. Posting to the the Protocol Discussion forum or opening an issue is the best way to go about this.
Before you begin, vet your idea, this will save you time. Ask the Aeternity community first if an idea is original to avoid wasting time on something that will be be rejected based on prior research (searching the Internet does not always do the trick). It also helps to make sure the idea is applicable to the entire community and not just the author. Just because an idea sounds good to the author does not mean it will work for most people in most areas where Aeternity is used. Examples of appropriate public forums to gauge interest around your AEX include the and the issues section of this repository. In particular, the issues section of this repository is an excellent place to discuss your proposal with the community and start creating more formalised language around your AEX.
Once the champion has asked the Aeternity community as to whether an idea has any chance of acceptance, a draft AEX should be presented as a pull request. This gives the author a chance to flesh out the draft AEX to make properly formatted, of high quality, and to address initial concerns about the proposal.
{
"<subscription_type>": {
"<account_public_key>": {
"name": "<optional_human_readable_account_name>"
}
}
} {
"<subscription_type>": {
"<account_public_key>": {
"name": "<optional_human_readable_account_name>"
}
}
}A short (~200 word) description of the technical issue being addressed.
The following describes standard functions a token contract and contract working with specified token can implement to prevent accidentally sends of tokens to contracts and make token transactions behave like ether transactions.
Moreover, where designing the tokens we looked carfully at compliance work and wanted to make it easier for integrating compliance workflow. With this respect token transactions contains data field to be used freely to pass additional information (eg: reference) from a holder or an operator.
The goal is to make this specification useful with Native Tokens.
The token contract MUST implement the above interface. The implementation MUST follow the specifications described below.
An operator is an address which is allowed to send and burn tokens on behalf of some holder.
NOTE: A holder MAY have multiple operators at the same time.
A default operator is an implicitly authorized operator for all holders. The rules below apply to default operators:
The token contract MUST define default operators at creation time. The default operators set MAY be empty.
The token contract MUST NOT add or remove default operators after contract instantiation.
AuthorizedOperator events MUST NOT be emitted when defining default operators.
A holder MUST be allowed to revoke a default operator (unless the holder is the default operator).
The following rules apply to any operator:
An address MUST always be an operator for itself. Hence an address MUST NOT ever be revoked as its own operator.
NOTE: A holder MAY authorize an already authorized operator. An AuthorizedOperator MUST be emitted each time.
NOTE: A holder MAY revoke an already revoked operator. A RevokedOperator MUST be emitted each time.
meta() : meta_infReturns information about token as a meta_inf record.
total_supply() : intReturns: Total supply of tokens currently in circulation.
NOTE: MUST be equal to the sum of the balances of all addresses—as returned by the balance_of function.
NOTE: MUST be equal to the sum of all the minted tokens as defined in all the Minted events minus the sum of all the burned tokens as defined in all the Burned events.
balance_of(holder: address) : option(int)Get the balance of the holder account. If the account is not registered (didn't use any token) then it MUST return None. Oherwise it MUST return non negative integer.
default_operators() : list(address)List of addresses of all the default operators.
is_operator_for(operator: address, holder: address) : boolMUST return true if the operator address is an operator for the holder. Otherwise MUST return false.
authorize_operator(operator: address)Set a third party operator address as an operator of Call.caller to transfer and burn tokens on its behalf.
NOTE: The holder (Call.caller) is always an operator for itself. This right SHALL NOT be revoked.
revoke_operator(operator: address)Remove the right of the operator address to be an operator for Call.caller and to send and burn tokens on its behalf.
NOTE: The holder (Call.caller) is always an operator for itself. This right SHALL NOT be revoked.
transfer(to: address, amount: int, data: string)Send the amount of tokens from the Call.caller (holder) to the to address. data an information provided by the holder.
In the Transfer event, the operator and the holder MUST both be Call.caller.
op_transfer(from: address, to: address, amount: int, data: string, op_data: string)Send the amount of tokens on behalf of the from address to the to address.
Reminder: If the operator address (Call.caller) is not an authorized operator of the from address, then the send process MUST abort.
burn(amount: int, data: string)Burn the amount of tokens from the Call.caller address.
In the Burned event, the operator and the holder MUST both be Call.caller.
op_burn(from: address, amount: int, data: string, op_data: string)Burn the amount of tokens on behalf of the from address.
Reminder: If the operator address (Call.caller) is not an authorized operator of the from address, then the burn process MUST abort.
AuthorizedOperator eventIndicates the authorization of operator as an _operator for holder.
The token contract MUST emit an AuthorizedOperator event with the correct values when a holder authorizes an address as its operator (even if it's already authorized or it's defined as a default oprator).
RevokeOperator eventIndicates the revokation of operator as an _operator for holder.
The token contract MUST emit an RevokeOperator event with the correct values when a holder revokes an address as its operator (even if it's already revoked or it's defined as a default oprator).
tokensToTransfer hookThe tokensToSend hook notifies of any request to decrement the balance (transfer and burn) for a given holder. Any address (regular or contract) wishing to be notified of token debits from their address MAY register for receiving this hooks
Interface:
Notify a request to transfer or burn (if to is 0x0) an amount tokens from the from address to the to address by the operator address. The holder MAY block a send or burn process by aborting. (I.e., reject the withdrawal of tokens from its account.)
to MUST be 0x0 for a burn.
amount MUST be the number of tokens the holder sent or burned. MUST be non negative integer (0 is possible).
data MUST contain the extra information (if any) provided to the send or the burn process.
NOTE: A regular address MAY register a different address—the address of a contract—implementing the interface on its behalf. A contract MAY register either its address or the address of another contract but said address MUST implement the interface on its behalf.
TODO
tokensReceived hookThe tokensToSend hook notifies of any request to increment the balance (transfer and mint) for a given holder. Any address (regular or contract) wishing to be notified of token credits from their address MAY register for receiving this hooks
Interface:
Notify a send or mint (if from is 0x0) of amount tokens from the from address to the to address by the operator address.
TOOD
This token provides a foundation for compliance tokens. Because of the need for refence field (data), and introduction of operators (often required in the industry and interoperability with other smart contracts), the interface is not backward compatible with proposed AEX-9 simple token standard.
TODO
register token interfaces Probably we don't need it because Sophia is strong static typed language.
AEX: 11
Title: Fungible Token Standard
Author: Robert Zaremba (@robert-zaremba)
License: BSD-3-Clause
Discussions-To: <URL>
Status: Draft
Type: Interface
Created: 2019-09-19A holder MUST be allowed to re-authorize a previously revoked default operator.
op_data MUST contain the extra information provided by the address which triggered the decrease of the balance (if any).
contract AEX11Token {
record meta_info = { name : string
symbol : string
decimals : int}
entrypoint meta(): meta_info
entrypoint total_supply() external : int
entrypoint balance_of(holder: address) : option(int)
external default_operators() : list(address)
external is_operator_for(operator: address, holder: address) : bool
stateful external authorize_operator(operator: address)
stateful external revoke_operator(operator: address)
entrypoint stateful transfer(to: address, amount: int, data: string)
entrypoint stateful op_transfer(from: address, to: address, amount: int, data: string, op_data: string)
entrypoint stateful burn(amount: int, data: string)
entrypoint stateful op_burn(from: address, amount: int, data: string, op_data: string)
datetype event =
// operator, from, to, amount ,data, op_data
Transfer(indexed address, // operator
indexed address, // from
address, // to
int, // amount
string, // data
string) // op_data
| Mint(indexed address, // operator
indexed address, // to
int, // amount
string, // data
string) // op_data
| Burned(indexed address, // operator
indexed address, // from
int, // amount
string, // data
string) // op_data
| AuthorizedOperator(indexed address, // operator
indexed address) // holder
| RevokedOperator(indexed address, // oprator
indexed address) // holder
}stateful entrypoint tokensToTransfer(
operator: address,
from: address,
to: address,
amount: int,
data: string,
op_data: string
)function tokensReceived(
operator: address,
from: address,
to: address,
amount: int,
data: string,
op_data: string
)This document aims to outline a standard and define how fungible tokens should be created and used on aeternity blockchain.
The following standard allows for the implementation of a standard API for tokens within smart contracts. This standard provides basic functionality to transfer tokens, as well as allow tokens to be approved so they can be spent by another on-chain third party.
This standard will allow decentralized applications and wallets to handle fungible tokens in a standardized way. A standard interface allows any tokens to be re-used by other applications, e.g. from wallets to decentralized exchanges.
The provided specification is following the ERC20 standard introduced in Ethereum for fungible tokens. This standard is proven to be working, it will help with interoperability and easier developer onboarding as the main differences will be Sophia syntax related. Another goal with following the ERC20 standard, is to learn from its mistakes and not repeat them.
The newly proposed standard should be easy to use but extendable with more functionality, both first and third party as shown by splitting allowance, minting, burning and swapping into optional extensions.
This function returns a hardcoded list of all implemented extensions on the deployed contract.
This function returns meta information associated with the token contract.
This function returns the total token supply.
This function returns the full balance state for static calls, e.g. by a blockchain explorer.
This function returns the account balance of another account with address owner, if the account exists. If the owner address is unknown to the contract None will be returned. Using option type as a return value allows us to determine if the account has balance of 0, more than 0, or the account has never had balance and is still unknown to the contract.
This function allows transfer of value amount of tokens to to_account address and MUST fire the Transfer event. The function SHOULD abort if the Call.caller's account balance does not have enough tokens to spend.
Note: Transfers of 0 values MUST be treated as normal transfers and fire the Transfer event.
This event MUST be triggered and emitted when tokens are transferred, including zero value transfers.
The transfer event arguments should be as follows: (from_account, to_account, value)
Mint (optional if token creation happens) - MUST trigger when tokens are minted and thus are newly available in the given token contract, this also applies to tokens created using the init method on contract creation.
The mint event arguments should be as follows: (account, value)
This section covers the extendability of the basic token - e.g. mintable, burnable and allowances.
When a token contract implements an extension its name should be included in the aex9_extensions array, in order for third party software or contracts to know the interface.
Any extensions should be implementable without permission. Developers of extensions MUST choose a name for aex9_extensions that is not yet used. Developers CAN make a pull request to the reference implementation for general purpose extensions and maintainers choose to eventually include them.
This function mints value new tokens to account. The function SHOULD abort if Call.caller is not the owner of the contract state.owner.
Mint - MUST trigger when tokens are minted and thus are newly available in the given token contract, this also applies to tokens created using the init method on contract creation.
The mint event arguments should be as follows: (account, value)
This function burns value of tokens from Call.caller.
Burn - MUST trigger when tokens are burned and thus are no longer available in the given token contact.
The burn event arguments should be as follows: (account, value)
Allows for_account to withdraw from your account multiple times, up to the value amount. If this function is called again it overwrites the current allowance with value.
Note: To prevent attack vectors (like the ones possible in ERC20) clients SHOULD make sure to create user interfaces in such a way that they set the allowance first to 0 before setting it to another value for the same spender. THOUGH the contract itself shouldn't enforce it, to allow backwards compatibility with contracts deployed before.
Transfers value amount of tokens from address from_account to address to_account, and MUST fire the Transfer event.
The transfer_allowance method is used for a withdraw workflow, allowing contracts to transfer tokens on your behalf. This can be used for example to allow a contract to transfer tokens on your behalf and/or to charge fees in sub-currencies. The function SHOULD abort unless the from_account account has deliberately authorized the sender of the message via some mechanism.
Note: Transfers of 0 values MUST be treated as normal transfers and fire the Transfer event.
This function returns the amount which for_account is still allowed to withdraw from from_account, where record allowance_accounts = { from_account: address, for_account: address }. If no allowance for this combination of accounts exists, None is returned.
This function returns all allowances stored in state.allowances record.
This function will look up the allowances and return the allowed spendable amount from from_account for the transaction sender Call.caller. If there is no such allowance present result is None, otherwise Some(int) is returned with the allowance amount.
This function allows the Call.caller to change the allowed spendable value for for_account with value_change. This adds the value_change to the current allowance value. If used for increasing allowance amount a positive value should be passed, if the desired outcome is to lower the value of the allowed spendable value a negative value_change should be passed.
Resets the allowance given for_account to zero.
Allowance - MUST trigger on any successful allowance creation or change.
The approval event arguments should be as follows: (from_account, for_account, value)
This function burns the whole balance of the Call.caller and stores the same amount in the swapped map.
This function returns the amount of tokens that were burned trough swap for the provided account.
This function returns all swapped tokens that are stored in contract state.
Swap - MUST trigger when tokens are swapped and thus are no longer available in the given token contact.
The swap event arguments should be as follows: (account, value)
There are several implementations available at the moment, but they lack a thing or two (that is why this standard is being proposed).
Example implementations:
AEX: 9
Title: Fungible Token Standard
Author: @mradkov, @thepiwo
License: ISC
Discussions-To: https://forum.aeternity.com/t/aex-9-fungible-token/3565
Status: Review
Type: Standards Track
Created: 2019-05-11meta_info
meta_info
total_supply
int
balances
map(address, int)
owner
address
balance
option(int)
to_account
address
value
int
from_account
address
to_account
address
value
int
account
address
value
int
account
address
value
int
account
address
value
int
value
int
account
address
value
int
for_account
address
value
int
from_account
address
to_account
address
value
int
allowance_accounts
allowance_accounts
allowances
map(allowance_accounts, int)
from_account
address
option(int)
for_account
address
value_change
int
unit
for_account
address
unit
from_account
address
for_account
address
value
int
value
int
()
unit
account
address
int
int
swapped
map(address, int)
account
address
value
int
contract interface FungibleTokenInterface =
record meta_info =
{ name : string
, symbol : string
, decimals : int }
datatype event =
Transfer(address, address, int)
entrypoint aex9_extensions : () => list(string)
entrypoint meta_info : () => meta_info
entrypoint total_supply : () => int
entrypoint owner : () => address
entrypoint balances : () => map(address, int)
entrypoint balance : (address) => option(int)
entrypoint transfer : (address, int) => unitentrypoint aex9_extensions() : list(string)entrypoint meta_info() : meta_inforecord meta_info =
{ name : string
, symbol : string
, decimals : int }entrypoint total_supply() : intentrypoint balances() : map(address, int)entrypoint balance(owner: address) : option(int)stateful entrypoint transfer(to_account: address, value: int) : unitTransfer(address, address, int)Mint(address, int)stateful entrypoint mint(account: address, value: int) : unitMint(address, int)stateful entrypoint burn(value: int) : unitBurn(address, int)stateful entrypoint create_allowance(for_account: address, value: int) : unitstateful entrypoint transfer_allowance(from_account: address, to_account: address, value: int)entrypoint allowance(allowance_accounts : allowance_accounts) : option(int)record allowance_accounts =
{ from_account: address
, for_account: address }entrypoint allowances() : allowancesentrypoint allowance_for_caller(from_account: address) : option(int)stateful entrypoint change_allowance(for_account: address, value_change: int)stateful entrypoint reset_allowance(for_account: address)Allowance(address, address, int)stateful entrypoint swap() : unitstateful entrypoint check_swap(account: address) : intstateful entrypoint swapped() : map(address, int)Swap(address, int)A standard implementation of non-fungible tokens for the æternity ecosystem. Initially, the design goal of the primary interface was to be as compatible with ERC-721 as possible, so that anyone who can work with ERC-721 can work with this interface. However, specifically when it comes to dealing with metadata a decision was taken to provide contract developers as much flexibility as possible.
Core differences to the well-known ERC-721 standard:
Unsafe transactions are not supported. Therefore, all transactions are ought to be safe
Usage of zero-address for minting or burning is avoided. Thus, explicit events for minting and burning have been defined
Token transfers do not require the (owner) address to be passed. Only the recipient address needs to be provided to the entrypoint
High flexibility when it comes to dealing with metadata is provided
The following standard describes standard interfaces for non-fungible tokens. The proposal contains a primary interface and secondary interfaces (extensions) for optional functionality that not everyone might need.
Returns a hardcoded list of all implemented extensions on the deployed contract.
Returns meta information associated with the contract.
Note:
The base_url is optional and is only intended to be used if the metadata_type is URL. As known from ERC-721 this can be used to resolve metadata for a specific NFT which can be fetched from an URL based on the token id
The metadata_type MUST be defined on contract level and MUST NOT be mixed across various NFTs in a contract
Returns metadata associated with an NFT.
Note:
The metadata can be set in the constructor, as well as by implementing extensions like e.g. mintable
The metadata to use depends on the metadata_type defined on contract level and provides certain flexibility:
for URL
Returns the total amount of NFTs in circulation.
Returns the number of NFTs owned by the account with address owner in the contract. If the owner address is unknown to the contract, None will be returned. Using option type as a return value allows us to determine if the account owns 0, more than 0, or the account has never owned a balance and is still unknown to the contract.
Returns the owner's address for the provided token_id if the NFT is minted. If the NFT isn't minted, None will be returned.
Transfers NFT with ID token_id from the current owner to the to address. Will invoke IAEX141Receiver.on_aex141_received if the to address belongs to a contract. If provided, data will be submitted with the invocation of IAEX141Receiver.on_aex141_received. Emits the Transfer event.
Note: For security reasons reentrancy is not possible. Therefore contracts cannot use this entrypoint to transfer NFTs to itself. Use transfer_to_contract instead to cover this scenario.
Throws if:
Call.caller is NOT the current owner or NOT approved to transfer on behalf of the owner;
token_id is NOT a valid token;
the invocation of IAEX141Receiver.on_aex141_received fails.
Transfers NFT with ID token_id from the current owner to the contract calling this entrypoint. As reentrancy is not possible for security reasons, this entrypoint MUST be used if a contract (e.g. NFT marketplace) wants to transfer the NFT to itself on behalf of the owner. Emits the Transfer event.
Throws if:
Call.caller is NOT a contract or NOT approved to transfer on behalf of the owner;
token_id is NOT a valid token;
Sets the approved address to interact on behalf of an owner for the NFT with ID token_id. If enabled is true the operator address is approved, if false the approval is revoked. Throws unless caller is the current NFT owner, or an authorized operator of the current owner. Emits the Approval event.
Enables or disables approval for an operator address to manage all of the caller's NFTs. If enabled is true, the operator address is approved, if false, the approval is revoked. Emits the ApprovalForAll event.
Returns the address approved to interact with the NFT with ID token_id or returns None if no approval has been set. Throws if NFT with ID token_id does not exist.
Returns true if approved address is approved to transact for NFT with ID token_id.
Returns true if operator is approved to commit transactions on behalf of owner.
Indicates whether an address is an authorized operator for another address.
This event MUST be triggered and emitted when tokens are transferred.
The event arguments should be as follows: (from, to, token_id)
This event MUST be triggered and emitted upon approval, including revocation of approval.
The event arguments should be as follows: (owner, approved, token_id, enabled).
Enabled is of type string, because of a limit of 3 on indexed values. Since address, int and bool are automatically indexed, having enabled as bool would cause an error, as it would be the 4th indexed item. Use "true" or "false" as return values instead.
This event MUST be triggered and emitted upon a change of operator status, including revocation of approval for all NFTs in the contract.
The event arguments should be as follows: (owner, operator, approved)
For idiomatic reasons approved is, just as with Approval, of type string
The standard only allows safe transfers of tokens. On transfer a check MUST be performed which checks if the recipient is a contract and if so the transfer MAY ONLY happen if on_aex141_received returns true.
Deals with receiving NFTs on behalf of a contract. Contracts MUST implement this interface to be able to receive NFTs. Mint and transfer transactions will invoke the on_aex141_received function.
Returns true or false to signal whether processing the received NFT was successful or not.
This section covers the extendability of the basic token - e.g. mintable, burnable.
When an NFT contract implements an extension its name should be included in the aex141_extensions array, in order for third party software or contracts to know the interface.
Any extensions should be implementable without permission. Developers of extensions MUST choose a name for aex141_extensions that is not yet used. Developers CAN make a pull request to the reference implementation for general purpose extensions and maintainers choose to eventually include them.
The mintable extension SHOULD be used for generic NFT minting without specific requirements in regards to minting.
Note: If you aim to reuse the same metadata across many NFTs you might prefer the mintable_templates extension.
Issues a new token to the provided address. If the owner is a contract, IAEX141Receiver.on_aex141_received will be called with data if provided.
Emits the Mint event.
Throws if the call to IAEX141Receiver.on_aex141_received implementation failed (safe transfer)
The mintable_limit extension SHOULD be used if the amount of NFTs to mint should be limited/capped. It MAY ONLY be used in combination with the mintable extension.
The initially defined token limit MUST be greater than or equal to 1.
Emits the TokenLimit event on contract creation.
Returns the limit / max amount of NFTs that can be minted.
Decreases the NFT limit/cap defined in the collection. An increase of the limit is forbidden.
Emits the TokenLimitDecrease event.
Throws if:
new_limit equals the current token_limit
new_limit is lower than the current total_supply
The burnable extension SHOULD be used if NFTs within a contract are intended to be burnable.
Burns the NFT with the provided token_id.
Emits the Burn event.
Throws if Call.caller is NOT the current owner or NOT approved to transfer on behalf of the owner.
The mintable_templates extension SHOULD be used if multiple NFTs within a contract should share the same metadata. The extension provides the possbility to create templates. Optionally an edition limit for each template can be defined on template creation. If no edition limit is provided, it's considered to be unlimited. The edition limit can be decreased at any point of time as long as the new value does not exceed the current edition supply. Also, templates can be deleted if no NFT has been created using the template.
Note:
On contract level the MAP MUST be used as metadata_type
the map stores the template_id and edition_serial of each NFT
if mutable_attributes
Returns the metadata type which is used for templates.
Returns the template for the provided template_id in case it exists, otherwise None.
Returns the total amount of templates that currently exist.
Creates a new template for specific immutable metadata. The immutable_metadata needs to be MetadataIdentifier in case template_metadata_type is T_URL and MetadataMap for T_MAP. The edition limit defines how many NFTs can be minted based on a specific template. If no edition limit is provided, an unlimited amount of NFTs can be created using the template. The edition limit can be decreased at any time.
Emits the TemplateCreation event.
Note: It's recommended to perform a check if immutable_metadata is considered valid according to individual requirements.
Throws in case the wrong variant for metadata is provided:
variant MUST be MetadataIdentifier if template_metadata_type is T_URL
variant MUST be MetadataMap if template_metadata_type is T_MAP
Deletes the template with the provided id.
Emits the TemplateDeletion event.
Throws if:
the provided template_id does not exist
an NFT based on this template has already been created
Mints a new NFT for the provided template id. If to is a contract, IAEX141Receiver.on_aex141_received will be called with data if provided.
Emits the TemplateMint event.
Throws if:
the provided template_id does not exist
the mint would exceed the current edition_limit of the template
the call to IAEX141Receiver.on_aex141_received implementation failed (safe transfer)
Decreases the edition limit of a specific template. An increase of the edition limit of a template is forbidden.
Emits the EditionLimitDecrease event.
Throws if:
the provided template_id does not exist
the new_limit is equal to or lower than 0
the new_limit is equal to or higher than the current edition limit
The mintable_templates_limit extension SHOULD be used to introduce a limit/cap for the amount of templates that may be in existence. It MAY ONLY be used in combination with the mintable_templates extension.
Returns the limit / max amount of templates that can be created.
Decreases the template limit/cap defined in the contract. An increase of the limit is forbidden.
Emits the TemplateLimitDecrease event.
Throws if:
new_limit equals to or is lower than 0
new_limit equals the current token_limit
new_limit is lower than the current total_supply
The mutable_attributes extension SHOULD be used if NFTs in the contract should store attributes which can change over time. This can e.g. be beneficial for game assets where users could level up their characters. The mutable attributes are expected to be provided in a JSON string.
Updates the JSON string that contains mutable attributes of the NFT.
Emits the MutableAttributesUpdate event.
Throws if the provided token_id does not exist.
Mint
This event is defined by the mintable extension and MUST be triggered whenever a new token is minted.
The event arguments should be as follows: (to, token_id)
TokenLimit
This event is defined by the mintable_limit extension and MUST be triggered in the init entrypoint during contract creation.
The event arguments should be as follows: (limit)
TokenLimitDecrease
This event is defined by the mintable_limit extension and MUST be triggered whenever the decrease_token_limit entrypoint is called.
The event arguments should be as follows: (old_limit, new_limit)
Burn
This event is defined by the burn extension and MUST be triggered whenever an NFT is burned.
The event arguments should be as follows: (owner, token_id)
TemplateCreation
This event is defined by the mintable_templates extension and MUST be triggered whenever a new template is created.
The event arguments should be as follows: (template_id)
TemplateDeletion
This event is defined by the mintable_templates extension and MUST be triggered whenever a template is deleted.
The event arguments should be as follows: (template_id)
TemplateMint
This event is defined by the mintable_templates extension and MUST be triggered whenever a new NFT is minted.
The event arguments should be as follows: (to, template_id, token_id, edition_serial). Sophia does not allow 4 "indexed" values as of writing the standard. For this reason edition_serial defined as type string.
The event arguments should be as follows: (to, template_id, token_id, edition_serial)
EditionLimit
This event is defined by the mintable_templates extension and MUST be triggered whenever a new template is created which specifies an edition limit.
The event arguments should be as follows: (template_id, edition_limit)
EditionLimitDecrease
This event is defined by the mintable_templates extension and MUST be triggered whenever the decrease_edition_limit entrypoint is called.
The event arguments should be as follows: (template_id, old_limit, new_limit)
TemplateLimit
This event is defined by the mintable_templates_limit extension and MUST be triggered in the init entrypoint during contract creation.
The event arguments should be as follows: (limit)
TemplateLimitDecrease
This event is defined by the mintable_templates_limit extension and MUST be triggered whenever the decrease_template_limit entrypoint is called.
The event arguments should be as follows: (old_limit, new_limit)
MutableAttributesUpdate
This event is defined by the mutable_attributes extension and MUST be triggered whenever the mutable attributes of an NFT are updated.
The event arguments should be as follows: (token_id, mutable_attributes)
There are currently following reference implementations available which follows defined standard:
implements the following extensions:
mintable
Additionally there exists following showcase for a simple NFT marketplace using AEX-141:
AEX: 141
Title: Non-Fungible Token Standard
Author: Arjan van Eersel <[email protected]> (@zkvonsnarkenstein), Marco Walz (@marc0olo), Philipp (@thepiwo), Rogerio (@jyeshe)
License: ISC
Discussions-To: https://forum.aeternity.com/t/aeternity-nft-token-standard/9781
Status: Review
Type: Interface
Created: 2021-09-11OBJECT_IDMetadataIdentifierURL can represent any URL and typically the NFT id is used to resolve the metadata using that URL, e.g.
ipfs:// serving as base_url and pointing to a folder stored on IPFS where immutable metadata is stored (recommended)
https:// serving as base_url and pointing to a traditional website where metadata is stored
...
OBJECT_ID can be used to refer to any kind of item which typically already exists (e.g. the VIN of a car)
for MAP use MetadataMap
MAP provides almost unlimited flexibility and allows any kind of metadata to be represented in a map
The template_metadata_type can either be T_URL (string, e.g. ipfs:// pointing to a JSON stored on IPFS) or T_MAP (map(string, string))
it is defined during contract creation
it is applied to ALL templates created with the contract
new_limit is lower than the current edition supplymintable_limitburnable
CollectionTemplateEditionNFTs.aes implements the following extensions:
mintable_templates
mintable_templates_limit
mutable_attributes
burnable
meta_info
meta_info
token_id
int
data
option(metadata)
total_supply
int
owner
address
balance
option(int)
token_id
int
owner
option(address)
token_id
int
data
option(string)
to
address
token_id
int
data
option(string)
approved
address
token_id
int
enabled
bool
operator
address
enabled
bool
token_id
int
approved
option(address)
token_id
int
approved
address
approved
bool
owner
address
approved
address
approved
bool
from
address
to
address
token_id
int
owner
address
approved
address
token_id
int
enabled
string
owner
address
operator
address
approved
string
from
option(address)
token_id
int
data
option(string)
owner
address
metadata
option(metadata)
data
option(string)
token_id
int
token_limit
int
new_limit
int
token_id
int
template_metadata_type
template_metadata_type
template
option(template)
template_supply
int
immutable_metadata
metadata
edition_limit
option(int)
template_id
int
template_id
int
to
address
template_id
int
data
option(string)
token_id
int
template_id
int
new_limit
int
template_limit
int
new_limit
int
token_id
int
mutable_attributes
string
to
address
token_id
int
limit
int
old_limit
int
new_limit
int
owner
address
token_id
int
template_id
int
template_id
int
to
int
template_id
int
token_id
int
edition_serial
string
template_id
int
edition_limit
int
template_id
int
old_limit
int
new_limit
int
limit
int
old_limit
int
new_limit
int
token_id
int
mutable_attributes
string
contract interface IAEX141 =
datatype metadata_type = URL | OBJECT_ID | MAP
datatype metadata = MetadataIdentifier(string) | MetadataMap(map(string, string))
record meta_info =
{ name: string
, symbol: string
, base_url: option(string)
, metadata_type : metadata_type }
datatype event
= Transfer(address, address, int)
| Approval(address, address, int, string)
| ApprovalForAll(address, address, string)
entrypoint aex141_extensions : () => list(string)
entrypoint meta_info : () => meta_info
entrypoint metadata : (int) => option(metadata)
entrypoint total_supply : () => int
entrypoint balance : (address) => option(int)
entrypoint owner : (int) => option(address)
stateful entrypoint transfer : (address, int, option(string)) => unit
stateful entrypoint transfer_to_contract : (int) => unit
stateful entrypoint approve : (address, int, bool) => unit
stateful entrypoint approve_all : (address, bool) => unit
entrypoint get_approved : (int) => option(address)
entrypoint is_approved : (int, address) => bool
entrypoint is_approved_for_all : (address, address) => boolentrypoint aex141_extensions() : list(string)entrypoint meta_info() : meta_infoentrypoint metadata(token_id: int) : option(metadata)entrypoint total_supply() : íntentrypoint balance(owner: address) : option(int)entrypoint owner(token_id: int) : option(address)stateful entrypoint transfer(to: address, token_id: int, data: option(string)) : unitstateful entrypoint transfer(to: address, token_id: int, data: option(string)) : unitstateful entrypoint approve(approved: address, token_id: int, enabled: bool) : unitstateful entrypoint approve_all(operator: address, enabled: bool) : unitentrypoint get_approved(token_id: int) : option(address)entrypoint is_approved(token_id: int, approved: address) : boolentrypoint is_approved_for_all(owner: address, operator: address) : booldatatype event
= Transfer(address, address, int)
| Approval(address, address, int, string)
| ApprovalForAll(address, address, string)Transfer(address, address, int)Approval(address, address, int, string)ApprovalForAll(address, address, string)contract interface IAEX141Receiver =
entrypoint on_aex141_received : (option(address), int, option(string)) => boolentrypoint on_aex141_received(from: option(address), token_id: int, data: option(string)) : boolcontract interface IAEX141Mintable =
datatype event = Mint(address, int)
stateful entrypoint mint : (address, option(metadata), option(string)) => intstateful entrypoint mint(owner: address, metadata: option(metadata), data: option(string)) : intcontract interface IAEX141MintableLimit =
datatype event
= TokenLimit(int)
| TokenLimitDecrease(int, int)
entrypoint token_limit : () => int
stateful entrypoint decrease_token_limit : (int) => unitentrypoint token_limit() : íntstateful entrypoint decrease_token_limit(new_limit: int) : unitcontract interface IAEX141Burnable =
datatype event = Burn(address, int)
stateful entrypoint burn : (int) => unitstateful entrypoint burn(token_id: int) : unitcontract interface IAEX141MintableTemplates =
datatype template_metadata_type = T_URL | T_MAP
datatype event
= TemplateCreation(int)
| TemplateDeletion(int)
| TemplateMint(address, int, int, string)
| EditionLimit(int, int)
| EditionLimitDecrease(int, int, int)
record template =
{ immutable_metadata: metadata
, edition_limit: option(int)
, edition_supply: int }
entrypoint template_metadata_type : () => template_metadata_type
entrypoint template : (int) => option(template)
entrypoint template_supply : () => int
stateful entrypoint create_template : (metadata, option(int)) => int
stateful entrypoint delete_template : (int) => unit
stateful entrypoint template_mint : (address, int, option(string)) => int
stateful entrypoint decrease_edition_limit : (int, int) => unitentrypoint template_metadata_type() : template_metadata_typeentrypoint template(template_id: int) : option(template)entrypoint template_supply() : íntstateful entrypoint create_template(immutable_metadata: metadata, edition_limit: option(int)) : intstateful entrypoint delete_template(template_id: int) : unitstateful entrypoint template_mint(to: address, template_id: int, data: option(string)) : intstateful entrypoint decrease_edition_limit(template_id: int, new_limit: int) : unitcontract interface IAEX141MintableTemplatesLimit =
datatype event
= TemplateLimit(int)
| TemplateLimitDecrease(int, int)
entrypoint template_limit : () => int
stateful entrypoint decrease_template_limit : (int) => unitentrypoint template_limit() : íntstateful entrypoint decrease_template_limit(new_limit: int) : unitcontract interface IAEX141MutableAttributes =
datatype event = MutableAttributesUpdate(int, string)
stateful entrypoint update_mutable_attributes : (int, string) => unitstateful entrypoint update_mutable_attributes(token_id: int, mutable_attributes: string) : unitMint(address, int)TokenLimit(int)TokenLimitDecrease(int, int)Burn(address, int)TemplateCreation(int)TemplateMint(address, int, int, string)EditionLimit(int, int)EditionLimitDecrease(int, int, int)TemplateLimit(int)TemplateLimitDecrease(int, int)MutableAttributesUpdate(token_id, string)

