2

How can my smart contract or application access USD/HBAR exchange rate info from a file on the Hedera network (Hedera File Service)?

The file I'm interested in is 0.0.112. I tried doing a query with the SDK from my application and it seems to get me the info, but how can I use this in a contract and how real-time is this price info?

async function main() {
    const fileQuery = new FileContentsQuery().setFileId("0.0.112");
    const contents = await fileQuery.execute(client);
    console.log(contents.toString());
}
main();
bguiz
  • 27,371
  • 47
  • 154
  • 243
FungalFury
  • 23
  • 3

2 Answers2

0

You’re moving in the right direction. A few points:

As you found out, an external app can access that exchange rate in 0.0.112 by just doing a FileContentsQuery() with the SDK. You could also use this mirror REST API call (https://mainnet-public.mirrornode.hedera.com/api/v1/network/exchangerate), which also exposes the exchange rate info in the HFS file.

If you need to access that exchange rate info from 0.0.112 in a smart contract, then I’d suggest checking out HIP-475 (https://hips.hedera.com/hip/hip-475), so you may end up with something like:

import "./IExchangeRate.sol";

abstract contract SelfFunding {
    uint256 constant TINY_PARTS_PER_WHOLE = 100_000_000;
    address constant PRECOMPILE_ADDRESS = address(0x168);

    function tinycentsToTinybars(uint256 tinycents) internal returns (uint256 tinybars) {
        (bool success, bytes memory result) = PRECOMPILE_ADDRESS.call(
            abi.encodeWithSelector(IExchangeRate.tinycentsToTinybars.selector, tinycents));
        require(success);
        tinybars = abi.decode(result, (uint256));
    }

    modifier costsCents(uint256 cents) {
        uint256 tinycents = cents * TINY_PARTS_PER_WHOLE;
        uint256 requiredTinybars = tinycentsToTinybars(tinycents);
        require(msg.value >= requiredTinybars);
        _;
    } 
}

Note that the exchange rate in 0.0.112 is NOT a live price oracle from exchanges. Instead, it’s a weighted median of the recent HBAR-USD exchange rate on major exchanges. So if you need live pricing data, the info from 0.0.112 won’t work.

You’ll likely have to use APIs from crypto exchanges (preferably average from a few exchanges). This query can not be executed from a smart contract. This is typically executed in an external app and then that app can invoke a smart contract if the smart contract logic needs to have this information.

Ed Marquez
  • 488
  • 1
  • 10
0

Updated answer, for newer Solidity version, also making use of the Exchange Rate system contract from HIP-475, which is available at 0x168, with the following interface:

interface IExchangeRate {
    function tinycentsToTinybars(uint256 tinycents) external returns (uint256);
    function tinybarsToTinycents(uint256 tinybars) external returns (uint256);
}

As long as your use case does require a high degree of accuracy or "liveness", this does an OK job of the conversion.

The following example demonstrates how to use the exchange rate system contract to convert from USD cents to HBAR cents.

Try it out in Remix

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.18;

interface IExchangeRate {
    // Given a value in tinycents (1e-8 US cents or 1e-10 USD), returns the 
    // equivalent value in tinybars (1e-8 HBAR) at the current exchange rate 
    // stored in system file 0.0.112. 
    // 
    // This rate is a weighted median of the the recent" HBAR-USD exchange 
    // rate on major exchanges, but should _not_ be treated as a live price 
    // oracle! It is important primarily because the network will use it to 
    // compute the tinybar fees for the active transaction. 
    // 
    // So a "self-funding" contract can use this rate to compute how much 
    // tinybar its users must send to cover the Hedera fees for the transaction.
    function tinycentsToTinybars(uint256 tinycents) external returns (uint256);

    // Given a value in tinybars (1e-8 HBAR), returns the equivalent value in 
    // tinycents (1e-8 US cents or 1e-10 USD) at the current exchange rate 
    // stored in system file 0.0.112. 
    // 
    // This rate tracks the the HBAR-USD rate on public exchanges, but 
    // should _not_ be treated as a live price oracle! This conversion is
    // less likely to be needed than the above conversion from tinycent to
    // tinybars, but we include it for completeness.
    function tinybarsToTinycents(uint256 tinybars) external returns (uint256);
}

contract Exchange {
    IExchangeRate constant ExchangeRate =
        IExchangeRate(address(0x168));

    event ConversionResult(uint256 inAmount, uint256 outAmount);

    function convert(uint256 usdCents) external returns (uint256 hbarCents) {
        hbarCents = ExchangeRate.tinycentsToTinybars(usdCents * 100_000_000) / 1_000_000;
        emit ConversionResult(usdCents, hbarCents);
    }
}

Note that as of time of writing, 1,000.00 USD is 19,584.80 HBAR.

exchange-rate-coinbase-pair

However an input of 100,000 (usdCents) returns an output of 1,969,667 (hbarCents), which is approximately 0.6% off of the live value. So don't use this in a DEX or anything like that.

bguiz
  • 27,371
  • 47
  • 154
  • 243