The fungible token standard is the foundation of any Ecosystem . As far as I know, there are many developers who are doing unnecessary waiting because of the uncertainty of the standard, which is slowly poisoning the IC ecology.
We’ve seen developers ask this question about the NNS ledger before, and I’m going to ask this question to sns and try to give some solutions. The following points comes from my discussions with members of the Cansiter Builder DAO (learn about us).
We’ve also seen some discussion about token standards in the community, but I’m getting tired of BULLSHIT being painless about the implementation details, we should look directly at some core issues:
Why do I trust a token contract: IC has no main chain data, how to ensure that status values such as balance status and transaction history are not tampered with? So as to achieve cryptographically verifiable? Cross-container calls are initiated by contracts. How to prevent containers from forging users to initiate transactions?
Usability issue: Why should the design of ICP’s ledger container be used in SNS? How can developers use tokens under the token standard without functions such as approve/transferFrom?
As we all know, there will be a block list in the blockchain structure of Ethereum, and the block contains transactions, which is a relatively classic structure in the blockchain. But IC is completely different, IC has no main chain data.
From the design of the community’s token standards, developers have absorbed the experience of different token standards from Ethereum, such as some very basic things like name, symbol, decimals, totalSupply, as well as balanceOf, allowance, approve, transferFrom, transfer these very basic interfaces. This makes it easier for developers to introduce tokens into the business, otherwise there will be no ecology.
Another very important part is how to make adjustments to suit the characteristics of the IC network. For example, IC does not have a history log. If the developer implements the log by himself, and the smart contract of the IC can be updated, how to ensure that the token transaction is trusted based on cryptography, and the history will not be modified by the attacker?
And the mutual calls between the smart contracts of the IC are initiated by the contract itself. If a token contract interacts with another DeFi contract, the attacker can even initiate a fake transaction to steal value without the user’s signature.
The lack of mainchain data makes it difficult for users to trust a token contract, because it goes against the core value of cryptocurrencies - TrustLess. Unless we rebuild a chain structure that can be easily verified. Under the token standard, structures such as transaction records need to be included.
The asynchronous architecture causes IC to have no traditional atomicity, but this is due to the characteristics of the underlying framework, not the problem that the token standard should solve. Therefore, the atomicity problem of IC should not be considered in the design of the token standard.
But is there a solution to the problem of atomicity? Of course, there are two solutions in the distributed world: distributed transactions and sagas. We think sagas is the better option because it’s not have Intrusiveness for development. Intrusiveness refers to using code to adapt distributed transactions to solve the problem of atomicity, while sagas does not require the developer’s code to pay attention to atomicity itself, and is solved through external process coordination.
At present, we have not seen the specific implementation code, but in the forum discussion and understanding of the ICP ledger, we can initially understand the current scheme of SNS:
NNS plans to release a dedicated SNS subnet dedicated to issuing tokens. A subnet can run up to 15 token systems (including governance, ledger, and configuration three contracts), allowing third-party developers to deploy by themselves, which can be regarded as NNS community version. The foundation seems to be preparing to allow developers to issue tokens in the SNS using the ICP ledger contract.
The SNS subnet x33ed can already be seen, and there are currently 34 nodes running, but no container deployment yet. Of course, this subnet and other subnets have the same basic structure.
Because the token contract follows the implementation method of ICP’s ledger. In such ledger contracts of ICP, there are no interfaces such as approve and transferFrom, so they cannot be directly used by other contracts. If the developer uses SNS to launched tokens, he must be forced to create a wrap token contract for their token by themselves and then use it in the dapp, just like using Warped ICP.
Tokens issued through sns may only be used directly for trading in centralized exchanges (black jokes about decentralization). If you want to use this token in swap or dapp, you must use its warp token, and the warp token contract itself must be an ERC20-like token contract, so there are problems such as state tampering.
The addition of SNS instead brings double risks to the token from the SNS network itself and the wrap token contract, which makes the token even more untrustworthy (strictly speaking, it is difficult for us to even trust the ICP ledger). Developers might as well use the ERC20-like standard to issue tokens directly. It confuses me.
We don’t want to simply complain, but to make the IC ecosystem develop better, so we try to put forward some ideas for discussion with the community.
The core problem of IC’s token standard is that IC cannot provide a chain data structure like Ethereum to prove that transactions are trusted and signed by users. Therefore, token issuers have no way to provide cryptographic-level proofs to users and developers.
The history of the token can be tampered with when the container is upgraded, attackers can send fake transactions through the upgraded container without the need for user signatures, etc., which brings huge risks.
Looking back at the development history of crypto, cryptography-level self-certification and trustlessness are the foundation of value, otherwise everything is built on quicksand.
how to solve this problem? Maybe we can imitate the design of BTC in the token contract and implement a database with a blockchain structure. Through the built-in implementation of a blockchain data structure, the proof of all transaction records of the token standard is completed, and the self-certification is completed through the ledger blockchain.
In the classic bitcoin chain proof structure, the next block will contain the hash of the previous block. Any changes on the chain will change the hashes of all subsequent blocks, thus achieving credible proof of the entire transaction history.
We can add a Trust Machine layer to the token contract. For example, we create a Trust Machine canister and remove its controller. It is only used to store the block height and blockhash of the token, as a three-party certificate canister that cannot be tampered with.
When the contract does not have a Controller and the code is open source, the data can only be stored but not deleted and modified. The trust machine container itself can win the trust of the community and developers. If the blockhash and blockheight of the token are stored in the trust machine contract, the token can achieve cryptographic self-evidence, thereby solving the trust crisis (because there is a three-party depository trust machine that cannot be tampered with).
The trust machine needs to provide an additional proof of time. Here you can use the Ledger container of ICP, and you can read the last blockhash and last blockheight of the last block at any time.
In the trust machine, when a token submits the blockhash, the trust machine automatically obtains the blockhash and blockheight of the last block of the current ICP Ledger. This can complete the two-level proof structure of token time and data.
We are actually building a blockchain trust machine with proof of state in a contract, which is a chain within a chain. If we can assign multiple tokens to the same trust machine contract, can we achieve atomicity of transactions between these tokens? This idea is actually very similar to the EVM project Aurora on NEAR.
Self-description of token
On Ethereum, token information needs to be submitted in many centralized places, which will bring inconsistency. Since IC has built-in storage, it can maintain blockchain updates on the chain, and this design can be integrated into Dfinity. These are considerations at the token’s standard interface level.
We should not continue to use the token interface design method of the ICP ledger, otherwise developers can only use the wrapped tokens for transactions, which adds another layer of risk. Based on this, we recommend the following designs:
approve/transferFrom is great for preventing double payments. If there is an interface such as approve/transferFrom, as long as approve a certain quota to the market (for example, if the order is 100 ICP, then approve the quota of 100 ICP to the market), and execute a transferFrom while creating the order. If transferFrom is successful, the creation of the order and the sending of the item to the user are completed, which is simpler for developers.
This is not needed anymore. After the blockchain is implemented, the token will get the same tx hash for the same transaction, the same sender, the same amount, and the same timestamp, and will be rejected by the blockchain. Therefore, only a timestamp is required (the nonce parameter in the update method of the original token is removed, and an optional timestamp is used to replace this method is simpler and easier to use), and the client call can avoid repeated transactions and replay attacks.
Because there is no main chain data, the container only maintains its own data, so we need to store the transaction history and status of the token, which is easy to understand.
The IC supports the storage built into the container, and this part of the storage can be used to complete the storage of its own records instead of EVM events.
The storage space of a container is divided into 4G memory at runtime and 8G stable memory, but for a token with a large transaction history such as USDT, the storage space of a container is far from enough, and expansion needs to be considered.
Here we propose an idea to record an index mapping in the token. An index inside the token points to the storage of the history through an additional archive container. For example, when there are more than 2,000 historical transaction records, 1,000 records will be migrated to the archived container each time, which can alleviate the storage anxiety of the container where the current token is located. There is 4G of memory in each container, and the index occupies a very limited location. By pointing to the container through the index structure, there is theoretically unlimited storage possibility.
Storage containers are automatically created during automatic expansion, and the first expanded container is created only when the transaction is greater than or equal to a certain number, which can save the developer’s cycle to the greatest extent.
When the token cycles are not enough, there will be obstacles in creating a container. To this end, a fallback strategy can be added. By default, the transaction records are stored in the 4G local container storage. After the cycles are enough, the container will be automatically transferred to archive.
The token implements a metric by default, which can obtain the cycle balance of the token, so each token can be monitored for cycles, so as to get an early warning of insufficient cycles.
All failure scenarios such as archiving failures and auto-scaling failures have undergone rigorous testing to ensure that they work properly in a production environment.