Tutorial: How to Use offchain-utils
This tutorial explains how to integrate offchain-utils
(opens in a new tab) into a Substrate-based blockchain to securely fetch external API data.
π Clone and Set Up the Substrate Parachain Template
- Clone the Substrate Node Template (opens in a new tab) repository:
-
π¦ The template is using the Rust language.
-
π Check the Rust installation instructions (opens in a new tab) for your system.
-
π οΈ Depending on your operating system and Rust version, there might be additional packages required to compile this template - please take note of the Rust compiler output.
Build
π¨ Use the following command to build the node without launching it:
cargo build --release
Run the node locally:
Current polkadot-omni-node
version not working with Offchain worker template. We will wait for the new release.
For now we can clone the Polkadot SDK monorepo (opens in a new tab) and build that project to get the latest polkadot-omni-node
.
1. Clone the repo Polkadot SDK monorepo (opens in a new tab)
2. Assuming you've built the binary using:
cargo build --release
you'll find the executable in the target/release directory.
3. Make It Executable
Though Cargo usually sets the execute permission, you can ensure itβs executable by running:
chmod +x target/release/polkadot-omni-node
4. Move the Binary to a Global PATH Directory
For example, copy it to /usr/local/bin (you might need sudo privileges):
sudo cp target/release/polkadot-omni-node /usr/local/bin/
5. Verify Global Installation
After copying, verify that itβs accessible globally by running:
polkadot-omni-node --help
If everything is set up correctly, you should see the help output for the polkadot-omni-node command.
Getting Started
π Step 1: Install offchain-utils
Add the crate to your Substrate runtime by running:
cargo add offchain-utils
Alternatively, manually add it to your Cargo.toml:
[dependencies]
offchain-utils = "0.1.0" # Ensure you use the latest published version
π Step 2: Implement API Key Retrieval in the Pallet
Modify your pallet to use offchain-utils::OffchainApiKey for fetching the API key.
π Define API Key Fetcher
use offchain_utils::offchain_api_key::OffchainApiKey;
pub struct CustomApiKeyFetcher;
impl OffchainApiKey for CustomApiKeyFetcher {}
π Retrieve API Key Securely
impl<T: Config> Pallet<T> {
fn get_api_key() -> Result<String, &'static str> {
match CustomApiKeyFetcher::fetch_api_key_for_request("key_name") {
Ok(key) => Ok(key),
Err(err) => {
log::error!("Failed to fetch API key: {:?}", err);
Err("API key not found in keystore")
}
}
}
}
β This method retrieves the API key securely from the keystore, avoiding on-chain exposure.
π Step 3: Implement Off-Chain Worker
Modify your pallet to include the offchain_worker hook:
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn offchain_worker(block_number: BlockNumberFor<T>) {
log::trace!(target: "logger", "Ping from offchain workers!");
// Retrieve API Key securely
let api_key = match Self::get_api_key() {
Ok(key) => key,
Err(e) => {
log::error!("Offchain worker error: {:?}", e);
return;
}
};
log::info!("API key retrieved: {}", api_key);
// Fetch external data using the API key
let request = HttpRequest::new("https://api.example.com/data")
.add_header("Authorization", &format!("Bearer {}", api_key));
match DefaultOffchainFetcher::fetch_string(request) {
Ok(response) => log::info!("Received API response: {}", response),
Err(_) => log::error!("HTTP request failed"),
}
// Retrieve parent block hash (example usage of on-chain data)
let parent_hash = <system::Pallet<T>>::block_hash(block_number - 1u32.into());
log::debug!(
"Current block: {:?} (parent hash: {:?})",
block_number,
parent_hash
);
}
}
β This worker securely retrieves an API key and fetches external data.
π Step 4: Submit Data On-Chain (Optional)
If the API response needs to be stored on-chain, use signed transactions:
let signer = Signer::<T, T::AuthorityId>::all_accounts();
let results = signer.send_signed_transaction(|_account| {
Call::store_api_response { response: api_response.clone() }
});
π― Next Steps
Now that youβve integrated secure API key storage and off-chain worker execution, explore the architecture to understand how it works.
Additionally, consider using Subway (opens in a new tab) as a secure RPC gateway to optimize and protect your Substrate JSON-RPC calls.
β Check out our example Template (opens in a new tab) of offchain-utils
β Learn about the Architecture of offchain-utils
β Secure Your RPC Calls with Subway (opens in a new tab)