Only this pageAll pages
Powered by GitBook
1 of 74

Fantom Opera

Loading...

Loading...

Sonic Migration

Loading...

Loading...

Loading...

Wallets

Loading...

Loading...

Loading...

Loading...

Loading...

Staking

Loading...

Loading...

Loading...

Loading...

Build on Opera

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Funding

Loading...

Run a Node

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Technology

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Security

Loading...

Loading...

Introduction

This documentation offers comprehensive details on using and developing applications on the Fantom Opera chain.

Fantom Opera migrated to a new chain called Sonic in December, 2024. Follow the instructions in Migration if you're holding FTM.

Opera

Opera is a blockchain and smart contract platform that provides developers with exceptional scalability and storage capabilities, while delivering a fast and seamless experience for users. Its native token is FTM.

The majority of this documentation aims to help users learn more about Opera and guide developers on building applications on the chain. Bridge assets to Opera to get started.

Bridge

You can bridge assets to Opera using the protocols below to unlock access to a wealth of apps with instant, cheap transactions.

Wormhole

Use PortalBridge to bridge USDC from Ethereum to Opera. Upon completion, you will receive USDC.e on Opera, the canonical stablecoin for our chain.

You can only bridge the USDC stablecoin from Ethereum and not from any other chains.

Squid Router

To bridge any asset from most EVM chains to Opera, use Squid. Powered by Axelar, Squid automatically swaps and bridges assets for you. It leverages existing DEXs on each chain to swap and bridge native tokens.

Axelar handles all cross-chain gas conversion from the source-chain token to the destination-chain token, so users don’t need to maintain wallets on different chains, hold tokens for gas fees, or make multiple transactions to complete a transfer.

For example, you can bridge ETH on Arbitrum to FTM on Opera in one step.

Providers

Oracles

API

GraphQL

Contracts

Mainnet

Overview

Running a validator node on the Opera chain presents a unique opportunity to actively contribute to the security and decentralization of the network. By participating as a validator, you help validate transactions and create new blocks, ensuring the integrity and efficiency of the blockchain.

This involvement not only supports the broader ecosystem but also provides potential financial incentives through rewards distributed to validators. Additionally, being a validator offers a deeper understanding and engagement with blockchain technology, positioning individuals and organizations at the forefront of a rapidly evolving industry. It is a chance to be part of a community driving innovation and fostering a robust, decentralized future.

Follow the tutorials in the menu to get started!

Rabby

Rabby Wallet includes a feature that automatically switches the wallet to the appropriate chain based on the site you visit. As such, it should switch to Opera automatically when you visit an app.

For any questions, please reach out to us.

Migration FAQ

Below are the most frequently asked questions specifically regarding the migration process from Fantom Opera to Sonic.

What if I hold FTM somewhere other than Opera?

Check out the .

Does the upgrade portal handle app token migrations?

No, the official upgrade portal only supports FTM to S migration on Opera. Each project must implement its own migration solution.

What happens to my LP positions during migration?

LP positions must be broken, tokens migrated individually, and positions re-created on Sonic.

What happens if I don't migrate during the two-way period?

You'll still be able to upgrade from FTM to S but not back to FTM from S.

Will my transaction history migrate?

No, transaction history remains on the original chain. Sonic is a fresh chain with no history.

How do I know the migration method used by a token I'm holding?

Check your project's announcements or contact the team directly.

fWallet

is the official wallet for the Opera chain, required to participate in staking or governance.

You can connect to fWallet using various third-party wallets, including Rabby, MetaMask, Coinbase Wallet, and others.

With fWallet, you can:

  • Send FTM and other tokens

  • Swap tokens, powered by OpenOcean

For any questions, please .

Coinbase Wallet

comes preloaded with the Opera chain. As such, it should switch to Opera automatically when you visit an app.

For any questions, please .

Overview

Developers should build on the Opera chain due to its impressive performance, scalability, and low transaction costs, all enabled by its .

Opera provides fast transaction finality, typically within one second, which ensures a seamless user experience for applications. Additionally, the chain's compatibility with the Ethereum Virtual Machine (EVM) allows developers to easily port their existing Ethereum apps to Opera with minimal modifications

Furthermore, Opera's commitment to decentralization and high throughput positions it as a forward-thinking platform capable of supporting a wide range of applications, from DeFi to NFTs and beyond, making it a versatile and attractive choice for developers aiming to build next-generation blockchain solutions.

Follow the tutorials in the menu to get started!

Sonic Upgrade Handbook
innovative technology stack

Overview

Staking on Opera involves locking up a certain amount of FTM (Opera's native token) to support the network's operations, such as block production and validation. In return, users receive rewards in the form of additional FTM. This process helps secure the network and maintain its integrity.

Validators are required to stake at least 50,000 FTM to validate transactions and produce blocks to earn rewards. However, users can delegate any amount from 1 FTM to existing validators to earn rewards without needing the technical knowledge to operate a validator.

Opera uses a fluid staking model. Users can either stake without a lock-up period for the minimum annual percentage rate (APR) or select a lock-up period between 14 and 365 days for an increased APR. This model combines long-term sustainability for the network with flexibility for stakes.

Rewards

In the fluid staking model, your effective APR:

  • Increases proportionally with your lock-up period

  • Decreases proportionally with the average lock-up period of all stakers

  • Decreases proportionally with the total amount of FTM staked by all stakers

To get an estimate for potential rewards, please use our rewards calculator.

Staking on Opera

There are two ways to participate in staking on Opera:

  • Run a validator node

  • Delegate to an existing validator node

Comparison
Delegation
Validator Node

Passive

✅

❌

Minimum requirements

1 FTM

50,000 FTM

Needed expertize

None

Technical

Rewards

Staking rewards minus a 15% fee paid to delegated validator

Staking rewards plus a 15% fee from delegators' rewards

Running a validator node earns more rewards but requires active management, operational costs, and technical knowledge.

Unit Test Contract

Smart contract unit tests are written in JavaScript. In this tutorial, we provide two examples of unit testing using Hardhat and Truffle.

Unit Testing in Hardhat

https://github.com/Fantom-foundation/unittestexample-hardhat

Unit Testing in Truffle

https://github.com/Fantom-foundation/unittestexample-truffle

Each example repository contains the following:

  • contracts Folder: Contains smart contract files.

  • test Folder: The unit test files are under the test folder.

  • README.md File: Contains instructions for compiling, testing, deploying, and verifying the smart contracts.

In the above examples, unit tests are included in one single JavaScript file. However, you can have as many tests and files as you need for your project. In the examples, there are three types of tests:

  1. Checking if a value is what is expected

  2. Checking if an event is fired with the correct arguments

  3. Checking if a revert has occurred

Stake FTM

Staking is a vital part of securing the Opera chain through proof-of-stake. Validator nodes must stake FTM, incentivizing them to act honestly in block production as they risk losing their stake otherwise.

However, FTM holders can also participate by staking their FTM and delegating it to an existing validator node to earn rewards. Opera offers several staking benefits, including no minimum stake, no mandatory lock-up period, and liquid staking services!

How to Stake FTM

  1. Head to fWallet.

  2. Click on Staking

  3. Choose the amount of FTM you would like to stake and then click on Choose a validator

  4. Click on Create new delegation

  5. Choose a validator from the list

  6. Click Stake and continue

  7. Choose between the 1.8% base APR with no lock-up period or lock your stake for up to 365 days for an increased APR

Once you have staked, you’ll begin to earn rewards actively. 15% of the rewards you earn will be distributed as a fee to the node to which you’ve delegated your stake.

To increase your FTM stake amount at any time, you can repeat the process above to create a new delegation. As such, you can create several delegations with multiple lock-up periods.

Rewards and Compounding

The Rewards section of the Staking page shows the amount of FTM you have earned from staking.

You can compound these rewards into your delegations to increase your stake and earn more rewards, or you can claim all rewards to receive the FTM directly in your wallet.

Unbonding Period

Note that there's a seven-day unbonding period when you unstake your FTM. After initiating the unstaking process, you must wait for seven days. Once this period has passed, return to the Staking page, navigate to the Withdraw tab, and withdraw your FTM to your wallet.

Additionally, while it is possible to unstake your FTM before your chosen lock-up period ends, you’ll receive only half of the base rate of 1.8% APR rewards; however, since you receive rewards actively, the penalty will be deducted from your staked FTM as you unstake.

For example, you stake and lock 100,000 FTM for one year, giving you a 6% APR, which equals 6,000 FTM in total rewards. Approximately at day 300, you’ll have received almost 5,000 FTM, at which point you decide to unstake before the one-year period ends.

Your updated APR is 0.9%, half the base rate. With your 100,000 FTM, your rewards would equal 900 FTM. However, since you’ve already received 5000 FTM, you’ll receive only 95,900 FTM when you unstake to account for the extra 4100 FTM in rewards you forfeit by unstaking early.

As such, you will never receive less FTM than you staked originally.

Governance

Governance on a decentralized platform empowers token holders to actively shape and influence its future, ensuring it evolves in the right direction. On Opera, governance is an on-chain process that lets FTM (Opera's native token) stakers submit and vote on proposals that determine changes to the platform’s mechanics and tokenomics.

Voting

To vote on a proposal, go to the governance section in your fWallet, open an active proposal, and cast your vote on the choice with which you agree most. Note that you’ll need to stake FTM tokens to vote. Each token equals one vote.

When you delegate your stake to a validator, your voting power is still equal to the number of FTM tokens you’ve staked. However, if you choose not to vote on a proposal, your voting power is given to your validator who adds your votes to their own. This helps to increase overall participation and prevent low voting turnouts. Once you do vote, they lose your voting power.

Proposals

Governance proposals outline potential actions that aim to improve Opera. If you’ve staked FTM, you can submit one by paying a 100 FTM fee through the governance section of your fWallet.

Proposals aren’t restricted to a simple yes or no option — voters can express their level of agreement across a range of custom options, which allows for more nuanced decision-making.

Proposals have a set date by which a minimum number of voters must have participated, and they must have agreed above a certain percentage. For most proposals, at least 55% of FTM stakers must cast their vote, and the average agreement among them must also be 55% or higher for the proposal to pass, both of which need to be achieved by a certain date.

If a proposal passes, the Fantom Foundation will implement the proposed changes or upgrades.

Getting Started

GraphQL API

Public access point: https://xapi.fantom.network

The API server delivers a high-performance GraphQL API for Opera, offering access to both low-level and aggregated blockchain data for remote clients. This allows client developers to focus on their application's business logic, without having to manage the complexity of data and entity relationships within Opera.

The API server is published on the fantom-api-graphql GitHub repository.

We also support standard Ethereum Web3 API. Use the websocket provider with our public API address wss://wsapi.fantom.network to interact with the Opera chain using the Web3 client library.

Technology

We utilize several technologies to ensure the API functions effectively. If you intend to run your own instance, please review our setup and ensure you have at least a basic understanding of these technologies before proceeding.

First, you will need access to the Opera full-node Lachesis RPC interface. While you can use a properly configured remote interface, this may significantly impact performance and potentially compromise the security of your deployment. Be mindful of the security risks associated with exposing the RPC to outside access, especially if you enable "personal" commands on your node while storing your account keys in the Lachesis key store. We recommend using a local IPC channel for communication between the Lachesis node and the API server. The IPC interface is available by default. Refer to the Lachesis launch instructions and go-opera repository for detailed deployment and configuration guidance.

Some aggregated and relationship data are stored off-chain in a MongoDB database, particularly data that is not easily accessible via the basic node RPC interface. This includes references between an account and its transaction history or the chronological position of transactions. The API server initializes the database, populates it with historical data, and keeps it synchronized with the full node through internal subscriptions. While you don't need to manage the database separately, you must provide a well-configured and reliable database connection address to the API server.

Finally, the server is developed using Go, a fast, secure, and excellent programming language. You will need to install and configure a Go development environment to build your copy of the API server. Please follow the official Go installation instructions.

Finally, the server itself is developed using Go, excellent, fast and secure programming language. You will need to install and configure Go development environment to be able to build your copy of the API server. Please follow the official installation instructions.

Diamond Proxy Pattern

With the previous proxy pattern, we can only have one logic or implementation smart contract. However, there are cases when we want to have one proxy with more than one logic smart contract. In this case, we need the diamond proxy pattern, which is also known as the multi-facet proxy pattern.

After verifying your proxy smart contract and its logic (implementation) smart contract, you can execute the functions via FTMScan. However, like the other EVM explorers, such as Etherscan, FTMScan can’t support a verified diamond proxy and its logic smart contracts, also known as facets. Fortunately, now we have a diamond inspector called Louper which also supports the Opera mainnet and testnet.

Here are two samples of a diamond proxy and its facets which can be inspected using Louper:

  • https://github.com/Fantom-foundation/diamondproxy-hardhat

  • https://github.com/Fantom-foundation/diamondproxy-truffle

Overview

Fantom has migrated to the new Sonic chain and its S token. Users holding FTM can upgrade to S.

Users holding FTM: If you're a user holding FTM wanting to upgrade to the S token, follow the instructions on the Sonic Upgrade Handbook. Users holding app tokens: If you're a user holding an app token on Opera, follow the instructions on the App Token Migration page.

If you're an Opera app developer wanting to migrate to Sonic, follow the instructions on the App Token Migration page.

If you run a validator node on Opera and you choose to migrate, please follow these steps:

  • Un-delegate your stake first.

  • Wait at least 2 epochs (≈15 minutes) before you shut down your node.

  • Turn off the node and back up your validator key securely.

Migration Overview

Here is an overview of the migration timeline and associated events:

Event
Period

Sonic launch

December 18, 2024

Migration period

Launch onwards

Two-way swap between FTM and S

December 18, 2024 – March 31, 2025

One-way swap (FTM to S only)

March 31, 2025 onwards

Opera will continue to operate, but all future development focus will shift to Sonic.

Overview

Our Opera chain provides developers with exceptional scalability and storage capabilities while delivering a fast and seamless user experience.

Opera achieves beyond 2,000 transactions per second with near one-second finality for immediate, irreversible transactions and utilizes a cutting-edge storage system for efficient data management.

Learn more about the Opera technology stack:

  • Consensus

  • Database Storage

  • Proof of Stake

  • Transaction Fees

Transaction Fees

Every transaction on Opera requires a fee, paid in FTM (Opera's native token), to prevent spam attacks and compensate validators for processing transactions.

Rewards Distribution

The current distribution of transaction fees is:

  • 5% burned

  • 10% to the Ecosystem Vault

  • 15% to Gas Monetization

  • 70% to validators

Testnet

Proof of Stake

The Opera chain is secured using a proof-of-stake (PoS) mechanism.

In PoS on Opera, validators must lock their FTM (Opera's native token); if they act maliciously in the network, they lose their tokens. Validators are incentivized to act in the network's best interest as their own funds are at stake. Since validators do not need to perform computations, this approach is a much more energy-efficient alternative to proof-of-work for achieving resistance to Sybil attacks!

A Sybil attack is an attack where a malicious actor runs a large number of validators to allow them an unsafe amount of influence over the network. PoS makes it costly to set up these validators and allows the network to punish validators for malicious behavior, increasing the costs of attacks.

Opera requires validator nodes to lock up at least 50,000 FTM to validate transactions and produce blocks.

API

Tutorials

Implementation Details

Rewards Estimation

Estimated rewards calculation uses the current status of the blockchain to approximate the amount of FTM rewarded for participating in staking. The network uses a proof-of-stake consensus variant to ensure the security of the data inside the blockchain structure.

To calculate the rewards, we use the baseRewardPerSecond value of the latest sealed epoch, but the total staked amount of tokens is calculated elsewhere. The epoch provides the total value of self-staked tokens and the amount of total delegated tokens within that epoch. However, the self-staked total does not account for temporarily offline nodes and includes delegated stakes in the process of being undelegated. Consequently, this value is not accurate for our calculations.

To get the current total staked value, we iterate all the staking records and collect the total staked amount from individual staking.

Delegation Limits

The delegation limit is determined by multiplying the staker's current self-staked amount by a fixed rate specified in the SFC contract. Currently, the maximum allowed delegations are set at 15 times the self-staked FTM.

To calculate the remaining delegation limit, we subtract the current delegated amount from the total limit. This value is provided by the API, so you don't need to perform the calculation manually. Note that tokens in the process of undelegation are not included in the delegated amount and therefore do not count towards the delegation limit.

fWallet
Stake and delegate FTM
Claim staking rewards
Create and vote on governance proposals
reach out to us
Coinbase Wallet
reach out to us

Create Fixed-Cap Asset

Fixed-cap assets are fungible tokens for which the supply is determined at the time of asset creation. No additional quantities can be generated afterward.

Create the Asset

  1. Write the token smart contract:

    1. Functions

      1. Fixed-cap assets

        1. constructor(cap)

        2. cap()

        3. _beforeTokenTransfer(from, to, amount)

      2. General

        1. name()

        2. symbol()

        3. decimals()

        4. totalSupply()

        5. balanceOf(account)

        6. transfer(recipient, amount)

        7. allowance(owner, spender)

        8. approve(spender, amount)

        9. transferFrom(sender, recipient, amount)

        10. increaseAllowance(spender, addedValue)

        11. decreaseAllowance(spender, subtractedValue)

        12. _transfer(sender, recipient, amount)

        13. _mint(account, amount)

        14. _burn(account, amount)

        15. _approve(owner, spender, amount)

        16. _setupDecimals(decimals_)

  2. Compile your code into bytecode

  3. Deploy your fixed-cap asset by sending your code in a transaction to the Opera network

  4. Navigate to to check that your token has been created

This contract is designed to be unopinionated, allowing developers to access the internal functions in ERC-20 (such as ) and expose them as external functions in the way they prefer. On the other hand, (such as ) are designed using opinionated patterns to provide developers with ready-to-use, deployable contracts.

Verify Contract

Verifying your smart contract creates transparency, thus increasing trust. Follow these steps to verify your smart contract on Opera:

  1. Go to .

  2. Input your contract address.

  3. Choose compiler type (single file is recommended).

  4. Choose the compiler version (according to your contract).

  5. Choose the open-source license.

  6. Click Continue.

    1. If you select a single file as compiler type, then you can use a library like sol-merger to flatten your contract. It creates a single file that contains all the imports.

    2. Make sure there is only one line of // SPDX-License-Identifier. If there are multiple lines of this, remove them except the first one.

  7. Choose yes for optimization (if you deployed your contract using Hardhat).

  8. Paste the source code.

  9. If the contract has a constructor, then you need to create the ABI code for the values passed to the constructor. Use . Copy the result and paste it to the Argument (Constructor) section.

  10. Tick the box to indicate that you are not a robot.

  11. Verify and publish.

Verify cia Command Line and Script

You can verify your contracts via a Hardhat or Truffle command line. You need to get a scan API key from first. Register if you do not have an account and then create a scan API Key. Hover over your username and then choose API Keys. You then can create an API Key.

You then need to store the API Key in a .env file which is read by your configuration file, such as: API_KEY={your API Key}. Your Hardhat configuration hardhat.config.js may look like this:

Your Truffle configuration truffle-config.js may look like:

Below are two examples of command lines to verify your contract, one in Hardhat and the other in Truffle configuration.

In the above examples, it is assumed that you supplied some values to the constructor when you deployed the contract.

  • Using the Hardhat command line: When verifying using the Hardhat command line, you need to specify the values in a JavaScript file. In the example above, it is assumed that the file is under the scripts folder. Please refer to the .

  • Using the Truffle command line: You do not need to specify any value that you passed to the contract's constructor. Truffle records these when you deploy the contract.

If you use Hardhat, you can also use the verify:verify command in a script to verify your contract. You can include this script in your deployment script. .

Deploy Contract

Opera utilizes a major part of the Ethereum Virtual Machine (EVM) in the backend. Smart contracts are written in Solidity and they can function on Opera as they do on Ethereum.

To deploy a smart contract, you send an Opera transaction containing your bytecode without specifying any recipients. You will need FTM tokens to pay the gas fees when deploying smart contracts. To request your testnet FTMs on the testnet, you can use the .

After the contract is deployed, it will be available to all users of the Opera network. Smart contracts have an Opera address like other accounts.

Requirements

  • Bytecode (compiled code) of your smart contract

  • FTM for gas costs

  • Deployment script/plugin

  • Access to an Opera node, either by running your own node or API access to a node

Example of Smart Contract Deployment

If you are deploying a smart contract on Opera for the first time, you may take a look at the .

The repository contains the materials to deploy a smart contract using Hardhat and Truffle tools. Instructions to work with the mainnet and testnet are included.

Deployment Using Remix

If you want to deploy a smart contract using Remix to the Opera testnet, you just need to connect MetaMask to the Opera Testnet and choose Injected Provider - MetaMask in the Environment option.

You can see that the network ID is set to 4002, which is the Opera testnet's chain ID. When you deploy the contract, it will be deployed to the Fantom testnet.

Additional Resources

Tools

  • : Development environment for editing, compiling, debugging, and deploying your smart contracts using the EVM.

  • : Development environment, testing framework, and asset pipeline for blockchains using the EVM.

  • : IDE that’s used to write, compile, debug, and deploy Solidity code in your browser.

  • : Solidity is an object-oriented, high-level programming language for implementing smart contracts.

  • : OpenZeppelin Contracts helps you minimize risk by using battle-tested libraries of smart contracts for Ethereum and other blockchains.

  • : Complete Web3 development framework that provides everything you need to connect your applications and games to decentralized networks.

Create Variable-Cap Asset

Variable-cap assets are fungible tokens for which additional quantities can be minted after creation.

Create the Asset

  1. Write the token smart contract:

    1. Functions

      1. name()

      2. symbol()

      3. decimals()

      4. totalSupply()

      5. balanceOf(account)

      6. transfer(recipient, amount)

      7. allowance(owner, spender)

      8. approve(spender, amount)

      9. transferFrom(sender, recipient, amount)

      10. increaseAllowance(spender, addedValue)

      11. decreaseAllowance(spender, subtractedValue)

      12. _transfer(sender, recipient, amount)

      13. _mint(account, amount)

      14. _burn(account, amount)

      15. _approve(owner, spender, amount)

      16. _setupDecimals(decimals_)

  2. Compile your code into bytecode

  3. Deploy your Variable-cap Asset by sending your code in a transaction to the Fantom network

  4. Navigate to to check that your token has been created

  5. You can use the _mint function to create additional units of the token

This contract is designed to be unopinionated, allowing developers to access the internal functions in ERC-20 (such as ) and expose them as external functions in the way they prefer. On the other hand, (such as ) are designed using opinionated patterns to provide developers with ready to use, deployable contracts.

MetaMask

Follow the tutorial below to add the Opera mainnet or testnet to .

How to Add Opera to MetaMask

Desktop

  1. Open your MetaMask extension

  2. Click the network icon in the top-left corner

  3. Click Add Network

  4. Click Add a Network Manually

  5. Fill in the details below:

    1. Network Name: Opera

    2. RPC URL: https://rpcapi.fantom.network

    3. Chain ID: 250

    4. Currency Symbol: FTM

    5. Block Explorer URL: https://ftmscan.com/

  6. Click Save

  7. Switch your MetaMask wallet to Opera

Mobile

  1. Open your MetaMask app

  2. Click the network drop-down list at the top

  3. Click Add Network

  4. Click Custom Networks

  5. Fill in the details below:

    1. Network Name: Opera

    2. RPC URL: https://rpcapi.fantom.network

    3. Chain ID: 250

    4. Currency Symbol: FTM

    5. Block Explorer URL: https://ftmscan.com/

  6. Click Add and Confirm

  7. Switch your MetaMask wallet to Opera

Opera Testnet

To add the Opera testnet to MetaMask, follow the steps above but replace the network details with:

  1. Network Name: Opera Testnet

  2. RPC URL: https://rpc.testnet.fantom.network/

  3. Chain ID: 0xfa2

  4. Currency Symbol: FTM

  5. Block Explorer URL:

You can access the for some free testnet FTM.

For any questions, please .

Web3 API

Signing a Transaction

Here’s an to update the blockchain state by creating a transaction. demonstrates updating a state variable in a deployed contract:

  1. We call the function setGreeting and store the transaction in a variable tx

  2. We then pass the tx along with contract details

  3. The function signTransaction creates a block of data containing all the necessary information like gas price, contract information, network information, etc., and calls the web3 method to sign the transaction.

Gas Price

We have added to fetch the gas price:

  1. web3.eth.getGasPrice() gets the latest gas price

  2. web3.eth.getFeeHistory() gets the gas price using custom parameters, such as:

    1. No. of blocks (the number of previous blocks it searches)

    2. String/BN value to request the newest block in the requested range

    3. Percentile options (1-99th percentile)

    4. CallBack

getFeeHistory() will calculate an average of the blocks (mentioned in the first parameter) and figure out the best gas prices based on the percentile for slow/average/fast options. This API also returns the baseFeePerGas (introduced after EIP-1559) which is added to the gas prices outputted by the above call.

Rest of the Web3 API calls and examples on Opera:

Transfer

We have added two ways to sign a transaction with a of transferring FTM between accounts.

transfer

The takes a simple approach and uses the predefined web3 method to determine the gas price required to send FTM between two accounts.

transferWithParams

transfers with additional parameters in its transaction block.

The code snippet mentioned above employs the variables maxFeePerGas and maxPriorityFeePerGas. Following the London fork, each block now includes a baseFeePerGas. This base fee represents the minimum cost required for sending a transaction on the network. Unlike miners, the network itself determines the base fee. The base fee varies from block to block depending on the previous block's capacity.

When submitting a transaction, you must also provide a "tip" in the maxPriorityFeePerGas field. To ensure that the miner has an incentive to process your transaction, the minimum tip amount you should offer is 1 wei. The likelihood of your transaction being included in a block increases as you offer a higher tip. To initiate a transaction on the network, users have the option to specify the maximum amount they are willing to pay for their transaction to be executed. This parameter is called maxFeePerGas. However, for a transaction to be successfully executed, the max fee specified must be greater than or equal to the sum of the base fee and the maxPriorityFeePerGas.

Liquid Staking

Liquid staking on Opera allows users to unlock liquidity from staked assets, opening new opportunities within DeFi. Traditionally, if you , you lock your tokens to support the network's operation and security in exchange for rewards. However, doing this makes your FTM illiquid, meaning they cannot be used for other DeFi opportunities until they’re unstaked.

On Opera, two protocols aim to fix this limitation: and . Learn about them below.

What is Liquid Staking?

Liquid staking addresses the limitations of traditional staking by delegating your tokens to a validator and then minting synthetic tokens that represent the staked assets on a 1:1 basis. These tokens can then be freely traded and used in DeFi protocols for lending, borrowing, or earning additional yield while the original assets remain staked and continue earning staking rewards.

For example, suppose you stake 100 FTM through one of the liquid staking providers below. In that case, you receive the equivalent value in their FTM liquid staking tokens, which you can use in any DeFi application that supports them.

Liquid staking on Opera enhances the utility of its native token and enriches its DeFi ecosystem, making it more attractive to users seeking to maximize the productivity of their assets. This mechanism fosters greater capital efficiency within the network, allowing users to participate in securing Opera while also engaging in other DeFi activities without having to choose one over the other.

Beethoven X

Beethoven X is a DeFi platform on Opera that offers liquid staking through its sFTMx token. Follow these steps to liquid stake using Beethoven X:

  1. Head to

  2. Select how many FTM you wish to stake

  3. Click Stake and confirm in your wallet

You have now staked your FTM through Beethoven X and received an equivalent value in sFTMx tokens! These tokens are reward-bearing, meaning their quantity is stable, but they gain in value such that their redemption ratio grows daily to reflect Opera staking rewards. The tokens can now also be used across various DeFi applications while they continue to earn staking rewards from the Opera chain.

To unstake your FTM, head to the Unstake tab. However, please note that any FTM tokens you unstake will only be available for withdrawal after 7 days, identical to the waiting period required for unstaking native FTM. As an alternative to unstaking, you can swap from sFTMx to FTM using Beethoven X’s DEX on the Swap page.

15% of the staking rewards that you earn are given to the validator on which your underlying FTM is staked, and then Beethoven X takes a 10% protocol fee from the remaining rewards that you receive. about liquid staking on Beethoven X.

Ankr Protocol

Ankr offers liquid staking on Opera through its ankrFTM token. Follow these steps to liquid stake using Ankr:

  1. Visit

  2. Select how many FTM you wish to stake

  3. Click Get ankrFTM and confirm in your wallet

You have now staked your FTM through Ankr and received an equivalent value in ankrFTM tokens! These tokens are reward-bearing, meaning their quantity is stable, but they gain in value such that their redemption ratio grows daily to reflect Opera staking rewards. The tokens can now also be used across various DeFi applications while they continue to earn staking rewards from the Opera chain.

To unstake your FTM, head to the , scroll to the Liquid staking section, and click the minus symbol next to the ankrFTM token. Note the unstaking period given above the Unstake button. As an alternative to unstaking, you can swap from ankrFTM to FTM on various DEXs.

15% of the staking rewards that you earn are given to the validator on which your underlying FTM is staked, and then Ankr takes a 15% technical service fee from the remaining rewards that you receive. about liquid staking on Ankr.

FAQ

Node Requirements

Minimum hardware requirements:

  • AWS EC2 m5.xlarge with 4 vCPUs (3.1 GHz), SSD (gp2) storage

  • CPU > 3.0 GHz

  • SSD (or NVMe SSD) (N.B. AWS gp2 has up to 16000 IOPS)

  • Network Bandwidth: 1 Gbps (N.B. m5.xlarge has up to 10 Gbps)

  • Storage: Depending on the types of your nodes.

Equivalent or better specs are recommended.

Data Pruning

To save machine's storage, you may use pruning (for further ).

  • Manual pruning:

    Stop the node, then issue the following command: ./opera snapshot prune-state

  • Automatic pruning:

    You can run a node with the --gcmode flag, and it will prune old data on the fly.

    • Full gcmode: This can be enabled by --gcmode full, which prunes old VM tries. It will need more RAM. Often, --cache 15000 (or more RAM) is required to ensure normal operation in this mode.

    • Light gcmode: The light gcmode version (adopted from geth) is less aggressive. It is used by --gcmode light flag. It requires less RAM usage than full gcmode.

Cache Size

One can specify the cache size by --cache <size in MB>. The default value of cache size is 3200 MB. Nodes tend to sync faster with a larger cache size.

Transaction Ordering

Transactions in blocks aren't necessarily sorted by gas price. Even though transactions in each event and in the txpool are sorted by gas price, events are sorted by their topological ordering in the DAG. A new block includes transactions from multiple confirmed events in that topological order, which is used by our .

Penalty for Unspent Gas

It is recommended that you make sure to submit transactions with a reasonable gas limit amount.

This is because transactions with excessive gas limits will have less chance to be included, due to a gas limit for each block, and the originating power of validators. There is penalty of 10% for unspent gas. This penalty is introduced as a disincentive against excessive transaction gas limits.

The disincentive is required because Opera is a leaderless decentralized aBFT chain and blocks are not known in advance to a validator (unlike Ethereum miners) until blocks are created from confirmed events. There is no single proposer who can originate transactions for a whole block and so validators don't know the used gas in advance.

This penalty will be increased shortly (up to 50%) to prevent transactions with excessive transaction gas limits.

Installation

Requirements

Please note that an official binary distribution is not available at the moment. To build your own GraphQL API, you need to have the following:

  • Working database installation.

  • Go version 1.13 or later .

  • up and running.

You do have another option. For initial testing and development, you can use our own testing playground. The API server is deployed at https://xapi2.fantom.network/api for regular GraphQL queries and at https://xapi2.fantom.network/graphql for WebSocket subscriptions.

Feel free to connect and try your queries. Fine-tune your application before committing to deploying your own instance.

Building Process

Building your API server is a fairly straightforward process. First, clone the repository to your local machine. Do not clone the project into $GOPATH, due to the . Instead, use any other location.

Once you have the copy on your machine, build the executable:

The API server executable will be created in the <build> folder of the project. You can change the location by changing the output path in the Go building command above.

To run your copy of the API server, simply run:

Configuration

The API server already contains some most common configuration options as default values, and you don't have to change them most of the time. The default values are:

  • Network binding address is localhost.

  • Default listening port is 16761.

  • Default Lachesis IPC interface is ~/.lachesis/data/lachesis.ipc.

  • MongoDB connection address is mongodb://localhost:27017.

  • Default logging level is INFO.

If you want to change one of the default values, you need to create a configuration file. The API server can read configuration options from several configuration formats, namely JSON, TOML, YAML, HCL, envfile, and Java properties config files.

Please choose the one you are most familiar with. The name of the configuration file is expected to be "apiserver" with an extension that corresponds with the file format of your choosing.

Example YAML file looks like this:

You can keep the config file in the same location as the API server executable, or you can save it in the home folder under the .fantomapi sub-folder. On MacOS, the expected path is Library/FantomApi.

Contract Library

is a free contract-centered blockchain explorer created by . First released in 2018, Contract Library continuously decompiles and analyzes all deployed contracts for human inspection and offers a platform for exposing vulnerabilities. Contracts added to the Opera chain appear in the library almost immediately.

Contract Library offers users the ability to:

Contract Library’s debugger is orders of magnitude more scalable than alternatives on other networks, enabling debugging complex transactions within a reasonable amount of time. This is achieved by implementing an efficient model of the EVM to offload computation from the Web3 client.

Proxy Pattern

In the proxy pattern, we have two smart contracts: proxy contract and implementation contract.

The proxy contract acts as a proxy, delegating all calls to the contract it is the proxy for. In this context, it will also be referred to as the Storage Layer. The implementation contract is the contract that you want to upgrade or patch. This is the contract that the Proxy contract will be acting as a proxy for. In this context, it is the Logic Layer.

The Proxy contract stores the address of the implementation contract or logic layer as a state variable. Unlike normal contracts, the user doesn't actually send calls directly to the logic layer, which is your original contract. Instead, all calls go through the proxy and this proxy delegates the calls to this logic layer — the implementation contract at the address that the proxy has stored — returning any data it received from the logic layer to the caller or reverting for errors.

The key thing to note here is that the proxy calls the logic contract through the delegatecall function. Therefore, it is the proxy contract that actually stores state variables, i.e. it is the storage layer. It is like you only borrow the logic from the implementation contract (logic layer) and execute it in the proxy's context affecting the proxy's state variables in storage.

Openzeppelin provides a library to create an upgradeable proxy pattern called TransparentUpgradeableProxy.

Creating the Proxy and Proxy Admin Implicitly

Below are two samples of the creation of a TransparentUpgradeableProxy, one in Hardhat using @openzeppelin/hardhat-upgrades and the other one in Truffle using @openzeppelin/truffle-upgrades

When you deploy, the package behind the scenes creates a ProxyAdmin and a TransparentUpgradeableProxy as well.

In the Hardhat configuration, you just need to verify the TransparentUpgradeableProxy. The Hardhat utility will also verify the ProxyAdmin and the logic smart contract, Box. Unfortunately, in the Truffle configuration, we can’t do this. We can only verify the logic smart contract.

Creating the Proxy and Proxy Admin Explicitly

We can create the proxy and proxy admin explicitly by inheriting TransparentUpgradeableProxy in @openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol and ProxyAdmin in @openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol.

Below are two samples of creating the proxy and proxy admin explicitly.

Unfortunately, MyProxy can’t be verified using the command line in Hardhat or Truffle, but we can flatten it and then verify it on FTMScan directly.

Database Storage

Opera uses database storage to store its world state, which includes account information, virtual machine bytecode, smart contract storage, etc. This database has a feature called live pruning, which removes historical data automatically, reducing storage needs for validators as the blockchain grows.

Previously, pruning required validator nodes to go offline, risking financial and operational issues for them. Now, validators can use live pruning without going offline, ensuring continuous operation and saving on disk space and costs by discarding historical data in real-time.

Live pruning works by splitting the database into two types: LiveDB and ArchiveDB. The LiveDB contains the world state of the current block only, whereas the ArchiveDB contains the world states of all historical blocks. Validators use only LiveDB, while archive nodes have both LiveDB and ArchiveDB to handle historical data requests through the RPC interface.

Opera's database storage uses efficient tree-like or hierarchical structures, which simplifies data retrieval. Importantly, it still provides cryptographic signatures for a world state and archive capabilities using an incremental version of a prefix algorithm. Additionally, it utilizes a native disk format instead of storing the world state indirectly through key-value stores like LevelDB or PebbleDB.

Fantom Safe

is an open-source smart contract wallet operating on the Opera chain, designed for multi-signature management of crypto assets and interactions with other smart contracts, making it ideal for team fund management.

You can designate owners of the safe and specify the minimum number of approvals required for a transaction to be executed. This differs from a single-key wallet, also known as an externally owned account (EOA), where one person holds the private key and approves transactions independently.

Fantom Safe supports FTM and tokens on the Opera chain, allowing you to transfer them to the safe as usual. You can view the fiat values of your assets on your dashboard and conveniently send funds from it.

Getting Started

As is based on Gnosis Safe, you can use most tutorials based on them. For selecting the Opera mainnet or test net, use the menu in the top-right corner.

Transaction Service API

We offer an sent via Fantom Safe smart contracts.

Troubleshooting

Due to the caching mechanism, transaction nonces can become unsynchronized. If a transaction cannot be created from your Safe, verify that the current nonce for your Safe transaction matches the nonce displayed in the Safe UI.

To resolve this, open the settings menu of your Fantom Safe to view the current nonce. Copy this nonce into your Safe transaction, and the issue should be resolved.

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/extensions/ERC20Capped.sol
the Explorer
_mint
ERC-20 Presets
ERC-20PresetMinterPauser
testnet faucet
example given here
Compiling
Deploying a Smart Contract on Ethereum
Hardhat
Truffle
Remix
Solidity
OpenZeppelin
thirdweb
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol
the Explorer
_mint
ERC-20 Presets
ERC-20PresetMinterPauser
// Change maxFeePerGas and maxPriorityFeePerGas as per the current gas prices
var transaction = {
    to: TO_ADDRESS,
    from: address,
    nonce: nonceVal,
    value: testWeb3.utils.toHex(testWeb3.utils.toWei('0.1', 'ether')),
    gas: testWeb3.utils.toHex(21000),
    maxFeePerGas: '0x174876E800', // 100 Gwei 
    maxPriorityFeePerGas: '0xBA43B7400', // 50 Gwei
    chainId
};
example of signing a transaction
This example
two methods
https://github.com/Fantom-foundation/web3-methods/wiki
simple example
first method
This method
stake FTM
Beethoven X
Ankr Protocol
Beethoven X’s sFTMx page
Learn more here
Ankr’s FTM Liquid Staking page
Ankr Staking dashboard
Learn more here
details
DAG aBFT consensus algorithm
$ git clone https://github.com/Fantom-foundation/fantom-api-graphql.git
$ cd fantom-api-graphql
$ go build -o ./build/apiserver ./cmd/apiserver
$ build/apiserver
server:
    bind: 127.0.0.1:16761    
lachesis:
    url: /var/opera/data/lachesis.ipc
log:
    level: Debug
mongo:
    url: mongodb://127.0.0.1:27017
cors:
    origins: *
MongoDB
configured and ready
Lachesis full node
Go modules
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
https://github.com/Fantom-foundation/transparentproxy-hardhat
https://github.com/Fantom-foundation/transparentproxy-truffle
https://github.com/Fantom-foundation/transparentproxy2-hardhat
https://github.com/Fantom-foundation/transparentproxy2-truffle
Fantom Safe
Fantom Safe
API to keep track of transactions
Contract Library
Dedaub

Examine contract code at all levels, including bytecode, decompiled representations, and source code, if available.

Check token balances and allowances ‒ both as grantor and grantee ‒ for every contract and account.

Read the values of storage locations of a contract.

Find identical contracts, as well as similar public functions.

Interact with a contract (“read/write contract”), even if it has no published source code.

Decompile arbitrary bytecode ‒ for instance, to examine attack contracts or to see if high-level code is translated into gas-efficient sequences.

Examine transaction traces and effects, filter past actions by function, and perform local debugging of transactions.

Public Endpoints

Opera Mainnet

RPC

  • https://rpcapi.fantom.network/

  • https://rpc.ankr.com/fantom/

  • https://fantom-mainnet.public.blastapi.io/

  • https://rpc.ftm.tools

  • https://fantom-rpc.gateway.pokt.network

  • https://1rpc.io/ftm

  • https://fantom.publicnode.com

  • https://fantom.blockpi.network/v1/rpc/public

  • https://fantom.drpc.org/

  • https://rpc.fantom.gateway.fm

  • https://endpoints.omniatech.io/v1/fantom/mainnet/public

  • https://fantom.api.onfinality.io/public

  • https://fantom.rpc.thirdweb.com/

Chain ID: 250 Symbol: FTM Explorer: https://ftmscan.com List provided by CompareNodes.

GraphQL

  • https://xapi.fantom.network/

WS

  • wss://wsapi.fantom.network/

  • wss://fantom-mainnet.public.blastapi.io/

Transaction Tracing

  • https://rpcapi-tracing.fantom.network


Opera Testnet

RPC

  • https://rpc.ankr.com/fantom_testnet/

  • https://xapi.testnet.fantom.network/lachesis/

  • https://rpc.testnet.fantom.network/

  • https://fantom-testnet.public.blastapi.io/

  • https://fantom-testnet.publicnode.com

  • https://fantom-testnet.drpc.org/

  • https://endpoints.omniatech.io/v1/fantom/testnet/public

  • https://fantom-testnet.rpc.thirdweb.com/

Chain ID: 4002 Symbol: FTM Explorer: https://testnet.ftmscan.com/

WS

  • wss://fantom-testnet.public.blastapi.io/

Transaction Tracing

  • https://rpcapi-tracing.testnet.fantom.network

Run Read-Only Node

Validator Parameters

  • Minimum hardware requirements: AWS EC2 m5.large with 8GB RAM, 2 vCPUs, and at least 300GB of Amazon EBS General Purpose SSD (gp2) storage (or equivalent).

  • We would recommend going with Ubuntu Server 22.04 LTS (64-bit).

Network Settings

Open up port 22 for SSH, as well as port 7946 for both TCP and UDP traffic. A custom port can be used with "--port <port>" flag when running your Opera node.

Install Required Tools

You are still logged in as the new user via SSH. Now we are going to install Go and Opera.

First, install the required build tools:

# Install build-essential
(validator)$ sudo apt-get install -y build-essential
# Install go
(validator)$ wget https://go.dev/dl/go1.19.3.linux-amd64.tar.gz
(validator)$ sudo tar -xvf go1.19.3.linux-amd64.tar.gz
(validator)$ sudo mv go /usr/local
# Export go paths
(validator)$ vi ~/.bash_aliases
# Append the following lines
export GOROOT=/usr/local/go
export GOPATH=$HOME/go
export PATH=$GOPATH/bin:$GOROOT/bin:$PATH
#
source ~/.bash_aliases

Run a Read Node

You can run your read node using go-opera 1.1.3-rc.5 (full sync or snapsync mode).

# Install Opera
(validator)$ git clone https://github.com/Fantom-foundation/go-opera.git
(validator)$ cd go-opera/
(validator)$ git checkout release/1.1.3-rc.5
(validator)$ make

Validate your Opera installation:

$./build/opera help

VERSION:
1.1.3-rc.5

db.preset

When using version 1.1.3, you need to add the db.preset argument (introduced since 1.1.2) to the starting Opera command. You can see options for this parameters with opera help command. For standard conditions, please use this option:

  • db.preset=ldb-1

You can use different db presets, either --db.preset ldb-1 OR --db.preset legacy-db or --db.preset pbl-1. Note that ldb-1 is recommended.

Download a genesis file from this list of genesis files.

# Start opera node
(validator)$ cd build/
(validator)$ wget https://download.fantom.network/testnet-6226-no-mpt.g
(validator)$ nohup ./opera --genesis testnet-6226-no-mpt.g --nousb \
            --db.preset ldb-1 &

You can start a node with a syncmode flag. There are two possible options:

  • "--syncmode snap", and

  • "--syncmode full" (by default).

For archive node and validator node, you should use full syncmode.

For the latest update, please check our GitHub.

Troubleshooting

Validator Node

Rerun a Node if Stopped

If your node is stopped (for any reason), please examine the server log to identify if there were any issues. After fixing the issues (if any), you can run the node in read mode to sync to the latest block. After it is synced up, you can stop the node and run in validator mode.

Make sure your node is synced in read mode first before it is run in validator mode.

Migration to New Server

If you'd like to migrate your node to a new server, please follow these steps:

  1. Set up a read node on a new server and allow it to run to sync to the latest block.

  2. Stop the old node for at least 40 minutes before running the validator mode on the new server

  3. After the old node is stopped for 40 minutes, then you can run in validator mode on the new server. You should not let the old node run again as it will result in a double-sign and slashing of your validator node.

How to Stop Node

Find the running process of opera using ps and then kill the process by id.

After your node is stopped, if you want to rerun it, don't run directly in validator mode. Instead, please make sure your node is synced in read mode first before it's run in validator mode.

Offline Node

If your validator node is down for more than 5 days, then it will become offline (i.e. pruned from the network).

For offline nodes, you can undelegate and wait for 7 days to withdraw (bonding time). After that, you can transfer funds to a new wallet and make a new validator if you wish. If undelegating a locked stake or locked delegation before the locked period has expired, it will incur a penalty.

How to Permanently Shut Down Node

To shut down a node permanently, you can simply stop running the node in validator mode for 5 days or more.

How to Unstake

If your node stake is locked, you will first need to call unlockStake() to unlock it. Note that a penalty will apply for early unlocking before the lock-up period has expired. Then you can call undelegate(), to unstake your stake. Then there is a waiting period of 7 days (so-called bonding time) after undelegation. This is required before you can call withdraw() to take out your stake.

Syncing Error

If your node is in a dirty state (it may happen occasionally), please run: opera --db.preset legacy-ldb db heal --experimental alternatively, you may do a fresh resync as follows:

  • Stop the node

  • Remove the current (broken) datadir (the default datadir is located at ~/.opera)

  • Download and build the latest version go-opera 1.1.3-rc5

  • Run your node again in read mode

Slow Syncing

Check your machine specs if it meets the minimum requirements.

  • IOPS greater than 5000 (higher is better)

  • Connection speed > 1 Gbps (some run with 10 or 20 if they can)

  • Cores: More than 4 cores (the number of cores is not important unless you will use it for serving API calls)

  • CPU: > 3 GHz.

You can also check the following flags if you're using them to run your node. You can adjust to values suitable to your usage.

  • Maxpeers Flag: default is 50, you can adjust it depending on your machine

  • Cache Flag: --cache 15792 (A larger value can give better performance)

  • Gcmode: Gcmode is not enabled by default. If enabled, gcmode (light or full) will take some extra CPU and time

You can also increase the value of ulimit on your machine.

Increase Open Files Limit

You can check your current limit value on Linux with the command ulimit -n. The default value of 1024 may be not enough in some cases.

You can adjust the value to the recommended 500.000 open files limit by either:

  • ulimit -n 500000

  • Change it in the /etc/security/limits.conf configuration file, limit type nofile.

Gas Monetization

Apply for your app to participate in Gas Monetization.

The Gas Monetization program offers apps a 15% share of the gas fees they generate and aims to provide high-quality apps with a sustainable income, retain talented creators, and support network infrastructure.

With Gas Monetization, we seek to foster a thriving ecosystem for builders, similar to the ad-revenue model on traditional web platforms.

Participate in Gas Monetization

Approval to the Gas Monetization program is at our discretion. Apply to participate in Gas Monetization.

Upon approval to participate in the program, apps will begin to earn 15% of the gas fees they generate. The FTM earned through Gas Monetization can be used at the apps' discretion.

Quarterly Bonus

Every quarter, the top 12 apps on Opera that generated the most gas will receive a bonus. These 12 projects are divided equally into four tiers that determine the amount of FTM they receive:

  • Tier 1 (top three projects): 40% of FTM

  • Tier 2 (next three projects): 30% of FTM

  • Tier 3 (next three projects): 20% of FTM

  • Tier 4 (final three projects): 10% of FTM

The quarterly bonus comes from the 15% deducted from transaction fees of apps not participating in Gas Monetization. While 15% of all transaction fees on Opera are allocated to the Gas Monetization program, the fees from non-participating apps are pooled and distributed as the quarterly bonus.

Double-Spend Dilemma

In Gas Monetization, double counting is prevented by accurately tracking gas consumption within the EVM. The system traces all internal calls in a transaction and splits the reward based on the gas each sub-operation consumes.

This ensures that the sum of rewards across different projects never exceeds the total gas fee. An example is given below.

  • A trade consumes 100,000 units of gas with a total GasM fee of 0.017 FTM.

  • Inside this transaction, there are operations related to two projects: Project A and Project B.

  • Project A, the DEX aggregator, is responsible for consuming 37,000 units of gas, while Project B, the liquidity pool on the DEX that the aggregator interacts with, uses 63,000 units of gas.

  • The total reward is split based on the gas consumption:

    • Project A receives 37% of the fee (0.00629 FTM).

    • Project B receives 63% of the fee (0.01071 FTM).

  • The total reward still adds up to 100% of the 0.017 FTM GasM fee, ensuring no over-distribution.

Terms & Conditions

We reserve the right to suspend rewards to participating apps for any reason deemed necessary at its sole discretion, including fraudulent user activity or the overall well-being of the Opera ecosystem.

npx hardhat verify --contract contracts/YourContractFile.sol:YourContractName --constructor-args scripts/argument.js --network testnet {contract address}
truffle run verify YourContract@{contract address} --network testnet
FTMScan Verify Contract
https://abi.hashex.org/
FTMScan
Hardhat documentation
Here is an example
MetaMask
https://testnet.ftmscan.com/
Opera Testnet Faucet
reach out to us

Ledger

Follow the tutorial below to add FTM to your Ledger device and interact with apps on Opera.

How to Add FTM to Ledger

  1. Open Ledger Live

  2. Click on My Ledger

  3. Under App Catalog, find Ethereum and click Install

    1. Since Ledger lacks a native Opera application, we use the Ethereum application as it supports all EVM-compatible chains, including Opera.

  4. Follow the instructions to install the Ethereum app

  5. Click Add Account

  6. Select FTM in the drop-down list and add the account

How to Send FTM to Ledger

  1. Open Ledger Live

  2. Click Accounts

  3. Click on your Fantom account

  4. Click Receive

  5. Choose your Fantom account in the drop-down list

  6. Click Continue

  7. Verify the address on your Ledger device and then copy

  8. Send FTM to this address from CEXs or other wallets

How to Use Apps with Ledger

To use apps on Opera with your Ledger device, you must connect it to a Web3 wallet first, such as Rabby or MetaMask.

Rabby

  1. Open your Rabby Wallet extension

  2. Click on your wallet address at the top

  3. Click Add New Address and then Connect Hardware Wallets

  4. Click Ledger

  5. Connect your Ledger and click Next

  6. Click Allow and choose your Ledger device in the list

  7. Choose your address on the list that contains FTM and click Done

You can now interact with apps on Opera using Rabby. Whenever you need to sign a transaction or message, Ledger will prompt you to approve it on your physical hardware wallet.

MetaMask

  1. Open your MetaMask extension

  2. Click the drop-down list at the top

  3. Click Add Account or Hardware Wallet

  4. Click Add Hardware Wallet

  5. Click Ledger

  6. Click Continue and choose your Ledger device in the list

  7. Choose your address on the list that contains FTM and click Unlock

You can now interact with apps on Opera using MetaMask. Whenever you need to sign a transaction or message, Ledger will prompt you to approve it on your physical hardware wallet.

For any questions, please reach out to us.

Stablecoin

Opera's canonical stablecoin is USDC.e, supported by Circle and Wormhole.

USDC.e on Wormhole is bridged from native USDC, located in a smart contract on Ethereum, and holds the potential to be upgraded to native USDC in the future by Circle. It's the official, endorsed stablecoin of the Opera ecosystem.

Bridge USDC to Opera

To bridge USDC from Ethereum to Opera, use PortalBridge. You will receive USDC.e on Opera upon completion, which is the canonical stablecoin for our chain.

You can only bridge USDC from Ethereum and not from any other chains. For other assets, please refer to Squid Router.

Native vs. Bridged USDC

According to Circle, the differences between native USDC and bridged USDC are:

Unlike native USDC, bridged USDC isn’t minted by Circle itself. On Opera, USDC.e is minted by Wormhole, bridged from native USDC located in a smart contract on Ethereum. At any point, Circle can then obtain ownership of the USDC.e token contract on Opera and upgrade it to native USDC.

Pathway to Native USDC

Circle has outlined these steps for upgrading bridged USDC to native USDC:

  1. Wormhole has deployed USDC.e on the network

  2. USDC.e is used to bootstrap initial liquidity in the ecosystem

  3. USDC.e reaches a significant supply, amount of holders, and number of app integrations

  4. Circle and the Fantom Foundation/Wormhole jointly elect to securely transfer ownership of the USDC.e token contract to Circle

  5. Upon obtaining ownership, Circle upgrades USDC.e to native USDC and seamlessly retains existing supply, holders, and app integrations

The Fantom Foundation will provide liquidity for the USDC.e stablecoin on Wormhole, making it easy for the token to spread across the network and for users to bridge assets to Opera. As USDC.e begins to proliferate and gain a vast amount of holders and app integrations, Circle may consider stepping in to upgrade its token contract to native USDC.

In this scenario, the upgrade would be seamless and wouldn’t require developers to update contract addresses or users to swap to the new token. The automatic changeover ensures that native USDC on Opera can leverage the built-up USDC.e liquidity so there isn't any interruption on the network.

Native USDC

  • Issued by Circle, a regulated fintech company

  • Backed by US dollars and always redeemable 1:1

  • Official form of USDC on a given blockchain

  • Interoperable with multiple blockchain networks via Circle’s Cross-Chain Transfer Protocol

Bridged USDC

  • Created by a third party, e.g. Wormhole

  • Backed by native USDC on another blockchain locked in a smart contract

  • Not compatible with Circle’s Cross-Chain Transfer Protocol

  • Upgradable to native USDC

thirdweb

Create Contract

To create a new smart contract using the thirdweb CLI, follow these steps:

  1. In your CLI run the following command:

    
    npx thirdweb create contract
    
  2. Input your preferences for the command line prompts:

    1. Give your project a name

    2. Choose your preferred framework: Hardhat or Foundry

    3. Name your smart contract

    4. Choose the type of base contract: Empty, ERC20, ERC721, or ERC1155

    5. Add any desired extensions

  3. Once created, navigate to your project’s directory and open in your preferred code editor.

  4. If you open the contracts folder, you will find your smart contract; this is your smart contract written in Solidity. The following is code for an ERC721Base contract without specified extensions. It implements all of the logic inside the ERC721Base.sol contract, which implements the ERC721A standard.

    
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    
    import "@thirdweb-dev/contracts/base/ERC721Base.sol";
    
    contract Contract is ERC721Base {
        constructor(
            string memory _name,
            string memory _symbol,
            address _royaltyRecipient,
            uint128 _royaltyBps
        ) ERC721Base(_name, _symbol, _royaltyRecipient, _royaltyBps) {}
    }
    

    This contract inherits the functionality of ERC721Base through the following steps:

    • Importing the ERC721Base contract

    • Inheriting the contract by declaring that our contract is an ERC721Base contract

    • Implementing any required methods, such as the constructor.

  5. After modifying your contract with your desired custom logic, you may deploy it to Opera using Deploy.

Alternatively, you can deploy a prebuilt contract for NFTs, tokens, or marketplace directly from the thirdweb Explore page:

  1. Go to the thirdweb Explore page: https://thirdweb.com/explore

  2. Choose the type of contract you want to deploy from the available options: NFTs, tokens, marketplace, and more.

  3. Follow the on-screen prompts to configure and deploy your contract.

For more information on different contracts available on Explore, check out thirdweb’s documentation.

Deploy Contract

Deploy allows you to deploy a smart contract to any EVM-compatible network without configuring RPC URLs, exposing your private keys, writing scripts, and other additional setups, such as verifying your contract.

  1. To deploy your smart contract using deploy, navigate to the root directory of your project and execute the following command:

    
    npx thirdweb deploy
    

    Executing this command will trigger the following actions:

    • Compiling all the contracts in the current directory.

    • Providing the option to select which contract(s) you wish to deploy.

    • Uploading your contract source code (ABI) to IPFS.

  2. When it is completed, it will open a dashboard interface to finish filling out the parameters.

    • _name: contract name

    • _symbol: symbol or "ticker"

    • _royaltyRecipient: wallet address to receive royalties from secondary sales

    • _royaltyBps: basis points (bps) that will be given to the royalty recipient for each secondary sale, e.g. 500 = 5%

  3. Select Opera (Fantom) as the network

  4. Manage additional settings on your contract’s dashboard as needed such as uploading NFTs, configuring permissions, and more.

For additional information on Deploy, please reference thirdweb’s documentation.

If you have any further questions or encounter any issues during the process, please reach out to thirdweb support at support.thirdweb.com.

Chainstack

This section guides you through deploying a Hello World smart contract using Chainstack and Foundry on the Opera testnet.

If you have any questions, reach out in the Chainstack Discord.

Deploy an Opera Testnet Node

You need a node to deploy a smart contract to the chain. To get your node:

  1. Sign up with Chainstack.

  2. Deploy a testnet node.

  3. Get the deployed node’s HTTPS endpoint.

Install Foundry

Foundry is a development toolkit to work with smart contracts.

  1. Install Rust.

  2. Install Foundry.

Initialize With Foundry

In your project directory, run foundry init. This will create a boilerplate project.

Fund Your Account

You need to pay gas on the network to deploy the contract. Get testnet FTM through the faucet.

Create the Hello World Contract

In the initialized Foundry project in src/, create HelloWorld.sol:

// SPDX-License-Identifier: None

// Specifies the version of Solidity, using semantic versioning.
// Learn more: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma
pragma solidity >=0.8.9;

// Defines a contract named `HelloWorld`.
// A contract is a collection of functions and data (its state). Once deployed, a contract resides at a specific address on the Fantom blockchain. Learn more: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html
contract HelloWorld {

   //Emitted when update function is called
   //Smart contract events are a way for your contract to communicate that something happened on the blockchain to your app front-end, which can be 'listening' for certain events and take action when they happen.
   event UpdatedMessages(string oldStr, string newStr);

   // Declares a state variable `message` of type `string`.
   // State variables are variables whose values are permanently stored in contract storage. The keyword `public` makes variables accessible from outside a contract and creates a function that other contracts or clients can call to access the value.
   string public message;

   // Similar to many class-based object-oriented languages, a constructor is a special function that is only executed upon contract creation.
   // Constructors are used to initialize the contract's data. Learn more:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors
   constructor(string memory initMessage) {

      // Accepts a string argument `initMessage` and sets the value into the contract's `message` storage variable).
      message = initMessage;
   }

   // A public function that accepts a string argument and updates the `message` storage variable.
   function update(string memory newMessage) public {
      string memory oldMsg = message;
      message = newMessage;
      emit UpdatedMessages(oldMsg, newMessage);
   }
}

Deploy the Contract

At this point, you are ready to deploy your contract:

  • You have your own node on the Opera testnet through which you will deploy the contract.

  • You have Foundry that you will use to deploy the contract.

  • You have a funded account that will deploy the contract.

To deploy the contract, run:

forge create HelloWorld --constructor-args "Hello" --contracts CONTRACT_PATH --private-key PRIVATE_KEY --rpc-url HTTPS_ENDPOINT
  • CONTRACT_PATH — path to your HelloWorld.sol file.

  • PRIVATE_KEY — the private key from your account.

  • HTTPS_ENDPOINT — your node's endpoint.

Example:

forge create HelloWorld --constructor-args "Hello" --contracts /root/foundry/src/HelloWorld.sol --private-key d8936f6eae35c73a14ea7c1aabb8d068e16889a7f516c8abc482ba4e1489f4cd --rpc-url https://nd-123-456-789.p2pify.com/3c6e0b8a9c15224a8228b9a98ca1531d

Congratulations! You have deployed your Hello World smart contract on Opera!

See the Chainstack documentation for more tutorials and tools.

FAQ

This guide addresses as many questions you may have about Opera as possible. Please refer to this FAQ section before asking questions on our social channels. If your question is still unanswered, please reach out to us.

General Questions

What is a consensus algorithm?

A consensus algorithm is a mechanism for reaching agreement among nodes in distributed networks. It removes the need for a central authority and allows the whole network to trustlessly agree on data and the ordering of events.

Nodes in the network maintain an exact copy of the ledger, allowing applications built on top of the consensus protocol to function correctly.

What is finality?

Finality means that any party cannot change or reverse a transaction. ABFT consensus algorithms, such as the one Opera uses, have a very low time to finality because they achieve absolute finality. Absolute finality means a transaction is considered final once included in a block. In the case of Opera, the chain can achieve finality in 1–2 seconds.

Conversely, Nakamoto consensus protocols rely on probabilistic finality. In this case, the probability that a transaction will not be reverted increases with time. The more blocks created on top of a block, thereby confirming it as correct, the more difficult and costlier it would be to revert a transaction in that block. At some point, it becomes theoretically impossible to alter older blocks, increasing the probabilistic finality to nearly 100%.

Bitcoin has a finality of 30–60 minutes; when using Bitcoin, you must wait a few block confirmations before considering the transaction final and irreversible. Ethereum has a finality of a few minutes.

FTM Token

Is FTM an ERC-20 token?

Opera has an ERC-20 token, but it cannot be used directly on the Opera chain.

Here is a breakdown of the different FTM tokens in circulation at the moment:

1. Opera FTM: Used on the Opera chain 2. ERC-20: Exists on the Ethereum network

Note that Opera addresses share the same structure as Ethereum addresses (0x…), but they are not Ethereum addresses.

Can I send ERC-20 FTM tokens to my Opera wallet?

No. You need to transfer them over to Opera using a bridge.

What’s the purpose of the FTM token?

The FTM token has several use cases within the Opera ecosystem. It is essential for a well-functioning, healthy network.

  1. Securing the network Opera uses a proof-of-stake system that requires validators to hold FTM. Anyone with at least 50,000 FTM can run their validator node to earn rewards and secure the network. Every FTM holder can delegate their tokens to a validator (while keeping full custody of their funds) to receive staking rewards. Validators then charge a small fee for their services. By locking in their FTM, validators help the network to be decentralized and secure.

  2. Paying for network fees To compensate validators for their services and prevent transaction spam, every action performed within the Opera network costs a small fee, paid in FTM.

  3. Voting in on-chain governance Decisions regarding the Opera ecosystem are made using transparent on-chain voting. Votes are weighted according to the amount of FTM held by an entity. 1 FTM equals 1 vote.

  4. Additional use cases FTM is used to earn APR/APY on many DeFi platforms.

How can I stake FTM?

Check out our guide.

Can I stake FTM on exchanges like Binance?

You cannot stake FTM on exchanges at the moment.

How to run a validator on Opera?

To run a validator node on the Opera chain, the following is required:

  1. A minimum stake of 50,000 FTM

  2. Google GCP N2 instance or AWS i3en.xlarge with 4 vCPUs (3.1 GHz) and at least 1TB of local NVMe SSD storage (or equivalent).

Check out our guide.

Where can I store FTM?

You can store FTM on various hardware and hot wallets. Check out the Wallets section to learn more.

Opera Chain

What is Opera?

Opera is our mainnet, a fully decentralized blockchain network with smart contract support. It is compatible with the Ethereum Virtual Machine and powered by our ABFT consensus algorithm. Thus, smart contracts developed on Ethereum can run on Opera, increasing scalability and security.

Is Opera compatible with Ethereum smart contracts?

Yes. Fantom is fully compatible with the Ethereum Virtual Machine (EVM) and supports Web3JS API and RPC.

All smart contracts written in Solidity or Vyper, compiled and deployed on Ethereum, are fully compatible with Opera.

What programming languages does Opera support?

Opera supports all the smart contract languages Ethereum supports for the EVM, including Solidity and Vyper.

When did Opera mainnet go live?

The Opera chain went live on December 27, 2019.

How is Opera governed?

The Fantom Foundation is currently in charge of the network's governance, advised by the community and validator nodes.

Find out more details on how governance works on Opera.

Schema Basics

Basics

The core idea of the GraphQL API is to describe available entities and attributes with a schema and let the client decide which parts, elements, and services it needs to perform its operation. Our schema is extensively commented and you should be able to grab the right data just by comparing your needs with the schema. Please let us know if you still miss anything important.

The API server supports introspection. It means that with a competent development tool, you can scan the available structure and build your GraphQL queries with full support of known structures.

Conventions

The API follows these conventions and recommendations:

  • GraphQL types are capital camelCases.

  • Attributes are regular camelCases.

  • Function calls are named and managed the same way as attributes.

  • Complex input and output values are encapsulated in a type instead of an expanded list.

  • Each type, attribute, and function has a comment describing its purpose.

Scalar Values

The API uses several different scalar values besides the default GraphQL set. Most of them encode big values or byte arrays of hashes and addresses. If your client application is built using JavaScript, or TypeScript, you can benefit from using the excellent Ethereum Web3.js library, which can help you deal with the decoding and validating process of most data types used here.

Transaction amounts, account balances, rewards, and basically all amount-related data are transferred as big integers encoded in prefixed hexadecimal format. It represents the smallest indivisible amount of value inside the blockchain, the so-called WEI.

One FTM token contains 10¹⁸ WEIs.

The following scalar values are used by the API:

  • Hash is a 32-byte binary string, represented by 0x prefixed hexadecimal number.

  • Address is a 20-byte Opera address, represented by 0x prefixed hexadecimal number.

  • BigInt is a large integer value. Input is accepted as either a JSON number or a hexadecimal number alternatively prefixed with 0x. Output is 0x prefixed hexadecimal.

  • Long is a 64-bit unsigned integer value represented by 0x prefixed hexadecimal number.

  • Bytes is an arbitrary length binary string, represented by a 0x-prefixed hexadecimal string. An empty byte string is represented by '0x'.

  • Cursor is an unspecified string value representing position in a sequential list of edges. Please see the following section for details about Cursor usage.

Pagination

The API employs cursor-based pagination instead of the commonly used <offset, count> or <from, to> architectures, which rely on numeric offsets from the top of the collection. In our scenario, the top of the collection is constantly changing, making it nearly impossible to track your position with a shifting anchor.

Our approach uses a cursor system, where each member of the collection has a unique identifier called a cursor. To obtain a slice of the collection, you specify the cursor value of a member and the number of neighboring members you need. If you don't specify a cursor, it defaults to either the top or bottom of the collection, determined by the count of neighbors you request.

For example, this query provides 15 consecutive blocks after the one with the cursor "0x134b7":

query BlocksList {
    blocks (cursor: "0x134b7", count: 15) {
        totalCount
        pageInfo {
            first
            last
            hasNext
            hasPrevious
        }
        edges {
            cursor
            block {
                hash
                number
                timestamp
                transactionCount
            }
        }
    }
}

If you just want the 10 most recent transactions, you skip the cursor and send the following query:

query TransactionsList {
    transactions (count: 10) {
        totalCount
        pageInfo {
            first
            last
            hasNext
            hasPrevious
        }
        edges {
            cursor
            transaction {
                hash
                from
                to
                value
                block {
                    timestamp
                }
            }
        }
    }
}

Please note that each response will contain several important bits of info that will help you navigate through the collection.

First is the pageInfo structure, defined by PageInfo type. It contains the first and last cursor of your current slice so you can use these values and ask for next X edges or previous -X edges.

Also each edge of the list contains not only the desired data element, but also a cursor, which can be used to create a link, if desired, to the same position in the collection regardless of the absolute distance of the element from the top or bottom.

App Token Migration

This page outlines token migration options for apps from Fantom Opera to the Sonic chain.

It provides comprehensive guidance for both token holders and project owners regarding their migration options.

Instructions for Token Holders

The official Sonic Labs upgrade portal will facilitate the FTM to S upgrade on a 1:1 basis.

Official Migration Contract Timeline

Phase
Duration
Conversion Type
Notes

Initial period

December 18, 2024 – March 31, 2025

Two-way FTM ↔ S

Full flexibility

Final phase

Indefinite

One-way FTM → S

No reverse conversion

CEX-Held FTM

If you hold FTM on a centralized exchange, we’re collaborating with major exchanges to enable users to upgrade FTM to S directly on their platforms. Stay tuned for updates from the CEX where you hold your FTM.

Alternatively, you can withdraw your FTM to Fantom Opera and upgrade to S through our upgrade portal.

Staked FTM

Users will be able to unlock their staked FTM and bridge immediately to Sonic following a 24-hour withdrawal waiting period. The new Sonic staking system features:

Parameter
Requirement

Withdrawal period

14 days

Minimum stake

1 S

LP Positions

If you hold liquidity positions, you'll need to:

  1. Break existing LP positions on Fantom

  2. Migrate each token individually following their specific processes

  3. Recreate your positions on Sonic

Other Token Holders

Contact your token's project team to learn their specific migration plan. Each project will choose between migration options such as:

  • Bridge migration through LayerZero

  • Airdrop migration based on snapshots

  • Hybrid approach combining both methods

Instructions for Project Owners

Projects have several options to migrate tokens from Fantom to Sonic. There is no one-size-fits-all solution — choose based on your token's setup and requirements.

Option 1: Bridge Migration

Day-one token bridging will be available through LayerZero at the Sonic launch.

Provider
Implementation
User Interface
Documentation

LayerZero

Technical OFT setup

Stargate

Limitations

  • Burnt LP remains on Fantom

  • If LP was sent to SCC, it can be bridged and recreated

  • Original token contract stays on Fantom (generally not an issue)


Option 2: Airdrop Migration

Deploy new token contract on Sonic with snapshot-based airdrop distribution.

Special Cases Requirements

Case
Handling Required
Solution

Multisigs

Yes

Different addresses on Sonic

LP tokens

Yes

Snapshot by LP weights

Burnt tokens

Optional

Can redistribute on Sonic

Initial LP

Yes

Additional funds for pairing

Advantages

  • Works for burnt tokens and LPs

  • No bridge needed

Limitations

  • Special handling for LP and multisig holders

  • More preparation work needed

  • Recreation of initial burnt LP requires additional funds


Option 3: Hybrid (Beethoven X Approach)

  1. Deploy new token on Sonic

  2. Enable bridging of original tokens

  3. Create migration contract for 1:1 swaps

  4. Allow two-way conversion initially

  5. Switch to one-way later

Example Implementation

  • New contract: sonicBeets

  • Users bridge fantomBeets to Sonic

  • Migration contract handles 1:1 exchange

  • Two-way conversion for limited time

  • Mirrors official FTM → S migration process

Covalent

Introduction

Covalent is a hosted blockchain data solution providing access to historical and current on-chain data for 100+ supported blockchains, including Opera.

Covalent maintains a full archival copy of every supported blockchain, meaning every balance, transaction, log event, and NFT asset data is available from the genesis block. This data is available via:

  1. Unified API — Incorporate blockchain data into your dApp with a familiar REST API

  2. Increment — Create and embed custom charts with no-code analytics

Use Covalent if you need:

  • Structured and enhanced on-chain data well beyond what you get from RPC providers

  • Broad and deep multi-chain data at scale

  • Enterprise-grade performance

Sign up to start building on Opera.

Unified API

The Covalent API is RESTful and offers the following for Opera:

Features
Type

Response Formats

JSON, CSV

Real-Time Data Latency

2 blocks

Batch Data Latency

30 minutes

Supported Network: chainName, chainId

Mainnet: fantom-mainnet, 250 Testnet: fantom-testnet, 4002

API Tiers

API Categories

Getting Started

  • API Key — Sign up for free

  • Quickstart — Summary of key resources to get you building immediately on the blockchain

  • API Reference — Try all the endpoints directly from your browser

  • Guides — Learn how to build dApps, fetch data, and expand your Web3 knowledge

Increment

Increment is a novel no-code charting and reporting tool powered by Covalent, revolutionizing how the Web3 space approaches analytics. Many analytics tools let you write SQL to create charts, but Increment is the only one to encode business logic - Reach, Retention, and Revenue - into an SQL compiler that can write valid SQL for you.

Use Cases

Increment can be used for:

  • Analyzing Blockchain Networks

  • Analyzing DEXs

  • Analyzing NFT Marketplaces

For example, click on the following table to get the latest number of active wallets, transactions, and tokens by day, week, month, or year for Fantom:

Getting Started

  • Increment — Log in via the Covalent Platform

  • Docs — Learn how to use Increment to build dynamic, custom charts

  • Data Models Demo — Build analytics in 3 clicks

  • Explore Models. Seek Alpha. — Browse all data models

  • Use Models. Become Alpha. — Use a data mode

Consensus

Consensus in a decentralized system is not just a process but a cornerstone of the system. Its mechanism guarantees a consistent and secure blockchain among all participants and ensures the system's integrity and reliability. Consensus ensures that transactions are consistently and securely validated and added to the blockchain. It's a critical element for effectively thwarting attempts by malicious actors to manipulate the network or its data. Opera uses Asynchronous Byzantine Fault Tolerance in combination with directed acyclic graphs to achieve consensus.

Practical Byzantine Fault Tolerance

Practical Byzantine Fault Tolerance (PBFT) is a consensus mechanism designed to enable decentralized systems to function correctly in the presence of malicious or faulty nodes. It is named after the Byzantine generals’ problem, which is an idea that illustrates the difficulties of achieving consensus in a decentralized system when some of the participants may be acting in bad faith.

In a PBFT system, nodes in a network communicate with each other to reach a consensus on the system's state, even when malicious actors are involved. To achieve this, they send messages back and forth that contain information about the system's state and the actions they propose.

Each node verifies the message it receives, and if it determines the message is valid, it sends a message to all the other nodes to indicate its agreement. In the context of cryptocurrencies, the message with which all nodes must agree is the blockchain, a ledger that stores a history of transactions.

Before the invention of cryptocurrencies, the major flaw with PBFT systems was their susceptibility to Sybil attacks. If an attacker controlled a sufficient number of nodes, they could control the entire system; there needed to be a deterrent to launch many nodes. Bitcoin first solved this problem with proof-of-work, forcing nodes to invest considerable energy to partake in the consensus.

Since then, many new solutions have been developed, such as proof-of-stake, which forces nodes to deposit tokens with monetary value, which Opera uses.

Hence, Practical Byzantine Fault Tolerance (PBFT) is a mechanism to achieve consensus. It forms a functioning decentralized system when coupled with proof-of-work or proof-of-stake to deter participants from messing with the network. However, Opera has decided to innovate on this mechanism by using Asynchronous Byzantine Fault Tolerance.

Asynchronous Byzantine Fault Tolerance

With Asynchronous Byzantine Fault Tolerance (ABFT), nodes can reach consensus independently and are not required to exchange final blocks sequentially to confirm transactions. At the same time, they exchange blocks, which is required to achieve consensus, and this is done asynchronously. Each node verifies transactions independently and is not required to incorporate blocks created by other miners or validators in sequential order.

This is opposed to PBFT systems, such as Bitcoin, in which the majority of nodes must agree to a block before it becomes final, which they must then sequentially order into their blockchain record. This slows down the network during high traffic; more on this in the consensus mechanism section further below.

Now that we have a basic understanding of Byzantine fault tolerance, let’s delve into the second part of Opera’s consensus mechanism, directed acyclic graphs.

Directed Acyclic Graphs

A graph is a non-linear data structure used to represent objects, called vertices, and the connections between them, called edges. A directed graph dictates that all its edges, the connections between objects, only flow in a certain direction. An acyclic graph does not contain any cycles, which makes it impossible to follow a sequence of edges and return to the starting point. As such, a directed acyclic graph (DAG) only flows in a certain direction and never repeats or cycles.

The diagram below is an example of a directed acyclic graph. Each oval is a vertex, and the lines connecting them are edges. The vertices only connect in one direction, downwards, and never repeat.

In our consensus algorithm, an event containing transactions is represented by a vertex in a DAG, and edges represent the relationships between the events. The edges may represent the dependencies between events indicating the order in which they were added to the DAG.

Events can be created and added to the DAG concurrently. The blocks do not need to be added in a specific order, which enables the system to achieve faster transaction times. It is not limited by the requirement to incorporate blocks sequentially, as is the case with many of the biggest blockchains currently available.

Opera's Consensus Mechanism

Opera uses a proof-of-stake, DAG-based, ABFT consensus mechanism. In this mechanism, each validator has its own local block DAG and batches incoming transactions into event blocks, which they add to their DAG as vertices — each event block is a vertex in the validator’s DAG that is full of transactions.

A validator’s block DAG

Before creating a new event block, a validator must first validate all transactions in its current event block and part of the ones it has received from other nodes; these are the event blocks it has received during the asynchronous exchange of event blocks explained in the section above. The new event block then is communicated with other nodes through the same asynchronous event communication.

During this communication, nodes share their own event blocks, and the ones they received from other nodes, with other validators that incorporate them in their own local DAGs. Consequently, this spreads all information through the network. The process is asynchronous as the event blocks shared between validators are not required to be sequential.

Unlike most blockchains, this DAG-based approach does not force validators to work on the current block that is being produced, which places restrictions on transaction speed and finality. Validators are free to create their own event blocks that contain transactions and share these with other validators on the network asynchronously, creating a non-linear record of transactions. This increases transaction speed and efficiency.

As an event block is sent and propagated across validators, it becomes a root event block once the majority of validators have received and agreed upon it. This root event block will eventually be ordered and included in the main chain, which is a blockchain that contains the final consensus among all event blocks that have become root event blocks.

Every validator stores and updates a copy of the main chain, which provides quick access to previous transaction history to process new event blocks more efficiently. As such, Opera's consensus mechanism combines a DAG-based approach that allows validators to confirm transactions asynchronously, which greatly increases speed, with a final blockchain that orders and stores all final transactions immutably and indefinitely.

Currently, the process of submitting a transaction and having it added to the Opera main chain through the consensus mechanism takes approximately 1-2 seconds. This involves the following steps:

  1. A user submits a transaction

  2. A validator node batches the transaction into a new event block

  3. The event block becomes a root event block once the majority of nodes have received it

  4. The root event block is ordered and finalized into the main chain as a block

When a user explores Opera through a block explorer, they view the final blocks on the Opera main chain. Event block generation and exchange in validators' DAGs is an internal process only and is not visible to end users.

For a more technical understanding of Opera's consensus mechanism, read Lachesis: Scalable Asynchronous BFT on DAG Streams.

API3

Introduction

is a collaborative project to deliver traditional API services to smart contract platforms in a decentralized and trust-minimized way. It is governed by a decentralized autonomous organization (DAO), namely the .

For more info about API3, check out its .

First-Party Oracles

An is a first-party oracle that pushes off-chain API data to your on-chain contract. Airnode lets API providers easily run their own oracle nodes. That way, they can provide data to any on-chain app that's interested in their services, all without an intermediary.

Using dAPIs — API3 Datafeeds

are continuously updated streams of off-chain data, such as the latest cryptocurrency, stock and commodity prices. They can power various decentralized applications such as DeFi lending, synthetic assets, stablecoins, derivatives, NFTs, and more.

The data feeds are continuously updated by using signed data. App owners can read the on-chain value of any dAPI in real-time. Due to being composed of first-party data feeds, dAPIs offer security, transparency, cost-efficiency, and scalability in a turn-key package.

Apart from relying on deviation threshold and heartbeat configuration updates, unlike traditional data feeds, enables apps using dAPIs to auction off the right to update the data feeds to searcher bots. Searcher bots can bid for price updates through the OEV Network to update the data feeds. All the OEV proceeds go back to the app.

The enables users to connect to a dAPI and access the associated data feed services. to learn more about how dAPIs work.

Subscribing to dAPIs

The lets users access dAPIs on both the and .

Exploring, Selecting, and Configuring Your dAPI

The provides a list of all the dAPIs available across multiple chains, including testnets. You can filter the list by mainnet or testnet chains. After selecting the chain, you can now search for a specific dAPI by name. Once selected, you will land on the details page (eg ETH/USD on the Opera Testnet) where you can find more information about the dAPI.

The currently supported configurations for dAPIs are:

Deviation
Heartbeat

Activating Your dAPI

If a dAPI is already activated, make sure to check the expiration date and update the parameters. You can update the parameters and extend the subscription by purchasing a new configuration.

After selecting the dAPI and the configuration, you will be presented with an option to purchase the dAPI and activate it. Make sure to check the time and amount of the subscription. If everything looks good, click on Purchase.

You can then connect your wallet and confirm the transaction. Once it's confirmed, you will be able to see the updated configuration for the dAPI.

Getting the Proxy Address

Once you are done configuring and activating the dAPI, you can now integrate it. To do so, click on the Integrate button on the dAPI details page.

You can now see the deployed proxy contract address. You can now use this to read from the configured dAPI.

Reading From a dAPI

Here's an example of a basic contract that reads from a dAPI:

  • setProxyAddress() is used to set the address of the dAPI Proxy Contract.

  • readDataFeed() is a view function that returns the latest price of the set dAPI.

You can read more about dAPIs .

Additional Resources

Here are some additional developer resources:

GetBlock

In this guide, we will walk you through the steps of using , , and , all of which are popular developer tools, to create and deploy a simple smart contract on Opera. Note that this tutorial will use the Opera testnet, but the steps remain the same for the mainnet.

Requirements

Before we begin, make sure you have the following:

  • MetaMask or another Web3 wallet

  • Remix: An online IDE used to write, compile, deploy, and debug Solidity code

  • GetBlock API key: GetBlock provides access to RPC endpoints for various blockchain networks, including Opera

    • To obtain an API key from GetBlock, register on and find the API key in your account

  • GetBlock RPC endpoint

    • To obtain an RPC endpoint from GetBlock, register on and add a new endpoint by choosing Opera (Fantom) as the protocol and testnet as the network

Create Contract

A) Configure MetaMask to connect to Opera using GetBlock RPC endpoints

  1. Open MetaMask and set up a custom network with the following details:

    1. Network Name: Opera Testnet

    2. New RPC URL: The GetBlock RPC endpoint you created

    3. Chain ID: Obtain the Chain ID for Opera from GetBlock documentation or API (e.g. 4002)

    4. Symbol: FTM

    5. Block Explorer URL:

B) Get test tokens on the Fantom testnet

  1. Visit the

  2. Input your MetaMask wallet address

  3. Complete the CAPTCHA and request the testnet FTM

C) Connect Remix with the Opera network using GetBlock RPC endpoints

  1. Go to the

  2. On the Remix home page, choose the Solidity environment

  3. With the Opera network selected in MetaMask, go to Remix and click the Deploy and Run Transactions button on the left side, which is the fourth button

  4. In the Remix environment section, select Custom - External HTTP Provider and enter the GetBlock RPC endpoint URL you created for Opera

D) Create a smart contract

  1. Click the File Explorer button on the left side on Remix, which is the first button

  2. Right click and choose New File to create a new Solidity file

  3. Enter the file name as "SimpleStorage.sol"

  4. Copy and paste the following example Solidity code into the SimpleStorage.sol file:contract SimpleStorage {

E) Compile the smart contract

  1. Click the Solidity Compiler button on the left side, which is the third button

  2. Enable auto-compile for convenience

  3. Click the Compile SimpleStorage.sol button

  4. Check for the green sign indicating successful compilation

F) Deploy the smart contract on the Opera testnet using Remix

  1. Click the Deploy and Run Transactions button on the left side, which is the fourth button

  2. Select the SimpleStorage.sol contract from the dropdown menu

  3. Click the Deploy button

  4. Confirm the transaction in the MetaMask pop-up window

  5. Once confirmed, you will see a message at the bottom right indicating the pending creation of the SimpleStorage contract

  6. You can click on the transaction line or the debug button to view more details of the transaction

  7. Copy the transaction hash for future reference

G) Use the Testnet Explorer

  1. Use the to explore transactions and contracts on the Opera network

  2. Paste the transaction hash into the search field at the top of the screen to view the details of your transaction

H) Interact with the deployed smart contract

  1. In Remix, under the Deploy and Run Transactions section, expand the SimpleStorage contract by clicking the > symbol

  2. The orange buttons represent functions that change information on the blockchain (state changes) and require gas to execute

  3. The blue buttons represent read-only functions that do not modify the blockchain and do not require gas

  • Get function

    1. Click the Get button to retrieve the stored value, but since we have not set any value yet, it will return the default value

  • Set function

    • Enter a value in the field next to the Set button

    • Click the Set button

    • Confirm the transaction in the MetaMask pop-up window

    • Wait for the transaction to be confirmed

    • After confirmation, you can check the transaction details in the bottom-right section


Congratulations! You have learned how to use Remix, MetaMask, and GetBlock RPC endpoints to create and deploy a smart contract on Opera.

This guide demonstrates the compatibility of Ethereum developer tools with Opera, providing you with options for building decentralized applications on both platforms.

If you have any feedback or questions, you may ask them on the .

OFT Standard
    uint storedData;

    function set(uint x) public {
        storedData = x;
    }

    function get() public view returns (uint) {
        return storedData;
    }
}
Remix
MetaMask
GetBlock RPC endpoints
GetBlock.io
GetBlock.io
https://explorer.testnet.fantom.network
Opera Testnet Faucet
Remix website
Explorer
GetBlock Discord channel

0.25%

24 hours

0.5%

24 hours

1%

24 hours

5%

24 hours

// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

import "@openzeppelin/[email protected]/access/Ownable.sol";
import "@api3/contracts/api3-server-v1/proxies/interfaces/IProxy.sol";

contract DataFeedReaderExample is Ownable {
    // The proxy contract address obtained from the API3 Market UI.
    address public proxyAddress;

    // Updating the proxy contract address is a security-critical
    // action. In this example, only the owner is allowed to do so.
    function setProxyAddress(address _proxyAddress) public onlyOwner {
        proxyAddress = _proxyAddress;
    }

    function readDataFeed()
        external
        view
        returns (int224 value, uint256 timestamp)
    {
        // Use the IProxy interface to read a dAPI via its
        // proxy contract .
        (value, timestamp) = IProxy(proxyAddress).read();
        // If you have any assumptions about `value` and `timestamp`,
        // make sure to validate them after reading from the proxy.
    }
}
API3
API3 DAO
documentation
Airnode
dAPIs
first-party oracles
OEV Network
API3 Market
Click here
API3 Market
Opera mainnet
testnet
API3 Market
here
Try deploying it on Remix!
API3 Market
API3 Docs
dAPI Docs
QRNG Docs
GitHub
Medium
Free Tier
Premium Tier
Balances
NFTs
Transactions
Security
Log Events & Others
Source: Central Blockchain Council of America

Run API Node

The Opera client was previously referred to as the "Opera client powered by Sonic technology", or simply, the "Sonic client".

However, given that the name "Sonic" has now been reserved for our new, upcoming network, any mention of "Sonic" on these pages, GitHub, or pieces of code, refers to the current Opera client.

Types of API Nodes

Before you start your Opera API node, you need to decide which type you want to deploy based on your use case. There are two possible modes of operation:

  • Pruned API Node Smaller database footprint utilizing around 700 GB of storage initially. It can respond to state-related RPC/socket requests, allows you to interact with the network state, trace smart contract execution, and submit signed transactions to the network. The state history is limited to the block from which the state DB is created, usually using a validator-type genesis file. If you try to query an older state, an error will be given.

  • Archive API Node Larger database size using approximately 5.5 TB of storage initially. It can respond to all state-related RPC/socket requests for the whole blockchain history, allows you to interact with the network state at any block, trace smart contract execution, and submit signed transactions to the network. It's usually created from an archive genesis file.

Steps to Run API Node

  1. Set up an Appropriate Server Instance

  2. Install Required Development Tools

  3. Build the Opera Node Software

  4. Prime the Opera Database

  5. Synchronize With the Network

1. Set up an Appropriate Server Instance

A dedicated hardware (also called bare metal) will provide the best possible performance and speed. If you want to integrate your node into a broader infrastructure of your project, you may want to consider a cloud service provider too. We recommend choosing one of the big cloud providers, e.g. Google GCP or Amazon AWS.

Node Hardware Specification

A minimal configuration for the Opera API node is a computation node with at least 4 vCPUs and 32 GB of RAM. You will also need either at least 1.2 TB or 7 TB of local NVMe storage space for the Opera database.

The proper size depends on your selected API node type. Remotely connected storage systems, e.g. Amazon EBS, usually don't provide the required latency and random access performance required to run an API node. The Google GCP N2D instances with local SSD drives, or Amazon AWS i3en.xlarge with the instance storage, match the minimal requirements.

The number of vCPUs and RAM available to the Opera node dictates the final performance and responsiveness of the API interface. Please scale the size of your server specification for the expected amount of RPC requests. We recommend instances with 8 vCPUs and 64 GB of RAM for smaller projects with less traffic, or 16 vCPUs and 128 GB of RAM for public API endpoints.

More RAM is needed to run "debug" API functionality in an RPC API node. For such a scenario, we recommend 128 GB.

Another option is to deploy several smaller nodes and load balance the incoming traffic between them. The deployment setup details suitable for your specific case may vary. We recommend using Ubuntu LTS Server (64-bit) or a similar Linux distribution.

Required Storage Capacity

  • 1.2 TB of local NVMe/SSD storage is sufficient to start a pruned API node using a validator genesis file.

  • 7 TB of local NVMe/SSD storage is needed to provision a full archive API node.

Obtain the latest genesis file.

Network Settings

The Opera node requires both TCP and UDP network traffic to be allowed on port 5050 by default. A custom port can be used with --port <port> flag when running your node.

The RPC/socket interface of the node can be opened for incoming HTTP traffic on port 80, but Opera does not support SSL/TLS encryption. You will need to add a proxy server between the public HTTP interface and the Opera node to be able to secure your connections with an SSL/TLS certificate.

Your cloud provider may offer a load-balanced application proxy solution, which includes certificate management. If you want to deploy such infrastructure yourself, you may want to check the HAProxy or Nginx community projects.

2. Install Required Development Tools

You need the essential building tools and Go compiler version 1.21 or newer to build the Opera client and bundled tools.

First, install the required build tools:

sudo apt-get install -y build-essential git

Install the latest Go compiler from binary distribution:

sudo rm -rf /usr/local/go
wget https://go.dev/dl/go1.22.4.linux-amd64.tar.gz
sudo tar -xzf go1.22.4.linux-amd64.tar.gz -C /usr/local/
rm -f go1.22.4.linux-amd64.tar.gz

Setup Go language export paths:

sudo tee /etc/profile.d/golang.sh > /dev/null <<EOT
export GOROOT=/usr/local/go
export GOPATH=\$HOME/go
export PATH=\$PATH:\$GOROOT/bin:\$GOPATH/bin

EOT

source /etc/profile.d/golang.sh

3. Build the Opera Node Software

Building the Opera binary requires the essential software development tools and the Go language version 1.21 or newer to be available. The previous section contains basic steps to deploy the required applications on the recommended Linux distribution. Please refer to your operating system manuals to install the development tools needed for this step.

Please check the latest release of the Opera node and adjust your commands accordingly.

git clone https://github.com/Fantom-foundation/Sonic.git
cd Sonic
git fetch --tags && git checkout -b v1.2.1-c tags/v1.2.1-c
make all

You can confirm the Opera release by executing the version subcommand on the created binary.

build/sonicd version

Optionally, you may want to copy the Opera node app and the tooling into the system-wide bin folder to make it available without explicitly specifying the binary path.

sudo mv build/sonic* /usr/local/bin/

4. Prime the Opera Database

You need a valid state database to be able to participate in the network. The fastest and easiest way is to use a genesis file. Please check the official repository, where new signed genesis files are published, and pick the latest one suitable for your selected node type. You will need approximately 50 GB of storage for a pruned genesis file and 350 GB of storage space for the full archive genesis download.

Obtain the latest genesis file and the corresponding checksum:

wget https://files.fantom.network/mainnet-285300-archive.g
wget https://files.fantom.network/mainnet-285300-archive.g.md5

Check your download using the MD5 checksum file:

$ md5sum --check mainnet-285300-archive.g.md5
mainnet-285300-archive.g: OK

Use the Opera tooling to prime the state database using the downloaded genesis file. Refer to these steps to determine the amount of cache and memory consumption soft limit for your node. The --datadir flag should point to your local NVMe storage mount path.

GOMEMLIMIT=58GiB sonictool \
    --datadir ~/.sonic \
    --cache 25600 \
    genesis mainnet-285300-archive.g

The Opera tool creates a primed state database from the genesis file. The last step is a validation procedure. It confirms that the expected state was built successfully reaching the expected root hash. You can refer to the official repository to verify that the state hash is indeed correct.

StateDB imported successfully, stateRoot matches module=gossip-store
  index=81990246 root="f60cd4…38652f"

Calculate Cache Size and Memory Consumption Soft Limit Values

The Opera node utilizes a large amount of RAM to achieve great speed and responsiveness. It should be the only user-space application consuming your system resources.

$ grep MemTotal /proc/meminfo
MemTotal:       65641928 kB
  • Approximately 40% of the available RAM should be used as the --cache for both the sonictool and sonicd applications. The final value is denominated in MiB. For example: 64,000 MiB RAM * 40% = 25,600 MiB.

  • Approximately 90% of the available RAM should be used as the application memory consumption limit. This limit is controlled by theGOMEMLIMIT environment variable. The final value includes abbreviated units. We recommend using GiB for simplicity. For example: 64 GiB RAM * 90% ≈ 58 GiB.

5. Synchronize With the Network

Your node database has been primed to a certain epoch in the past using a genesis file and the Opera tool. Now you need to get the node up to the current state.

To do it, your node needs to be started in the read-only mode and establish a connection with the network.Please make sure your firewall is configured correctly to allow such communication. The amount of memory dedicated to the node cache and the memory consumption limit are outlined above.

The RPC and/or socket interfaces are turned off by default. You need to specify which one you want to start and what range of services you want it to offer. You can find the default configuration in the example below. Please adjust the port, namespace, and domain-related options based on your specific needs.

The HTTP API offers the Web3 interface for the request-response type of interaction with the Opera chain. The WS API supports a sustained network connection with the ability to subscribe to certain network events, e.g. new blocks being created or contracts being interacted with.

GOMEMLIMIT=58GiB sonicd \
    --datadir ~/.sonic \
    --cache 25600 \
    --http \
    --http.addr=0.0.0.0 \
    --http.port=8080 \
    --http.corsdomain="*" \
    --http.vhosts="*" \
    --http.api=eth,web3,net,ftm,txpool,abft,dag \
    --ws \
    --ws.addr=0.0.0.0 \
    --ws.port=8081 \
    --ws.origins="*" \
    --ws.api=eth,web3,net,ftm,txpool,abft,dag

The Opera node should start and connect with the network, advancing the state by creating and processing new blocks. Please notice the age part of the log message, which tells you how far in the past these blocks are. The age should not exceed a couple of seconds once the node is fully synchronized.

Node Providers

Storing wallets on managed servers is not recommended, and it is advised to use only unmanaged servers for running validator nodes.

However, if you prefer to run a validator node using a provider rather than your own hardware, you can find a comprehensive list of providers below that offer the setup of an Opera node.

Managed Node Providers

(Bware Labs)

(read-only)

(read-only)

(read-only)

List provided by CompareNodes.

Cloud Providers

  • Amazon AWS (+)

  • Digital Ocean

  • Google GCP

  • Microsoft Azure

Bare Metal Providers

Gathered from the community's suggestions.

  • Vultr

  • OVH

  • Webtropia

  • Leaseweb

  • Latitude

Chainlink

Introduction

Chainlink enables smart contracts on Opera to leverage extensive off-chain resources, such as tamper-proof price data, verifiable randomness, external APIs, and much more. This documentation describes access to various cryptocurrency price data available to be integrated into decentralized applications running on Opera.

  • For implementation instructions, please refer to

  • For LINK token and faucet details, please refer to .

Note, off-chain equity and ETF assets are only traded during standard market hours (9:30 AM – 4:00 PM ET Monday–Friday). Using these feeds outside of those windows is not recommended.

Supported Token Pairs

Currently, the list of supported symbols can be found below. Going forward, this list will continue to expand based on developer needs and community feedback.

Opera Mainnet

Pair
Dec
Contract Address / Proxy

Opera Testnet

Pair
Dec
Contract Address / Proxy

Querying Prices

Currently, there are two methods for developers to query prices from the Chainlink data feed: Through the Solidity PriceConsumer smart contract running on the hosting blockchain and through an external interface utilizing Chainlink AggregatorInterface ABI.

Solidity

To consume price data, your smart contract should reference , which defines the external functions implemented by Price Feeds. The latest RoundData function returns five values representing information about the latest price data. See Price Feeds API Reference for more details.

The Graph

Introduction

Getting historical data on a smart contract can be frustrating when you’re building a dApp. The Graph provides an easy and decentralized option to query smart contract data through APIs known as subgraphs. Its infrastructure relies on a decentralized network of indexers, enabling your dApp to achieve true decentralization.

These subgraphs only take a few minutes to set up. To get started, follow these three steps:

All developers receive 100k free queries per month on the decentralized network. After these free queries, you only pay based on usage at $4 for every 100k queries.

1. Initialize Your Subgraph Project

Create a Subgraph on Subgraph Studio⁠

Go to the and connect your wallet. Once your wallet is connected, you can begin by clicking Create a Subgraph. When choosing a name for your subgraph, it is recommended to use title case, e.g. “Subgraph Name Chain Name.”

You will then land on your subgraph’s page. All the CLI commands you need will be visible on the right side of the page:

Install the Graph CLI⁠

On your local machine run the following:

Initialize your Subgraph⁠

You can copy this directly from your subgraph page to include your specific subgraph slug:

You’ll be prompted to provide some info on your subgraph like this:

Simply have your contract verified on the block explorer and the CLI will automatically obtain the ABI and set up your subgraph. The default settings will generate an entity for each event.

2. Deploy and Publish

Deploy to Subgraph Studio⁠

First, run these commands:

Then run these to authenticate and deploy your subgraph. You can copy these commands directly from your subgraph’s page in Studio to include your specific deploy key and subgraph slug:

You will be asked for a version label. You can enter something like v0.0.1, but you’re free to choose the format.

Test Your Subgraph⁠

You can test your subgraph by making a sample query in the playground section. The Details tab will show you an API endpoint. You can use that endpoint to test from your dApp.

Publish Your Subgraph to The Graph’s Decentralized Network

Once your subgraph is ready to be put into production, you can publish it to the decentralized network. On your subgraph’s page in Subgraph Studio, click on the Publish button:

Before you can query your subgraph, Indexers need to begin serving queries on it. To streamline this process, you can curate your own subgraph using GRT.

When publishing, you’ll see the option to curate your subgraph. As of May 2024, it is recommended that you curate your own subgraph with at least 3,000 GRT to ensure that it is indexed and available for querying as soon as possible.

3. Query Your Subgraph

Congratulations! You can now query your subgraph on the decentralized network!

For any subgraph on the decentralized network, you can start querying it by passing a GraphQL query into the subgraph’s query URL which can be found at the top of its Explorer page.

Here’s an example from the by Messari:

The query URL for this subgraph is:

Now, you simply need to fill in your own API Key to start sending GraphQL queries to this endpoint.

Getting Your Own API Key

In Subgraph Studio, you’ll see the API Keys menu at the top of the page. Here you can create API Keys.

Appendix

Sample Query

This query shows the most expensive CryptoPunks sold:

Passing this into the query URL returns this result:

Trivia: Looking at the top sales on the , it looks like the top sale is Punk #5822, not #9998. Why? Because they censor the flash-loan sale that happened.

Sample Code

Additional Resources:

  • To explore all the ways you can optimize and customize your subgraph for better performance, read more about .

  • For more information about querying data from your subgraph, read more .

Images used in this article are sourced from .

1RPC
3xpl
Allnodes
Ankr
All That Node
Bitquery
Blast
Blockdaemon
BLOCKSIZE
BlockPI Network
Chainbase
Chainstack
ChainUp Cloud
Covalent
DRPC
Dwellir
Gateway.fm
GetBlock
Grove
Infura
Lava Network
Moralis
NodeReal
NOWNodes
OMNIA Protocol
OnFinality
P2P
Particle Network
QuickNode
RockX
SpeedyNodes
Subsquid
Tatum
Taurus
thirdweb
Zeeve

AAVE / USD

8

0xE6ecF7d2361B6459cBb3b4fb065E0eF4B175Fe74

BNB / USD

8

0x6dE70f4791C4151E00aD02e969bD900DC961f92a

BTC / USD

8

0x8e94C22142F4A64b99022ccDd994f4e9EC86E4B4

CREAM / USD

8

0xD2fFcCfA0934caFdA647c5Ff8e7918A10103c01c

DAI / USD

8

0x91d5DEFAFfE2854C7D02F50c80FA1fdc8A721e52

ETH / USD

8

0x11DdD3d147E5b83D01cee7070027092397d63658

FTM / USD

8

0xf4766552D15AE4d256Ad41B6cf2933482B0680dc

LINK / USD

8

0x221C773d8647BC3034e91a0c47062e26D20d97B4

SNX / USD

8

0x2Eb00cC9dB7A7E0a013A49b3F6Ac66008d1456F7

SUSHI / USD

8

0xCcc059a1a17577676c8673952Dc02070D29e5a66

USDC / USD

8

0x2553f4eeb82d5A26427b8d1106C51499CBa5D99c

USDT / USD

8

0xF64b636c5dFe1d3555A847341cDC449f612307d0

BTC / ETH

18

0x2a7e704E84d5093F3F31121D76A9b2343ccA13B8

BTC / USD

8

0x65E8d79f3e8e36fE48eC31A2ae935e92F5bBF529

ETH / USD

8

0xB8C458C957a6e6ca7Cc53eD95bEA548c52AFaA24

FTM / USD

8

0xe04676B9A9A2973BCb0D1478b5E1E9098BBB7f3D

LINK / ETH

18

0xFE514ef0883F868fc2AE477A65e162f80CE9cD5D

LINK / USD

8

0x6d5689Ad4C1806D1BA0c70Ab95ebe0Da6B204fC5

USDT / USD

8

0x9BB8A6dcD83E36726Cc230a97F1AF8a84ae5F128

pragma solidity ^0.6.7;

import "@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol";

contract PriceConsumerV3 {

    AggregatorV3Interface internal priceFeed;

    /**
     * Network: Kovan
     * Aggregator: ETH/USD
     * Address: 0x9326BFA02ADD2366b30bacB125260Af641031331
     */
    constructor() public {
        priceFeed = AggregatorV3Interface(0x9326BFA02ADD2366b30bacB125260Af641031331);
    }

    /**
     * Returns the latest price
     */
    function getThePrice() public view returns (int) {
        (
            uint80 roundID, 
            int price,
            uint startedAt,
            uint timeStamp,
            uint80 answeredInRound
        ) = priceFeed.latestRoundData();
        return price;
    }
}
Chainlink's documentation
LINK token contracts
AggregatorV3Interface
npm install -g @graphprotocol/graph-cli
graph init --studio <SUBGRAPH_SLUG>
$ graph codegen
$ graph build
$ graph auth --studio <DEPLOY_KEY>
$ graph deploy --studio <SUBGRAPH_SLUG>
{
  trades(orderBy: priceETH, orderDirection: desc) {
    priceETH
    tokenId
  }
}
{
  "data": {
    "trades": [
      {
        "priceETH": "124457.067524886018255505",
        "tokenId": "9998"
      },
      {
        "priceETH": "8000",
        "tokenId": "5822"
      },
//      ...
const axios = require('axios');

const graphqlQuery = `{
  trades(orderBy: priceETH, orderDirection: desc) {
    priceETH
    tokenId
  }
}`;
const queryUrl = '<https://gateway-arbitrum.network.thegraph.com/api/[api-key]/subgraphs/id/HdVdERFUe8h61vm2fDyycHgxjsde5PbB832NHgJfZNqK>'

const graphQLRequest = {
  method: 'post',
  url: queryUrl,
  data: {
    query: graphqlQuery,
  },
};

// Send the GraphQL query
axios(graphQLRequest)
  .then((response) => {
    // Handle the response here
    const data = response.data.data
    console.log(data)

  })
  .catch((error) => {
    // Handle any errors
    console.error(error);
  });
Initialize Your Subgraph Project
Deploy and Publish
Query From Your dApp
Subgraph Studio
CryptoPunks Ethereum subgraph
https://gateway-arbitrum.network.thegraph.com/api/**[api-key]**/subgraphs/id/HdVdERFUe8h61vm2fDyycHgxjsde5PbB832NHgJfZNqK
CryptoPunks website
creating a subgraph here
here
The Graph

Band Protocol

Introduction

Developers building on Opera can now leverage Band Protocol’s decentralized oracle infrastructure. With Band’s oracles, they now have access to various cryptocurrency price data to integrate into their applications.

Supported Tokens

Currently, the list of supported symbols can be found below. Going forward, this list will continue to expand based on developer needs and community feedback.

Crypto

AAVE

BAND

BNB

BTC

COVER

BAND

CREAM

CRV

ETH

FTM

BNB

HEGIC

KP3R

LINK

SFI

BTC

SNX

SUSHI

YFI

Stablecoins

FRAX

USDC

SUSD

Commodities

OIL

XAG

XAU

Forex

CHF

CNY

EUR

GBP

JPY

Price Pairs

The methods in the following sections can work with any combination of base/quote token pairs, as long as the base and quote symbols are supported by the dataset.

Querying Prices

Currently, there are two methods for developers to query prices from Band’s oracle: through Band’s StdReference smart contract on BSC and through their bandchain.js JavaScript helper library.

Solidity Smart Contract

To query prices from Band’s oracle, a smart contract should reference Band’s StdReference contract, specifically the getReferenceData and getReferenceDatabulk methods.

getReferenceData takes two strings as the inputs, the base and quote symbol, respectively. It then queries the StdReference contract for the latest rates for those two tokens and returns a ReferenceData struct, shown below.

struct ReferenceData {
    uint256 rate; // base/quote exchange rate, multiplied by 1e18.
    uint256 lastUpdatedBase; // UNIX epoch of the last time when base price gets updated.
    uint256 lastUpdatedQuote; // UNIX epoch of the last time when quote price gets updated.
}

getReferenceDataBulk instead takes two lists, one of the base tokens, and one of the quotes. It then proceeds to similarly queries the price for each base/quote pair at each index, and returns an array of ReferenceData structs.

For example, if we call getReferenceDataBulk with ['BTC','BTC','ETH'] and ['USD','ETH','BNB'], the returned ReferenceData array will contain information regarding the pairs:

  • BTC/USD

  • BTC/ETH

  • ETH/BNB

Contract Addresses

Blockchain
Contract Address
Explorer

Opera (Mainnet)

0xDA7a001b254CD22e46d3eAB04d937489c93174C3

Example Usage

This contract demonstrates an example of using Band’s StdReference contract and the getReferenceData function.

BandChain.JS

Band’s node helper library bandchain.js also supports a similar getReferenceData function. This function takes one argument, a list of token pairs to query the result. It then returns a list of corresponding rate values.

Example Usage

The code below shows an example usage of the function:

const { Client } = require('@bandprotocol/bandchain.js');

// BandChain's REST Endpoint
const endpoint = 'https://rpc.bandchain.org';
const client = new Client(endpoint);

// This example demonstrates how to query price data from
// Band's standard dataset
async function exampleGetReferenceData() {
  const rate = await client.getReferenceData(['BTC/ETH','BAND/EUR', 'FTM/USD']);
  return rate;
}

(async () => {
  console.log(await exampleGetReferenceData());
})();

The corresponding result will then be similar to:

$ node index.js
[ 
    { 
        pair: 'BTC/ETH',
        rate: 30.998744363906173,
        updatedAt: { base: 1615866954, quote: 1615866954 },
        requestID: { base: 2206590, quote: 2206590 } 
    },
    { 
        pair: 'BAND/EUR',
        rate: 10.566138918332376,
        updatedAt: { base: 1615866845, quote: 1615866911 },
        requestID: { base: 2206539, quote: 2206572 } 
    }, 
    { 
        pair: 'FTM/USD',
        rate: 0.36662363,
        updatedAt: { base: 1615866851, quote: 1615866960 },
        requestID: { base: 2206543, quote: 0 } 
    }
]

For each pair, the following information will be returned:

  • pair: The base/quote symbol pair string.

  • rate: The resulting rate of the given pair.

  • updated: The timestamp at which the base and quote symbols were last updated on BandChain. For USD, this will be the current timestamp.

  • rawRate: This object consists of two parts.

    • value is the BigInt value of the actual rate, multiplied by 10^decimals.

    • decimals is then the exponent by which rate was multiplied by to get rawRate.

Run Validator Node

Validator Parameters

  • Minimum stake: 50,000 FTM.

  • Minimum hardware requirements: AWS EC2 m5.large with 8GB RAM, 2 vCPUs, and at least 300 GB of Amazon EBS General Purpose SSD (gp3) storage (or equivalent).

  • We would recommend going with Ubuntu Server 22.04 LTS (64-bit).

Network Settings

Open up port 22 for SSH, as well as port 7946 for both TCP and UDP traffic. A custom port can be used with "--port <port>" flag when running your testnet node.

Steps to Run Validator

1. Install Required Tools

You are still logged in as the new user via SSH. Now we are going to install Go and Opera.

First, install the required build tools:

Install Opera:

Validate your Opera installation:

db.preset

When using version 1.1.3, you need to add the db.preset argument (introduced since 1.1.2) for starting the Opera command. For standard conditions, please use this option:

  • db.preset=ldb-1

You can use different db presets, either --db.preset ldb-1 or --db.preset legacy-db. It's not recommended to use pbl-1 for validator nodes.

2. Register Testnet Validator

Next, register your Opera testnet validator node. You need to create a validator wallet. The wallet is the validator’s identity on the network, which it uses to authenticate, sign messages, etc.

First, start the Opera read-only node and download a genesis file from this .

3. Create a Validator Wallet

The node is currently running and syncing the network in your console. To create a wallet, open a new console window, connect to the server via SSH, and enter the following commands.:

After entering the command, you will get prompted to enter a password for the account. It will look something like this:

4. Create a New Validator Key

We have to create a validator private key with which to sign consensus messages. It can be done only using go-opera:

Follow the prompts and supply the password. The output is as follows:

5. Create Your Validator via The SFC

You should wait for your node to sync to the latest block of the network before proceeding. To proceed, open up the console where you entered the commands to create the validator wallet previously and attach it to the Opera node console:

By doing so, you will get a JavaScript console where you can directly interact with the Opera node and e.g. send transactions (which you will do in a moment):

Now initialize the SFC contract ABI variable:

Also, initialize the SFC contract object itself:

After initializing both variables, you can now interact with the network’s SFC. Enter the following command to check that everything works as expected:

If it looks like the above, everything is working. Next, try to get your validator ID from the SFC using your previously generated validator wallet address:

This should return "0" as you are not registered as a validator yet: Next, unlock your validator wallet to be able to execute the registration transaction (make sure to use the password you set before):

This will return “true” if unlocking the wallet was successful: Next, send the createValidator transaction to register your validator (the value is the representation of the smallest FTM unit, so dividing it by 1e18 will result in 500,000 FTM. Alternatively, you can use web3.toWei("500000.0", "ftm")). Use quotes for "0xYOUR_PUBKEY" and "0xYOUR_ADDRESS":

Make sure to check your registration transaction (could take a few moments to be confirmed):

Look for the status: “0x1” at the bottom, which means the transaction was successful. You can also copy the transaction hash and go to the Fantom Explorer and check your transaction there:

https://explorer.testnet.fantom.network/transactions/[YOURTX]

Finally, execute the following command again to check your validator ID:

It should now return something other than “0”. Congrats, you've registered a testnet validator!

6. Run Your Testnet Validator Node

The last step is to restart your node in validator mode! Close the Opera console window by typing “exit”. Then head back to the console window where you started your node with the following command:

For the latest update, please check .

Link
# Install build-essential
(validator)$ sudo apt-get install -y build-essential
# Install go
(validator)$ wget https://go.dev/dl/go1.19.3.linux-amd64.tar.gz
(validator)$ sudo tar -xvf go1.19.3.linux-amd64.tar.gz
(validator)$ sudo mv go /usr/local
# Export go paths
(validator)$ vi ~/.bash_aliases
# Append the following lines
export GOROOT=/usr/local/go
export GOPATH=$HOME/go
export PATH=$GOPATH/bin:$GOROOT/bin:$PATH
#
source ~/.bash_aliases
# Install Opera
(validator)$ git clone https://github.com/Fantom-foundation/go-opera.git
(validator)$ cd go-opera/
(validator)$ git checkout release/1.1.3-rc.5
(validator)$ make
$./build/opera help

VERSION:
1.1.3-rc.5
# Start opera node
(validator)$ cd build/
(validator)$ wget https://download.fantom.network/testnet-6226-no-mpt.g
(validator)$ nohup ./opera --genesis testnet-6226-no-mpt.g --nousb \
      --db.preset ldb-1 &
# Create validator wallet
(validator)$ ./opera account new
(validator)$ ./opera validator new
Your new key was generated

Public key:                  0xPubkey
Path of the secret key file:

- You can share your public key with anyone. Others need it to validate messages from you.
- You must NEVER share the secret key with anyone! The key controls access to your validator!
- You must BACKUP your key file! Without the key, it's impossible to operate the validator!
- You must REMEMBER your password! Without the password, it's impossible to decrypt the key!
# Attach to opera console
(validator)$ ./opera attach
abi = JSON.parse('[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"validatorID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"status","type":"uint256"}],"name":"ChangedValidatorStatus","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"toValidatorID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lockupExtraReward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lockupBaseReward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"unlockedReward","type":"uint256"}],"name":"ClaimedRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"validatorID","type":"uint256"},{"indexed":true,"internalType":"address","name":"auth","type":"address"},{"indexed":false,"internalType":"uint256","name":"createdEpoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"createdTime","type":"uint256"}],"name":"CreatedValidator","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"validatorID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"deactivatedEpoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"deactivatedTime","type":"uint256"}],"name":"DeactivatedValidator","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"toValidatorID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Delegated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"validatorID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"duration","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LockedUpStake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"toValidatorID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lockupExtraReward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lockupBaseReward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"unlockedReward","type":"uint256"}],"name":"RestakedRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"toValidatorID","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"wrID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Undelegated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"validatorID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"penalty","type":"uint256"}],"name":"UnlockedStake","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"UpdatedBaseRewardPerSec","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"blocksNum","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"period","type":"uint256"}],"name":"UpdatedOfflinePenaltyThreshold","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"validatorID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"refundRatio","type":"uint256"}],"name":"UpdatedSlashingRefundRatio","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"toValidatorID","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"wrID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdrawn","type":"event"},{"constant":true,"inputs":[],"name":"baseRewardPerSecond","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"contractCommission","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"currentSealedEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getEpochSnapshot","outputs":[{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"epochFee","type":"uint256"},{"internalType":"uint256","name":"totalBaseRewardWeight","type":"uint256"},{"internalType":"uint256","name":"totalTxRewardWeight","type":"uint256"},{"internalType":"uint256","name":"baseRewardPerSecond","type":"uint256"},{"internalType":"uint256","name":"totalStake","type":"uint256"},{"internalType":"uint256","name":"totalSupply","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"getLockupInfo","outputs":[{"internalType":"uint256","name":"lockedStake","type":"uint256"},{"internalType":"uint256","name":"fromEpoch","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"getStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"getStashedLockupRewards","outputs":[{"internalType":"uint256","name":"lockupExtraReward","type":"uint256"},{"internalType":"uint256","name":"lockupBaseReward","type":"uint256"},{"internalType":"uint256","name":"unlockedReward","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getValidator","outputs":[{"internalType":"uint256","name":"status","type":"uint256"},{"internalType":"uint256","name":"deactivatedTime","type":"uint256"},{"internalType":"uint256","name":"deactivatedEpoch","type":"uint256"},{"internalType":"uint256","name":"receivedStake","type":"uint256"},{"internalType":"uint256","name":"createdEpoch","type":"uint256"},{"internalType":"uint256","name":"createdTime","type":"uint256"},{"internalType":"address","name":"auth","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"getValidatorID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getValidatorPubkey","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"getWithdrawalRequest","outputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"lastValidatorID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"maxDelegatedRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"maxLockupDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"minLockupDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"minSelfStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"slashingRefundRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"stashedRewardsUntilEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalActiveStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSlashedStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"unlockedRewardRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"validatorCommission","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"version","outputs":[{"internalType":"bytes3","name":"","type":"bytes3"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"withdrawalPeriodEpochs","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"withdrawalPeriodTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"currentEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"getEpochValidatorIDs","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"validatorID","type":"uint256"}],"name":"getEpochReceivedStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"validatorID","type":"uint256"}],"name":"getEpochAccumulatedRewardPerToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"validatorID","type":"uint256"}],"name":"getEpochAccumulatedUptime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"validatorID","type":"uint256"}],"name":"getEpochAccumulatedOriginatedTxsFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"validatorID","type":"uint256"}],"name":"getEpochOfflineTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"validatorID","type":"uint256"}],"name":"getEpochOfflineBlocks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"uint256","name":"validatorID","type":"uint256"}],"name":"rewardsStash","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"uint256","name":"toValidatorID","type":"uint256"}],"name":"getLockedStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"sealedEpoch","type":"uint256"},{"internalType":"uint256","name":"_totalSupply","type":"uint256"},{"internalType":"address","name":"nodeDriver","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"auth","type":"address"},{"internalType":"uint256","name":"validatorID","type":"uint256"},{"internalType":"bytes","name":"pubkey","type":"bytes"},{"internalType":"uint256","name":"status","type":"uint256"},{"internalType":"uint256","name":"createdEpoch","type":"uint256"},{"internalType":"uint256","name":"createdTime","type":"uint256"},{"internalType":"uint256","name":"deactivatedEpoch","type":"uint256"},{"internalType":"uint256","name":"deactivatedTime","type":"uint256"}],"name":"setGenesisValidator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"uint256","name":"toValidatorID","type":"uint256"},{"internalType":"uint256","name":"stake","type":"uint256"},{"internalType":"uint256","name":"lockedStake","type":"uint256"},{"internalType":"uint256","name":"lockupFromEpoch","type":"uint256"},{"internalType":"uint256","name":"lockupEndTime","type":"uint256"},{"internalType":"uint256","name":"lockupDuration","type":"uint256"},{"internalType":"uint256","name":"earlyUnlockPenalty","type":"uint256"},{"internalType":"uint256","name":"rewards","type":"uint256"}],"name":"setGenesisDelegation","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes","name":"pubkey","type":"bytes"}],"name":"createValidator","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"validatorID","type":"uint256"}],"name":"getSelfStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"toValidatorID","type":"uint256"}],"name":"delegate","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"toValidatorID","type":"uint256"},{"internalType":"uint256","name":"wrID","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"undelegate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"validatorID","type":"uint256"}],"name":"isSlashed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"toValidatorID","type":"uint256"},{"internalType":"uint256","name":"wrID","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"validatorID","type":"uint256"},{"internalType":"uint256","name":"status","type":"uint256"}],"name":"deactivateValidator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"uint256","name":"toValidatorID","type":"uint256"}],"name":"pendingRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"uint256","name":"toValidatorID","type":"uint256"}],"name":"stashRewards","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"toValidatorID","type":"uint256"}],"name":"claimRewards","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"toValidatorID","type":"uint256"}],"name":"restakeRewards","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"validatorID","type":"uint256"},{"internalType":"bool","name":"syncPubkey","type":"bool"}],"name":"_syncValidator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"offlinePenaltyThreshold","outputs":[{"internalType":"uint256","name":"blocksNum","type":"uint256"},{"internalType":"uint256","name":"time","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"updateBaseRewardPerSecond","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"blocksNum","type":"uint256"},{"internalType":"uint256","name":"time","type":"uint256"}],"name":"updateOfflinePenaltyThreshold","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"validatorID","type":"uint256"},{"internalType":"uint256","name":"refundRatio","type":"uint256"}],"name":"updateSlashingRefundRatio","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256[]","name":"offlineTime","type":"uint256[]"},{"internalType":"uint256[]","name":"offlineBlocks","type":"uint256[]"},{"internalType":"uint256[]","name":"uptimes","type":"uint256[]"},{"internalType":"uint256[]","name":"originatedTxsFee","type":"uint256[]"}],"name":"sealEpoch","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256[]","name":"nextValidatorIDs","type":"uint256[]"}],"name":"sealEpochValidators","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"uint256","name":"toValidatorID","type":"uint256"}],"name":"isLockedUp","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"uint256","name":"toValidatorID","type":"uint256"}],"name":"getUnlockedStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"toValidatorID","type":"uint256"},{"internalType":"uint256","name":"lockupDuration","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"lockStake","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"toValidatorID","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"unlockStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]')
sfcc = web3.ftm.contract(abi).at("0xfc00face00000000000000000000000000000000")
// Sanity check
sfcc.lastValidatorID() // if everything is all right, will return a non-zero value
# Get your validator id
sfcc.getValidatorID("{VALIDATOR_WALLET_ADDRESS}")
# Unlock validator wallet
personal.unlockAccount("{VALIDATOR_WALLET_ADDRESS}", "{PASSWORD}", 60)
# Register your validator
tx = sfcc.createValidator("0xYOUR_PUBKEY", {from:"0xYOUR_ADDRESS", value: web3.toWei("500000.0", "ftm")}) // 500000.0 FTM
# Check your registration transaction
ftm.getTransactionReceipt(tx)
# Get your validator id
sfc.getValidatorID("{VALIDATOR_WALLET_ADDRESS}")
(validator)$ nohup ./opera --genesis testnet.g --nousb \
    --validator.id ID --validator.pubkey 0xPubkey --validator.password /path/to/password &
Install Required Tools
Register Testnet Validator
Create a Validator Wallet
Create a New Validator Key
Create Your Validator via The SFC
Run Your Testnet Validator Node
list of genesis files
our GitHub
example-network-status-increment-ftm
example-api-response-json

Run Validator Node

The Opera client was previously referred to as the "Opera client powered by Sonic technology", or simply, the "Sonic client".

However, given that the name "Sonic" has now been reserved for our new, upcoming network, any mention of "Sonic" on these pages, GitHub, or pieces of code, refers to the current Opera client.

Validator Parameters

  • Minimum Stake: 50,000 FTM.

  • Maximum Validator Size: 15x the self-stake amount.

  • Rewards: Currently ~6% APR (normal APR for self-stake + 15% of delegators' rewards). APR varies based on the amount of FTM staked on the entire network. For up-to-date APR, check our website.

Steps to Run Validator

  1. Launch a Server Instance

  2. Install Required Development Tools

  3. Build the Opera Node Software

  4. Prime the Opera Database

  5. Synchronize With the Network

  6. Create a Validator Wallet

  7. Create a Validator Signing Key

  8. Register Your Validator

  9. Run Your Validator Node

  10. Next Steps

1. Launch a Server Instance

You can run your validator node on dedicated hardware (also called bare metal) or use a cloud provider. We recommend choosing one of the established cloud providers, e.g. Google GCP or Amazon AWS.

Node Hardware Specifications

To run the node software, you need at least 4 vCPUs and 32 GB of RAM. We recommend getting at least 1 Gbps of redundant backbone connectivity to allow a stable and uninterrupted network traffic flow.

You will also need at least 1 TB of local SSD/NVMe storage. Remotely connected storage systems, e.g., Amazon EBS, usually don't provide the latency and random access performance the node requires. The Google GCP N2D instances with local SSD drives or Amazon AWS i3en.xlarge with the instance storage match the minimal requirements.

We recommend going with Ubuntu LTS Server (64-bit) or a similar Linux distribution.

Required Storage Capacity

  • 1 TB of local SSD/NVMe storage is sufficient for a pruned DB either on a validator or on a no-history pruned dataset.

  • 6 TB of local SSD/NVMe is needed if you'd like to run a full-size (non-pruned) datadir.

Obtain the latest genesis file.

Network Settings

An Opera node requires both TCP and UDP network traffic to be allowed on port 5050 by default. A custom port can be used with --port <port> flag when running your node.

2. Install Required Development Tools

You need the essential building tools and Go compiler version 1.21 or newer to build the Opera client and bundled tools.

First, install the required build tools:

sudo apt-get install -y build-essential git

Install the latest Go compiler from binary distribution:

sudo rm -rf /usr/local/go
wget https://go.dev/dl/go1.22.8.linux-amd64.tar.gz
sudo tar -xzf go1.22.8.linux-amd64.tar.gz -C /usr/local/
rm -f go1.22.8.linux-amd64.tar.gz

Set up Go language export paths:

sudo tee /etc/profile.d/golang.sh > /dev/null <<EOT
export GOROOT=/usr/local/go
export GOPATH=\$HOME/go
export PATH=\$PATH:\$GOROOT/bin:\$GOPATH/bin

EOT

source /etc/profile.d/golang.sh

3. Build the Opera Node Software

Building the Opera binary requires the essential software development tools and the Go language version 1.21 or newer to be available. The previous section contains basic steps to deploy the required applications on the recommended Linux distribution. Please refer to your operating system manuals to install the development tools needed for this step.

Please check the latest release of the Opera node and adjust your commands accordingly.

git clone https://github.com/Fantom-foundation/Sonic.git
cd Sonic
git fetch --tags && git checkout -b v1.2.1-g tags/v1.2.1-g
make all

You can confirm the Opera release by executing the version subcommand on the created binary.

build/sonicd version

Optionally, you may want to copy the Opera node app and the tooling into the system-wide bin folder to make it available without explicitly specifying the binary path.

sudo mv build/sonic* /usr/local/bin/

4. Prime the Opera Database

You need a valid state database to be able to participate in the network. The fastest and easiest way is to use a genesis file. Please check the official repository, where new signed genesis files are published, and pick the latest one suitable for a validator node. You will need approximately 50 GB of storage space for the genesis file download.

Download the latest genesis file and the corresponding checksum:

wget https://files.fantom.network/opera/mainnet/mainnet-311670-validator.g
wget https://files.fantom.network/opera/mainnet/mainnet-311670-validator.g.md5

Check your download using the MD5 checksum file:

$ md5sum --check mainnet-311670-validator.g.md5
mainnet-311670-validator.g: OK

Use the Opera tooling to prime the state database using the downloaded genesis file. Refer to these steps to determine the amount of cache and memory consumption soft limit for your node.

The --datadir flag should point to your local NVMe storage mount path. The --mode validator flag tells the tool to create a minimal required state database for your node.

GOMEMLIMIT=58GiB sonictool --datadir ~/.sonic --cache 25600 \
    genesis --mode validator mainnet-311670-validator.g

The Opera tool creates a primed state database from the genesis file. The last step is a validation procedure. It confirms that the expected state was built successfully reaching the expected root hash. You can refer to the official repository to verify that the state hash is indeed correct.

StateDB imported successfully, stateRoot matches module=gossip-store
  index=95005860 root="cd16ca…cf69c2"

Calculate Cache Size and Memory Consumption Soft Limit Values

The Opera node utilizes a large amount of RAM to achieve great speed and responsiveness. It should be the only user-space application consuming your system resources.

$ grep MemTotal /proc/meminfo
MemTotal:       65641928 kB
  • Approximately 40% of the available RAM should be used as the --cache for both the sonictool and sonicd applications. The final value is denominated in MiB. For example: 64,000 MiB RAM * 40% = 25,600 MiB. You should never set the value below 10,000 MiB.

  • Approximately 90% of the available RAM should be used as the application memory consumption limit. This limit is controlled by the GOMEMLIMIT environment variable. The final value includes abbreviated units. We recommend using GiB for simplicity. For example: 64 GiB RAM * 90% ≈ 58 GiB.

5. Synchronize With the Network

Your node database has been primed to a certain epoch in the past using a genesis file and the Opera tool. Now you need to get the node up to the current state.

To do it, your node needs to be started in the read-only mode and establish a connection with the network. Please make sure your firewall is configured correctly to allow such communication. The amount of memory dedicated to the node cache and the memory consumption limit are outlined above.

GOMEMLIMIT=58GiB sonicd --datadir ~/.sonic --cache 25600 --mode validator

The Opera node should start and connect with the network, advancing the state by creating and processing new blocks. Please notice the age part of the log message, which tells you how far in the past these blocks are. The age should not exceed a couple of seconds once the node is fully synchronized.

6. Create a Validator Wallet

Your Opera node is up and running in the read-only mode, synchronizing with the network. Now you need to create a wallet that will identify and control your validator account.

The Opera tool does contain a utility function to create such a wallet, but we strongly recommend doing it off the server and in a secure way. The best approach is to use one of the hardware wallets available on the market or a trustworthy software wallet capable of sending transactions to the Opera network.

Make sure to keep the validator wallet private and secure. Never share this wallet or the corresponding key with anyone. The safety of your validator funds depends on it.

sonictool --datadir ~/.sonic account new

Once your validator wallet is created, you need to put the initial amount of funds in it. The registration process involves placing these funds as a stake in your account. If you don't have native FTM available on your Opera wallet already, you may need to purchase them using a crypto exchange.

7. Create a Validator Signing Key

An Opera validator node signs special types of messages broadcasted to the Opera network. A sequence of these messages from the confirmed active validators is what allows the network to process transactions safely and securely. Your node needs a signing key to be able to participate. Use the Opera tool to create this key on the server.

Please back up your consensus signing key safely. You may need to deploy it on a new server in case of a hardware or software failure.

The key cannot be used to access your funds, but it can be utilized to invoke a double-sign error, which is penalized. Please handle your validator key with proper care.

Create the validator signing key on the node server:

sonictool --datadir ~/.sonic validator new

Follow the prompts and supply a secure password:

Notice the public key part of the application output message starting with 0xc00472c4966b0213…. This is the public key used in the registration process in the next step to let the network know what key is going to be used by your node as its distinct signature.

Note that the picture above demonstrates the output of the validator key creation. Your real public key and the path to the key file will be different.

8. Register Your Validator

The network needs to be aware of your validator key to accept it. The registration process is done by invoking a smart contract called SFC. This contract is responsible for the management of stakes and related rewards.

You can find the SFC contract here: 0xFC00FACE00000000000000000000000000000000

You can use the Explorer to invoke the appropriate registration function called createValidator. You will need to include at least the minimal stake to be able to finish the registration. The "pubkey" field is going to contain the public key created in the previous step. Use the validator wallet key to sign the registration transaction.

If you want to interact with the SFC contract directly, please use the latest available ABI for the interface definition.

Once your registration is confirmed, you can use the Explorer to get the ID assigned to your validator. The SFC allows you to query this ID for your specific validator wallet using getValidatorID function.

9. Run Your Validator Node

You are almost done. Now you need to restart your running node with the validator signing key unlocked. Please make sure your node is already synchronized with the network before proceeding.

Stop the Opera application and wait until the node gracefully terminates:

pkill sonicd

The Opera node may run for several minutes until it stops. Please make sure to never terminate it forcefully. If the Opera node cannot properly close its persistent database, you will have to restore the database from a backup or a genesis file again.

Start the Opera node again, adding your validator public key and the validator ID. If you start your node in a non-interactive environment, e.g. as a dystemd service, you may need to add a path to the password file so the validator key can be unlocked. Alternatively, you can use a detached terminal (i.e. tmux) to run the node. In that case, the node will ask for the password when started.

GOMEMLIMIT=58GiB sonicd \
    --datadir ~/.sonic \
    --cache 25600 \
    --mode validator \
    --validator.id <your validator ID> \
    --validator.pubkey <your public key, i.e. 0xc00472c4966b0213…> \
    --validator.password <path to a file with the public key password>

Next Steps

Congratulations! Your validator node is up and running. Now you may be wondering what to do next. You can find some useful hints below.

Validator Name and Logo on the Explorer

If you'd like to set up a name and logo for your node, please check the this repository for additional information and guides.

Community and Help

The important part of running a validator on Opera is the community.

We would like our validators to get involved in the network's future. fWallet allows you to use your stake to vote for important changes in the network configuration and development. You can also check our social channels for additional information about upcoming events, upgrades, and planned governance votes. If you have any problems with the node, our awesome participants are ready to provide additional information and help.

Monitoring and Health

Each validator is expected to actively maintain their node and participate in securing the network traffic, but we realize accidents happen. If your node drops offline, the Explorer will show its address as being in maintenance mode. You can also check a simplified HTTP interface with a list of nodes that did not emit new consensus events recently.

If your node is not running properly for a short time, nothing happens. You will not receive any rewards during the downtime, but this is the only consequence. If your node is offline for more than 5 days, or if it's constantly dropping off the network, the guarding algorithm will suspend your validator wallet and the signing key in the SFC contract. In that case, you will not be able to join again and the only option would be to withdraw your stake and start over.

example-increment-chart

Transaction Tracing

Transaction tracing enables a deeper look into smart contract inner calls and account creation calls. It is inspired by the OpenEthereum (Parity) node trace module, which is used in a lot of projects.

For now, the following JSON-RPC methods are implemented:

  • trace_block

  • trace_transaction

  • trace_filter

  • trace_get

Currently, it is deployed on these endpoints for the Opera mainnet and testnet:

  • https://rpcapi-tracing.fantom.network

  • https://rpcapi-tracing.testnet.fantom.network

Start a New Go-Opera Tracing Node

Because of stored traces, a tracing node requires slightly bigger storage (around a quarter more). For more information, please follow the README.md file in the txtracing branch.

Building

You can check out and build the tx tracing release version from the git repository.

Running

It's recommended to launch a new node from scratch with the CLI option --tracenode as this flag has to be set to use stored transaction traces.

$ opera --genesis /path/to/genesis.g --tracenode

Enable JSON-RPC API with the option trace:

--http.api=eth,web3,net,ftm,trace

A complete example command:

opera \
    --genesis=/datadir/mainnet-5577-full-mpt.g \
    --port=5050 \
    --maxpeers=200 \
    --datadir=/datadir \
    --http \
    --http.addr=0.0.0.0 \
    --http.port=18545 \
    --http.corsdomain="*" \
    --http.vhosts="*" \
    --ws \
    --ws.addr=0.0.0.0 \
    --ws.port=18546 \
    --ws.origins="*" \
    --nousb \
    --db.preset pbl-1 \
    --tracenode \
    --http.api=eth,web3,net,ftm,trace

Genesis File

For the first start of the node, you have to specify the genesis file, which defines the blockchain and contains its history.

Please select a genesis file with a full history ("full-mpt" in the filename) if you need transaction traces for historical states. It's recommended to use the following genesis file, mainnet-5577-full-mpt.g, for a full-history tracing node.

Import, Export, and delete

Transaction traces are created on the node while processing transactions and then stored in a node database. You can export these traces and import them on other nodes.

This addition allows for responses to certain changes that may alter the trace content without requiring a complete resynchronization of the blockchain.

Another method is to delete traces, which will be recreated upon the next request to them from the RPC API. However, this approach is not recommended because recreating traces this way is slower when the first request targets a non-existent transaction trace.

All these operations must be performed while the node is stopped, as they involve modifying data in the core database. For both export and delete operations, you can specify a block range to limit the number of processed transaction traces.

The command for exporting is export txtraces <filename.gz> <from block> <to block>:

opera --tracenode --datadir=/path/to/opera/datadir export txtraces filename.gz 10000000 11000000

For importing, it's:

opera --tracenode --datadir=/path/to/opera/datadir import txtraces filename.gz

No file needs to be specified when using the delete option. delete txtraces <from block> <to block>

opera --tracenode --datadir=/path/to/opera/datadir delete txtraces 10000000 11000000

Go-Opera Version Migration

This section provides information on migrating a transaction tracing API node to a newer version of go-opera. It will indicate whether a full node resynchronization is required due to stored transaction traces or if only a code update is necessary to upgrade from the previous version.

  • release/txtracing/1.1.1-rc.2

  • release/txtracing/1.1.1-rc.3 — no resync from 1.1.1-rc.2, just update

  • release/txtracing/1.1.2-rc.2 — don't use this version

  • release/txtracing/1.1.2-rc.5 — resync from all previous versions and see notes below

When using version 1.1.2, you need to add the db.preset argument for the starting Opera command. You can view the available options for this parameter with the opera help command. For standard conditions, please use the following option:

  • db.preset=ldb-1


trace_block

Returns traces created at the given block.

Parameters

  1. Quantity or Tag — Integer of a block number, or the string 'earliest', 'latest', or 'pending'.

params: [
  "0x22DB3E" // 2284350
]

Returns

  • Array — Block traces.

Example

Request:

curl --data '{"method":"trace_block","params":["0x22DB3E"],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST https://rpcapi-tracing.fantom.network

Response:

{
   "jsonrpc":"2.0",
   "id":1,
   "result":[
      {
         "action":{
            "callType":"call",
            "from":"0x740c82e9009738b3ab6080e89da964a4704a6cba",
            "to":"0xd4e0aca70d1fe8979e7e1015b30a2085803d0244",
            "value":"0x0",
            "gas":"0x689e",
            "input":"0x6930fd2a0000000000000000000000000000000000000000000000000000000000000608"
         },
         "blockHash":"0x00000ce000001cf16b859ccacc281e32e2c17730ce48b3498bf1f7f05d90fe5c",
         "blockNumber":2284350,
         "result":{
            "gasUsed":"0x688f",
            "output":"0x"
         },
         "subtraces":1,
         "traceAddress":[
            
         ],
         "transactionHash":"0x58846dd2a9fa3ea9e700838db48fd9829ebbc95e103142ac7968f658fd1ed3ac",
         "transactionPosition":0,
         "type":"call"
      },
      {
         "action":{
            "callType":"delegatecall",
            "from":"0xd4e0aca70d1fe8979e7e1015b30a2085803d0244",
            "to":"0x1125eabf1eba69ef00266b7a038141474d8afff1",
            "value":"0x0",
            "gas":"0x125c",
            "input":"0x6930fd2a0000000000000000000000000000000000000000000000000000000000000608"
         },
         "blockHash":"0x00000ce000001cf16b859ccacc281e32e2c17730ce48b3498bf1f7f05d90fe5c",
         "blockNumber":2284350,
         "result":{
            "gasUsed":"0xf62",
            "output":"0x"
         },
         "subtraces":0,
         "traceAddress":[
            0
         ],
         "transactionHash":"0x58846dd2a9fa3ea9e700838db48fd9829ebbc95e103142ac7968f658fd1ed3ac",
         "transactionPosition":0,
         "type":"call"
      }
   ]
}

trace_transaction

Returns all traces of the given transaction.

Parameters

  1. Hash — Transaction hash

params: ["0x58846dd2a9fa3ea9e700838db48fd9829ebbc95e103142ac7968f658fd1ed3ac"]

Returns

  • Array — Traces of given transaction

Example

Request:

curl --data '{"method":"trace_transaction","params":["0x58846dd2a9fa3ea9e700838db48fd9829ebbc95e103142ac7968f658fd1ed3ac"],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST https://rpcapi-tracing.fantom.network

trace_filter

Returns traces matching the given filter.

Parameters

  1. Object — The filter object

    • fromBlock: Quantity or Tag — (optional) From this block.

    • toBlock: Quantity or Tag — (optional) To this block.

    • fromAddress: Array — (optional) Sent from these addresses.

    • toAddress: Address — (optional) Sent to these addresses.

    • after: Quantity — (optional) The offset trace number

    • count: Quantity — (optional) Integer number of traces to display in a batch.

params: [{
  "fromBlock": "0x22DB29", // 2284329
  "toBlock": "0x22DB34", // 2284340
  "toAddress": ["0xda7a001b254cd22e46d3eab04d937489c93174c3"],
  "after": 20,
  "count": 10
}]

Returns

  • Array — Traces matching the given filter.

Example

Request:

curl -H "Content-Type: application/json" -X POST --data '{"method":"trace_filter","params":[{"fromBlock":"0x22DB29", "toBlock":"0x22DB34", "fromAddress":["0xda7a001b254cd22e46d3eab04d937489c93174c3"], "count":10, "after":20}],"id":1,"jsonrpc":"2.0"}'  https://rpcapi-tracing.fantom.network

trace_get

Returns trace at given position.

Parameters

  1. Hash — Transaction hash.

  2. Array — Index positions of the traces.

params: [
  "0x58846dd2a9fa3ea9e700838db48fd9829ebbc95e103142ac7968f658fd1ed3ac",
  ["0x0"]
]

Returns

  • Object — Trace object

Example

Request:

curl -H "Content-Type: application/json" -X POST --data '{"method":"trace_get","params":["0x58846dd2a9fa3ea9e700838db48fd9829ebbc95e103142ac7968f658fd1ed3ac",["0x0"]],"id":1,"jsonrpc":"2.0"}' https://rpcapi-tracing.fantom.network

debug_traceBlockByNumber

The method returns a full stack trace of all invoked opcodes of all transaction that were included in a block identified by a block number. The parent of this block must be present or it will fail.

Parameters

  1. Number - Block number to be traced.

  2. Object - (optional) Options for the trace.

    • tracer: String - (optional) Setting this will enable JavaScript-based transaction tracing, described below.

    • timeout: String - (optional) Overrides the default timeout of 5 seconds for JavaScript-based tracing calls. Check documentation for ParseDuration for valid values.

Returns

  • Array - A trace object per transaction inside the block.

Example

curl -H "Content-Type: application/json" -X POST --data '{"method":"debug_traceBlockByNumber","params":["0x2F5DD49"],"id":1,"jsonrpc":"2.0"}' https://rpcapi-tracing.fantom.network

debug_traceBlockByHash

This method returns a full stack trace of all invoked opcodes of all transactions that were included in a block identified by a hash. The parent of this block must be present or it will fail.

Parameters

  1. Hash — Hash of the block to be traced.

  2. Object — (optional) Options for the trace.

    • tracer: String — (optional) Setting this will enable JavaScript-based transaction tracing, described below.

    • timeout: String — (optional) Overrides the default timeout of 5 seconds for JavaScript-based tracing calls. Check documentation for ParseDuration for valid values.

Returns

  • Array — A trace object per transaction inside the block.

Example

curl -H "Content-Type: application/json" -X POST --data '{"method":"debug_traceBlockByHash","params":["0x0002825a0000008c2bc8a86b966c34797db3c21916ca033f4bd68461801217d0"],"id":1,"jsonrpc":"2.0"}' https://rpcapi-tracing.fantom.network

debug_traceTransaction

This method will try to run the transaction in the same way it was executed on the network. It will replay any transactions that occurred before it in the block and then attempt to execute the transaction corresponding to the given hash.

Parameters

  1. Hash — Hash of the transaction to be traced.

  2. Object — (optional) Options for the trace.

    • tracer: String — (optional) Setting this will enable JavaScript-based transaction tracing, described below.

    • timeout: String — (optional) Overrides the default timeout of 5 seconds for JavaScript-based tracing calls. Check documentation for ParseDuration for valid values.

Returns

  • Object — A trace object of the traced transaction.

Example

curl -H "Content-Type: application/json" -X POST --data '{"method":"debug_traceTransaction","params":["0xa6598be1f06b0aff5caddec2685ec53599f064f7b8ff9eaded0a719a378c8802"],"id":1,"jsonrpc":"2.0"}' https://rpcapi-tracing.fantom.network

JavaScript-Based Tracer

Specifying the tracer option in the second argument of the debug tracing calls enables JavaScript-based tracing. In this mode, tracer is interpreted as a JavaScript expression that is expected to evaluate an object which must expose the result and fault methods. There exists 4 additional methods, namely: setup, step, enter, and exit. enter and exit must be present or omitted together.

Setup Method

Method setup is invoked once, in the beginning when the tracer is being constructed for each transaction being traced. It takes in one argument, config, which allows users to pass in options to the tracer. config is to be JSON-decoded for usage and its default value is "{}".

Step Method

Method step is a function that takes two arguments, log and db, and is called for each step of the EVM, or when an error occurs, as a transaction is traced.

log has the following fields:

  • op: Object, an OpCode object representing the current opcode

  • stack: Object, a structure representing the EVM execution stack

  • memory: Object, a structure representing the contract’s memory space

  • contract: Object, an object representing the account executing the current operation

And the following methods:

  • getPC() — returns a number with the current program counter

  • getGas() — returns a number with the amount of gas remaining

  • getCost() — returns the cost of the opcode as a number

  • getDepth() — returns the execution depth as a number

  • getRefund() — returns the amount to be refunded as a number

  • getError() — returns information about the error if one occurred, otherwise returns undefined

If an error is non-empty, all other fields should be ignored.

For efficiency, the same log object is reused on each execution step, updated with current values; make sure to copy values you want to preserve beyond the current call.

log.op has the following methods:

  • isPush() — returns true if the opcode is a PUSHn

  • toString() — returns the string representation of the opcode

  • toNumber() — returns the opcode’s number

log.memory has the following methods:

  • slice(start, stop) — returns the specified segment of memory as a byte slice

  • getUint(offset) — returns the 32 bytes at the given offset

  • length() — returns the memory size

log.stack has the following methods:

  • peek(idx) — returns the idx-th element from the top of the stack (0 is the topmost element) as a big.Int

  • length() — returns the number of elements in the stack

log.contract has the following methods:

  • getCaller() — returns the address of the caller

  • getAddress() — returns the address of the current contract

  • getValue() — returns the amount of value sent from caller to contract as a big.Int

  • getInput() — returns the input data passed to the contract

db has the following methods:

  • getBalance(address) — returns a big.Int with the specified account’s balance

  • getNonce(address) — returns a Number with the specified account’s nonce

  • getCode(address) — returns a byte slice with the code for the specified account

  • getState(address, hash) — returns the state value for the specified account and the specified hash

  • exists(address) — returns true if the specified address exists

If the step function throws an exception or executes an illegal operation at any point, it will not be called on any further VM steps, and the error will be returned to the caller.

Result Method

Method result is a function that takes two arguments, ctx and db, and is expected to return a JSON-serializable value to return to the RPC caller.

ctx is the context in which the transaction is executed and has the following fields:

  • type — String, one of the two values, CALL and CREATE

  • from — Address, sender of the transaction

  • to — Address, target of the transaction

  • input — Buffer, input transaction data

  • gas — Number, gas budget of the transaction

  • gasUsed - Number, amount of gas used in executing the transaction (excludes txdata costs)

  • gasPrice — Number, gas price configured in the transaction being executed

  • intrinsicGas — Number, intrinsic gas for the transaction being executed

  • value — big.Int, amount to be transferred in wei

  • block — Number, block number

  • output — Buffer, value returned from EVM

  • time — String, execution runtime

Fault Method

Method fault is a function that takes two arguments, log and db, just like step, and is invoked when an error happens during the execution of an opcode which wasn’t reported in step. The method log.getError() has information about the error.

Ender and Exit Methods

Methods enter and exit are respectively invoked on stepping in and out of an internal call. More specifically, they are invoked on the CALL variants, CREATE variants, and also for the transfer implied by a SELFDESTRUCT.

enter takes a callFrame object as argument which has the following methods:

  • getType() — returns a string which has the type of the call frame

  • getFrom() — returns the address of the call frame sender

  • getTo() — returns the address of the call frame target

  • getInput() — returns the input as a buffer

  • getGas() — returns a number that has the amount of gas provided for the frame

  • getValue() — returns a big.Int with the amount to be transferred only if available, otherwise undefined

exit takes in a frameResult object which has the following methods:

  • getGasUsed() — returns amount of gas used throughout the frame as a Number

  • getOutput() — returns the output as a buffer

  • getError() — returns an error if one occurred during execution and undefined otherwise

Note that several values are Golang big.Int objects, not JavaScript numbers or JS bigints. As such, they have the same interface as described in the godocs. Their default serialization to JSON is as a JavaScript number; to serialize large numbers accurately, call .String() on them. For convenience, big.NewInt(x) is provided, and will convert a uint to a Go BigInt.

Moralis

Introduction

Moralis helps leading cryptocurrency and blockchain companies grow and innovate faster with high-quality, insightful data tools on Opera and other EVM chains. By using Moralis, your team can focus on growing your product and your business while minimizing the time and money you spend on data infrastructure.

Moralis offers APIs and real-time data streams for NFT, token, wallet, and raw blockchain data, and market insights and discovery data.

Getting Started

To use any of the Moralis APIs, you need to register for a free Moralis account and get your API key. You will find your API key under your account settings.

Moralis APIs

Below you'll find details about the different APIs that Moralis offers and some examples of the endpoints available.

NFT API

The Moralis NFT API can be used to quickly build NFT functionality into your wallet or portfolio application or to spin up an NFT marketplace. You can use it to fetch NFTs owned by particular wallets, get NFT transfers and sales, or track prices of recent NFT sales.

The NFT API automatically indexes all NFTs and metadata across all available chains.

Endpoints
  • Get Multiple NFTs

  • Get NFTs by Wallet

  • Get NFTs by Contract

  • Get NFT Metadata

  • Get NFT Transfers by Wallet

  • Get NFT Transfers by Contract

  • Get NFT Transfers in a block

  • Get NFT Transfers in a block range

  • Get NFT Transfer by Token ID

  • Get NFT Collections by Wallet

  • Get NFT Collection Metadata

  • Get NFT Owners by Contract

  • Get NFT Owners by Token ID

  • Get NFT Trades by Marketplace

  • Get NFT Lowest Price

  • Get NFT Stats

  • Get NFT Collection Stats

Example call

Below is an example where we call the Get NFTs by Wallet endpoint ( `{wallet_address}/nft`) with the Moralis JS SDK.

Request:

```js
const response = await Moralis.EvmApi.nft.getWalletNFTs({
"chain": "0x1",
"format": "decimal",
"mediaItems": false,
"address": "0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326"
});
```

Response:

```json
{
"page": 1,
"page_size": 100,
"cursor": null,
"result": [
{
"token_address": "0xbd3531da5cf5857e7cfaa92426877b022e612cf8",
"token_id": "8165",
"owner_of": "0x3e18e3987b3b73f4e7cb80e2b25776df7a30bb8b",
"block_number": "18333219",
"block_number_minted": "12878275",
"token_hash": "f7d53f87dc31367e148b2ec957cb585b",
"amount": "1",
"possible_spam": false,
"contract_type": "ERC721",
"name": "PudgyPenguins",
"symbol": "PPG",
"token_uri": "https://ipfs.moralis.io:2053/ipfs/bafybeibc5sgo2plmjkq2tzmhrn54bk3crhnc23zd2msg4ea7a4pxrkgfna/8165",
"metadata": "{\"attributes\":[{\"trait_type\":\"Background\",\"value\":\"Tangerine\"},{\"trait_type\":\"Skin\",\"value\":\"Normal\"},{\"trait_type\":\"Body\",\"value\":\"Kimono Orange\"},{\"trait_type\":\"Face\",\"value\":\"Squad\"},{\"trait_type\":\"Head\",\"value\":\"Cowboy Hat\"}],\"description\":\"A collection 8888 Cute Chubby Pudgy Penquins sliding around on the freezing ETH blockchain.\",\"image\":\"ipfs://QmNf1UsmdGaMbpatQ6toXSkzDpizaGmC9zfunCyoz1enD5/penguin/8165.png\",\"name\":\"Pudgy Penguin #8165\"}",
"last_token_uri_sync": "2023-09-26T22:39:34.136Z",
"last_metadata_sync": "2023-10-13T10:22:08.029Z",
"minter_address": "0xbff79922fcbf93f9c30abb22322b271460c6bebb",
"normalized_metadata": {
"name": "Pudgy Penguin #8165",
"description": "A collection 8888 Cute Chubby Pudgy Penquins sliding around on the freezing ETH blockchain.",
"animation_url": null,
"external_link": null,
"image": "ipfs://QmNf1UsmdGaMbpatQ6toXSkzDpizaGmC9zfunCyoz1enD5/penguin/8165.png",
"attributes": [
{
"trait_type": "Background",
"value": "Tangerine",
"display_type": null,
"max_value": null,
"trait_count": 0,
"order": null
},
{
"trait_type": "Skin",
"value": "Normal",
"display_type": null,
"max_value": null,
"trait_count": 0,
"order": null
},
{
"trait_type": "Body",
"value": "Kimono Orange",
"display_type": null,
"max_value": null,
"trait_count": 0,
"order": null
},
{
"trait_type": "Face",
"value": "Squad",
"display_type": null,
"max_value": null,
"trait_count": 0,
"order": null
},
{
"trait_type": "Head",
"value": "Cowboy Hat",
"display_type": null,
"max_value": null,
"trait_count": 0,
"order": null
}
]
},
"media": {
"mimetype": "image/png",
"parent_hash": "0x9b326b3d983984f6cfc60a88d16ed3ed5774cc12f230c753509cf2e34485685d",
"status": "success",
"updatedAt": "2023-10-13T10:22:07.684Z",
"media_collection": {
"low": {
"height": 100,
"width": 100,
"url": "https://nft-preview-media.s3.us-east-1.amazonaws.com/evm/0x1/0xbd3531da5cf5857e7cfaa92426877b022e612cf8/0x96a9919f09d1df24d87658f4a8962c297587e2d4aab98e551081023b86c9d463/low.png"
},
"medium": {
"height": 250,
"width": 250,
"url": "https://nft-preview-media.s3.us-east-1.amazonaws.com/evm/0x1/0xbd3531da5cf5857e7cfaa92426877b022e612cf8/0x96a9919f09d1df24d87658f4a8962c297587e2d4aab98e551081023b86c9d463/medium.png"
},
"high": {
"height": 500,
"width": 500,
"url": "https://nft-preview-media.s3.us-east-1.amazonaws.com/evm/0x1/0xbd3531da5cf5857e7cfaa92426877b022e612cf8/0x96a9919f09d1df24d87658f4a8962c297587e2d4aab98e551081023b86c9d463/high.png"
}
},
"original_media_url": "ipfs://QmNf1UsmdGaMbpatQ6toXSkzDpizaGmC9zfunCyoz1enD5/penguin/8165.png"
},
"verified_collection": true
}
],
"status": "SYNCED"
}
```

Token API

The Moralis Token API has all the information you need about ERC20 tokens, including ownership, transfers, and token prices. Use it to add ERC20 support to your wallet or enrich your token pages with in-depth token metadata.

The Token API includes token logos and spam detection.

Endpoints
  • Get ERC20 Token Balance By Wallet

  • Get ERC20 Metadata by Contract

  • Get ERC20 Metadata by Symbol

  • Get ERC20 Token Prices

  • Get ERC20 Token Allowance

  • Get ERC20 Token Transfers By Wallet

  • Get ERC20 Token Transfers By Contract

  • Get DEX Token Pair Reserves

  • Get DEX Token Pair Address

  • Get ERC20 Token Stats

Example call

Request:

```js
const response = await Moralis.EvmApi.token.getWalletTokenBalances({
"chain": "0x1",
"address": "0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326"
});
```

Response:

```json
{
"token_address": "0x2d30ca6f024dbc1307ac8a1a44ca27de6f797ec22ef20627a1307243b0ab7d09",
"name": "Kylin Network",
"symbol": "KYL",
"logo": "https://cdn.moralis.io/eth/0x67b6d479c7bb412c54e03dca8e1bc6740ce6b99c.png",
"thumbnail": "https://cdn.moralis.io/eth/0x67b6d479c7bb412c54e03dca8e1bc6740ce6b99c_thumb.png",
"decimals": "",
"balance": "123456789",
"possible_spam": false,
"verified_collection": false
}
```

Market Data API

The Moralis Market Data API helps you retrieve top coins and NFT collections based on market cap and trading volume. You can use it to build market discovery pages, coin listing pages, or display winners and losers.

Endpoints
  • Get the top ERC20 tokens by market cap

  • Get the top ERC20 tokens by price change

  • Get the top NFT collections by market cap

  • Get the top NFT collections by trading volume

Example call

Request:

```js
const response = await Moralis.EvmApi.marketData.getTopERC20TokensByMarketCap({});
```

Response:

```json
[
{
"rank": "1",
"token_name": "Wrapped Ether",
"token_symbol": "WETH",
"token_logo": "https://cdn.moralis.io/eth/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2.png",
"token_decimals": "18",
"contract_address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"price_usd": "0.0285",
"price_24h_percent_change": "0.0285",
"price_7d_percent_change": "0.0285",
"market_cap_usd": "0.0285"
}
]
```

Blockchain API

Using the Moralis Blockchain API you can fetch basic data about blocks, transactions, logs, and events. The Blockchain API also supports transaction decoding, which automatically decodes the transaction logs for you, without submitting any ABIs.

Endpoints
  • Get Block by Block number or Block Hash

  • Get Block by Date

  • Get Transaction by Transaction Hash

  • Get Native Transactions by Wallet

  • Get Decoded Transactions by Transaction Hash

  • Get Decoded Transactions by Wallet

  • Get Internal Transactions by Transaction Hash

  • Get Logs by Contract

  • Get Events by Contract

  • Get Block Stats

Example call

Request:

```js
const response = await Moralis.EvmApi.transaction.getTransactionVerbose({
"chain": "0x1",
"transactionHash": "0x012b9b98e21664117ec0b499d726a39f492ac8bd402cca8bebcbd163b9f75760"
});
```

Response:

```json
{
"hash": "0x1ed85b3757a6d31d01a4d6677fc52fd3911d649a0af21fe5ca3f886b153773ed",
"nonce": "1848059",
"transaction_index": "108",
"from_address": "0x267be1c1d684f78cb4f6a176c4911b741e4ffdc0",
"to_address": "0x003dde3494f30d861d063232c6a8c04394b686ff",
"from_address_label": "Binance 1",
"to_address_label": "Binance 2",
"value": "115580000000000000",
"gas": "30000",
"gas_price": "52500000000",
"input": "0x",
"receipt_cumulative_gas_used": "4923073",
"receipt_gas_used": "21000",
"receipt_status": "1",
"block_timestamp": "2021-05-07T11:08:35.000Z",
"block_number": "12386788",
"block_hash": "0x9b559aef7ea858608c2e554246fe4a24287e7aeeb976848df2b9a2531f4b9171",
"logs": {
"log_index": "273",
"transaction_hash": "0xdd9006489e46670e0e85d1fb88823099e7f596b08aeaac023e9da0851f26fdd5",
"transaction_index": "204",
"address": "0x3105d328c66d8d55092358cf595d54608178e9b5",
"data": "0x00000000000000000000000000000000000000000000000de05239bccd4d537400000000000000000000000000024dbc80a9f80e3d5fc0a0ee30e2693781a443",
"topic0": "0x2caecd17d02f56fa897705dcc740da2d237c373f70686f4e0d9bd3bf0400ea7a",
"topic1": "0x000000000000000000000000031002d15b0d0cd7c9129d6f644446368deae391",
"topic2": "0x000000000000000000000000d25943be09f968ba740e0782a34e710100defae9",
"block_timestamp": "2021-05-07T11:08:35.000Z",
"block_number": "12386788",
"block_hash": "0x9b559aef7ea858608c2e554246fe4a24287e7aeeb976848df2b9a2531f4b9171",
"decoded_event": {
"signature": "Transfer(address,address,uint256)",
"label": "Transfer",
"type": "event",
"params": [
{
"name": "from",
"value": "0x26C5011483Add49801eA8E3Ee354fE013895aCe5",
"type": "address"
}
]
}
},
"decoded_call": {
"signature": "transfer(address,uint256)",
"label": "transfer",
"type": "function",
"params": [
{
"name": "_to",
"value": "0x1CA455A55108874A95C84620dDA2566c54D17953",
"type": "address"
}
]
},
"internal_transactions": {
"transaction_hash": "0x057Ec652A4F150f7FF94f089A38008f49a0DF88e",
"block_number": 12526958,
"block_hash": "0x0372c302e3c52e8f2e15d155e2c545e6d802e479236564af052759253b20fd86",
"type": "CALL",
"from": "0xd4a3BebD824189481FC45363602b83C9c7e9cbDf",
"to": "0xa71db868318f0a0bae9411347cd4a6fa23d8d4ef",
"value": "650000000000000000",
"gas": "6721975",
"gas_used": "6721975",
"input": "0x",
"output": "0x"
}
}
```

Moralis Streams

Moralis Streams is an API for real-time blockchain data, sent over webhooks. Using Streams, you can avoid polling other APIs and instead only be notified when something has actually happened. You can configure the Stream to notify you only when specific things happen on-chain, such as transactions, transfers, mints, or any other custom event. Streams support custom events and customer filters.

Streams can be created either in the Streams User Interface once you're signed into the Moralis Admin Panel, or through the API.

Guides
  • How to listen to all ERC20 transfers over certain amount sent by specific address

  • How to listen all NFT transfers

  • How to monitor ENS Domain Registrations

  • How to monitor for ERC20 token burns or mints

  • How to Monitor Crypto Wallet Addresses with Streams

Other Resources

  • Moralis Documentation

  • Moralis Account Login

  • Moralis Tutorials

  • Tutorial: Build a DEX With Moralis

  • Tutorial: Get Any Token Price With Moralis

Schema Structure

Full schema of the GraphQL API is:

# Hash is a 32 byte binary string, represented by 0x prefixed hexadecimal.
scalar Hash

# Address is a 20 byte Opera address, represented as 0x prefixed hexadecimal number.
scalar Address

# BigInt is a large integer value. Input is accepted as either a JSON number,
# or a hexadecimal string alternatively prefixed with 0x. Output is 0x prefixed hexadecimal.
scalar BigInt

# Long is a 64 bit unsigned integer value.
scalar Long

# Bytes is an arbitrary length binary string, represented as 0x-prefixed hexadecimal.
# An empty byte string is represented as '0x'.
scalar Bytes

# Cursor is a string representing position in a sequential list of edges.
scalar Cursor

# Root schema definition
schema {
    query: Query
    mutation: Mutation
    subscription: Subscription
}

# Entry points for querying the API
type Query {
    "Total number of accounts active on the Opera blockchain."
    accountsActive:Long!

    "Get an Account information by hash address."
    account(address:Address!):Account!

    """
    Get list of Contracts with at most <count> edges.
    If <count> is positive, return edges after the cursor,
    if negative, return edges before the cursor.
    For undefined cursor, positive <count> starts the list from top,
    negative <count> starts the list from bottom.
    ValidatedOnly specifies if the list should contain all the Contracts,
    or just contracts with validated byte code and available source/ABI.
    """
    contracts(validatedOnly: Boolean = false, cursor:Cursor, count:Int!):ContractList!

    """
    Get block information by number or by hash.
    If neither is provided, the most recent block is given.
    """
    block(number:Long, hash: Hash):Block

    "Get transaction information for given transaction hash."
    transaction(hash:Hash!):Transaction

    """
    Get list of Blocks with at most <count> edges.
    If <count> is positive, return edges after the cursor,
    if negative, return edges before the cursor.
    For undefined cursor, positive <count> starts the list from top,
    negative <count> starts the list from bottom.
    """
    blocks(cursor:Cursor, count:Int!):BlockList!

    """
    Get list of Transactions with at most <count> edges.
    If <count> is positive, return edges after the cursor,
    if negative, return edges before the cursor.
    For undefined cursor, positive <count> starts the list from top,
    negative <count> starts the list from bottom.
    """
    transactions(cursor:Cursor, count:Int!):TransactionList!

    "Get the id of the current epoch of the Opera blockchain."
    currentEpoch:Long!

    """
    Get information about specified epoch. Returns current epoch information
    if id is not provided.
    """
    epoch(id: Long!): Epoch!

    "The last staker id in Opera blockchain."
    lastStakerId: Long!

    "The number of stakers in Opera blockchain."
    stakersNum: Long!

    """
    Staker information. The staker is loaded either by numeric ID,
    or by address. null if none is provided.
    """
    staker(id: Long, address: Address): Staker

    "List of staker information from SFC smart contract."
    stakers: [Staker!]!

    """
    The list of delegations for the given staker ID.
    Cursor is used to obtain specific slice of the staker's delegations.
    The most recent delegations are provided if cursor is omitted.
    """
    delegationsOf(staker:Long!, cursor: Cursor, count: Int = 25): DelegatorList!

    "Get the details of a delegator by it's address."
    delegation(address:Address!): Delegator

    "Returns the current price per gas in WEI units."
    gasPrice: Long!

    "Get price details of the Opera blockchain token for the given target symbols."
    price(to:String!):Price!

    """
    Get calculated staking rewards for an account or given
    staking amount in FTM tokens.
    At least one of the address and amount parameters must be provided.
    If you provide both, the address takes precedence and the amount is ignored.
    """
    estimateRewards(address:Address, amount:Long):EstimatedRewards!
}

# Mutation endpoints for modifying the data
type Mutation {
    """
    SendTransaction submits a raw signed transaction into the block chain.
    The tx parameter represents raw signed and RLP encoded transaction data.
    """
    sendTransaction(tx: Bytes!):Transaction

    """
    Validate a deployed contract byte code with the provided source code
    so potential users can check the contract source code, access contract ABI
    to be able to interact with the contract and get the right metadata.
    Returns updated contract information. If the contract can not be validated,
    it raises a GraphQL error.
    """
    validateContract(contract: ContractValidationInput!): Contract!
}

# Subscriptions to live events broadcasting
type Subscription {
    "Subscribe to receive information about new blocks in the blockchain."
    onBlock: Block!

    "Subscribe to receive information about new transactions in the blockchain."
    onTransaction: Transaction!
}


# StakerInfo represents extended staker information from smart contract.
type StakerInfo {
    "Name represents the name of the staker."
    name: String

    "LogoUrl represents staker logo URL."
    logoUrl: String

    "Website represents a link to stakers website."
    website: String

    "Contact represents a link to contact to the staker."
    contact: String
}

# DelegatorList is a list of delegations edges provided by sequential access request.
type DelegatorList {
    "Edges contains provided edges of the sequential list."
    edges: [DelegatorListEdge!]!

    """
    TotalCount is the maximum number of delegations
    available for sequential access.
    """
    totalCount: BigInt!

    "PageInfo is an information about the current page of delegation edges."
    pageInfo: ListPageInfo!
}

# BlockListEdge is a single edge in a sequential list of blocks.
type DelegatorListEdge {
    "Cursor defines a scroll key to this edge."
    cursor: Cursor!

    "Delegator represents the delegator provided by this list edge."
    delegator: Delegator!
}

# Delegator represents a delegation on Opera blockchain.
type Delegator {
    "Address of the delegator account."
    address: Address!

    "Identifier of the staker the delegation belongs to."
    toStakerId: Long!

    "Epoch in which the delegation was made."
    createdEpoch: Long!

    "Timestamp of the delegation creation."
    createdTime: Long!

    "Epoch in which the delegation was deactivated."
    deactivatedEpoch: Long

    "Timestamp of the deactivation of the delegation."
    deactivatedTime: Long

    "Amount delegated. It includes all the pending un-delegations."
    amount: BigInt!

    """
    Amount delegated. The current amount still registered
    by SFC contract as delegated amount. It does include pending
    deactivation, but does not include partial un-delegations.
    """
    amountDelegated: BigInt
    
    "Amount locked in pending un-delegations and withdrawals."
    amountInWithdraw: BigInt!

    "Amount of rewards claimed."
    claimedReward: BigInt

    "Pending rewards for the delegation."
    pendingRewards: PendingRewards!

    "List of withdraw requests of the delegation."
    withdrawRequests: [WithdrawRequest!]!

    "List of full delegation deactivation."
    deactivation: [DeactivatedDelegation!]!
}

# Account defines block-chain account information container
type Account {
    "Address is the address of the account."
    address: Address!

    "Balance is the current balance of the Account in WEI."
    balance: BigInt!

    """
    TotalValue is the current total value fo the account in WEI.
    It includes available balance,
    delegated amount and pending staking rewards.
    """
    totalValue: BigInt!

    "txCount represents number of transaction sent from the account."
    txCount: Long!

    """
    txList represents list of transactions of the account
    in form of TransactionList.
    """
    txList (cursor:Cursor, count:Int!): TransactionList!

    "Details of a staker, if the account is a staker."
    staker: Staker

    "Details of delegation, if the account is a delegator."
    delegation: Delegator

    "Details about smart contract, if the account is a smart contract."
    contract: Contract
}

# EstimatedRewards represents a calculated rewards estimation for an account or amount staked
type EstimatedRewards {
    "Amount of FTM tokens expected to be staked for the calculation."
    staked: Long!

    """
    dailyReward represents amount of FTM tokens estimated
    to be rewarded for staked amount in average per day.
    """
    dailyReward: BigInt!

    """
    weeklyReward represents amount of FTM tokens estimated
    to be rewarded for staked amount in average per week.
    """
    weeklyReward: BigInt!

    """
    monthlyReward represents amount of FTM tokens estimated
    to be rewarded for staked amount in average per month.
    """
    monthlyReward: BigInt!

    """
    yearlyReward represents amount of FTM tokens estimated
    to be rewarded for staked amount in average per year.
    """
    yearlyReward: BigInt!

    """
    currentRewardYearRate represents average reward rate
    for any staked amount in average per year.
    The value is calculated as linear gross proceeds for staked amount
    of tokens yearly.
    """
    currentRewardRateYearly: Int!

    """
    Total amount of staked FTM tokens used for the calculation in WEI units.
    The estimation uses total staked amount, not the effective amount provided
    by the last epoch. The effective amount does include current
    un-delegations and also skips offline self-staking and flagged staking.
    """
    totalStaked: BigInt!

    """
    Information about the last sealed epoch of the Opera blockchain.
    The epoch provides useful information about total FTM supply,
    total amount staked, rewards rate and weight, fee, etc.
    """
    lastEpoch: Epoch!
}

# TransactionList is a list of transaction edges provided by sequential access request.
type TransactionList {
    # Edges contains provided edges of the sequential list.
    edges: [TransactionListEdge!]!

    # TotalCount is the maximum number of transactions available for sequential access.
    totalCount: BigInt!

    # PageInfo is an information about the current page of transaction edges.
    pageInfo: ListPageInfo!
}

# TransactionListEdge is a single edge in a sequential list of transactions.
type TransactionListEdge {
    cursor: Cursor!
    transaction: Transaction!
}

# Transaction is an Opera block chain transaction.
type Transaction {
    # Hash is the unique hash of this transaction.
    hash: Hash!

    # Nonce is the number of transactions sent by the account prior to this transaction.
    nonce: Long!

    # Index is the index of this transaction in the block. This will
    # be null if the transaction is in a pending pool.
    index: Long

    # From is the address of the account that sent this transaction
    from: Address!

    # Sender is the account that sent this transaction
    sender: Account!

    # To is the account the transaction was sent to.
    # This is null for contract creating transactions.
    to: Address

    # contractAddress represents the address of smart contract deployed by this transaction;
    # null if the transaction is not contract creation
    contractAddress: Address

    # Recipient is the account that received this transaction.
    # Null for contract creating transaction.
    recipient: Account

    # Value is the value sent along with this transaction in WEI.
    value: BigInt!

    # GasPrice is the price of gas per unit in WEI.
    gasPrice: BigInt!

    # Gas represents gas provided by the sender.
    gas: Long!

    # GasUsed is the amount of gas that was used on processing this transaction.
    # If the transaction is pending, this field will be null.
    gasUsed: Long

    # InputData is the data supplied to the target of the transaction.
    # Contains smart contract byte code if this is contract creation.
    # Contains encoded contract state mutating function call if recipient
    # is a contract address.
    inputData: Bytes!

    # BlockHash is the hash of the block this transaction was assigned to.
    # Null if the transaction is pending.
    blockHash: Hash

    # BlockHash is the hash of the block this transaction was assigned to.
    # Null if the transaction is pending.
    blockNumber: Long

    # Block is the block this transaction was assigned to. This will be null if
    # the transaction is pending.
    block: Block

    # Status is the return status of the transaction. This will be 1 if the
    # transaction succeeded, or 0 if it failed (due to a revert, or due to
    # running out of gas). If the transaction has not yet been processed, this
    # field will be null.
    status: Long
}

# Represents epoch information.
type Epoch {
    "Number the epoch end."
    id: Long!

    "Timestamp of the epoch end."
    endTime: BigInt!

    "Epoch duration in seconds."
    duration: BigInt!

    "Fee at the epoch."
    epochFee: BigInt!

    "Total base reward weight on epoch."
    totalBaseRewardWeight: BigInt!

    "Total transaction reward weight on epoch."
    totalTxRewardWeight: BigInt!

    "Base reward per second of epoch."
    baseRewardPerSecond: BigInt!

    "Total amount staked."
    stakeTotalAmount: BigInt!

    "Total amount delegated."
    delegationsTotalAmount: BigInt!

    "Total supply amount."
    totalSupply: BigInt!
}

# ContractList is a list of smart contract edges provided by sequential access request.
type ContractList {
    # Edges contains provided edges of the sequential list.
    edges: [ContractListEdge!]!

    # TotalCount is the maximum number of contracts available for sequential access.
    totalCount: BigInt!

    # PageInfo is an information about the current page of contract edges.
    pageInfo: ListPageInfo!
}

# TransactionListEdge is a single edge in a sequential list of transactions.
type ContractListEdge {
    cursor: Cursor!
    contract: Contract!
}

# Price represents price information of core Opera token
type Price {
    "Source unit symbol."
    fromSymbol: String!

    "Target unit symbol."
    toSymbol: String!

    "Price of the source symbol unit in target symbol unit."
    price: Float!

    "Price change in last 24h."
    change24: Float!

    "Price change in percent in last 24h."
    changePct24: Float!

    "Open 24h price."
    open24: Float!

    "Highest 24h price."
    high24: Float!

    "Lowest 24h price."
    low24: Float!

    "Volume exchanged in last 24h price."
    volume24: Float!

    "Market cap of the source unit."
    marketCap: Float!

    "Timestamp of the last update of this price value."
    lastUpdate: Long!
}

# Represents staker information.
type Staker {
    "Id number the staker."
    id: Long!

    "Staker address."
    stakerAddress: Address!

    "Amount of total staked tokens in WEI."
    totalStake: BigInt

    "Amount of own staked tokens in WEI."
    stake: BigInt

    "Amount of tokens delegated to the staker in WEI."
    delegatedMe: BigInt

    """
    Maximum total amount of tokens allowed to be delegated
    to the staker in WEI.
    This value depends on the amount of self staked tokens.
    """
    totalDelegatedLimit: BigInt!

    """
    Maximum amount of tokens allowed to be delegated to the staker
    on a new delegation in WEI.
    This value depends on the amount of self staked tokens.
    """
    delegatedLimit: BigInt!

    "Is this a validator record."
    isValidator: Boolean!

    "Is the staker active."
    isActive: Boolean!

    "Is the staker considered to be cheater."
    isCheater: Boolean!

    "Is the staker offline."
    isOffline: Boolean!

    "Epoch in which the staker was created."
    createdEpoch: Long!

    "Timestamp of the staker creation."
    createdTime: Long!

    "Epoch in which the staker was deactivated."
    deactivatedEpoch: Long!

    "Timestamp of the staker deactivation."
    deactivatedTime: Long!

    "How many blocks the staker missed."
    missedBlocks: Long!

    "Number of seconds the staker is offline."
    downtime: Long!

    "Proof of importance score."
    poi: BigInt

    "Base weight for rewards distribution."
    baseRewardWeight: BigInt

    "Weight for transaction rewards distribution."
    txRewardWeight: BigInt

    "Validation score."
    validationScore: BigInt

    "Origination score."
    originationScore: BigInt

    "Amount of rewards claimed in WEI."
    claimedRewards: BigInt

    "Amount of rewards claimed by delegators in WEI."
    delegationClaimedRewards: BigInt

    """
    List of delegations of this staker. Cursor is used to obtain specific slice
    of the staker's delegations. The most recent delegations
    are provided if cursor is omitted.
    """
    delegations(cursor: Cursor, count: Int = 25):DelegatorList!

    """
    Status is a binary encoded status of the staker.
    Ok = 0, bin 1 = Fork Detected, bin 256 = Validator Offline
    """
    status: Long!

    "StakerInfo represents extended staker information from smart contract."
    stakerInfo: StakerInfo

    """
    List of withdraw requests of the stake.
    Contains only withdrawal requests of the staking account,
    not the requests of the stake delegators.
    """
    withdrawRequests: [WithdrawRequest!]!
}

# PendingRewards represents a detail of pending rewards for staking and delegations
type PendingRewards {
    "Pending rewards amount."
    amount: BigInt!

    "The first unpaid epoch."
    fromEpoch: Long!

    "The last unpaid epoch."
    toEpoch: Long!
}

# WithdrawRequest represents a request for partial stake withdraw.
type WithdrawRequest {
    "Address of the authorized request."
    address: Address!

    "Address of the receiving account."
    receiver: Address!

    "Account receiving the withdraw request."
    account: Account!

    "Staker Id of the staker involved in the withdraw request."
    stakerID: Long!

    "Details of the staker involved in the withdraw request."
    staker: Staker!

    "Unique withdraw request identifier."
    withdrawRequestID: BigInt!

    "Is this a partial delegation withdraw, or staker withdraw?"
    isDelegation: Boolean!

    "Amount of WEI requested to be withdrawn."
    amount: BigInt!

    "Block in which the withdraw request was registered."
    requestBlock: Block!

    """
    Block in which the withdraw request was finalized.
    The value is NULL for pending request.
    """
    withdrawBlock: Block

    """
    Amount of WEI slashed as a penalty for cheating.
    The penalty is applied not only to staker withdraw,
    but also to delegations of a cheating staker.
    The value is NULL for pending requests.
    """
    withdrawPenalty: BigInt
}

# DeactivatedDelegation represents a prepared delegation full withdraw.
# Fully withdrawn delegations must be prepared first and finalized
# only after the lock down period passes.
type DeactivatedDelegation {
    "Address of the delegator."
    address: Address!

    "Staker Id of the staker involved in the withdraw request."
    stakerID: Long!

    "Details of the staker involved in the withdraw request."
    staker: Staker!

    "Block in which the delegation deactivation was registered."
    requestBlock: Block!

    """
    Block in which the delegation was withdrawn.
    The value is NULL for pending request.
    """
    withdrawBlock: Block

    """
    Amount of WEI slashed as a penalty for cheating.
    The penalty is applied to delegators' rewards
    in case their staker is flagged.
    The value is NULL for pending requests.
    """
    withdrawPenalty: BigInt
}

# Block is an Opera block chain block.
type Block {
    # Number is the number of this block, starting at 0 for the genesis block.
    number: Long!

    # Hash is the unique block hash of this block.
    hash: Hash!

    # Parent is the parent block of this block.
    parent: Block

    # TransactionCount is the number of transactions in this block.
    transactionCount: Int

    # Timestamp is the unix timestamp at which this block was mined.
    timestamp: Long!

    # txHashList is the list of unique hash values of transaction
    # assigned to the block.
    txHashList: [Hash!]!

    # txList is a list of transactions assigned to the block.
    txList: [Transaction!]!
}

# Contract defines block-chain smart contract information container
type Contract {
    "Address represents the contract address."
    address: Address!

    "DeployedBy represents the smart contract deployment transaction reference."
    deployedBy: Transaction!

    "transactionHash represents the smart contract deployment transaction hash."
    transactionHash: Hash!

    "Smart contract name. Empty if not available."
    name: String!

    "Smart contract version identifier. Empty if not available."
    version: String!

    """
    License specifies an open source license the contract was published with.
    Empty if not specified.
    """
    license: String!

    "Smart contract author contact. Empty if not available."
    supportContact: String!

    "Smart contract compiler identifier. Empty if not available."
    compiler: String!

    "Smart contract source code. Empty if not available."
    sourceCode: String!

    "Smart contract ABI definition. Empty if not available."
    abi: String!

    """
    Validated is the unix timestamp at which the source code was validated
    against the deployed byte code. Null if not validated yet.
    """
    validated: Long

    "Timestamp is the unix timestamp at which this smart contract was deployed."
    timestamp: Long!
}

# ContractValidationInput represents a set of data sent from client
# to validate deployed contract with the provided source code.
input ContractValidationInput {
    "Address of the contract being validated."
    address: Address!

    "Optional smart contract name. Maximum allowed length is 64 characters."
    name: String

    "Optional smart contract version identifier. Maximum allowed length is 14 characters."
    version: String

    "Optional smart contract author contact. Maximum allowed length is 64 characters."
    supportContact: String

    """
    License specifies an open source license the contract was published with.
    Empty if not specified.
    """
    license: String

    "Optimized specifies if the compiler was set to optimize the byte code."
    optimized: Boolean = true

    """
    OptimizeRuns specifies number of optimization runs the compiler was set
    to execute during the byte code optimizing.
    """
    optimizeRuns: Int = 200

    "Smart contract source code."
    sourceCode: String!
}

# BlockList is a list of block edges provided by sequential access request.
type BlockList {
    # Edges contains provided edges of the sequential list.
    edges: [BlockListEdge!]!

    # TotalCount is the maximum number of blocks available for sequential access.
    totalCount: BigInt!

    # PageInfo is an information about the current page of block edges.
    pageInfo: ListPageInfo!
}

# BlockListEdge is a single edge in a sequential list of blocks.
type BlockListEdge {
    cursor: Cursor!
    block: Block!
}

# ListPageInfo contains information about a sequential access list page.
type ListPageInfo {
    # First is the cursor of the first edge of the edges list. null for empty list.
    first: Cursor

    # Last if the cursor of the last edge of the edges list. null for empty list.
    last: Cursor

    # HasNext specifies if there is another edge after the last one.
    hasNext: Boolean!

    # HasNext specifies if there is another edge before the first one.
    hasPrevious: Boolean!
}