All pages
Powered by GitBook
1 of 19

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

AEX X

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.

Simple Summary

"If you can't explain it simply, you don't understand it well enough." Provide a simplified and layman-accessible explanation of the AEX.

Abstract

A short (~200 word) description of the technical issue being addressed.

Motivation

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.

Specification

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.

Rationale

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.

Backwards Compatibility

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.

Implementation

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

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)>

aex-10

Simple Summary

This aexpansion specify the derivation path used in the Aeternity blockchain for deterministic wallets

Motivation

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.

Specification

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 .

References

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-04
SLIP-0044: Registered coin types for BIP-0044
  • BIP32-Ed25519: Hierarchical Deterministic Keys over a Non-linear Keyspace

  • BIP-0044
    SLIP-0010
    SLIP-0044
    Deterministic wallets
    BIP-0032: Hierarchical Deterministic Wallets
    BIP-0044: Multi-Account Hierarchy for Deterministic Wallets
    SLIP-0010: Universal private key derivation from master private key

    .github

    ISSUE_TEMPLATE

    PULL_REQUEST_TEMPLATE

    When opening a pull request to submit a new AEX, please use the suggested template: https://github.com/aeternity/AEXs/blob/master/aex-X.md

    Aeternity Expansions

    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.

    Quick Aexpansions filter

    • Recently updated

    For open Aexpansions

    Goals

    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.

    Contributing

    If you want to contribute please follow these steps:

    1. Read

    2. Fork this repository

    3. Add your proposal to your fork, using the template provided

    4. 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 .

    Status terms

    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.

    AEXs

    Code
    Status
    Description

    Copyright

    This README is released under the license.

    AEX-130: æpps Metadata Format Specification

    Simple Summary

    The goal of this document is to specify a format for metadata which æpps provide to æpp listing services, decentralized æpp stores, æpp browsers, and wallets.

    Abstract

    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

    AEX-1

    Active

    AEX process

    AEX-2

    Withdrawn

    Third-party Wallet Provider Support

    AEX-3

    Active

    Secret storage format

    AEX-4

    Withdrawn

    æternity wallet deep linking specification

    Help wanted
    Stage active
    Stage finalized
    All
    Most commented
    Least active
    AEX-1
    AEX-1
    AEX-1
    CC0-1.0
    A format which is consistenly used by developers is needed to facilitate the uniform and useful user experience of browsing aepps inside æpps browsers, wallets, and listing services.

    Motivation

    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.

    Specification

    Format

    Required members

    • Name of aepp

    • aepp icons

    • Networks the aepp is available on [mainnet, testnet]

    Recommended members

    • aepp description

    • Category (select from predermined list)

    • Author

    • Author URL

    • Related applications

    • Age restrictions/rating

    References

    Reference visuals for the result of implementing such metadata format are included below.

    A service listing aepps
    Detail view of a aepp metadata rendered inside a aepp browser

    Rationale

    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).

    Other considerations

    • 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.

    Future Considerations

    • 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.

    Implementation

    Required members

    name member

    Documentation: https://www.w3.org/TR/appmanifest/#name-member

    icons member

    Documentation: https://www.w3.org/TR/appmanifest/#icons-member

    aeternity_network_ids 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).

    Recommended members

    description member

    Documentation: https://www.w3.org/TR/appmanifest/#description-member

    category member

    Documentation: https://www.w3.org/TR/appmanifest/#categories-member List of known values: https://github.com/w3c/manifest/wiki/Categories

    author member

    The author member is a string that represents the name of author.

    author_url member

    The author_url member is a string that represents the URL of the author's website.

    Additional members

    iarc_rating_id member

    Could be used to set age restrictions/ratings. Documentation: https://www.w3.org/TR/appmanifest/#iarc_rating_id-member

    Reference implementation

    http://aeternity.com/aepp-base-example/webmanifest.json


    References

    Web App Manifest standard: https://www.w3.org/TR/appmanifest/ Extensions Registry: https://github.com/w3c/manifest/wiki/Extensions-Registry


    Comments

    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.

    Copyright

    AEX-3

    Simple Summary

    The document describes and defines the secret storage approach used by æternity.

    Motivation

    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.

    Specification

    • 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.

    Secret types

    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.

    Example

    Reference

    • https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition

    AEX-4

    Simple Summary

    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.

    Motivation

    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.

    Specification

    URI Scheme

    • ae:

    • aeternity:

    Methods

    1. View/Open Account in the wallet

      URI: aeternity:<wallet_address>

      Associated wallet app will:

      1. Open the wallet app

      2. Check if the address exists or not

    Reference

    • 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 8

    Simple Summary

    This document defines a standard how arbitrary messages or payload can be signed using an aeternity keypair.

    Motivation

    Message signing can be used in a wide variety of scenarios. Common use cases include:

    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-09
    AEX: 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-03
    AEX: 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-03

    If 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.

    1. Vendor generates a address for receiving payment.

    2. 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.

    1. The user generates the transaction to be signed

    2. 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.

    3. 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

  • AEX-5
    AEX-7
    AEX-8
    AEX-9
    AEX-10
    AEX-130
    AEX-141

    (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.

    Specification

    Flow

    1. DApp / Website / Wallet prepares a signing request containing a message (challenge) and other parameters (see below)

    2. Signing request is sent to the Wallet / Signer containing the Private Key

    3. User can select an identity (= keypair)

    4. Message will be signed

    5. User is either being redirected to the provided callback URL, or the signed message is displayed (eg. QR code)

    Encoding

    The signature should be encoded in hex: Buffer.from(signature).toString("hex");

    Message Signing Request

    Message Signing Response

    The response can contain an array of signatures and publicKeys, which allows a single message to be signed by multiple identities at once.

    Signing the message

    Verifying the signature

    Example

    Serialization and Transport Layer

    Serialization and Transport Layer are not part of this propaosal.

    The Serialization is being discussed as part of AEX-7 Data Serialization

    Security

    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);
    // true

    AEX 1

    Simple Summary

    This document describes the process a proposal has to go through in order to be accepted into the AEX repository.

    Motivation

    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.

    Specification

    AEX Types

    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.

    Workflow

    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.

    Stages

    Stage
    Purpose
    Entrance Criteria
    Changes Expected

    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.

    Transferring AEX Ownership

    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 :).

    Editors

    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.

    List of editors

    • Sascha Hanse (@knarz)

    • Philipp Piwowarsky (@thepiwo)

    Format

    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.

    Preamble

    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.

    Copyright

    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+:

    References

    Many parts of this document are inspired by or adapted with only minor modifications from many of the already existing standardisation processes.

    AEX 5

    Simple Summary

    This document describes the process and messages required for inter wallet communication between aeternity supported wallets.

    Motivation

    The purpose of AEXs is to provide specification and JSON-RPC compatible messages required for an inter-wallet communication channel.

    AEX 7

    WITHDRAWN

    This proposal has been withdrawn because of overlap with AEX-2. A new AEX will be created at a later stage that is compatible with AEX-2 and focuses on the transport layer only.

    Simple Summary

    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-01

    AEXS

    Final

    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

  • FDL-1.3: GNU Free Documentation License, version 1.3

  • GPL-2.0+: GNU General Public License (GPL), version 2 or newer

  • LGPL-2.1+: GNU Lesser General Public License (LGPL), version 2.1 or newer

  • 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

    Aeternity forums
    BSD-2-Clause
    BSD-3-Clause
    CC0-1.0
    GNU-All-Permissive
    Internet Systems Consortium License
    Apache License, version 2.0
    Expat/MIT/X11 license
    GNU Affero General Public License (AGPL), version 3 or newer
    EIP-1
    BIP-2
    TC39
    The Internet Standards Process

    minor

    This document defines a serialization standard to facilitate data transfer between DApps, Wallets and devices.

    Motivation

    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

    Specification

    Serialization - RLP

    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

    Encoding

    The resulting string will be encoded with base58 because it is URL-safe, which is important for some transport layers.

    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

    Example

    This example would result in the following, base58 encoded, string:

    The payload can be another RLP array, depending on the MessageType.

    Paging / Chunks

    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.

    Message Types

    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.)

    Versioning

    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.

    Rationale

    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.

    Adding context to a request

    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:

    1. It will increase the size of both the request and response QR.

    2. 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.

    Implementation

    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.0
    License: CC0-1.0
             BSD-3-Clause
    License-Code: Apache-2.0
    AEX: 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
        ]
      ]
    ];
    2hDLW1FiwvQs5ofPUgi5CgAJKDWiNncCoETXf7DGdkkDmrhN3z
    function 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.

    Current Process (with regards to AEX-2)

    1. SDK connects with a wallet

    2. Wallet shares the account address with the SDK

    3. When the SDK needs the wallet to sign the transaction it asks the wallet

    4. Wallet signs and returns the signed transaction.

    Proposed process with inter-wallet communication

    1. SDK connects with a wallet

    2. Wallet returns its current address and other account addresses it may help the SDK in getting signature for.

    3. When the SDK needs the wallet to sign the transaction it asks the wallet

    4. Wallet performs steps in the below order

      1. checks if it can sign the transaction.

      2. else it checks if it holds a direct link to the wallet that can sign the transaction and forwards the request to it.

      3. 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.

    5. Once the transaction is signed, it is returned back to the requesting SDK for further processing.

    Specification

    JSON-RPC 2.0 Methods

    General

    • 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

    Start and Close

    • 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

    Address Update

    • 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

    Sign and Broadcast Transaction

    • 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

    Example Flow

    [WIP]

    AEX 2

    Simple Summary

    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').

    Motivation

    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.

    Specification

    Naming Convention

    Wherever possible, this document tries to closely follow the who.what.how naming convention for JSON-RPC methods.

    JSON-RPC 2.0 Specification

    Error

    JSON-RPC 2.0 response error object that is used to communicate any error occurred while processing the request.

    Types of errors

    Methods

    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

    Notifications

    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.

    Example Flow

    Reference Implementation

    • 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

    References

    • 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-29
    AEX: 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-04
    • id - same id as in the corresponding request

    • status - Value will always be equal to ack

    Object
    • id - same id as in the corresponding request

    • status - Value will always be equal to ack

    - Object containing only a single account currently in use by the wallet.
  • 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

    UUID v4 standards
    UUID v4 standards
    UUID v4 standards
    UUID v4 standards
    UUID v4 standards
    UUID v4 standards
    {
      "current": {
          "ak_2iBPH7HUz3cSDVEUWiHg76MZJ6tZooVNBmmxcgVK6VV8KAE688": {
              "name": "this property is optional"
            }
        }
    }
    Object
    • 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

  • : Network id used by the wallet
  • 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.

    aexpansion

    Discussions-To:

    Paste here the link to the topic on forum.aeternity.com

    Content URL

    Paste here the url where the content of the AEX can be found

    ATTENTION!

    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>"
              }
          }
      }
    template
    Pull Request
    AEX-1
    Aeternity forums

    AEX 11 Fungible Token Standard

    Abstract

    A short (~200 word) description of the technical issue being addressed.

    Motivation

    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.

    Specification

    Interface

    The token contract MUST implement the above interface. The implementation MUST follow the specifications described below.

    Operators

    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.

    Functional Specification

    General Notes

    meta() : meta_inf

    Returns information about token as a meta_inf record.

    total_supply() : int

    Returns: 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) : bool

    MUST 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 event

    Indicates 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 event

    Indicates 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).

    Hooks

    tokensToTransfer hook

    The 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 hook

    The 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

    Backwards Compatibility

    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.

    Implementation

    TODO

    Further consideration

    • 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-19
  • A 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
    )

    aex-9

    AEX 9

    Simple Summary

    This document aims to outline a standard and define how fungible tokens should be created and used on aeternity blockchain.

    Abstract

    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.

    Motivation

    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.

    Specification

    Basic Token

    Interface

    Methods

    aex9_extensions()

    This function returns a hardcoded list of all implemented extensions on the deployed contract.

    meta_info()

    This function returns meta information associated with the token contract.

    return
    type

    total_supply()

    This function returns the total token supply.

    return
    type

    balances()

    This function returns the full balance state for static calls, e.g. by a blockchain explorer.

    return
    type

    balance()

    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.

    parameter
    type
    return
    type

    transfer()

    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.

    parameter
    type

    Events

    Transfer

    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)

    parameter
    type

    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)

    parameter
    type

    Extensions

    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.

    Extension Mintable ("mintable")

    mint()

    This function mints value new tokens to account. The function SHOULD abort if Call.caller is not the owner of the contract state.owner.

    parameter
    type

    Events

    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)

    parameter
    type

    Extension Burnable ("burnable")

    burn()

    This function burns value of tokens from Call.caller.

    parameter
    type

    Events

    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)

    parameter
    type

    Extension Allowance ("allowances")

    create_allowance()

    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.

    parameter
    type

    transfer_allowance()

    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.

    parameter
    type

    allowance()

    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.

    parameter
    type

    allowances()

    This function returns all allowances stored in state.allowances record.

    return
    type

    allowance_for_caller()

    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.

    parameter
    type
    return
    type

    change_allowance()

    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.

    parameter
    type
    return
    type

    reset_allowance()

    Resets the allowance given for_account to zero.

    parameter
    type
    return
    type

    Events

    Allowance - MUST trigger on any successful allowance creation or change.

    The approval event arguments should be as follows: (from_account, for_account, value)

    parameter
    type

    Extension Swappable ("swappable")

    swap()

    This function burns the whole balance of the Call.caller and stores the same amount in the swapped map.

    parameter
    type
    return
    type

    check_swap()

    This function returns the amount of tokens that were burned trough swap for the provided account.

    parameter
    type
    return
    type

    swapped()

    This function returns all swapped tokens that are stored in contract state.

    return
    type

    Events

    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)

    parameter
    type

    Implementation

    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:

    References

    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-11

    meta_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

    (REFERENCE) Aeternity Sophia Fungible Token
    (OUTDATED) Fungible token implementation example
    (OUTDATED) ERC20 Sophia Translation
    ERC-20
    ERC-20 attack vectors
    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) => unit
    entrypoint aex9_extensions() : list(string)
    entrypoint meta_info() : meta_info
    record meta_info =
      { name     : string
      , symbol   : string
      , decimals : int }
    entrypoint total_supply() : int
    entrypoint balances() : map(address, int)
    entrypoint balance(owner: address) : option(int)
    stateful entrypoint transfer(to_account: address, value: int) : unit
    Transfer(address, address, int)
    Mint(address, int)
    stateful entrypoint mint(account: address, value: int) : unit
    Mint(address, int)
    stateful entrypoint burn(value: int) : unit
    Burn(address, int)
    stateful entrypoint create_allowance(for_account: address, value: int) : unit
    stateful 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() : allowances
    entrypoint 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() : unit
    stateful entrypoint check_swap(account: address) : int
    stateful entrypoint swapped() : map(address, int)
    Swap(address, int)

    aex-141

    AEX-141: Non-Fungible Token Standard

    Abstract

    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

    Motivation

    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.

    AEX141 NFT

    IAEX141

    Methods

    aex141_extensions()

    Returns a hardcoded list of all implemented extensions on the deployed contract.

    meta_info()

    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

    return
    type

    metadata()

    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

    parameter
    type
    return
    type

    total_supply()

    Returns the total amount of NFTs in circulation.

    return
    type

    balance()

    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.

    parameter
    type
    return
    type

    owner()

    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.

    parameter
    type
    return
    type

    transfer()

    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.

    parameter
    type

    transfer_to_contract()

    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;

    parameter
    type

    approve()

    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.

    parameter
    type

    approve_all()

    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.

    parameter
    type

    get_approved()

    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.

    parameter
    type
    return
    type

    is_approved()

    Returns true if approved address is approved to transact for NFT with ID token_id.

    parameter
    type
    return
    type

    is_approved_for_all()

    Returns true if operator is approved to commit transactions on behalf of owner.

    Indicates whether an address is an authorized operator for another address.

    parameter
    type
    return
    type

    Events

    Transfer

    This event MUST be triggered and emitted when tokens are transferred.

    The event arguments should be as follows: (from, to, token_id)

    parameter
    type

    Approval

    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.

    parameter
    type

    ApprovalForAll

    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

    parameter
    type

    Receiver contract interface

    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.

    AEX141Receiver

    on_aex141_received()

    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.

    parameter
    type

    Extensions

    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.

    Extension Mintable ("mintable")

    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.

    mint()

    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)

    parameter
    type
    return
    type

    Extension Mintable Limit ("mintable_limit")

    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.

    token_limit()

    Returns the limit / max amount of NFTs that can be minted.

    return
    type

    decrease_token_limit()

    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

    parameter
    type

    Extension Burnable ("burnable")

    The burnable extension SHOULD be used if NFTs within a contract are intended to be burnable.

    burn()

    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.

    parameter
    type

    Extension Mintable Templates ("mintable_templates")

    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

    template_metadata_type()

    Returns the metadata type which is used for templates.

    return
    type

    template()

    Returns the template for the provided template_id in case it exists, otherwise None.

    return
    type

    template_supply()

    Returns the total amount of templates that currently exist.

    return
    type

    create_template()

    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

    parameter
    type
    return
    type

    delete_template()

    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

    parameter
    type

    template_mint()

    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)

    parameter
    type
    return
    type

    decrease_edition_limit()

    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

    parameter
    type

    Extension Mintable Templates Limit ("mintable_templates_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.

    template_limit()

    Returns the limit / max amount of templates that can be created.

    return
    type

    decrease_template_limit()

    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

    parameter
    type

    Extension Mutable Attributes ("mutable_attributes")

    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.

    update_mutable_attributes()

    Updates the JSON string that contains mutable attributes of the NFT.

    Emits the MutableAttributesUpdate event.

    Throws if the provided token_id does not exist.

    parameter
    type

    Extension Events

    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)

    parameter
    type

    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)

    parameter
    type

    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)

    parameter
    type

    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)

    parameter
    type

    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)

    parameter
    type

    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)

    parameter
    type

    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)

    parameter
    type

    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)

    parameter
    type

    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)

    parameter
    type

    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)

    parameter
    type

    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)

    parameter
    type

    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)

    parameter
    type

    Implementation

    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-11
    and
    OBJECT_ID
    use
    MetadataIdentifier
    • URL 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

  • extension is used, it also stores the mutable attributes
  • 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

  • the new_limit is lower than the current edition supply
    mintable_limit
  • burnable

  • 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

    aex141-nft-collection-example
    CollectionUniqueNFTs.aes
    aex141-nft-simple-marketplace
    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) => bool
    entrypoint aex141_extensions() : list(string)
    entrypoint meta_info() : meta_info
    entrypoint metadata(token_id: int) : option(metadata)
    entrypoint total_supply() : ínt
    entrypoint balance(owner: address) : option(int)
    entrypoint owner(token_id: int) : option(address)
    stateful entrypoint transfer(to: address, token_id: int, data: option(string)) : unit
    stateful entrypoint transfer(to: address, token_id: int, data: option(string)) : unit
    stateful entrypoint approve(approved: address, token_id: int, enabled: bool) : unit
    stateful entrypoint approve_all(operator: address, enabled: bool) : unit
    entrypoint get_approved(token_id: int) : option(address)
    entrypoint is_approved(token_id: int, approved: address) : bool
    entrypoint is_approved_for_all(owner: address, operator: address) : bool
    datatype 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)) => bool
    entrypoint on_aex141_received(from: option(address), token_id: int, data: option(string)) : bool
    contract interface IAEX141Mintable =
    
        datatype event = Mint(address, int)
    
        stateful entrypoint mint : (address, option(metadata), option(string)) => int
    stateful entrypoint mint(owner: address, metadata: option(metadata), data: option(string)) : int
    contract interface IAEX141MintableLimit =
    
        datatype event
            = TokenLimit(int)
            | TokenLimitDecrease(int, int)
    
        entrypoint token_limit : () => int
        stateful entrypoint decrease_token_limit : (int) => unit
    entrypoint token_limit() : ínt
    stateful entrypoint decrease_token_limit(new_limit: int) : unit
    contract interface IAEX141Burnable =
    
        datatype event = Burn(address, int)
    
        stateful entrypoint burn : (int) => unit
    stateful entrypoint burn(token_id: int) : unit
    contract 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) => unit
    entrypoint template_metadata_type() : template_metadata_type
    entrypoint template(template_id: int) : option(template)
    entrypoint template_supply() : ínt
    stateful entrypoint create_template(immutable_metadata: metadata, edition_limit: option(int)) : int
    stateful entrypoint delete_template(template_id: int) : unit
    stateful entrypoint template_mint(to: address, template_id: int, data: option(string)) : int
    stateful entrypoint decrease_edition_limit(template_id: int, new_limit: int) : unit
    contract interface IAEX141MintableTemplatesLimit =
    
        datatype event 
            = TemplateLimit(int)
            | TemplateLimitDecrease(int, int)
    
        entrypoint template_limit : () => int
    
        stateful entrypoint decrease_template_limit : (int) => unit
    entrypoint template_limit() : ínt
    stateful entrypoint decrease_template_limit(new_limit: int) : unit
    contract interface IAEX141MutableAttributes =
    
        datatype event = MutableAttributesUpdate(int, string)
    
        stateful entrypoint update_mutable_attributes : (int, string) => unit
    stateful entrypoint update_mutable_attributes(token_id: int, mutable_attributes: string) : unit
    Mint(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)
    aepp details
    aepps list