Documentation Index
Fetch the complete documentation index at: https://docs.goldsky.com/llms.txt
Use this file to discover all available pages before exploring further.
This guide walks you through building a Bitcoin price oracle that fetches the BTC/USD price every minute and writes it on-chain. It demonstrates cron triggers, contract codegen, external API calls, and collection storage.
How it works
- Cron trigger fires every minute
- CoinGecko API provides the current BTC/USD price
- On-chain contract receives the price via a typed contract class
- Collection stores the price for historical queries
Prerequisites
Project structure
bitcoin-oracle/
├── compose.yaml # Compose configuration
├── tsconfig.json # TypeScript config
├── src/
│ ├── contracts/
│ │ └── PriceOracle.json # Contract ABI (generates typed class)
│ ├── lib/
│ │ └── utils.ts # toBytes32 helper
│ └── tasks/
│ └── bitcoin-oracle.ts # Main task
Step 1: Set up the project
Clone the example repository:
git clone https://github.com/goldsky-io/documentation-examples.git
cd documentation-examples/compose/bitcoin-oracle
Step 2: Generate contract types
The project includes a PriceOracle.json ABI in src/contracts/. Generate the typed contract class:
This creates a typed PriceOracle class in .compose/generated/ that provides type-safe contract interaction.
Step 3: Understand the task
The bitcoin-oracle.ts task handles everything:
import { TaskContext } from "compose";
import { toBytes32 } from "../lib/utils";
const ORACLE_CONTRACT = "0x34a264BCD26e114eD6C46a15d0A3Ba1873CaA708";
export async function main(context: TaskContext) {
const { fetch, evm, collection } = context;
const wallet = await evm.wallet({ name: "bitcoin-oracle-wallet" });
// Instantiate the typed contract (generated from src/contracts/PriceOracle.json)
const oracle = new evm.contracts.PriceOracle(
ORACLE_CONTRACT,
evm.chains.polygonAmoy,
wallet
);
// Fetch Bitcoin price from CoinGecko API
const response = await fetch<{ bitcoin: { usd: number } }>(
"https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd",
{
max_attempts: 3,
initial_interval_ms: 1000,
backoff_factor: 2,
}
);
if (!response) {
throw new Error("Failed to fetch Bitcoin price");
}
const bitcoinPrice = response.bitcoin.usd;
const timestamp = Date.now();
// Convert to bytes32 format and write on-chain
const timestampAsBytes32 = toBytes32(timestamp);
const priceAsBytes32 = toBytes32(Math.round(bitcoinPrice * 100));
const { hash, receipt } = await oracle.write(timestampAsBytes32, priceAsBytes32);
// Store in a collection for historical queries
const priceHistory = await collection("bitcoin_prices");
const { id } = await priceHistory.insertOne({
price: bitcoinPrice,
timestamp: timestamp,
});
return {
success: true,
oracleHash: hash,
price: bitcoinPrice,
timestamp,
priceId: id,
};
}
Key Compose features used
context.fetch — HTTP requests with built-in retry and backoff
evm.wallet — managed wallet with gas sponsorship
evm.contracts.PriceOracle — typed contract class generated from ABI JSON via compose codegen
collection — persistent document storage for price history
The compose.yaml uses a cron trigger to run every minute:
name: "bitcoin-oracle"
api_version: "stable"
tasks:
- path: "./src/tasks/bitcoin-oracle.ts"
name: "bitcoin_oracle"
triggers:
- type: "cron"
expression: "* * * * *"
retry_config:
max_attempts: 2
initial_interval_ms: 1000
backoff_factor: 1
Step 5: Run locally
goldsky compose start --fork-chains
--fork-chains allows you to run a smart wallet locally. You can also use a private key wallet for your local Compose app. See more here.
The task runs immediately and then every minute. You should see logs showing the fetched price and transaction hash.
Step 6: Deploy to Goldsky
Customization
Change the price source
Replace the CoinGecko URL with any API that returns a JSON price:
const response = await fetch<{ price: number }>(
"https://your-api.com/price",
{ max_attempts: 3, initial_interval_ms: 1000, backoff_factor: 2 }
);
Use your own contract
- Drop your contract’s ABI JSON into
src/contracts/MyContract.json
- Run
goldsky compose codegen to generate the typed class
- Use it in your task:
const myContract = new evm.contracts.MyContract(
"0xYOUR_CONTRACT_ADDRESS",
evm.chains.baseSepolia,
wallet
);
await myContract.yourMethod(arg1, arg2);
Change the cron schedule
triggers:
- type: "cron"
expression: "*/5 * * * *" # every 5 minutes
Resources