Documentation Index
Fetch the complete documentation index at: https://initialabs-docs-evm-erc20-minievm-alignment.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Tutorial GitHub Repository
Compiling contracts that use
ConnectOracle.sol
requires the
viaIR
feature. For Foundry/Forge, this can be done by using the --via-ir flag. The
relevant methods for other tools may vary.
Foundry
For this tutorial, we will be using
Foundry toolkit to develop, compile,
and deploy our contracts. If you do not have Foundry installed, follow the
Foundry installation instructions.
Setup
Create a new project directory and initialize it with forge init:
mkdir connect-oracle
cd connect-oracle
forge init
Implementing the Contract
Before writing our contract, we first need to rename the template contract to
Oracle.sol
mv src/Counter.sol src/Oracle.sol
We then update the contract from the template to be our oracle contract. We
declare the IConnectOracle interface, which will be used to interact with the
ConnectOracle contract.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
interface IConnectOracle {
struct Price {
uint256 price;
uint256 timestamp;
uint64 height;
uint64 nonce;
uint64 decimal;
uint64 id;
}
function get_price(string memory pair_id) external view returns (Price memory);
function get_prices(string[] memory pair_ids) external view returns (Price[] memory);
}
contract Oracle {
IConnectOracle public connect;
We then need to define the constructor for our contract. This will be used to
initialize the contract with the ConnectOracle contract address.
The ConnectOracle contract is on MiniEVM precompiles. You can get its address by querying ${REST_URL}/minievm/evm/v1/connect_oracle where ${REST_URL} refers to the REST endpoint URL of the rollup.curl https://rest-evm-1.anvil.asia-southeast.initia.xyz/minievm/evm/v1/connect_oracle
The output will look like this:{
"address": "0x031ECb63480983FD216D17BB6e1d393f3816b72F"
}
constructor(address oracleAddress) {
connect = IConnectOracle(oracleAddress);
}
Once the constructor is implemented, we move on to defining the different
functions that our contract will have
oracle_get_price: This function will return the price of a single asset pair
oracle_get_prices: This function will return the price of multiple asset
pairs
function oracle_get_price() external view returns (uint256 price, uint256 timestamp) {
IConnectOracle.Price memory p = connect.get_price("BTC/USD");
return (p.price, p.timestamp);
}
function oracle_get_prices() external view returns (uint256[] memory prices) {
string[] memory pair_ids = new string[](2);
pair_ids[0] = "BTC/USD";
pair_ids[1] = "ETH/USD";
IConnectOracle.Price[] memory result = connect.get_prices(pair_ids);
prices = new uint256[](result.length);
for (uint256 i = 0; i < result.length; i++) {
prices[i] = result[i].price;
}
}
Our complete contract will then look like this:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
interface IConnectOracle {
struct Price {
uint256 price;
uint256 timestamp;
uint64 height;
uint64 nonce;
uint64 decimal;
uint64 id;
}
function get_price(string memory pair_id) external view returns (Price memory);
function get_prices(string[] memory pair_ids) external view returns (Price[] memory);
}
contract Oracle {
IConnectOracle public connect;
constructor(address oracleAddress) {
connect = IConnectOracle(oracleAddress);
}
function oracle_get_price() external view returns (uint256 price, uint256 timestamp) {
IConnectOracle.Price memory p = connect.get_price("BTC/USD");
return (p.price, p.timestamp);
}
function oracle_get_prices() external view returns (uint256[] memory prices) {
string[] memory pair_ids = new string[](2);
pair_ids[0] = "BTC/USD";
pair_ids[1] = "ETH/USD";
IConnectOracle.Price[] memory result = connect.get_prices(pair_ids);
prices = new uint256[](result.length);
for (uint256 i = 0; i < result.length; i++) {
prices[i] = result[i].price;
}
return prices;
}
}
Running forge compile will fail unless we provide a test file that matches the
Oracle.sol contract. Foundry expects a test file like Oracle.t.sol to exist
and import the contract under test. To resolve this, we will rename the existing
test file Counter.t.sol to Oracle.t.sol.
mv test/Counter.t.sol test/Oracle.t.sol
We will also replace the file contents with placeholder content.
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
import {Test, console} from "forge-std/Test.sol";
contract OracleTest is Test {
}
Now running forge compile should work without any errors.
forge compile;
# [Expected Output]:
# [⠢] Compiling...
# [⠰] Compiling 27 files with 0.8.21
# [⠃] Solc 0.8.21 finished in 6.25s
# Compiler run successful!
Deploying the Contract
Now that our contract is compiled and ready, we can deploy it to the MiniEVM. To
accomplish this, we will use Foundry’s forge script command. First, we need to
create a script file to handle the deployment. Create a new file named
Oracle.s.sol in the script directory.
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
import {Script, console} from "forge-std/Script.sol";
import {Oracle} from "../src/Oracle.sol";
contract OracleScript is Script {
Oracle public oracle;
function setUp() public {}
function run() public {
address oracleAddress = 0x031ECb63480983FD216D17BB6e1d393f3816b72F;
vm.startBroadcast();
oracle = new Oracle(oracleAddress);
vm.stopBroadcast();
}
}
Set your environment variables and run the deployment. Be sure to replace
PRIVATE_KEY with the deployer’s private key, and JSON_RPC_URL with your
rollup’s JSON-RPC endpoint.
export PRIVATE_KEY=0x...
export JSON_RPC_URL=https://jsonrpc-evm-1.anvil.asia-southeast.initia.xyz
forge script script/Oracle.s.sol:OracleScript \
--rpc-url $JSON_RPC_URL \
--private-key $PRIVATE_KEY \
--broadcast \
--via-ir \
--with-gas-price 0 \
--skip-simulation
Output should look like this:
[⠊] Compiling...
[⠊] Compiling 18 files with Solc 0.8.28
[⠒] Solc 0.8.28 finished in 918.49ms
Compiler run successful!
Script ran successfully.
SKIPPING ON-CHAIN SIMULATION.
##### 4303131403034904
✅ [Success] Hash: 0x8d9c488d7599fd867e45eee3b3a6ede24fec8f6459433051c341ef1937026bcf
Contract Address: 0x505500221090Cd06400125B4f41A266B89Ffd62e
Block: 10493369
Gas Used: 290728
✅ Sequence #1 on 4303131403034904 | Total Paid: 0. ETH (290728 gas * avg 0 gwei)
To query the oracle_get_price() function, use Foundry’s cast call command.
cast call 0x505500221090Cd06400125B4f41A266B89Ffd62e "oracle_get_price()" --rpc-url $JSON_RPC_URL
Output should look like this:
0x00000000000000000000000000000000000000000000000000000002c3cd0d430000000000000000000000000000000000000000000000001856610b4b695788
The output is an ABI-encoded hexadecimal result containing the price and
timestamp. The first 32 bytes represent the price, and the next 32 bytes
represent the timestamp.
| Field | ABI-Encoded Value | Decoded Value |
|---|
| Price | 0x2c3cd0d43 | 11874929987 |
| Timestamp | 0x1856610b4b695788 | 1753695806045116296 |