In the following, we report a usage example of the CredoraMetrics
contract, showing a JavaScript sample code based on the development environment.
Prerequisites
Must have an Ethereum or Sepolia wallet. You can fund your Sepolia wallet .
Sufficient ETH for transaction fees.
Node.js and npm installed.
Setup
Create a new app directory and install hardhat dependencies:
Copy 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:
Copy 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.
Copy 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:
Copy 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:
Copy 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
Copy 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
:
Copy 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:
Step 4: Deploy the Contract
Deploy the smart contract:
Copy npx hardhat run scripts/deploy.js
--network {ethereum/ethereumSepolia/flowTestnet/flowMainnet}
You should see output similar to:
Copy 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.
Copy 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}