Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Define Standard for Token List #22

Closed
Therecanbeonlyone1969 opened this issue May 29, 2022 · 7 comments
Closed

Define Standard for Token List #22

Therecanbeonlyone1969 opened this issue May 29, 2022 · 7 comments

Comments

@Therecanbeonlyone1969
Copy link
Collaborator

Define a standard way to define a token across L2s (L1s too) -> identify, discover and address a token for different L2s.

A possible inspiration to starting point could be ITSA.

@smartcontracts
Copy link

As a quick note: I think tackling this issue is much more feasible than #23 for various reasons that I'll get into over on that issue, so I'll be focusing a majority of my effort this week on adding context to this issue.

Problem Statement

I see two different issues in L2 land that can be solved with standardized token lists.

First, there's the obvious issue of coming to consensus over the "canonical" token on chain B that corresponds to some token on chain A. When we want to bridge token X from chain A to chain B, we fundamentally must create some new representation of the token on chain B. It's worth noting that this problem is not limited to L2s -- every chain connected via bridges must deal with the same issue. I therefore think it's a very obvious candidate for standardization.

Second, there's the less obvious but still important issue of standardizing around lists of bridges and their routes across different chains. Optimism is currently trying to design a system that describes the various bridges that support transfers to/from Optimism, the routes that those bridges will take, and the tokens those bridges support. Both of these issues are fundamental problems that would be highly valuable for the multi-chain world.

Existing Solutions

For lists of canonical tokens, L2s currently maintain their own customized versions of the Uniswap token list. Arbitrum maintains a token list with various custom extensions. Optimism also maintains a custom token list, but with different extensions. You should note that both of these custom extensions refer to the bridge that these tokens can be carried through. However, these are not the only bridges that the tokens can be carried through, which makes me strongly believe that the bridges and token lists should be separated. I'll get into a proposed strawman design in a second.

Currently, both Optimism and Arbitrum base "canonicity" on the token name + symbol pair. I think this is reasonable, but there's room for the argument that we should assign some unique identifier instead.

Strawman Proposal

Here I'll quickly sketch out a proposal for what a good token list could look like.

Canonical Token List

interface CanonicalTokenList {
  name: string
  logoURI: string
  keywords: string[]
  timestamp: string
  version: {
    major: number,
    minor: number,
    patch: number
  }
  tokens: Array<{
    chainId: number
    address: string
    name: string
    symbol: string
    decimals: number
    logoURI: string
    extensions: {
      rootTokenChainId: number
      rootTokenAddress: string
    }
  }>
}

Where rootTokenChainId is the ID of the chain where the token was originally deployed and rootTokenAddress is the address of the token on this original chain. I like this better than using name/symbol as canonical simply because tokens will likely compete on the right to use a certain name/symbol as the space of good names starts to shrink.

Bridge Token List

interface BridgeTokenList {
  name: string
  logoURI: string
  keywords: string[]
  timestamp: string
  version: {
    major: number,
    minor: number,
    patch: number
  }
  tokens: Array<{
    chainId: number
    address: string
    name: string
    symbol: string
    decimals: number
    logoURI: string
    extensions: {
      routes: Array<{
        fromChainId: number
        toChainId: number
      }>
    }
  }>
}

We'll really need to chat to some bridge teams to figure this out. I think we can actually make these two token lists work together, which would be great. I designed the above BridgeTokenList under this assumption. For example, we could have the bridge teams maintain their own BridgeTokenList and have them only include the "root" token as part of the array (so chain ID is equal to rootTokenChainId and address is equal to rootTokenAddress in the other list). Then it would be clear which canonical asset this list is referring to.

Similarly, a project like Optimism could then use various BridgeTokenLists to figure out which bridges have routes to/from the chain for a given asset.

Considerations

I think there's an interesting trade-off here between sovereignty and composability. It would be much, much easier if L2 teams all worked together to maintain a unified representation of every possible asset. However, this will inevitably create problems down the line with small/unproven teams attempting to add new items to the list. Unless someone is willing to create a whole governance process around this token list, it seems much more viable for individual teams to maintain their own list of tokens on their own networks. Same thing for bridge token lists. I need to think about this a little bit more.

@Therecanbeonlyone1969
Copy link
Collaborator Author

@smartcontracts awesome stuff. Love it

A couple of points to consider apply to both lists:

  1. tokenId as a unique and resolvable identifier in a dedicated namespace -> resolvable is important because it allows anyone to resolve the ID to its underlying structure. Similar to a DID. This would disambiguate a lot
  2. tokenType e.g. fungible / non-fungible
  3. tokenDesc a brief description of the token
  4. createdAt in UTC Unix time
  5. updatedAt in UTC Unix time
  6. chainId is a good start but I would prefer this also to be a unique namespace and the ID to be resolvable e.g. to the genesis block of the chain
  7. genesisBlockHash for validation purposes -> similar arguments hold for the chainIds in the extensions
  8. maybe even an addressType in case there are non-EVM compatible chains, and addressAlg to describe how the address is derived

As to governance -> use an approach similar to the DID registry as a possible starting point. the list could be maintained on IPFS using IPNS and updates could be included as a Verifiable Credential. We could anchor the IPFS hashes of the list on e.g. Ethereum Mainnet so we hve good versioning.

cc @tasdienes

@DZGoldman
Copy link
Contributor

Hi! Just wanted to give some thoughts from the Arbitrum side here: the "arbed" list you linked has some additional, outdated fields that we're only keeping for backwards compatabiltiy; a "fresh" list is this one, which uses this extension of the fields in uniswap's schema, i.e.,

{
logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028",
chainId: 42161,
address: "0x6314C31A7a1652cE482cffe247E9CB7c3f4BB9aF",
name: "1INCH Token",
symbol: "1INCH",
decimals: 18,
extensions: {
  bridgeInfo: {
    1: {
    tokenAddress: "0x111111111117dc0aa78b770fa6a738034120c302",
    originBridgeAddress: "0x09e9222e96e7b4ae2a407b98d48e330053351eee",
    destBridgeAddress: "0xa3A7B6F88361F48403514059F1F16C8E78d60EeC"
     }
   }
  }
}

In terms of canonicity, we don't use name + symbol, we do use top-level address (i.e., a list could theoretically repeat a name/symbol pair), similar to what you propose.

I like the idea of allowing for a "canonical" list vs. lists for different bridges; I believe our current schema is compatible / serves a similar purpose to the "canonical token list" schema.

R.e. "bridge token list," it seems like there's an additional address field needed; i.e., if it only includes the address of the root token, that leaves out the address of the token created via the bridge (which is presumably the point of the list in the first place).

@Therecanbeonlyone1969
Copy link
Collaborator Author

@DZGoldman ... what are your thoughts on resolvable unique identifiers and a resolver that I am proposing above such that users are not solely dependent on the providers (L2s, L1s, and bridges) to provide it?

@Therecanbeonlyone1969
Copy link
Collaborator Author

Therecanbeonlyone1969 commented Jun 28, 2022

Merging comments above into a new Token List proposal:

interface CanonicalTokenList {
  //resolvable URI to the place where this list can be publicly found e.g. IPFS
  tokenListId: string
  name: string
  logoURI: string
  keywords: string[]
  //createdAt: UTC Unix time
  createdAt: string
  //updatedAt: UTC Unix time
  updatedAt: string
  version: {
    major: number,
    minor: number,
    patch: number
  }
  tokens: Array<{
    chainId: number
    //chainURI: resolvable URI to the genesis block of a given chain
    chainURI: string
    //genesisBlockHash: The hash of the genesis block of the chain
    genesisBlockHash: string
    //tokenIssuerId: resolvable URI as an identifier for the token issuer
    tokenIssuerId:: string
    //tokenIssuerName: Name of the issuer as a strong or a resolvable URI to the equivalent to a name such as a website or verifiable credential attesting to the identity of the issuer (ideally) 
    tokenIssuerName: string
    //tokenId: resolvable URI to a CannonicalTokenList entry of a given token -> uri should have the following cannonical structure: token: 
    //<namspace>: UUID -> similar to W3C DIDs as an established pattern, or should be a URL
    tokenId: string 
    //tokenType: describes the type of token e.g. fungible, non-fungible and if a token is transferable or not
    tokenType: array
    //tokenDesc: Brief description of the token and its functionality
    tokenDesc: text
    //standard: Describes which standard the token adheres to e.g. ERC20, ERC721
    standard: string
    address: string
    //addressType (optional): type of address e.g. Ethereum name space et.
    addressType: string
    //addressAlg (optional): Algorihm used to create the address e.g. CREATE2
    addressAlg: string
    name: string
    symbol: string
    decimals: number
    logoURI: string
    extensions: {
      rootTokenChainId: number
      rootTokenAddress: string
    }
  }>
}

cc @DZGoldman @smartcontracts @tasdienes

PS: added tokenIssuerId and tokenIssuerName as extra fields to the array entry for a token

@Therecanbeonlyone1969
Copy link
Collaborator Author

Proposal for List Governance (as simple as possible):

  • The L2 WG repo is, at least initially, the source of truth for the standardized Token List
  • Updates to the List are done via PR by each Token Issuer or their delegate e.g. a L2 project
  • The URI for the Token List will be a public Github Link, at leat initially
  • Each Token Issuer or one of their delegates controls the List CRUD operations for a token listing in github
  • The tokenIssuerId and tokenId should resolve to verifiable credentials establishing a) the identity of the token issuer and its github handle(s) that can make PRs for a token CRUD operation, and b) the correctness of the token's token array entry where the token entry VC is issued by the token issuer

cc @DZGoldman @smartcontracts @tasdienes

@Therecanbeonlyone1969
Copy link
Collaborator Author

Draft standard merged with PR #26. Closing issue for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants