Usage Example

In the following, we report a usage example of the CredoraMetrics contract, showing a JavaScript sample code based on the HardHat development environment.

Prerequisites

  • Must have an Ethereum or Sepolia wallet. You can fund your Sepolia wallet here.

  • Sufficient ETH for transaction fees.

  • Node.js and npm installed.

Setup

Create a new app directory and install hardhat dependencies:

mkdir sample-consumer-app 
cd sample-consumer-app 
npm init -y 
npm install --save-dev hardhat
npm install --save-dev @nomicfoundation/hardhat-toolbox
npm install --save-dev hardhat-contract-sizer
npm install --save-dev @credora/on-chain-metrics

Now, configure the hardhat project by creating the file hardhat.config.js with this content:

require("@nomicfoundation/hardhat-toolbox")
require("hardhat-contract-sizer")

const { BigNumber } = require('ethers');
const { ethers } = require('ethers');

const SOLC_SETTINGS = {
  optimizer: {
    enabled: true,
    runs: 1_000,
  },
}
// Set EVM private keys (required)
const PRIVATE_KEY = process.env.PRIVATE_KEY
if (!PRIVATE_KEY) {
  throw Error("Set the PRIVATE_KEY environment variable with your EVM wallet private key")
}
const accounts = []
if (PRIVATE_KEY) {
  accounts.push(PRIVATE_KEY)
}

/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
  defaultNetwork: "localFunctionsTestnet",
  gas: 1000000, // Set the gas limit for transactions (in wei)
  solidity: {
    compilers: [
      {
        version: "0.8.20",
        settings: SOLC_SETTINGS,
      },
    ],
  },
  networks: {
    ethereum: {
      url: process.env.ETHEREUM_RPC_URL || "UNSET",
      gasPrice: undefined,
      nonce: undefined,
      accounts,
      chainId: 1,
      nativeCurrencySymbol: "ETH",
    },
    ethereumSepolia: {
      url: process.env.ETHEREUM_SEPOLIA_RPC_URL || "UNSET",
      gasPrice: undefined,
      nonce: undefined,
      accounts,
      chainId: 11155111,
      nativeCurrencySymbol: "ETH",
      },
    flowMainnet: {
      url: process.env.FLOWMAINNET_RPC_URL || "UNSET",
      gasPrice: undefined,
      nonce: undefined,
      accounts,
      chainId: 545,
      nativeCurrencySymbol: "FLOW",
    },
    flowTestnet: {
      url: process.env.FLOWTESTNET_RPC_URL || "UNSET",
      gasPrice: undefined,
      nonce: undefined,
      accounts,
      chainId: 545,
      nativeCurrencySymbol: "FLOW",
    }
  }
}

Get On-Chain Metrics Using Synchronous Getter Functions

Step 1: Add a new Task to the Hardhat Script

Extend the hardhat.config.js with a task that aims to fetch Credora data from the blockchain.

const divisionFactor = 18;

task("getNAV", "Get NAV data stored on-chain")
  .addParam("contract", "The contract address")
  .addParam("entity", "The entity name")
  .setAction(async (taskArgs, hre) => {
    const fs = require("fs");
    const contractAddress = taskArgs.contract;
    const entity = ethers.encodeBytes32String(taskArgs.entity);
    const signer = (await hre.ethers.getSigners())[0];

    // Get the ABI from the compiled JSON artifact
    const artifactPath = "node_modules/@credora/on-chain-metrics-test/contracts/abi/CredoraMetrics.json";
    const abi = JSON.parse(fs.readFileSync(artifactPath, "utf-8"));
    // Connect to the deployed contract
    const contract = new ethers.Contract(contractAddress, abi, signer);    
    const result = await contract.getNAV(entity,{ gasLimit: 1000000 });    
    
    let decimalResult = ethers.formatUnits(result, divisionFactor);
    console.log('NAV:', decimalResult);
    
});

The getNAV task receives in input the entity name and the contract address to call the getNAV smart contract function.

The returned value is an unsigned integer (uint256) and needs to be divided by a factor of 10^18 to convert it to its final floating-point format.

Step 2: Configure Environment Variables

Export the following environment variables:

ETHEREUM_RPC_URL, ETHEREUM_SEPOLIA_RPC_URL, FLOWTESTNET_RPC_URL (e.g. https://eth-sepolia.g.alchemy.com/v2/{ALCHEMY_APIKEY})
PRIVATE_KEY of the wallet

Step 3: Run

From the same directory, run the following by passing the address of the Credora contract and the name of the entity owning the data:

npx hardhat getNAV 
    --network {ethereum/ethereumSepolia/flowTestnet/flowMainnet} 
    --contract {address of the Credora contract} 
    --entity {entity name}

Get On-Chain Metrics Using Callbacks

Step 1: Write the Consumer Smart Contract

Navigate to the contracts directory and create a new Solidity file, e.g., MetricsConsumer.sol

pragma solidity ^0.8.20;

/**
 * @title Metrics Consumer Contract
 * @notice This contract implements the CredoraFunctions for managing financial asset metrics.
 */
import "@credora/on-chain-metrics/contracts/CredoraFunctions.sol";

contract MetricsConsumer is CredoraFunctions {

    // Example storage for metrics
    mapping(bytes32 => uint256) private scores;
    mapping(bytes32 => uint256) private nav;
    mapping(bytes32 => bytes8)  private nav_currency;
    mapping(bytes32 => bytes8)  private rae;
    mapping(bytes32 => uint256) private borrowCapacities;
    mapping(bytes32 => uint256) private impliedPDs;
    mapping(bytes32 => uint256) private impliedPDTenors;

    // Implementations of fulfill functions that update the state and emit events
    function fulfillScore(bytes32 _entity, uint256 _metric_data) external override {
        scores[_entity] = _metric; // Update the metric
    }

    function fulfillNAV(bytes32 _entity, uint256 _metric_data, bytes8 currency) external override {
        nav[_entity] = _metric; // Update the metric
    }

    function fulfillRAE(bytes32 _entity, bytes8 _metric_data) external override {
        rae[_entity] = _metric; // Update the metric
    }

    function fulfillBorrowCapacity(bytes32 _entity, uint256 _metric_data) external override {
        borrowCapacities[_entity] = _metric; // Update the metric
    }

    function fulfillImpliedPD(bytes32 _entity, uint256 _metric_data) external override {
        impliedPDs[_entity] = _metric; // Update the metric
    }

    function fulfillImpliedPDtenor(bytes32 _entity, uint256 _metric_data) external override {
        impliedPDTenors[_entity] = _metric; // Update the metric
    }
    function invalidateData(bytes32 _entity) external override {
        // Do something when data gets invalidated
    }
}

Step 2: Write a Deployment Script

Navigate to the scripts directory and create a new deployment script, e.g., deploy.js:

async function main() {
  const [deployer] = await ethers.getSigners();

  console.log("Deploying contracts with the account:", deployer.address);
  
  const MyContract = await ethers.getContractFactory("MetricsConsumer");
  const myContract = await MyContract.deploy();

  console.log("Contract deployed to address:", myContract.address);
}

main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });

Step 3: Compile the Contract

Compile the smart contract using Hardhat:

npx hardhat compile

Step 4: Deploy the Contract

Deploy the smart contract:

npx hardhat run scripts/deploy.js 
    --network {ethereum/ethereumSepolia/flowTestnet/flowMainnet}

You should see output similar to:

Deploying contracts with the account: 0xYourAccountAddress
Contract deployed to address: 0xYourContractAddress

Step 5: Test the callbacks

Once Credora has subscribed your contract, the callbacks can be self-tested by calling the testTriggerCallbacks function (only available on Testnets) on the Credora Smart Contract.

To do so, you can move inside the Credora package and run the related hardhat task (available at node_modules/@credora/on-chain-metrics/tasks/Testers/testTriggerCallbacks.js), which provides a set of random metrics and triggers the callbacks of the subscribed contract.

cd node_modules/@credora/on-chain-metrics && \
npx hardhat compile && \
npx hardhat testTriggerCallbacks 
    --entity {subscribed entityname} 
    --contract {address of the Credora contract} 
    --thirdpartyaddr {address of the consuming contract} 
    --network {ethereum/ethereumSepolia/flowTestnet/flowMainnet} 

Last updated