Skip to content

Commit

Permalink
feat: Multi Token Standard Contract [NEP] (#245)
Browse files Browse the repository at this point in the history
* feat: initial work for an emerging multi token standard
This features the traits and proposal background information
required have a proper specification for multiple tokens
in a contract, that can be symmetric with other multi
token contracts across chains.

* chore: wrong issue link

* chore: fix example comment on metadata

* fix: add missing memo field to guide level  entry for batch transfer

Co-authored-by: marco-sundsk <[email protected]>

* fix: add missing memo field to guide level  entry for transfer

Co-authored-by: marco-sundsk <[email protected]>

* fix: consolidate batch versions of balance_of and balance_bounds

* feat: add decimals to the contract to support tokens with precision reqs

* fix: This refactors metadata to be one token metadata.
The original thought was that splitting the metadata in two would allow
users to more easily represent metadata that correspond to series. That
said it was found that developers are more likely to write their own
metadata view logic by implementing the metadata provider.

The metadata provider is just the interface and the underlying
implementation can do the sharing of data the same if its split or not
.Therefore we decided to have on metadata interface to respond to
to request and to implement.

* feat: add answers to the unresolved questions in the NEP

* fix: missing mt prefix from balance and suppky methods

* chore: typo correction

Co-authored-by: Mike Purvis <[email protected]>

* chore: typo correction

Co-authored-by: Mike Purvis <[email protected]>

* fix: this makes symbol optional to be more in alignment with the
other metadata standards.

Symbol required doesn't make sense for NFT like tokens, and there
fore should be optional. This will also serve to help consumers
subjectively understand the nature of the token.

* fix: add verson information at the top

* fix: remove gnr8 example doesn't adhere to standard

Co-authored-by: Mike Purvis <[email protected]>

* chore: typo correction

Co-authored-by: Mike Purvis <[email protected]>

* chore: consistency of layout

Co-authored-by: Daryl Collins <[email protected]>

* chore: lint typo

Co-authored-by: Daryl Collins <[email protected]>

* chore: correct wording to make things more clear

Co-authored-by: Daryl Collins <[email protected]>

* chore: tidy

Co-authored-by: Daryl Collins <[email protected]>

* chore: tidy and clarify

Co-authored-by: Daryl Collins <[email protected]>

* chore: typo correction and tidy

Co-authored-by: Daryl Collins <[email protected]>

* chore: lint correction

Co-authored-by: Daryl Collins <[email protected]>

* chore: tidy

Co-authored-by: Daryl Collins <[email protected]>

* chore: typo correction

Co-authored-by: Daryl Collins <[email protected]>

* chore: clarify

Co-authored-by: Daryl Collins <[email protected]>

* chore: lint tidy

Co-authored-by: Daryl Collins <[email protected]>

* fix: refactor to full spec naming

Co-authored-by: Daryl Collins <[email protected]>

* fix: refactor to full spec naming

Co-authored-by: Daryl Collins <[email protected]>

* chore: refactor to full spec naming and tidy

Co-authored-by: Daryl Collins <[email protected]>

* fix: refactor to full spec naming

Co-authored-by: Daryl Collins <[email protected]>

* fix: refactor to full spec naming and tidy

Co-authored-by: Daryl Collins <[email protected]>

* chore: refactor to full spec naming and tidy

Co-authored-by: Daryl Collins <[email protected]>

* chore: refactor to full spec naming

Co-authored-by: Daryl Collins <[email protected]>

* chore: refactor to full spec naming and tidy

Co-authored-by: Daryl Collins <[email protected]>

* chore: refactor to full spec naming

Co-authored-by: Daryl Collins <[email protected]>

* chore: refactor to full spec naming and tidy

Co-authored-by: Daryl Collins <[email protected]>

* chore: refactor to full spec naming and tidy

Co-authored-by: Daryl Collins <[email protected]>

* chore: refactor to full spec naming and tidy

Co-authored-by: Daryl Collins <[email protected]>

* chore: refactor to full spec naming and tidy

Co-authored-by: Daryl Collins <[email protected]>

* chore: refactor to full spec naming and tidy

Co-authored-by: Daryl Collins <[email protected]>

* chore: refactor to full spec naming and tidy

Co-authored-by: Daryl Collins <[email protected]>

* chore: tidy

Co-authored-by: Daryl Collins <[email protected]>

* chore: clarification

Co-authored-by: Daryl Collins <[email protected]>

* chore: typo correction

Co-authored-by: Daryl Collins <[email protected]>

* feat: add an approval management standard proposal for discussion

* chore: clarify guide level explanation of balance

* chore: clarify guide level explanation of supply

* chore: clarify transfer tokens guide-level explanation

* chore: reorder key for guide-level explanation

* chore: add missing comments for balance_bounds and balance_of

* fix: update standard to only support approvals by specific id

* feat: refactor and add approval management back into the spec

* chore: tidy

Co-authored-by: Daryl Collins <[email protected]>

* fix: refactor metadata to split out metadata into it's own md

* feat: add enumeration standard to multi token standard

* chore: fix typos

* fix: add MT prefixed datastructure descritpion and mt_prefixed methods

* fix: refactor to have MT and mt_metadata prefixes and describe new MT metadata structures

* chore: fix typos

* fix: refactor Core description into typescript

* chore: fix typos and lint

* chore: lint and fix typos

* fix: add support for mt_tokens

* chore: fix typo and lint

* fix: typos and lint

* feat: add event spec for mt

* fix: refactor README to match other standards

* chore: lint, tidy, and typo correction to core

Co-authored-by: Mike Purvis <[email protected]>

* chore: fix typos with enumeration

Co-authored-by: Mike Purvis <[email protected]>

* chore: typos, lint, and tidy with approval management

Co-authored-by: Mike Purvis <[email protected]>

* chore: refactor naming consistency , typo, lint with events

Co-authored-by: Mike Purvis <[email protected]>

* chore: refactor to match event description

Co-authored-by: Mike Purvis <[email protected]>

* chore: typos and lint with metadata

Co-authored-by: Mike Purvis <[email protected]>

* chore: rm dead code for mt_approvals

* chore: comment argument order matches impl order

* chore: reorder comments to match impl args

* chore: rm duplicate event description

* fix: adjust consistency between standards for mt_tokens

* chore: fix comments referencing Token and MTBaseTokenMetadata

* chore: consistent spacing between metadata fields

* chore: newline

* fix: refactor events to match NFT events per standard spec

* chore: fix link consistency within markdown

* fix: token_ids to be an array for batch_balance_of

* fix: confusing comment on the return for mt_resolve_transfer

* chore: remove misleading comment from transfer methods

* fix: base_by_metadata_id should return array

* feat: add rationale section to further clarify decisions

* chore: tidy account to account_id

Co-authored-by: Olga Telezhnaya <[email protected]>

* chore: reword rationale to be more clear and less broad and more specific about limitations

* chore: bring clarity to token type explanation

* fix: add additional examples of authorized_id and clarify account_id

* fix: align mt standard wth nft standard ref/ event standard

* fix: update approvals to approved_account_ids to match other specs

* fix: update specs to use token_id vs. id to align with other specs

* fix: add new summary format for mt token spec

* chore: refactor nep from 246 to 245

* chore: set status to draft

* fix: refactor approvals to be iterative, vs included in Token.

This change comes about because with Multi Token standard there
may be many holders of a single token id. It may be gas prohibitive
to return back all of the approval data for a token in the Token
response.

The solution was to introduce a new method called mt_token_approvals
which allows one to iterate through, the list of token approvals
for a specific token id.

This is a minimal addition that allows consumers of the data to
find all the approvals for a particular token id.

* fix: add approval view logic for singular request

* fix: adjust mt_transfer* and mt_resolve to provide granular resolution

Prior to this fix, we had singular representation for token_ids that
may have many owners. There was no way to resolve approvals for those
types of tokens, typically ft style tokens.

This change links together the owners of the accounts to the ft. So
when an approval occurs we can resolve which account to transfer the
tokens from as well as handle resolution of transfer failures from
a singular token id with many owners.

* fix: add summary listing to main readme

* fix: refactor approval owner_id to approval_owner_id

* fix: add reference current reference implementation locations

* chore: add caution warning label to specification

* chore: update links to latest reference implementation

* chore: correct typos

Co-authored-by: marco-sundsk <[email protected]>
Co-authored-by: Mike Purvis <[email protected]>
Co-authored-by: Daryl Collins <[email protected]>
Co-authored-by: Olga Telezhnaya <[email protected]>
  • Loading branch information
5 people authored Apr 4, 2022
1 parent 3ff5cb0 commit c4dba0b
Show file tree
Hide file tree
Showing 8 changed files with 1,908 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Changes to the protocol specification and standards are called NEAR Enhancement
|[0178](https://github.com/near/NEPs/blob/master/neps/nep-0178.md) | Non Fungible Token Approval Management | @chadoh @thor314 | Final |
|[0181](https://github.com/near/NEPs/blob/master/neps/nep-0181.md) | Non Fungible Token Enumeration | @chadoh @thor314 | Final |
|[0199](https://github.com/near/NEPs/blob/master/neps/nep-0199.md) | Non Fungible Token Royalties and Payouts | @thor314 @mattlockyer | Final |
|[0245](https://github.com/near/NEPs/blob/master/neps/nep-0245.md) | Multi Token Standard | @zcstarr @riqi @jriemann @marcos.sun | Draft |
|[0297](https://github.com/near/NEPs/blob/master/neps/nep-0297.md) | Contract Events Standard | @telezhnaya | Final |
|[0330](https://github.com/near/NEPs/blob/master/neps/nep-0330.md) | Contract Metadata | @BenKurrek | Review |

Expand Down
583 changes: 583 additions & 0 deletions neps/nep-0245.md

Large diffs are not rendered by default.

503 changes: 503 additions & 0 deletions specs/Standards/MultiToken/ApprovalManagement.md

Large diffs are not rendered by default.

399 changes: 399 additions & 0 deletions specs/Standards/MultiToken/Core.md

Large diffs are not rendered by default.

84 changes: 84 additions & 0 deletions specs/Standards/MultiToken/Enumeration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Multi Token Enumeration([NEP-245](https://github.com/near/NEPs/discussions/246))

:::caution
This is part of the proposed spec [NEP-245](https://github.com/near/NEPs/blob/master/neps/nep-0245.md) and is subject to change.
:::

Version `1.0.0`

## Summary

Standard interfaces for counting & fetching tokens, for an entire Multi Token contract or for a given owner.

## Motivation

Apps such as marketplaces and wallets need a way to show all tokens owned by a given account and to show statistics about all tokens for a given contract. This extension provides a standard way to do so.

While some Multi Token contracts may forego this extension to save [storage] costs, this requires apps to have custom off-chain indexing layers. This makes it harder for apps to integrate with such Multi Token contracts. Apps which integrate only with Multi Token Standards that use the Enumeration extension do not even need a server-side component at all, since they can retrieve all information they need directly from the blockchain.

Prior art:

- [ERC-721]'s enumeration extension
- [Non Fungible Token Standard's](../NonFungibleToken/Enumeration.md) enumeration extension

## Interface

The contract must implement the following view methods:

// Metadata field is optional if metadata extension is implemented. Includes the base token metadata id and the token_metadata object, that represents the token specific metadata.

```ts
// Get a list of all tokens
//
// Arguments:
// * `from_index`: a string representing an unsigned 128-bit integer,
// representing the starting index of tokens to return
// * `limit`: the maximum number of tokens to return
//
// Returns an array of `Token` objects, as described in the Core standard,
// and an empty array if there are no tokens
function mt_tokens(
from_index: string|null, // default: "0"
limit: number|null, // default: unlimited (could fail due to gas limit)
): Token[] {}

// Get list of all tokens owned by a given account
//
// Arguments:
// * `account_id`: a valid NEAR account
// * `from_index`: a string representing an unsigned 128-bit integer,
// representing the starting index of tokens to return
// * `limit`: the maximum number of tokens to return
//
// Returns a paginated list of all tokens owned by this account, and an empty array if there are no tokens
function mt_tokens_for_owner(
account_id: string,
from_index: string|null, // default: 0
limit: number|null, // default: unlimited (could fail due to gas limit)
): Token[] {}
```

The contract must implement the following view methods if using metadata extension:

```ts
// Get list of all base metadata for the contract
//
// Arguments:
// * `from_index`: a string representing an unsigned 128-bit integer,
// representing the starting index of tokens to return
// * `limit`: the maximum number of tokens to return
//
// Returns an array of `MTBaseTokenMetadata` objects, as described in the Metadata standard, and an empty array if there are no tokens
function mt_tokens_base_metadata_all(
from_index: string | null,
limit: number | null
): MTBaseTokenMetadata[]
```


## Notes

At the time of this writing, the specialized collections in the `near-sdk` Rust crate are iterable, but not all of them have implemented an `iter_from` solution. There may be efficiency gains for large collections and contract developers are encouraged to test their data structures with a large amount of entries.

[ERC-721]: https://eips.ethereum.org/EIPS/eip-721
[storage]: https://docs.near.org/docs/concepts/storage-staking
187 changes: 187 additions & 0 deletions specs/Standards/MultiToken/Events.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
# Multi Token Event([NEP-245](https://github.com/near/NEPs/discussions/246))

:::caution
This is part of the proposed spec [NEP-245](https://github.com/near/NEPs/blob/master/neps/nep-0245.md) and is subject to change.
:::

Version `1.0.0`

## Summary

Standard interfaces for Multi Token Contract actions.
Extension of [NEP-297](../EventsFormat.md)

## Motivation

NEAR and third-party applications need to track
`mint`, `burn`, `transfer` events for all MT-driven apps consistently. This exension addresses that.

Note that applications, including NEAR Wallet, could require implementing additional methods to display tokens correctly such as [`mt_metadata`](Metadata.md) and [`mt_tokens_for_owner`](Enumeration.md).

## Interface
Multi Token Events MUST have `standard` set to `"nep245"`, standard version set to `"1.0.0"`, `event` value is one of `mt_mint`, `mt_burn`, `mt_transfer`, and `data` must be of one of the following relavant types: `MtMintLog[] | MtBurnLog[] | MtTransferLog[]`:



```ts
interface MtEventLogData {
EVENT_JSON: {
standard: "nep245",
version: "1.0.0",
event: MtEvent,
data: MtMintLog[] | MtBurnLog[] | MtTransferLog[]
}
}
```

```ts
// Minting event log. Emitted when a token is minted/created.
// Requirements
// * Contract MUST emit event when minting a token
// Fields
// * Contract token_ids and amounts MUST be the same length
// * `owner_id`: the account receiving the minted token
// * `token_ids`: the tokens minted
// * `amounts`: the number of tokens minted, wrapped in quotes and treated
// like a string, although the numbers will be stored as an unsigned integer
// array with 128 bits.
// * `memo`: optional message
interface MtMintLog {
owner_id: string,
token_ids: string[],
amounts: string[],
memo?: string
}

// Burning event log. Emitted when a token is burned.
// Requirements
// * Contract MUST emit event when minting a token
// Fields
// * Contract token_ids and amounts MUST be the same length
// * `owner_id`: the account whose token(s) are being burned
// * `authorized_id`: approved account_id to burn, if applicable
// * `token_ids`: the tokens being burned
// * `amounts`: the number of tokens burned, wrapped in quotes and treated
// like a string, although the numbers will be stored as an unsigned integer
// array with 128 bits.
// * `memo`: optional message
interface MtBurnLog {
owner_id: string,
authorized_id?: string,
token_ids: string[],
amounts: string[],
memo?: string
}

// Transfer event log. Emitted when a token is transferred.
// Requirements
// * Contract MUST emit event when transferring a token
// Fields
// * `authorized_id`: approved account_id to transfer
// * `old_owner_id`: the account sending the tokens "sender.near"
// * `new_owner_id`: the account receiving the tokens "receiver.near"
// * `token_ids`: the tokens to transfer
// * `amounts`: the number of tokens to transfer, wrapped in quotes and treated
// like a string, although the numbers will be stored as an unsigned integer
// array with 128 bits.
interface MtTransferLog {
authorized_id?: string,
old_owner_id: string,
new_owner_id: string,
token_ids: string[],
amounts: string[],
memo?: string
}
```

## Examples

Single owner minting (pretty-formatted for readability purposes):

```js
EVENT_JSON:{
"standard": "nep245",
"version": "1.0.0",
"event": "mt_mint",
"data": [
{"owner_id": "foundation.near", "token_ids": ["aurora", "proximitylabs_ft"], "amounts":["1", "100"]}
]
}
```

Different owners minting:

```js
EVENT_JSON:{
"standard": "nep245",
"version": "1.0.0",
"event": "mt_mint",
"data": [
{"owner_id": "foundation.near", "token_ids": ["aurora", "proximitylabs_ft"], "amounts":["1","100"]},
{"owner_id": "user1.near", "token_ids": ["meme"], "amounts": ["1"]}
]
}
```

Different events (separate log entries):

```js
EVENT_JSON:{
"standard": "nep245",
"version": "1.0.0",
"event": "mt_burn",
"data": [
{"owner_id": "foundation.near", "token_ids": ["aurora", "proximitylabs_ft"], "amounts": ["1","100"]},
]
}
```

Authorized id:

```js
EVENT_JSON:{
"standard": "nep245",
"version": "1.0.0",
"event": "mt_burn",
"data": [
{"owner_id": "foundation.near", "token_ids": ["aurora_alpha", "proximitylabs_ft"], "amounts": ["1","100"], "authorized_id": "thirdparty.near" },
]
}
```

```js
EVENT_JSON:{
"standard": "nep245",
"version": "1.0.0",
"event": "mt_transfer",
"data": [
{"old_owner_id": "user1.near", "new_owner_id": "user2.near", "token_ids": ["meme"], "amounts":["1"], "memo": "have fun!"}
]
}

EVENT_JSON:{
"standard": "nep245",
"version": "1.0.0",
"event": "mt_transfer",
"data": [
{"old_owner_id": "user2.near", "new_owner_id": "user3.near", "token_ids": ["meme"], "amounts":["1"], "authorized_id": "thirdparty.near", "memo": "have fun!"}
]
}
```

## Further methods

Note that the example events covered above cover two different kinds of events:
1. Events that are not specified in the MT Standard (`mt_mint`, `mt_burn`)
2. An event that is covered in the [Multi Token Core Standard](https://nomicon.io/Standards/MultiToken/Core.html#mt-interface). (`mt_transfer`)

This event standard also applies beyond the three events highlighted here, where future events follow the same convention of as the second type. For instance, if an MT contract uses the [approval management standard](https://nomicon.io/Standards/MultiToken/ApprovalManagement.html), it may emit an event for `mt_approve` if that's deemed as important by the developer community.

Please feel free to open pull requests for extending the events standard detailed here as needs arise.

## Drawbacks

There is a known limitation of 16kb strings when capturing logs.
This can be observed from `token_ids` that may vary in length
for different apps so the amount of logs that can
be executed may vary.
Loading

0 comments on commit c4dba0b

Please sign in to comment.