Skip to content

Storing Data on Arkiv

This guide targets the Tiramisu testnet. Check Networks for current testnets and faucets.

In this part, you build the Node.js script that publishes data. This script fetches data from CoinGecko and uses the Arkiv SDK to write it to the blockchain.

Open backend/index.js and build it step by step.

First, import the functions that you need from the Arkiv SDK, then set up the wallet client. This client authenticates you with your private key, so you can write data.

  • Directorybackend/
    • index.js
    • .env
  • Directoryfrontend/
  • package.json
backend/index.js
import { createWalletClient } from '@arkiv-network/sdk';
import { tiramisu } from '@arkiv-network/sdk/chains';
import { http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
// Load the private key from the environment file.
const privateKey = process.env.PRIVATE_KEY;
if (!privateKey) {
throw new Error("PRIVATE_KEY is not set in the .env file.");
}
// Create an account object from the private key.
const account = privateKeyToAccount(privateKey);
// Create a wallet client to interact with Arkiv.
const client = createWalletClient({
chain: tiramisu, // Use the Tiramisu testnet.
transport: http(),
account: account,
});
console.log(`Backend service connected as: ${client.account.address}`);

Run your script to see your wallet address:

Terminal window
node --env-file backend/.env backend/index.js

The output looks like this: Backend service connected as: 0x1234...

Before you create entities on Arkiv, you need test GLM to pay for gas fees. Tiramisu uses GLM as its native gas token.

Get test GLM from the Arkiv faucet. Use the address that the previous step printed. If the faucet does not work for you, ask in the Arkiv Discord.

Next, add a function that calls the CoinGecko API and fetches your cryptocurrency data.

backend/index.js
import { createWalletClient } from '@arkiv-network/sdk';
import { tiramisu } from '@arkiv-network/sdk/chains';
import { http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import axios from 'axios';
// Load the private key from the environment file.
const privateKey = process.env.PRIVATE_KEY;
if (!privateKey) {
throw new Error("PRIVATE_KEY is not set in the .env file.");
}
// Create an account object from the private key.
const account = privateKeyToAccount(privateKey);
// Create a wallet client to interact with Arkiv.
const client = createWalletClient({
chain: tiramisu, // Use the Tiramisu testnet.
transport: http(),
account: account,
});
console.log(`Backend service connected as: ${client.account.address}`);
// Construct the CoinGecko API URL
const params = new URLSearchParams({
vs_currency: 'usd',
ids: 'bitcoin,ethereum,golem',
sparkline: 'false'
});
const COINGECKO_URL = `https://api.coingecko.com/api/v3/coins/markets?${params}`;
async function fetchCryptoData() {
try {
const response = await axios.get(COINGECKO_URL);
console.log('Successfully fetched data from CoinGecko.');
return response.data;
} catch (error) {
console.error('Error fetching data from CoinGecko:', error.message);
return []; // Return an empty array on failure.
}
}

This is the core of the backend. This function takes all the crypto data and creates entities on Arkiv in one batch operation using the executeBatch method.

  • Payload: the main data that you want to store, structured as JSON.
  • Attributes: key-value pairs that act as tags or metadata. Attributes make your data queryable later.
  • expires: entities can be configured to expire automatically after a specific time. After the block at which an entity expires, queries do not return it.
backend/index.js
import { createWalletClient } from '@arkiv-network/sdk';
import { tiramisu } from '@arkiv-network/sdk/chains';
import { ExpirationTime, jsonToPayload } from '@arkiv-network/sdk';
import { http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import axios from 'axios';
// Load the private key from the environment file.
const privateKey = process.env.PRIVATE_KEY;
if (!privateKey) {
throw new Error("PRIVATE_KEY is not set in the .env file.");
}
// Create an account object from the private key.
const account = privateKeyToAccount(privateKey);
// Create a wallet client to interact with Arkiv.
const client = createWalletClient({
chain: tiramisu, // Use the Tiramisu testnet.
transport: http(),
account: account,
});
console.log(`Backend service connected as: ${client.account.address}`);
// Construct the CoinGecko API URL
const params = new URLSearchParams({
vs_currency: 'usd',
ids: 'bitcoin,ethereum,golem',
sparkline: 'false'
});
const COINGECKO_URL = `https://api.coingecko.com/api/v3/coins/markets?${params}`;
async function fetchCryptoData() {
try {
const response = await axios.get(COINGECKO_URL);
console.log('Successfully fetched data from CoinGecko.');
return response.data;
} catch (error) {
console.error('Error fetching data from CoinGecko:', error.message);
return []; // Return an empty array on failure.
}
}
async function uploadDataToArkiv(cryptoData) {
if (cryptoData.length === 0) {
console.log("No crypto data to upload.");
return;
}
try {
// Create payload objects for all tokens
const createPayloads = cryptoData.map(tokenData => {
const {
id,
current_price,
market_cap,
price_change_percentage_24h
} = tokenData;
return {
payload: jsonToPayload({
price: current_price,
marketCap: market_cap,
change24h: price_change_percentage_24h,
timestamp: Date.now(),
}),
contentType: 'application/json',
attributes: {
token: id, // 'bitcoin', 'ethereum', or 'golem'
},
expires: ExpirationTime.fromHours(3), // Data expires after 3 hours.
};
});
const result = await client.executeBatch({
creates: createPayloads
});
// Log success for each created entity
result.createdEntities.forEach((entityKey, index) => {
const tokenId = cryptoData[index].id;
console.log(`Created entity for ${tokenId}. Key: ${entityKey}`);
});
} catch (error) {
console.error('Failed to create entities:', error.message);
}
}

Finally, create a main loop that ties everything together. This function fetches the data and uploads all tokens in one batch operation.

backend/index.js
import { createWalletClient } from '@arkiv-network/sdk';
import { tiramisu } from '@arkiv-network/sdk/chains';
import { ExpirationTime, jsonToPayload } from '@arkiv-network/sdk';
import { http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import axios from 'axios';
// Load the private key from the environment file.
const privateKey = process.env.PRIVATE_KEY;
if (!privateKey) {
throw new Error("PRIVATE_KEY is not set in the .env file.");
}
// Create an account object from the private key.
const account = privateKeyToAccount(privateKey);
// Create a wallet client to interact with Arkiv.
const client = createWalletClient({
chain: tiramisu, // Use the Tiramisu testnet.
transport: http(),
account: account,
});
console.log(`Backend service connected as: ${client.account.address}`);
// Construct the CoinGecko API URL
const params = new URLSearchParams({
vs_currency: 'usd',
ids: 'bitcoin,ethereum,golem',
sparkline: 'false'
});
const COINGECKO_URL = `https://api.coingecko.com/api/v3/coins/markets?${params}`;
async function fetchCryptoData() {
try {
const response = await axios.get(COINGECKO_URL);
console.log('Successfully fetched data from CoinGecko.');
return response.data;
} catch (error) {
console.error('Error fetching data from CoinGecko:', error.message);
return []; // Return an empty array on failure.
}
}
async function uploadDataToArkiv(cryptoData) {
if (cryptoData.length === 0) {
console.log("No crypto data to upload.");
return;
}
try {
// Create payload objects for all tokens
const createPayloads = cryptoData.map(tokenData => {
const {
id,
current_price,
market_cap,
price_change_percentage_24h
} = tokenData;
return {
payload: jsonToPayload({
price: current_price,
marketCap: market_cap,
change24h: price_change_percentage_24h,
timestamp: Date.now(),
}),
contentType: 'application/json',
attributes: {
token: id, // 'bitcoin', 'ethereum', or 'golem'
},
expires: ExpirationTime.fromHours(3), // Data expires after 3 hours.
};
});
const result = await client.executeBatch({
creates: createPayloads
});
// Log success for each created entity
result.createdEntities.forEach((entityKey, index) => {
const tokenId = cryptoData[index].id;
console.log(`Created entity for ${tokenId}. Key: ${entityKey}`);
});
} catch (error) {
console.error('Failed to create entities:', error.message);
}
}
async function runUpdateCycle() {
console.log("\n--- Starting new update cycle ---");
const cryptoData = await fetchCryptoData();
if (cryptoData.length > 0) {
// Upload all tokens in a single executeBatch call
await uploadDataToArkiv(cryptoData);
}
}
// Run the cycle on start, and then every 60 seconds.
runUpdateCycle();
setInterval(runUpdateCycle, 60000); // 60000 ms = 60 seconds

The backend is complete. Run it from your terminal:

Terminal window
node --env-file backend/.env backend/index.js

The logs show your address and each entity it creates. Do not stop the script: it must stay active. In the next section, you build a frontend to see the data.