Multi-Token Standard Discussion

Hi all I wanted to start a conversation and gather interest for developing a multi-token standard contract. This would be very similar to the NFT and FT standards that have been developed. The principle motivation would be to be able to give wallet devs, and smart contract developers a standard to build contracts that support different asset classes and that can be batch swapped. This would have a similar impact to erc-1155 and the Ethereum ecosystem.

Summary

Having a single contract represent both NFTs and FTs can greatly improve efficiency as demonstrated by Enjin Coin. The ability to make batch requests with multiple asset classes can reduce a many transactions transaciton to a single transaction to trade around both NFTs and FTs that are a part of same token contract.

Motivation

We already have a standard for NFTs and FTs and have tackled the perhaps trickest issue which is account storage management. This seems like a nice next logical step towards building a great ecosystem that supports the many uses cases of multi-token smart contracts.

Taking a nod from @mattlockyear I’ll lay out the same sort of ground logic that was involved in the NFT Standard discussions.

Goal

Soft consensus here on the interface and high level architecture, then a migration to Github for impls.

Rules of Engagement(shamelessly cribbed from the NFT Standard discussion):

What’s needed:

  • Bring all ideas to the table
  • Look at existing patterns and solutions
  • choose/generate a solution that satisifies the Near ecosystem needs

Here’s what we don’t want

  • promoting a particular approach
  • not listening to others
  • trying to “own” the standard
  • starting my own standard

Background
EIP-1155 : ERC: Multi Token Standard · Issue #1155 · ethereum/EIPs · GitHub
Example of an NFT Series : GitHub - near-apps/gnr8
Things from the NFT Discussions: Improved NFT Standard · Discussion #171 · near/NEPs · GitHub
Things from the NFT Discussions: NFT Standard Discussion

Discussion Starters

  1. Standard interface for a MultiToken Standard
    Deal with storage should we follow the same pattern in NFT and FT standards?
    Keep the interface the same or at least 1-1 corresponding with erc-1155 for bridging ?

  2. Suggested architecture and reference implementation that tackles how to:
    deal with token type assets FT, NFT, … ?
    deal with unique identifiers for token ids across classes/namespacing ?
    deal with wallets

These are just a few questions to get the mind going, feel free to comment and drop thoughts and concerns, or things you’d like to see.

5 Likes

Love it!

Hoping to build on the discussion framework you created, I’ll share a few ideas, given my knowledge of existing patterns and solutions.

To choose/generate a solution that satisifies the Near ecosystem needs, we understand our vision of a world where all people have control of their data, money, and power of governance. How can multi-tokens help us achieve that goal? Why are they necessary?

Fungibility is just one spectrum of possible token designs, and I believe combining the interfaces would result in opportunities for adoption by wallets and exchanges. Not only can these more complex structures provide greater accountability for everyone using NEAR apps.

I agree that ability to make batch requests with multiple asset classes is a killer feature, and I’m so excited to explore all kinds of use cases for multi-tokens! For example, reputation is a complex idea requiring many levels to define properly, and nobody has really succeeded in developing a legitimate credit system for the open web. My feeling is it has to be validated on the network of participants representing themselves with accounts.

Responding to your questions:

  1. I think we should follow the same pattern as the NFT and FT standards, although I wonder if there is a need for matching ERC-1155. Considering ERC-20 tokens can be converted into NEP-141 tokens across the bridge, I guess ERC-1155 tokens could also interoperate.
  2. GNR8 is the best reference implementation for a kind of multi-token on NEAR, as far as I know. I’m curious to explore how account storage relates to dealing with unique identifiers of token IDs across classes / namespaces. Everyone has their own settings :grinning_face_with_smiling_eyes:

One thing I’d potentially bring up is the mysterious ERC-888 standard…

mapping (address => mapping (string => uint)) balances;

This weird loop might be useful in contextualizing systems for imbued value.

4 Likes

This was awesome, the high level bit , with the Fungibility link, I think the paper was interesting. I think it makes sense to view fungibility under the lens of attributes, context, and utility. I like this framework it makes sense. The one thing I think that was still curious about was how much rationality plays into, when the paper gets into pricing. I think it might be too focused on pricing as a component of rational localized information decisions ( i.e. rational preferences for attributes vs macro trends outside of attributes). I think the signals are maybe to noisy for price prediction, though who knows :smiley:

Tech thoughts: I think the pre-existing standards format makes sense , particularly the NFT Standards pattern, with approvals.

Not sure what the ERC-888 standard adds here is the thought to think about partial ownership of NFTs ? My only concern there would be the increase in complexity perhaps I’d need to give a bit more thought :slight_smile:

Thanks again for the thoughts!

3 Likes

Here’s a quick proposal of an interface just to describe what should be supported by a core implementation. I’m spiking ahead here, but nothing has to be set in stone . Here’s what I think might be useful. Feat/initial token by zcstarr · Pull Request #1 · shipsgold/multi-token-standard-impl · GitHub

Here’s a quick summary

multi_transfer -> basic token transfer given a token id and amount
multi_transfer_call -> basic token transfer given token id, amount and call receiver impl
multi_transfer_batch -> transfer multiple tokens of different types and amounts in a single call
multi_transfer_batch_call -> ditto , with a call receiver
balance_of -> return the balance given a tokenId and account_id
balance_of_batch -> return balances given tokenIds and an account_id
total_supply -> return total supply given token_id
total_supply_batch -> return total supply given token_ids

I think the work done for enumeration can be used as an optional implementation that will help fill out the gaps by the simple view methods listed here.

Curious what do you all think? @mikedotexe @mattlockyer I know you all have dug into these quite a bit with the NFT standards any thoughts/suggestions?

3 Likes

Hey, Riqi here from Paras. We run our marketplace before the NEAR NFT standard, so we basically copy the erc-1155 into NEAR ecosystem. Very excited to discuss more this standardization because I think there will be more use-case that is suitable for Multi Token Standard, especially in gaming scene.

I I think we only need to focus on erc-1155 due to its popularity compared to erc-888. I also agree that we need to have NEAR Multi-Token Standard to make sure we have the interoperability with NFTs in EVM-chain.

4 Likes

Should there be a dependency on the existing FT and NFT standards?

I wanted to put in this conversation that happened a little out of band and open the discussion up.

Conversation that happened out of band from riqi

One big thing that we should discuss is probably the pros/cons of creating new standard based on erc1155 (Feat/initial token by zcstarr · Pull Request #1 · shipsgold/multi-token-standard-impl · GitHub) or use current FT/NFT standard in a single contract (Feat/use standard by zcstarr · Pull Request #2 · shipsgold/multi-token-standard-impl · GitHub)

#PR1

Background:
So for the first PR (pointed to by the last commit) I started down the road of kind of duplicating the logic from the NFT and FT contracts, to try and find some overlap. At one point I even had the token data in a single tree map to try and unify the storage concerns. Going down that road, I found myself almost exactly duplicating the logic that was already implemented in NFT and FT. It felt actually not that useful from my perspective. I ended up needing the same implementation specific logic as the actual NFT and FT standards.

I originally assumed I might find some more simplicity or savings, or that I would need different logic than what already existed. This didn’t really seem to be the case too much outside of the storage cost calculations.

The other thing to consider here is that this would be the impl, so the interfaces for someone implementing this on their own would be the same.

Here’s my take on the pros and cons
Pros:

  • Decoupled implementation from ft and nft, meaning changes in either spec
    have no ramifications on multi-token standard
  • Possibility to have unified data storage for the token a single Lookup or Treemap,
    I don’t think that saves a ton of gas.
  • The ability to do radically different things, i.e. just overall flexibility

Cons:

  • Decoupled from ft and nft standards, so changes to these standards have to find their way into multi token standard
  • Duplicate logic , can be mitigated by re thinking maybe some of the standard into some mod that might be useful across standards
  • Less tried and tested

PR#2
[Feat/use standard by zcstarr · Pull Request #2 · shipsgold/multi-token-standard-impl · GitHub] currently merged into pull/1
[Feat/initial token by zcstarr · Pull Request #1 · shipsgold/multi-token-standard-impl · GitHub]

Background:
In this PR I simply used the pre-existing libraries for FT and NFT and created structure around this to support all the operations, by wrapping around these structures to support development use cases. This means adding some additional structure to smooth out
the way you interact with the contract.

Pros:

  • The standard will be largely drop-in compatible with the latest versions of the FT and NFT spec
  • Tried and tested
  • Terser implementation, easier to dissect if you’re familiar with the existing FT and NFT spec
  • You essentially can get the same support for all the NFT and FT features fairly easily

Cons:

  • Tied to the ft and nft spec, so you might have to consider any cascading impact on the multi token standard for changes to
    ft and nft.
  • There are I believe minor storage gains that might be made keeping token ids in one structure, I’d have to measure it out to be certain the difference between 1 map with more data vs 2 maps with less data each.
  • The coupling might limit the spec from supporting more behavior that’s different from the ft and nft specs.

What are people’s thoughts?

This is just my take, having done a little of the work as a very WIP/POC take, as the PRs are not complete/100% functional yet.

3 Likes

This is basically copying the erc-1155 right, and I really like this approach. I talked with HashRush and OutplayGames and they’re also looking for erc-1155 equivalent on NEAR right now.

Even though it has many duplicate logic from NFT/FT standard, I think there are some use-cases that make erc-1155 a good fit, especially in the gaming scene.

One example is Potion in MMORPG. You might need to bring 1000s of Potion, if we use the current NFT standard, it needs a couple of transactions. Let say the contract can mint 50 NFTs per transaction, we need 20 transactions and that’s gonna be ~60 seconds (3 sec/tx) which is a bad UX.

With that example we have 2 options:

  1. We can use the #PR1 which is based on erc-1155 and is a well-known standard in the ecosystem, we might have the bridge between ETH erc-1155 and NEAR #PR1 later on. Basically NFTs but with supply, minting edition 1 and edition 1000 only in a single tx (Enjin uses this approach)
  2. For some items that have large quantities, we can use FT standard that only has some information/metadata on top of it but developers can just use off-chain metadata for images, etc. (Axie’s SLP uses this approach)

Curious what do you guys think about this.

3 Likes

Even though it has many duplicate logic from NFT/FT standard, I think there are some use-cases that make erc-1155 a good fit, especially in the gaming scene.

I might be missing something.

Both PRs implement the same public interfaces and logically and mostly performance wise the same. I think they would be for the most part erc-1155 compatible interfaces. I noted what’s there, here for ease and what the erc-1155 spec looks like.

It’s probably worth just laying out those traits for discussion to make sure everything is covered and we’re all on the same page and naming makes sense.

The interfaces on hand for the PRs


fn multi_transfer(&mut self,
        receiver_id: ValidAccountId,
        token_id: TokenId,
        amount: U128,
        approval_id: Option<U64>,
        memo: Option<String>
    );
    
fn multi_transfer_call(
        &mut self,
        receiver_id: ValidAccountId,
        token_id: TokenId,
        amount: U128,
        approval_id: Option<U64>,
        memo: Option<String>,
        msg: String,
    ) -> PromiseOrValue<bool>;
    
fn multi_batch_transfer(&mut self,
        receiver_id: ValidAccountId,
        token_id: Vec<TokenId>,
        amounts: Vec<U128>,
        approval_ids: Option<u64>,
        memo: Option<String>,
        msg: String,       
    );
fn multi_batch_transfer_call(
        &mut self,
        receiver_id: ValidAccountId,
        token_ids: Vec<TokenId>,
        amounts: Vec<U128>,
        approval_ids: Option<u64>,
        memo: Option<String>,
        msg: String,
    ) -> PromiseOrValue<bool>;
fn nft_token(self, token_id: TokenId)-> Option<non_fungible_token::Token>;
fn ft_metadata(&self, token_id: TokenId) -> Option<FungibleTokenMetadata>;
fn ft_balance_of(&self, owner_id: ValidAccountId, token_id: TokenId)-> U128;
fn ft_total_supply(&self, token_id: TokenId) -> U128;

//receiver
fn multi_on_transfer(
        &mut self,
        sender_id: AccountId,
        previous_owner_id: AccountId,
        token_ids: Vec<TokenId>,
        amounts: Vec<u128>,
        msg: String,
    ) -> PromiseOrValue<bool>;

//resolver
fn multi_resolve_transfer(
        &mut self,
        previous_owner_id: AccountId,
        receiver_id: AccountId,
        token_ids: Vec<TokenId>,
        amounts: Vec<u128>,
        approvals: Option<Vec<HashMap<AccountId, u64>>>,
    ) -> bool;

ERC-1155

function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;
function safeBatchTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external;
function balanceOf(address _owner, uint256 _id) external view returns (uint256);
function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids) external view returns (uint256[] memory);
function setApprovalForAll(address _operator, bool _approved) external;
function isApprovedForAll(address _owner, address _operator) external view returns (bool);

Receiver
function onERC1155Received(address _operator, address _from, uint256 _id, uint256 _value, bytes calldata _data) external returns(bytes4);
function onERC1155BatchReceived(address _operator, address _from, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external returns(bytes4);       

The diff atm I think

balanceOfBatch //trivial
onERC1155Received //trivial everything is batch by default atm trivial
isApprovedForAll  
setApprovalForAll
token-ids are strings vs uint256 

Main difference right now is I think token ids and approvals. Token Ids I think from an ecosystem make sense as string, as they can be made erc-1155 directly compatible by implementer, approvals are indeed different, but are more of a philosophical thing I think.

Approval can be written specifically for MultiToken transfer under both implementation strategies. It can also be written to support fts. The approach would be to write an approvals extension for multi token that uses it’s own data structure.

I think the most contentious bit is the setApprovalForAll. It doesn’t really have a parallel currently.

I think perhaps by design. One approach could be to just allow approvals for FTs, with the same pre existing NFT semantics. I think it depnds on where people stand.

What’s the use case for setApprovalForAll vs. setApprovalForSome ?
where some just means given a set of token-ids approve this person to make txs , for fungible tokens this would be without amount specification.

@riqi is this what you were thinking about? Also here how do people feel about setApprovalAll I noticed these kinds of things were taken in a different direction for the NFT standard?

1 Like

These are some key differences between ERC-1155 and the PR. The tokens in ERC-1155 are treated to be more of like an “NFT” with its own metadata standard. I think Semi-Fungible Token is a good way to represent the ERC-1155.

In ETH ecosystem, ERC-1155 is treated as a new asset class, the newly minted ERC-1155 token is not treated as ERC-20 or ERC-721. From the PR, it differentiates the tokens in the contract as either FT or NFT (which has different metadata). So, not sure how we can make the interoperability between ERC-1155 and NEAR Multi-Token Standard later on.

Before we move on to the Approval process, my biggest concern right now is whether we should create Multi-Token Standard that:

  1. Separate its tokens into either FT or NFT
    or
  2. Create a new asset class, which is Semi-Fungible Token, just like ERC-1155
3 Likes

These are great points. Thanks for clarifying :slight_smile: I thought you were thinking more along the lines of the implementation details here orz. I grok what you’re talking about now .

1 Like

Thank @zcstarr for telling me this place. As some of you may have noticed, I just created a PR in NEPs for multiple-fungible-token, here.

I am very happy that there is a good place to talk about related things with U guys. I want to take a brief introduction about my PR here for saving your time to read through the whole PR docs.

My PR is actually an extension of NEP-141, you will find they have identical work flow. And The reason why I prefer split it to a new standard, is the change of the interface signature makes it impossible to be backward compatible. That is to say, you can not use this new standard to access current NEP-141 token contract, or using NEP-141 interface to access this MFT token contract. So to avoid confusion, I choose to make it independent.

And the biggest advantage of this MFT protocol, is it can be easily understood and implemented by those who know NEP-141. Futhermore, very few works need to be done if they wanna upgrade their existing NEP-141 contracts to adapt this new protocol and also maintain the whole NEP-141 interface (means they choose to obey both protocols). The backend logic of these two standards are highly similar, so core business logic may be untouched, when they add the new interface support for this MFT protocol to enable accessbility of both protocols.

5 Likes

Multi Token Standard Meeting - 7/7/21 9:30 PM UTC (2:30pm PDT/ 5:30pm EDT)

If you’re interested feel free to drop by additionally here is the location of the full agenda and listing for the meeting. Meeting Agenda.

Location

TBA - day of meeting in the standards discord or ping me for an invite on discord zcstarr

Background

We’re here to discuss what we’d like to have happen with regards to representing semi-fungible tokens. The original thought was that we might create a standard that would be the erc-1155 of NEAR.

What makes ERC-1155 special? ERC-1155 has several features that are of note,approvals, batch transactions, and metadata that is exclusively specified outside of the contract. ERC-1155 relies on events to keep track of things like supply.

What you should bring

  • Yourself
  • Openess to ideas
  • Thoughts on the meeting goals

Agenda

  • Brief intros - 1-2 minutes
  • Meeting Goals
  • Follow up steps

Meeting Goals

  • Understand the requirements for the multi-token standard. What we’d like to see.
    • is it erc-1155 compatibility?
    • exchange listability?
    • partial ownership of NFTs?
    • wallet requirements?
  • Get clear on the shape of the standard
    • Should it be a completely new asset or should it be the composition of the existing standards ft or nft?
  • Reach soft consensus on this or set a future date for further discussion.
2 Likes

Hey all, so we met on 7/7/7 here are the notes .

The overview was pretty positive, most were pro new asset class vs. just composition of FT and NFT. We’re looking to come to consensus next week about it, if you have opinions drop them here. Additionally we have some follow ups about events and event handling. To get a full picture of where we’re at and heading take a peek at the notes on the impl github.

Cheers!

2 Likes

Background

Based on the discussions we had from the meeting. Here is a Core Impl proposal that I think
ticks alot of the discussion boxes. From the previous multi prefix we now have the more terse sft. There is a supply tracking, which deviates from erc-1155, it just seems useful and we can do it. I think simply with an optional supply . The meaning being that if you don’t fill out supply you have no concept of supply for that particular token. ERC-1155 relies on event aggregation to create a supply view.

Approvals in ERC-1155 differ from the ones in the NEAR ecosystem, and it has been mentioned multiple times already that _call convention does exist, and effectively can do the same thing as approvals but better in the sense that you don’t have to approval all your holdings, you can delegate specific amounts.

Core Interface proposal for Semi Fungible Tokens

// TokenId is assumed string 
// Single value transfer
fn sft_transfer(&mut self,
        receiver_id: ValidAccountId,
        token_id: TokenId,
        amount: U128,
        memo: Option<String>
    );
// Single value transfer with call    
fn sft_transfer_call(
        &mut self,
        receiver_id: ValidAccountId,
        token_id: TokenId,
        amount: U128,
        memo: Option<String>,
        msg: String,
    ) -> PromiseOrValue<bool>;
    
fn sft_batch_transfer(&mut self,
        receiver_id: ValidAccountId,
        token_id: Vec<TokenId>,
        amounts: Vec<U128>,
        memo: Option<String>,
        msg: String,       
    );
fn sft_batch_transfer_call(
        &mut self,
        receiver_id: ValidAccountId,
        token_ids: Vec<TokenId>,
        amounts: Vec<U128>,
        memo: Option<String>,
        msg: String,
    ) -> PromiseOrValue<bool>;

fn sft_balance_of(&self, owner_id: ValidAccountId, token_id: TokenId)-> U128;
fn sft_balance_of_batch(&self, owner_id: Vec<ValidAccountId>, token_id: Vec<TokenId>)-> Vec<U128>;

fn sft_total_supply(&self, token_id: TokenId) -> Option<U128>;
fn sft_total_supply_of_batch(&self, token_id: Vec<TokenId>) -> Vec<Option<U128>>;

// receiver for handling _call
fn sft_on_transfer(
        &mut self,
        sender_id: AccountId,
        previous_owner_id: AccountId,
        token_ids: Vec<TokenId>,
        amounts: Vec<u128>,
        msg: String,
    ) -> PromiseOrValue<bool>;

// resolver for handling _call 
fn sft_resolve_transfer(
        &mut self,
        previous_owner_id: AccountId,
        receiver_id: AccountId,
        token_ids: Vec<TokenId>,
        amounts: Vec<u128>,
    ) -> bool;

Core Events

Events are currently being discussed, but the events that we’d probably like are

// TokenId is assumed string
Mint(to:AccountId, amount:Vec<U128>, tokenIds:Vec<TokenId>) // differs from ERC-1155 , ERC-1155 overloads transfer with a from 0x0 address
Transfer(to:AccountId, from:AccountId, amount:Vec<U128>, tokenIds:Vec<TokenId>)
URI(uri: string, tokenID:TokenId);

Let me know what you all think. Does this make sense for a NEP. I feel like the events are a bit out of scope, but I noted them here so we could discuss. The supply issue is a bit of duplicated data, it’s suggested there to make it less painful for contract consumers.

2 Likes

I wanted to drop this here, just for an update on what’s happening. Things are shaping up for the NEP here’s a WIP: Preview, feel free to drop comments etc… as it’s coming together about sections needed etc… . I’m looking to make a PR to NEPs around Tuesday the 27th.

Have a great weekend ya’ll!

Easier readin’

1 Like

Filed the initial NEPs in NEP format feel free to comment.

We have a community meeting to talk about the Neps scheduled for next tuesday
08/04/21 7- 8PM UTC-8 Meeting link to be included here closer to the date.

2 Likes

We’re looking to have a community meeting about the NEP for the standard on the 3rd. Here’s a link to the agenda, if you’d like a meeting invite just ping me on discord zcstarr or check the standards channel day of meeting.

Meeting Agenda

2 Likes

Pretty positive meeting about the NEP. Still looking to get a few opinions in on it before fully proceeding. Here’s the latest notes from the meeting.

2 Likes

Next multi token standards meeting is scheduled for 08/17/2021 7PM PDT. It’s largely a reschedule of the last meeting just to verify feedback. Check the standards channel in discord day of if you’d like an invite. Here’s the agenda for the meeting. See you then.

1 Like

From the last token meeting 8/17/21 - Overall another great meeting with the community on Multi Token Standard. I think we’re at consensus on the NEP, still circulating the results of this meeting and then going to move forward.

Here’s the meeting notes ! Feel free to drop comments or thoughts

1 Like