Skip to main content

Migrating from v1

This guide is for applications moving from FTSOv1 to FTSOv2. Briefly, FTSOv2 comprises of:

  • Block-Latency Feeds: These feeds are updated with each new block, approximately every 1.8 seconds. They can be accessed through FtsoV2Interface and are available directly on-chain.
  • Anchor Feeds: These feeds are provided through Scaling with a latency of 90 seconds. Feeds can be verified using FtsoV2Interface but are not immediately available on-chain.

A key difference between the two is the introduction of a payment mechanism for data access. This system helps prevent unnecessary data requests and ensures sustainable funding. For more details, refer to the FeeCalculator contract, which calculates fees for data access using the calculateFeeByIds and calculateFeeByIndices methods.

Additionally, a new Long Term Support (LTS) system has been launched to ensure continued access to essential data and metadata within the Flare ecosystem. A series of LTS contracts have been introduced, each aligned with a specific product in the Flare ecosystem. It is strongly recommended to use these LTS contracts for data access instead of querying individual contracts or interfaces, as they are designed for long-term stability, even as underlying protocols evolve or migrate.

Deprecated contracts (v1)

PriceSubmitter

The PriceSubmitter contract (IPriceSubmitter) is being deprecated as part of the transition to the new version of the FTSO system.

If you were using PriceSubmitter for the following purposes, here are the recommended alternatives:

  • Accessing other important contracts: Use the FlareContractRegistry instead.

  • Voting-related functionalities: These are now integrated into the new FTSO system.

  • Random number generation (getCurrentRandom or getRandom, applicable to Flare only): Switch to the new RandomNumberV2Interface. The new protocol updates random numbers every 90 seconds (aligned with the voting epoch duration, as returned by votingEpochDurationSeconds). This setting is immutable but could change if the protocol configuration is updated. In contrast, the old protocol updated random numbers every 180 seconds.

Required Changes:

  • Use RandomNumberV2 instead of PriceSubmitter (ensure you update to the new contract address and interface).
  • Method updates:
    • getCurrentRandomgetRandomNumber
    • getRandom(epochId)getRandomNumberHistorical(epochId)

FTSO

The legacy FTSO contract is being deprecated and replaced. A minimal proxy will be deployed at the same address to provide basic backward compatibility. The replacement will be FtsoProxy, which will respond to a limited subset of calls that the old FTSO contract handled. Although the addresses will change, the FTSORegistry will be updated to point to the new contract addresses.

  • Random number retrieval: Switch to the new RandomNumberV2Interface. The proxy contract will still return current and historical random numbers, but they will be uniform across all FTSO contracts, and randomness will be sourced from the new provider.

  • Fetching the current price: For methods like getCurrentPrice, getCurrentPriceDetails, or getCurrentPriceWithDecimals, switch to the FTSOv2Interface. While the proxy interface will continue to function temporarily, it will not receive future updates.

  • Fetching historical prices: For methods such as getEpochPrice, or retrieving prices for specific voters or trusted data providers, the proxy contract WILL REVERT. It is essential to migrate to the new FTSOv2Interface for these functionalities.

FTSORegistry

The FTSORegistry (IFTSORegistry) is being deprecated. While you can continue to use it temporarily with its backward-compatible methods, it is strongly recommended to transition to the new interfaces as soon as possible, as FTSORegistry will no longer be maintained in the future.

  • Getting FTSOs: The methods getFtso, getFtsoBySymbol, getFtsoIndex, getFtsoSymbol, and getSupportedIndices/Symbols/Ftsos are now deprecated. They will still return the proxy implementation of FTSO, but these proxies will not be maintained moving forward. It is recommended to update your code to use the FTSOv2Interface for price retrieval. Additional details like decimals and timestamps for individual FTSOs are now considered deprecated and can be obtained by querying prices directly through FTSOv2.

  • Reading prices using methods such as getCurrentPrice(_ftso_index/_symbol) or their array and decimal implementations is also deprecated. Although these methods will still return correct values, they will not be updated in the future. Use the FTSOv2Interface instead to ensure future compatibility.

  • Getting supported FTSOs: This function will return correct results for legacy price pairs but will not be updated for new pairs introduced in the FTSOv2 system. To retrieve information about available pairs in the new system, you should transition to the FTSOv2Interface interface.

  • Read prices directly from FtsoV2Interface: This will give you up-to-date prices and more detailed information. Additionally, the FtsoV2Interface is part of the Long Term Support (LTS) system, ensuring it will be maintained for an extended period.

  • New system indexing and ID scheme:

    • The indexing and ID scheme has changed. Old indices are now invalid, so do not use the previous getFeedByIndex method with the old indices.
    • IDs in the new system are 21-byte values, formatted as "${OLD_FEED_NAME}/USD" and zero-padded.
    • Use the getFeedById method in the new interface to retrieve old prices with this format: getFeedById(bytes21(bytes.concat(bytes1(1), bytes(string.concat(OLD_FEED_NAME, "/USD"))))).

FTSORewardManager

The reward system is undergoing significant changes. While an implementation of the old FTSORewardManager contract is provided, it will only support the most basic claim types and require pre-provided reward proofs.

  • Claiming rewards: Switch to the new RewardsV2Interface. The proxy contract will still allow you to claim rewards from the FTSO system, but it will not support claiming fees for data providers.

  • Claiming rewards when delegating by amount: This feature is no longer supported in the new system.

LTS interfaces

The primary goal of the Long Term Support (LTS) interfaces is to offer a stable and reliable way to access essential data and metadata within the Flare ecosystem. These interfaces are designed for long-term maintenance, ensuring continuity even as underlying contracts evolve or protocols migrate to new versions. Each LTS interface is aligned with a specific product within the Flare ecosystem, providing consistency and ease of use over time.

InterfaceContract registry nameNotes
ProtocolsV2InterfaceProtocolsV2Primary interface for managing protocol related metadata.
RewardsV2InterfaceRewardsV2Primary interface for managing all protocol rewards.
RandomNumberV2InterfaceRandomNumberV2Primary interface for random number generation.
FtsoV2InterfaceFtsoV2Primary interface for interacting with FTSOv2.
warning
  • RandomNumberV2Interface: In addition to providing random numbers, the new methods also return a _isSecureRandom flag. Learn more about this flag in the guide on Secure Random Numbers.
  • FtsoV2Interface: Provides access to both block-latency and anchor feeds. You can retrieve feeds using the getFeedById or getFeedByIndex methods. These methods are now payable, and while the current fee is set to 0, it is advisable to use FeeCalculator to calculate the fee and be prepared for potential future changes.

Migration proxies

For the time being, a set of proxy contracts is provided to allow access to the old data and reward systems.

Do not use for new developments

These proxies offer a temporary solution to ensure that previously deployed contracts can continue functioning until they are fully updated.

FtsoProxy

The FtsoProxy contract is designed to maintain backward compatibility with the old FTSO contract. While it will be deployed at different addresses from the original FTSO contract, the FTSORegistry will be updated to point to these new addresses.

Methods in FtsoProxy
FtsoProxy.sol
   /**
* Always return true, as the proxy is always active.
*/
function active() external pure returns (bool) {
return true;
}

/**
* Will return the current epoch id correctly as defined by FSP
*/
function getCurrentEpochId() external view returns (uint256) {
}

/**
* Will return the epoch id correctly as defined by FSP.
* Beware, the function will produce different results than the old FTSO contract.
*/
function getEpochId(uint256 _timestamp) external view returns (uint256) {
}

/**
* Will return the current random correctly
*/
function getRandom(uint256 _votingRoundId) external view returns (uint256 _randomNumber) {
}

/**
* @dev Deprecated - reverts
*/
function getEpochPrice(uint256) external pure returns (uint256) {
revert("not supported");
}

/**
* Will return current price epoch data as defined by FSP
*/
function getPriceEpochData() external view
returns (
uint256 _epochId,
uint256 _epochSubmitEndTime,
uint256 _epochRevealEndTime,
uint256 _votePowerBlock,
bool _fallbackMode
)
{
}

/**
* Will return the price epoch configuration as defined by FSP
*/
function getPriceEpochConfiguration() external view
returns (
uint256 _firstEpochStartTs,
uint256 _submitPeriodSeconds,
uint256 _revealPeriodSeconds
)
{
}

/**
* @dev Deprecated - reverts
*/
function getEpochPriceForVoter(uint256, address) external pure returns (uint256) {
revert("not supported");
}

/**
* Will return the current price correctly
*/
function getCurrentPrice() external view returns (uint256, uint256) {
}

/**
* Will return the current price with decimals correctly
*/
function getCurrentPriceWithDecimals()
external view
returns (
uint256 _value,
uint256 _timestamp,
uint256 _decimals
)
{
}


/**
* Will return the current price with details correctly
*/
function getCurrentPriceDetails()
external view
returns (
uint256,
uint256,
PriceFinalizationType,
uint256,
PriceFinalizationType
)
{
}

/**
* @dev Deprecated - reverts
*/
function getCurrentPriceFromTrustedProviders() external pure returns (uint256, uint256) {
revert("not supported");
}

/**
* @dev Deprecated - reverts
*/
function getCurrentPriceWithDecimalsFromTrustedProviders() external pure returns (uint256, uint256, uint256) {
revert("not supported");
}

/**
* Will return the current random correctly
*/
function getCurrentRandom() external view returns (uint256 _currentRandom) {
}

FtsoRewardManagerProxy

The FtsoRewardManagerProxy contract is designed to maintain backward compatibility with the old FTSORewardManager contract. It will be deployed at a different address than the original contract.

This proxy only supports the most basic reward claim types and requires that reward proofs be provided in advance.

Methods in FtsoRewardManagerProxy
FtsoRewardManagerProxy.sol
    /**
* @dev Claims rewards correctly for delegation fees, assuming the proofs were already provided.
*/
function claimReward(
address payable _recipient,
uint256[] calldata _rewardEpochs
)
external
returns (uint256 _rewardAmount)
{
}

/**
* @dev Claims rewards correctly for delegation fees, assuming the proofs were already provided.
*/
function claim(
address _rewardOwner,
address payable _recipient,
uint256 _rewardEpoch,
bool _wrap
)
external
returns (uint256 _rewardAmount)
{
}


/**
* @dev Returns the current fee percentage for the data provider.
*/
function getDataProviderCurrentFeePercentage(address _dataProvider)
external view
returns (uint256 _feePercentageBIPS)
{
}

/**
* @dev Returns the fee percentage for the data provider for the given reward epoch.
*/
function getDataProviderFeePercentage(
address _dataProvider,
uint256 _rewardEpoch
)
external view
returns (uint256 _feePercentageBIPS)
{
}

/**
* @dev Returns the fee percentage changes for the data provider.
*/
function getDataProviderScheduledFeePercentageChanges(address _dataProvider) external view
returns (
uint256[] memory _feePercentageBIPS,
uint256[] memory _validFromEpoch,
bool[] memory _fixed
)
{
}

/**
* @dev Returns the epoch reward correctly
*/
function getEpochReward(uint256 _rewardEpoch) external view
returns (uint256 _totalReward, uint256 _claimedReward)
{
}

/**
* @dev Returns the reward state correctly
*/
function getStateOfRewards(
address _beneficiary,
uint256 _rewardEpoch
)
external view
returns (
address[] memory _dataProviders,
uint256[] memory _rewardAmounts,
bool[] memory _claimed,
bool _claimable
)
{
}

/**
* @dev Returns the epochs with claimable rewards correctly
*/
function getEpochsWithClaimableRewards() external view
returns (uint256 _startEpochId, uint256 _endEpochId)
{
}

/**
* @dev Returns the next claimable reward epoch correctly
*/
function nextClaimableRewardEpoch(address _rewardOwner) external view returns (uint256) {
}

/**
* @dev Returns the epochs with unclaimed rewards correctly
*/
function getEpochsWithUnclaimedRewards(address _beneficiary) external view
returns (uint256[] memory _epochIds)
{
}

/**
* @dev Returns the claimed rewardr correctly
*/
function getClaimedReward(
uint256 _rewardEpoch,
address _dataProvider,
address _claimer
)
external view
returns (
bool _claimed,
uint256 _amount
)
{
}

/**
* @dev Returns the reward epoch to expire next correctly
*/
function getRewardEpochToExpireNext() external view returns (uint256) {
}

/**
* @dev Returns the reward epoch vote power block correctly
*/
function getRewardEpochVotePowerBlock(uint256 _rewardEpoch) external view returns (uint256) {
}

/**
* @inheritdoc IFtsoRewardManager
*/
function getCurrentRewardEpoch() external view returns (uint256) {
return rewardManager.getCurrentRewardEpochId();
}

/**
* @inheritdoc IFtsoRewardManager
*/
function getInitialRewardEpoch() external view returns (uint256 _initialRewardEpoch) {
return rewardManager.getInitialRewardEpochId();
}

/**
* @inheritdoc IFtsoRewardManager
* @dev Deprecated
*/
function claimRewardFromDataProviders(
address payable,
uint256[] calldata,
address[] calldata
)
external pure returns (uint256)
{
// return 0
}

/**
* @inheritdoc IFtsoRewardManager
* @dev Deprecated
*/
function claimFromDataProviders(
address,
address payable,
uint256[] calldata,
address[] calldata,
bool
)
external pure returns (uint256)
{
// return 0
}

/**
* @inheritdoc IFtsoRewardManager
* @dev Deprecated - reverts
*/
function autoClaim(address[] calldata, uint256) external pure {
revert("not supported, use RewardManager");
}

/**
* @inheritdoc IFtsoRewardManager
* @dev Deprecated - reverts
*/
function setDataProviderFeePercentage(uint256)
external pure
returns (uint256)
{
revert("not supported, use WNatDelegationFee");
}

/**
* @dev Deprecated - returns empty array, empty array, false
*/
function getStateOfRewardsFromDataProviders(
address,
uint256,
address[] calldata
)
external pure
returns (
uint256[] memory,
bool[] memory,
bool
)
{
}

/**
* Deprecated - returns 0, 0
*/
function getDataProviderPerformanceInfo(
uint256,
address
)
external pure
returns (
uint256,
uint256
)
{
}

Usable existing interfaces

InterfaceDescription
FlareContractRegistryProvides access to the addresses of all essential contracts within the Flare ecosystem. It is deployed at a fixed address across all networks, with updates to addresses managed through governance. This registry is the recommended entry point for contract interactions on all Flare networks. For easier use, consider utilizing the ContractLibrary from the flare-periphery-contracts package, which wraps the IFlareContractRegistry and simplifies interactions with Flare's smart contracts.
FeeCalculatorOn-chain prices are now subject to potential fees, though initial fees are set to 0. The FeeCalculator contract is designed to be flexible, allowing for the introduction of fees in future use cases. It calculates fees for accessing data using the calculateFeeByIds and calculateFeeByIndices methods. To stay prepared for future updates to the fee structure, it is advisable to use the FeeCalculator contract in relevant applications.

Example migration contract

FtsoV2MigrationExample.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9.0;

import {ContractRegistry} from "@flarenetwork/flare-periphery-contracts/coston2/ContractRegistry.sol";
import {FtsoV2Interface} from "@flarenetwork/flare-periphery-contracts/coston2/FtsoV2Interface.sol";

contract FtsoV2FeedConsumer {
FtsoV2Interface internal ftsoV2;

/**
* @dev Constructor initializes the FTSOv2 contract by fetching the FtsoV2Interface address from the ContractRegistry.
*/
constructor() {
ftsoV2 = ContractRegistry.getFtsoV2();
}

/**
* @dev Converts a feed name to a bytes21 ID with a fixed category (1) and USD quote.
* @param _name The name of the feed, e.g. FLR.
* @return A bytes21 ID representing the feed with USD quote.
*/
function convertToFeedId(
string memory _name
) internal pure returns (bytes21) {
return
bytes21(
bytes.concat(
bytes1(uint8(1)), // Category 1 for crypto feeds
bytes(string.concat(_name, "/USD")) // Append "/USD" to the feed name
)
);
}

/**
* @dev Fetches the current feed values, decimals, and timestamp from FtsoV2 for the specified feeds.
* @param _feedNames An array of feed names.
* @return _feedValues The current values of the specified feeds.
* @return _decimals The number of decimals for each feed.
* @return _timestamp The timestamp of the latest update for the feeds.
*/
function getFtsoV2CurrentFeedValues(
string[] memory _feedNames
)
external
payable
returns (
uint256[] memory _feedValues,
int8[] memory _decimals,
uint64 _timestamp
)
{
bytes21[] memory feedIds = new bytes21[](_feedNames.length);

// Preprocess feed names to feed IDs
for (uint256 i = 0; i < _feedNames.length; i++) {
feedIds[i] = convertToFeedId(_feedNames[i]);
}

// Fetch feed data from the FtsoV2 contract
(
uint256[] memory feedValues,
int8[] memory decimals,
uint64 timestamp
) = ftsoV2.getFeedsById(feedIds);

return (feedValues, decimals, timestamp);
}

/**
* @dev Fetches the current feed values in Wei (adjusted for decimals) and timestamp from FtsoV2 for the specified feeds.
* @param _feedNames An array of feed names.
* @return _feedValues The current values in Wei.
* @return _timestamp The timestamp of the latest update for the feeds.
*/
function getFtsoV2CurrentFeedValuesInWei(
string[] memory _feedNames
)
external
payable
returns (uint256[] memory _feedValues, uint64 _timestamp)
{
bytes21[] memory feedIds = new bytes21[](_feedNames.length);

// Preprocess feed names to feed IDs
for (uint256 i = 0; i < _feedNames.length; i++) {
feedIds[i] = convertToFeedId(_feedNames[i]);
}

// Fetch feed values in Wei and timestamp from FtsoV2 contract
(uint256[] memory feedValues, uint64 timestamp) = ftsoV2
.getFeedsByIdInWei{value: msg.value}(feedIds);

return (feedValues, timestamp);
}
}