Skip to main content

Make a volatility incentive

info

Before reading this guide, make sure you understand FTSOv2's Volatility Incentive Mechanism.

This guide provides code examples demonstrating how to make an FTSOv2 volatility incentive offer using various programming languages. To make a volatility incentive offer, you need three key pieces of information:

  1. RPC Endpoint URL: The RPC Endpoint URL determines which network your code will interact with. You can use a node provider service or point to your own RPC node. A comprehensive list of public and private RPC endpoints for all Flare networks is available on the Network Configuration page.

  2. Contract Address: The address for the FastUpdateIncentiveManager contract varies by network. You can obtain this address in two ways:

    • From the Solidity Reference page: Find the FastUpdateIncentiveManager address for each network on the Solidity Reference page.

    OR

    • Query the FlareContractRegistry Contract: The FlareContractRegistry contract has the same address across all networks. You can query it to get the FastUpdateIncentiveManager contract address. Refer to the specific language guides for examples:
  3. Cost of Increasing the Sample Size: FTSOv2 allows you to increase the sample size, i.e., the expected number of providers who can submit a block-latency feed update. The cost for this increases dynamically with the expected sample size. A single volatility incentive lasts for a period of 8 blocks.

tip

All examples in this guide are available at developer-hub/examples.

This example uses web3.js to make an FTSOv2 volatility incentive offer on Flare Testnet Coston2.

volatility_incentive.js
// THIS IS EXAMPLE CODE. DO NOT USE THIS CODE IN PRODUCTION.
import { Web3 } from "web3";

// FastUpdatesIncentiveManager address (Flare Testnet Coston2)
// See https://dev.flare.network/ftso/solidity-reference
const INCENTIVE_ADDRESS = "0x58fb598EC6DB6901aA6F26a9A2087E9274128E59";
const RPC_URL = "https://coston2-api.flare.network/ext/C/rpc";
// ABI for FastUpdatesIncentiveManager contract
const ABI =
'[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_governanceSettings","internalType":"contract IGovernanceSettings"},{"type":"address","name":"_initialGovernance","internalType":"address"},{"type":"address","name":"_addressUpdater","internalType":"address"},{"type":"uint256","name":"_ss","internalType":"SampleSize"},{"type":"uint256","name":"_r","internalType":"Range"},{"type":"uint256","name":"_sil","internalType":"SampleSize"},{"type":"uint256","name":"_ril","internalType":"Range"},{"type":"uint256","name":"_x","internalType":"Fee"},{"type":"uint256","name":"_rip","internalType":"Fee"},{"type":"uint256","name":"_dur","internalType":"uint256"}]},{"type":"event","name":"DailyAuthorizedInflationSet","inputs":[{"type":"uint256","name":"authorizedAmountWei","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"GovernanceCallTimelocked","inputs":[{"type":"bytes4","name":"selector","internalType":"bytes4","indexed":false},{"type":"uint256","name":"allowedAfterTimestamp","internalType":"uint256","indexed":false},{"type":"bytes","name":"encodedCall","internalType":"bytes","indexed":false}],"anonymous":false},{"type":"event","name":"GovernanceInitialised","inputs":[{"type":"address","name":"initialGovernance","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"GovernedProductionModeEntered","inputs":[{"type":"address","name":"governanceSettings","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"IncentiveOffered","inputs":[{"type":"uint24","name":"rewardEpochId","internalType":"uint24","indexed":true},{"type":"uint256","name":"rangeIncrease","internalType":"Range","indexed":false},{"type":"uint256","name":"sampleSizeIncrease","internalType":"SampleSize","indexed":false},{"type":"uint256","name":"offerAmount","internalType":"Fee","indexed":false}],"anonymous":false},{"type":"event","name":"InflationReceived","inputs":[{"type":"uint256","name":"amountReceivedWei","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"InflationRewardsOffered","inputs":[{"type":"uint24","name":"rewardEpochId","internalType":"uint24","indexed":true},{"type":"tuple[]","name":"feedConfigurations","internalType":"struct IFastUpdatesConfiguration.FeedConfiguration[]","indexed":false,"components":[{"type":"bytes21","name":"feedId","internalType":"bytes21"},{"type":"uint32","name":"rewardBandValue","internalType":"uint32"},{"type":"uint24","name":"inflationShare","internalType":"uint24"}]},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"TimelockedGovernanceCallCanceled","inputs":[{"type":"bytes4","name":"selector","internalType":"bytes4","indexed":false},{"type":"uint256","name":"timestamp","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"TimelockedGovernanceCallExecuted","inputs":[{"type":"bytes4","name":"selector","internalType":"bytes4","indexed":false},{"type":"uint256","name":"timestamp","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"advance","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"cancelGovernanceCall","inputs":[{"type":"bytes4","name":"_selector","internalType":"bytes4"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"dailyAuthorizedInflation","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"executeGovernanceCall","inputs":[{"type":"bytes4","name":"_selector","internalType":"bytes4"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"fastUpdater","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IFastUpdatesConfiguration"}],"name":"fastUpdatesConfiguration","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IIFlareSystemsManager"}],"name":"flareSystemsManager","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"_addressUpdater","internalType":"address"}],"name":"getAddressUpdater","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"Scale"}],"name":"getBaseScale","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"getContractName","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"Fee"}],"name":"getCurrentSampleSizeIncreasePrice","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getExpectedBalance","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"SampleSize"}],"name":"getExpectedSampleSize","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getIncentiveDuration","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"getInflationAddress","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"Precision"}],"name":"getPrecision","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"Range"}],"name":"getRange","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"Scale"}],"name":"getScale","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"_lockedFundsWei","internalType":"uint256"},{"type":"uint256","name":"_totalInflationAuthorizedWei","internalType":"uint256"},{"type":"uint256","name":"_totalClaimedWei","internalType":"uint256"}],"name":"getTokenPoolSupplyData","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"governance","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IGovernanceSettings"}],"name":"governanceSettings","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initialise","inputs":[{"type":"address","name":"_governanceSettings","internalType":"contract IGovernanceSettings"},{"type":"address","name":"_initialGovernance","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isExecutor","inputs":[{"type":"address","name":"_address","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"lastInflationAuthorizationReceivedTs","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"lastInflationReceivedTs","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[],"name":"offerIncentive","inputs":[{"type":"tuple","name":"_offer","internalType":"struct IFastUpdateIncentiveManager.IncentiveOffer","components":[{"type":"uint256","name":"rangeIncrease","internalType":"Range"},{"type":"uint256","name":"rangeLimit","internalType":"Range"}]}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"productionMode","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"Range"}],"name":"rangeIncreaseLimit","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"Fee"}],"name":"rangeIncreasePrice","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[],"name":"receiveInflation","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IIRewardManager"}],"name":"rewardManager","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"SampleSize"}],"name":"sampleIncreaseLimit","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setDailyAuthorizedInflation","inputs":[{"type":"uint256","name":"_toAuthorizeWei","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setIncentiveParameters","inputs":[{"type":"uint256","name":"_ss","internalType":"SampleSize"},{"type":"uint256","name":"_r","internalType":"Range"},{"type":"uint256","name":"_x","internalType":"Fee"},{"type":"uint256","name":"_dur","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setRangeIncreaseLimit","inputs":[{"type":"uint256","name":"_lim","internalType":"Range"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setRangeIncreasePrice","inputs":[{"type":"uint256","name":"_price","internalType":"Fee"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setSampleIncreaseLimit","inputs":[{"type":"uint256","name":"_lim","internalType":"SampleSize"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"switchToProductionMode","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"allowedAfterTimestamp","internalType":"uint256"},{"type":"bytes","name":"encodedCall","internalType":"bytes"}],"name":"timelockedCalls","inputs":[{"type":"bytes4","name":"selector","internalType":"bytes4"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalInflationAuthorizedWei","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalInflationReceivedWei","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalInflationRewardsOfferedWei","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"triggerRewardEpochSwitchover","inputs":[{"type":"uint24","name":"_currentRewardEpochId","internalType":"uint24"},{"type":"uint64","name":"_currentRewardEpochExpectedEndTs","internalType":"uint64"},{"type":"uint64","name":"_rewardEpochDurationSeconds","internalType":"uint64"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateContractAddresses","inputs":[{"type":"bytes32[]","name":"_contractNameHashes","internalType":"bytes32[]"},{"type":"address[]","name":"_contractAddresses","internalType":"address[]"}]}]';

async function main() {
// Connect to an RPC node
const w3 = new Web3(RPC_URL);
const privateKey = process.env.ACCOUNT_PRIVATE_KEY.toString();
const wallet = w3.eth.accounts.wallet.add(privateKey);
// Set up contract instance
const incentive = new w3.eth.Contract(JSON.parse(ABI), INCENTIVE_ADDRESS);
// Get the current sample size, sample size increase price, precision, and scale
const sampleSizeIncreasePrice = await incentive.methods
.getCurrentSampleSizeIncreasePrice()
.call();
console.log(
"Sample Size Increase Price: %i, Current Sample Size: %i, Current Precision %i, Current Scale %i",
sampleSizeIncreasePrice,
await incentive.methods.getExpectedSampleSize().call(),
await incentive.methods.getPrecision().call(),
await incentive.methods.getScale().call(),
);

// Offer the incentive
const tx = await incentive.methods
.offerIncentive({ rangeIncrease: 0, rangeLimit: 0 })
.send({
from: wallet[0].address,
nonce: await w3.eth.getTransactionCount(wallet[0].address),
gasPrice: await w3.eth.getGasPrice(),
value: sampleSizeIncreasePrice,
});
console.log("Transaction hash:", tx.transactionHash);

// Log the new sample size, precision, and scale
console.log(
"New Sample Size: %i, New Precision %i, New Scale %i",
await incentive.methods.getExpectedSampleSize().call(),
await incentive.methods.getPrecision().call(),
await incentive.methods.getScale().call(),
);
}

main();