Token API Specification

Hi all,

My name is Peter, and I work with Autark. We have been working on a spec and implementation for a Token API and we’re hoping to receive feedback from the community on the current spec:

Current Issues:

  • Each app must individually track token transfers and populate a list of tokens to be displayed in the app’s front-end. This creates redundant processing and contract calls for each app and increases maintenance cost for developers to update each app individually when changes are necessary (e.g. if the token list must account for a non-compliant ERC-20, that change must be made for each app)

  • Tokens that may be relevant to an organization are not visible to an app unless they are sent to the Vault. This can be a UX pain point for apps that rely on tokens that are not necessarily held by the Vault, such as an organization’s voting token. For example, the Rewards app distributes funds via a MiniMeToken. If an organization wants to send funds to it’s token holders via the Rewards app, a user must copy the address of the voting token from the Token Manager app and paste it into a field in the Rewards app. But if the Token Manager were able to register the organization’s token, that token would automatically be in the dropdown in the Rewards app.

  • A smaller issue is the scenario where a user sends ERC-20 tokens directly to the Vault app. The Finance app does not update the balance when these kinds of transfers occur. However, if the wrapper kept a list of commonly used tokens, watched their contracts for transfers to the Vault, and updated the balance, this problem can be mitigated.

Solution:

By having a token registry that is maintained by the wrapper and subscribed to by each app, we can reduce app complexity and between-app inconsistencies. Furthermore, by opening an API endpoint that allows apps to register tokens, we can have apps like Token Manager register the organization’s token thus allowing it to be accessible across all other apps in the organization.

API Endpoints:

tokens(): The end point returns an observable that upon subscription returns the entire list of tokens as an object of key-value pairs using the address as the key, e.g.:

{
    ‘0x0000000000000000000000000000000000000000’: {
        name: ‘Ethereum’,
        symbol: ‘ETH’,
        decimals: 18,
        balance: 1000000000000000000,
    },
    ‘0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359’: {
        name: ‘Sai Stablecoin v1.0’
        symbol: ‘SAI’
       decimals: 18,
       balance: 1000000000000000000,
    }
}

On subsequent emissions of the observable we have two options on how to proceed:

  1. Upon any changes or additions to the token registry the individual token and it’s updated metadata is emitted by the observable with the same formatting, e.g.:
{
    ‘0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359’: {
        name: ‘Sai Stablecoin v1.0’
        symbol: ‘SAI’
        decimals: 18,
        balance: 2000000000000000000,
    }
}

So the frontend can simply update the token with the new metadata. This is a process that can also be handled by @aragon/api-react so that when the tokens hook is called it returns the current state of the token list.

  1. Alternatively, we may wish to consider returning the whole token list each time a token updates, e.g.:
{
    ‘0x0000000000000000000000000000000000000000’: {
        name: ‘Ethereum’,
        symbol: ‘ETH’,
        decimals: 18,
        balance: 1000000000000000000,
    },
    ‘0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359’: {
        name: ‘Sai Stablecoin v1.0’
        symbol: ‘SAI’
       decimals: 18,
       balance: 2000000000000000000,
    }
}

This would reduce any processing required on the front-end to update the token list with new changes. Instead the front-end could just replace the entire token list with the new one each time a token updates.

We’d appreciate any feedback from the community on which option they would prefer.

requestTokenAddressRegistration (address): This endpoint allows an app to register a token by passing its address. The wrapper will lookup the address’ name, symbol and decimals in the cache and the balance will be retrieved from the contract and stored in memory. If the wrapper does not have the token metadata in it’s cache, it will lookup those values on the contract and store them in the cache to reduce future contract calls. It will also begin to watch transfer events on the token’s contract and filter for events that include the address of the DAO’s default vault app (Vault, Agent, etc). When a transfer event occurs it will update the registry with the new balance and emit the new metadata on the tokens observable. Successful registration of the token returns a single emission observable that returns the address and metadata of the token.

Wrapper Logic:

When the wrapper first initializes it must create a new TokenRegistry class. TokenRegistry create a new Cache (called tokenCache) to store metadata, a new Object (called tokenRegistry) that will hold key-values pairs of token addresses and their balances and it will also create a new Subject (called tokenObservable) which can be subscribed to. Also, upon initialization the TokenRegistry must lookup the Vault address used by the organization in order to watch for transfer event to and from the Vault.

When an app registers a token, first the token is checked against tokenRegistry and the tokenCache and if it is not registered, the contract is queried for name, symbol, decimals, and balance. These values are saved to tokenRegistry and the name, symbol, and decimals are saved tokenCache. The values are also passed to tokenObservable via the next method. Finally, the TokenRegistry begins watching the contract’s Transfer event and filtering for transfers to or from the Vault. The TokenRegistry’s modify method is called to update tokenRegistry with the new balance and emit the updated metadata on tokenObservable.

When an app subscribes to the observable, first the TokenRegistry takes the registered tokens and converts the object into an observable using the of operator and concatenates it with tokenObservable so that the first emission is the entirety of tokenRegistery while subsequent emissions are changes of individual tokens emitted via the tokenObservable when new tokens are registered or balances are updated.

Alternatively, if we want to emit the entire token list on every change we can use a BehaviorSubject instead of a Subject as our observable. We would then just pass the entire tokenRegistry to the observable via the next method upon updates to a token balance. Any user who subscribes to the observable would be returned the last value that was emitted on the observable, so there would be no need to create an observable out of the tokenRegistry and concatenate it with tokenObservable.

Conclusion:

There are a number of things about the implementation that we thought were best decided by the community. In particular, it would be great to hear about how app developers would prefer to receive the data from the observable – only relevant changes or the entire list updated with new changes. Also, I’d be interested to hear if anyone has any issues with the format of the data. Maybe you’d prefer an array instead of an object?

But I’d really appreciate hearing any concerns or blockers that you think would prevent this API implementation getting merged into the Aragon API.

Thanks for your time!

2 Likes