Taking it up to 11

Filip Koprivec
11 min readJan 16, 2023

--

Moving the tokens around is great, but we can do more¹. We can make the price of our token reflect some price in the real world in real time.

As the following exercises will use the special powers of the Songbird and Flare network we will use some of the associated terminologies. Instead of playing on the goerli test network, we will be using the Coston or Coston2 test network.

The main idea of our contract will be to create another ERC20 token that will calculate the correct price set in BTC but payable in the native token (SGB, FLR or CFLR) using the FTSO system to convert between the prices. All the necessary configs are already prepared in the hardhat.config.ts file so you can just specify the correct network and run deployment and verification.

What is the price of an asset?

How do we get the price information in a smart contract? We already know that we can get some meta information from the msg object in the transaction, but what exactly can we get? The solidity documentation has a great section on this topic: Special Variables and functions, but unfortunately, BTC price is not among them.

The gist of the problem is that blockchains are usually² closed systems, the outer world can read the data from the blockchain, but reading the data from the outside world is non-trivial at best. The solution to this problem is to use a trusted oracle, which is a smart contract that is trusted to provide the correct information. But how do we know that the oracle is correct and can be trusted (we are essentially asking the same question as with blockchains, what is a consensus and is it correct in some sense)? The answer is that we don’t know this in general, but we can use a mechanism that makes it hard for oracle to cheat. Apart from cheating, we also want the oracle to be available and not dependent on a single entity or (attackable) source/infrastructure.

Flare (and Songbird) network uses a system called Flare Time Series Oracle (FTSO) to provide the price information. The FTSO is a decentralized oracle system that uses a combination of multiple (90+) oracles to provide the price information in a decentralized way so that the price information is not dependent on a single entity and additional protocol restrictions make it hard for the oracles to cheat. If you want to know more about how the FTSO works, you can read the FTSO documentation or the FTSO whitepaper.

You should read at least the first two sections of the FTSO documentation to understand how the FTSO works and how you can squeeze more than just the price information from it. But for now, we will just use a few key points:

  • There is a price datapoint for each (of currently 11 available) assets that are refreshed every 3 minutes (each price epoch).
  • Prices are provided by 90+ data providers, so the price is not dependent on a single entity and the median is taken to prevent price manipulation attacks
  • Assets are denominated with a specific number of decimal points. All this info is available directly on the chain by calling the appropriate contracts. This only holds for Flare and Coston2. On Songbird and Coston, assets are denominated in USD/10**5.
  • The price information is available to any smart contract on Flare/Songbird network.
  • The native token price is also available.

At the time of writing, the price information is available for the following assets on the Flare network: XRP, LTC, XLM, DOGE, ADA, ALGO, BCH, DGB, BTC, ETH, FIL, SGB . (Songbird has the same set). Also at the time of writing, the FTSO system on Songbird has been running for more than a year and has proven to be reliable and trustworthy without any issues³, has been used in production multiple times and is considered safe to use. The whole system is developed with upgradability in mind, so new FTSOs can be deployed with a governance vote (the SGB FTSO was deployed at a later point and effortlessly integrated into the system).

Getting the price information from the FTSO system

FTSO system is a collection of smart contracts that are deployed on the Flare network. We will use a Flare-provided library that contains the necessary interfaces and mocks to interact with the FTSO system and can be used in the same way as the openzeppelin libraries. The library we use has interfaces adapted to solidity version ^0.8.0, Each asset has its own FTSO contract implementing an IFtso interface which can (among other things) be used to get the price information for the asset. To know, which FTSO contract to use, we use a contract FtsoRegistry that contains a mapping from the asset name to the currently valid FTSO for the asset. This is important, as FTSOs can be upgraded and the registry should be considered the source of truth for the currently valid FTSO for each asset. But how do we get the address of the FtsoRegistry contract? We use a special hardcoded address 0x1000000000000000000000000000000000000003 where a special contract PriceSubmitter is deployed on both Flare and Songbird networks. The PriceSubmitter contract is a special contract that is used to submit the price information to the FTSO system and is the only contract that can submit the price information. Since the contract has a fixed address, the Flare team has added some additional methods to it so it can serve as a gateway for important contracts, that might change their address.

Flare FTSO schema
Songbird FTSO schema

Exercise:

  • Use Flare blockscout to find the price of SGB and BTC in USD and compare it to the price on CoinGecko. Start from PriceSubmitter work your way to the FtsoRegistry and try to find out how to get the price of SGB and BTC in USD (If you get stuck, check the documentation)
  • Do the same for the Songbird network on Songbird blockscout.

How exactly to get the price on Flare/Songbird? The easiest way is to use getPrice family of methods of the FtsoRegistry contract. There are two different methods, one accepts the asset symbol as a string (more user-friendly) and the other accepts the asset index (easier for computers), we will use the string one, as it is easier when using the FTOS system for the first time. One can also first access the FTSO contract for the specific asset and then read the prices. This is a bit more involved, but gives you more flexibility, as you can use the FTSO contract to get historical prices, additional price data etc.

Real-priced ERC20 token

We will now write a contract implementing an IERC20Metadata interface with a specific mint method that will be backed by the price information from the FTSO system. The token will be the same as before, except that an additional mint method will be present that will mint (create) tokens to a caller according to a specified amount of native assets. When a transaction is submitted to the mint method, the contract will check the price of the native asset and mint the corresponding amount of tokens to the caller. Native tokens (SGB on Songbird and FLR on Flare), will accumulate on the contract and the owner will be able to withdraw them at any time. To make it more interesting, we will also limit the maximal supply of tokens to some user-provided value.

Before we jump into the code, let’s first see how to calculate the correct price of the token. Our inputs are

  • Price of BTC in USD (technically in 10**(-5) USD, but that is not important in our calculation): pBtc
  • Price of FLR/SGB in USD (also with 10**(-5)): pFlr
  • Decimal precision of our token (amount of decimals): d
  • Amount of our tokens minted per one BTC (We could just use the amount minted in wei, but this makes the conversions more explicit): tPrice
  • Amount of native tokens (in wei) the user has sent to the contract: mValue

The amount of tokens minted is calculated as

And the full code is a bit more involved (but not much more).

This calculation uses fixed decimals to make it easier, but Flare supports different decimal precision for specific data feeds, so the smart contract code is modified to already account for this.

Feel the power of the FTSO system

There are a few new things here. We import additional contracts from the correct flare repository package. These are the interfaces we will need to interact with the contract on the network. You should be familiar with most of the fields, we just add some new fields to save the symbol of native tokens and tokens in which we will denominate our ERC token.

A new thing is modifier.

modifier onlyOwner(){
if(msg.sender != owner){
revert OnylOwner();
}
_;
}

Modifiers are special functions that can be grouped around other functions to add additional functionality (they are usually used for common checks and validations). Modifier functions are declared with the modifier keyword and are used by wrapping them around the function they are modifying. The modifier is defined similarly as a normal function, except, it has another special parameter _; which is used to indicate where the modified function should be inserted. In our case, the modifier first checks if the sender is the owner of the contract and if not, it reverts the transaction, otherwise it continues with the execution of the function.

We define a function to get the price submitter contract.

function getPriceSubmitter() public virtual view returns(IPriceSubmitter) {
return IPriceSubmitter(0x1000000000000000000000000000000000000003);
}

We could have made the priceSubmitter a constant, but this would make the testing much more difficult, so we opted for a function that returns the address of the price submitter contract (a smart compiler should be able to optimize this function call and make it a constant). The virtual keyword is used to force the function to be overridden in child contracts.

The only difficult parts are the _mint and getTokenPriceWei functions

Calculating the token price

We first acquire ftsoRegistry and cast it to the correct type (PriceSubmitter returns a supertype of FtsoRegistry due to implementation details). Then we get the current price of the foreign token and the native token and calculate the amount of tokens we will mint. We then check and update the total supply, mint the tokens to the sender and transfer any remaining value to the sender.

Songbird version is very similar, we just import different interfaces (and deploy it to a different network): https://gist.github.com/jO-Osko/4568a0c4df03f63a9961d8ae528495a3?file=DynamicTokenSongbird.sol

Testing

How can we test the dynamic token contract?

We first create another testable contract that will be used to test the dynamic token contract. The main point of the new contract is to have a settable PriceSubmitter contract address, so a mock price submitter can be used in tests. We also import mocks so that typechain will generate correct artifacts we can then use in tests.

All the testing will be done on an instance of TestableDynamicToken contract, but since the basic functionality is the same, we will effectively test the DynamicToken contract.

Exercise: Prepare a testable version of Songbird token.

The basic tests look like this:

Note: We import artifact differently this time (assuming that you also compiled Songbird version), as we have to disambiguate between artifacts for Flare and Songbird.

We use additional mocks provided in the flare periphery package to quickly simulate prices on the FTSO system.

Exercise: Write additional tests to check that value is returned correctly and that the supply ceiling is respected.

Additional exercise: Upgrade the contract, so that users can burn tokens and get back the value they are worth now. Are there any problems with this approach? Can the user get more value than they put in (or can the contract run out of tokens), explain why this is the case? Is the value of this token highly correlated to some other more liquid (maybe fictional) financial instrument?

Deploying

Deploying is done in the same way as deploying any other contract. Except that we can’t deploy this to goerli, since the price submitter contract is not deployed there. So we will use coston2 testnet, which mirrors the Flare network (similarly, coston mirrors Songbird mainnet).

Head over to the hardhat config file and find out the network configuration for coston testnet and where the faucet is located. As both flare and songbird use EVM and are Ethereum compatible, we can simply reuse the address from previous deployments to do things all over again. Get some CFLR2 from the faucet and deploy this dynamic token on the coston2 testnet either using cli or remix (pro tip: use cli). Use the scripts/deploy.js from before to remember how to do it and play around with the token using the blockscout. Don't forget to specify the correct network with --network coston2 when running deployment. Take care to deploy the token with the correct parameters depending on the target network (check which assets are available on the ftso registry on the network you are deploying on).

Verification

The larger our contracts are, the more difficult verification gets. We can still verify our contract the same way as before, but it gets progressively easier to verify larger contracts. The same verification plugin will work as before, but we will need to supply the constructor parameters. Create a new file verification/verifyDynamicTokenConstructor.js and add the following code, that exports constructor parameters

and then run npx hardhat verify DYNAMIC_TOKEN_ADDRESS --constructor-args verification/verifyDynamicTokenConstructor.js --network coston2

WOHO, you managed to deploy a proper contract that uses the raw power of the Flare FTSO ecosystem, congrats. Play around a bit and see how it responds to changing FTSO prices.

The next post will show how to do the same thing on Songbird network, where contracts are just a bit different. Songbird is a canary network for Flare, so you can use it to properly test big applications under real conditions before moving to Flare.

If you want to deploy this to a real network, you would use the same approach, but you will first need to purchase some real SGB or FLR tokens and use them to deploy the contract⁴.

Dang, you are on a good way to mastering the smart contract development and taming the power of Flare, but there is much more. If you follow the tutorial, you will uncover how to make your own NFT and connect it to even more powerful Flare components.

[1]: Well not everywhere, but some chains do support this. If your preferred chains do not support some sort of communication with the external world, you should call your chain representative and complain. But seriously, if you want to see this feature on your chain there is almost always a 3rd party solution to implement this via oracles/bridging or some other technology. Or you should just switch to Flare where this is already implemented and most of the blockchains are connected via bridges.

[2]: This is of course a generalization, but it holds pretty well.

[3]: Even when a glitch in December 2021 caused some temporary issues on major price aggregators, the FTSO system didn’t have any issues and continued to provide the correct price information.

[4]: Reply to my tweet with an address and link to your GitHub project with an interesting contract using the FTSO system (the contract should have some additional functionality — use your imagination, and be creative) and I will send some SGB and FLR tokens to the first 10 repositories I judge to be worth it, so you can test and deploy.

--

--

No responses yet