How JOULE moves
Five contracts and one gateway. Everything an app or an autonomous agent needs is a public function call or an HTTP request, with no account and no off-chain approval. Machine-readable copies live at /agents.md and /llms.txt.
Lifecycle
- 01VOLTStakeVOLT holders lock into VoltStaking. Longer locks carry more weight.
- 02mintEarn JOULEEach finalized hour, JOULE is minted to stakers pro rata to weight.
- 03GridTradeHolders list JOULE on the Grid at 0 to 90% off, or trade it on a DEX.
- 04burnActivateBuyers burn JOULE into a non-transferable balance keyed to an address.
- 05APICall modelsThe gateway reads that balance and meters every request at list price.
Pricing rule
91 levels, 0 to 90% off. Buys walk from the deepest level down; within a level, orders fill first-in, first-out. A buy touches at most 64 orders.
Fees
A protocol fee is added on top of what sellers receive on buy, and part of it can go to a referrer. buyAndActivate skips it; only the JOULE activation fee applies.
Sellers are paid by pull
Fills accrue to proceedsOf. Claim USDG, or swap to VOLT in the same call with a minimum-out guard.
Integrate in four calls
Fund, activate, derive a key, call a model. An agent with a wallet can do all of it without a human.
1 · Buy on-chain (Solidity)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
interface IERC20 { function approve(address, uint256) external returns (bool); }
interface IVoltExchange {
struct Quote { uint256 creditOut; uint256 usdgSpent; uint256 feeAtoms; uint256 fills; uint8 reason; }
function getQuoteForCredit(uint256 creditOut, uint256 maxFills) external view returns (Quote memory);
function getActivationQuote(uint256 usdgIn, uint256 maxFills)
external view returns (Quote memory q, uint256 creditedAtoms, uint256 activationFeeAtoms);
function buy(uint256 usdgIn, uint256 minCreditOut, address recipient, uint256 maxFills)
external returns (uint256 creditOut, uint256 usdgSpent);
function buyAndActivate(uint256 usdgIn, uint256 minCreditOut, bytes32 beneficiary, uint256 maxFills)
external returns (uint256 creditOut, uint256 usdgSpent, uint256 activationId);
}
contract AgentTopUp {
IVoltExchange public immutable grid;
IERC20 public immutable usdg;
constructor(IVoltExchange grid_, IERC20 usdg_) { grid = grid_; usdg = usdg_; }
/// Buy `credit` JOULE (6 decimals) into this contract.
/// Pass `minOut` from an off-chain quote: a same-tx quote gives no slippage protection.
function topUp(uint256 credit, uint256 minOut) external {
IVoltExchange.Quote memory q = grid.getQuoteForCredit(credit, 64);
uint256 budget = q.usdgSpent + q.feeAtoms; // fee is added on top of seller price
usdg.approve(address(grid), budget);
grid.buy(budget, minOut, address(this), 64);
}
/// Spend `usdgIn` and credit `agent`'s API balance directly (no fee on this path).
function fundAgent(uint256 usdgIn, uint256 minOut, address agent) external {
usdg.approve(address(grid), usdgIn);
grid.buyAndActivate(usdgIn, minOut, bytes32(uint256(uint160(agent))), 64);
}
}1b · Or from TypeScript
import { createWalletClient, createPublicClient, http, pad } from "viem";
import { VoltExchangeAbi } from "./abi/VoltExchange";
const credit = 25_000_000n; // $25 of inference (6 decimals)
const q = await pub.readContract({
address: EXCHANGE, abi: VoltExchangeAbi,
functionName: "getQuoteForCredit", args: [credit, 64n],
});
const budget = q.usdgSpent + q.feeAtoms;
// approve USDG for `budget`, then:
await wallet.writeContract({
address: EXCHANGE, abi: VoltExchangeAbi, functionName: "buy",
args: [budget, (q.creditOut * 995n) / 1000n, account.address, 64n],
});2 · Derive the API key
import { privateKeyToAccount } from "viem/accounts";
// The agent's own wallet. Load it from your secret store, never from source.
const account = privateKeyToAccount(process.env.AGENT_PRIVATE_KEY as `0x${string}`);
const epoch = 1; // bump to rotate: the first use of a higher epoch revokes older keys
const message = `Voltio API key · chain 4663 · epoch ${epoch}`;
const signature = await account.signMessage({ message });
// sk-volt-<epoch>-<base64(signature bytes)>
const apiKey = `sk-volt-${epoch}-${Buffer.from(signature.slice(2), "hex").toString("base64")}`;3 · Call a model
curl https://voltiodotso.app/api/v1/chat/completions \
-H "Authorization: Bearer $VOLTIO_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic/claude-sonnet-5",
"messages": [{"role": "user", "content": "Plan my next three tool calls."}],
"stream": false
}'4 · Check the balance
curl https://voltiodotso.app/api/v1/key \
-H "Authorization: Bearer $VOLTIO_KEY"
# {
# "object": "key",
# "address": "0xAgent…",
# "balance": { "currency": "USD", "available": "41.27", "used": "8.73", "credited": "50.00" },
# "rate_limit": { "requests_per_minute": 120, "concurrent": 8 }
# }Voltio API key · chain 4663 · epoch 1 (with your epoch). Beneficiaries are 32-byte left-padded addresses: pad(address, { size: 32 }).Contracts on Robinhood Chain
Not deployed on this network yet. Addresses will appear here at launch.
| Contract | Role | Address |
|---|---|---|
| JOULE | Credit token (ERC-20, 6 dp) · activation | not deployed |
| VoltExchange | The Grid · order book, quotes, affiliates | not deployed |
| VoltStaking | VOLT staking, hourly JOULE distribution | not deployed |
| VoltPayout | USDG → VOLT payout route | not deployed |
| VOLT | Staking token (ERC-20, 18 dp) | launching on pons |
| USDG | Payment stablecoin (6 dp) | 0x5fc5360d0400a0fd4f2af552add042d716f1d168 ↗ |
- Network
- Robinhood Chain
- Chain ID
- 4663
- RPC
- https://rpc.mainnet.chain.robinhood.com
- Explorer
- Blockscout ↗
- Deploy block
- 0
- Gateway base URL
- /api/v1