Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/api-reference/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ View transactions and accounts
| --------------- | ------------------------------------------------------------ |
| Name | `pod` |
| RPC | `https://rpc.podtestnet.dev` |
| Chain ID | `129301` |
| Chain ID | `1293` (`0x50d`) |
| Explorer | `https://explorer.pod.network` |
| Currency Symbol | `USD` |
| EVM Version | `Prague` (Ethereum block 22,431,084, Released May 7th, 2025) |
Expand Down
22 changes: 10 additions & 12 deletions doc/api-reference/applications-precompiles/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ You interact with Pod's precompiles the same way you would interact with any sma

### Reading State

Query the deposited balance of a token in the orderbook contract using `eth_call`.
Query an account's deposited balance of a token in the orderbook contract using `eth_call`.

{% tabs %}
{% tab title="JavaScript (ethers.js)" %}
Expand All @@ -26,12 +26,11 @@ import { ethers } from "ethers";
const provider = new ethers.JsonRpcProvider("https://rpc.podtestnet.dev");

const ORDERBOOK = "0x50d0000000000000000000000000000000000002";
const abi = ["function balanceOf(address token, address account) view returns (uint256)"];
const abi = ["function balanceOf(address token, address account) view returns (int256)"];
const orderbook = new ethers.Contract(ORDERBOOK, abi, provider);

const USDT = "0x0000000000000000000000000000000000000001";
const ACCOUNT = "0xYourAddress";
const balance = await orderbook.balanceOf(USDT, ACCOUNT);
const NVDAX = "0x0000000000000000000000000000000000000001"; // NVDAx (Tokenized NVIDIA)
const balance = await orderbook.balanceOf(NVDAX, "0xYourAddress");
console.log("Balance:", balance.toString());
```
{% endtab %}
Expand All @@ -46,14 +45,13 @@ ORDERBOOK = "0x50d0000000000000000000000000000000000002"
abi = [{"inputs": [{"name": "token", "type": "address"},
{"name": "account", "type": "address"}],
"name": "balanceOf",
"outputs": [{"name": "", "type": "uint256"}],
"outputs": [{"name": "", "type": "int256"}],
"stateMutability": "view", "type": "function"}]

orderbook = w3.eth.contract(address=ORDERBOOK, abi=abi)

USDT = "0x0000000000000000000000000000000000000001"
ACCOUNT = "0xYourAddress"
balance = orderbook.functions.balanceOf(USDT, ACCOUNT).call()
NVDAX = "0x0000000000000000000000000000000000000001" # NVDAx (Tokenized NVIDIA)
balance = orderbook.functions.balanceOf(NVDAX, "0xYourAddress").call()
print("Balance:", balance)
```
{% endtab %}
Expand All @@ -65,7 +63,7 @@ use alloy::{providers::ProviderBuilder, sol};
sol! {
#[sol(rpc)]
contract Orderbook {
function balanceOf(address token, address account) public view returns (uint256);
function balanceOf(address token, address account) public view returns (int256);
}
}

Expand All @@ -79,9 +77,9 @@ async fn main() -> eyre::Result<()> {
&provider,
);

let usdt: Address = "0x0000000000000000000000000000000000000001".parse()?;
let nvdax: Address = "0x0000000000000000000000000000000000000001".parse()?; // NVDAx (Tokenized NVIDIA)
let account: Address = "0xYourAddress".parse()?;
let balance = orderbook.balanceOf(usdt, account).call().await?;
let balance = orderbook.balanceOf(nvdax, account).call().await?;
println!("Balance: {}", balance._0);

Ok(())
Expand Down
6 changes: 4 additions & 2 deletions doc/api-reference/applications-precompiles/orderbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ where `signer` is the order owner, `nonce` is the `submitOrder` transaction's no
deadline = ceil((now + LAG) / auction_interval) * auction_interval
```

`LAG` is the headroom you add to `now` so the intent reaches enough validators before its target batch. It is capped at **10 minutes**; aim for **at least 1 minute** under normal conditions, smaller when you want to target a specific upcoming batch.
`LAG` is the headroom you add to `now` so the intent reaches enough validators before its target batch. Aim for **at least 1 minute** under normal conditions, smaller when you want to target a specific upcoming batch.

The alignment rule applies to **every deadline-bearing call** on this precompile — `deposit` and `withdraw` as much as orders, cancels, updates and triggers. All of them pass through the same validator check, so an unaligned deposit deadline is rejected just like an unaligned order deadline.

Expand Down Expand Up @@ -255,7 +255,9 @@ contract Orderbook {
* `ob_getOrders`. This is NOT the `submitOrder` tx hash.
* @param newSize The new size for the order.
* @param newPrice The new price for the order.
* @param token The token used to cover any additional collateral required by the update.
* @param token Currently unused — the engine ignores this parameter. Spot updates lock any
* additional collateral in the resting order's own token (base for sells, quote for
* buys); perp updates draw on native cross-margin.
* @param deadline The Unix timestamp after which this update is invalid in microseconds. Must be a multiple of the market's `auction_interval`.
*/
function update(
Expand Down
14 changes: 11 additions & 3 deletions doc/api-reference/guides/bridge-from-pod.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ const withdrawTx = await podBridge.withdraw(POD_TOKEN, amount, ethRecipient, ETH
const receipt = await withdrawTx.wait();

// 2. Get claim proof
// The response is { proof, committee_epoch, aux_tx_suffix }; the byte fields
// (`proof`, `aux_tx_suffix`) arrive as JSON arrays of integers, not hex strings.
const claimProof = await podProvider.send("pod_getBridgeClaimProof", [receipt.hash]);

// 3. Claim on Ethereum
Expand All @@ -70,7 +72,9 @@ const ethBridge = new ethers.Contract(
ethWallet
);
const claimTx = await ethBridge.claim(
ETH_TOKEN, amount, ethRecipient, claimProof.proof, claimProof.auxTxSuffix
ETH_TOKEN, amount, ethRecipient,
Uint8Array.from(claimProof.proof),
Uint8Array.from(claimProof.aux_tx_suffix)
);
await claimTx.wait();
```
Expand Down Expand Up @@ -136,12 +140,16 @@ let withdraw_receipt = pod_bridge
.get_receipt().await?;

// 2. Get claim proof
// The response is { proof, committee_epoch, aux_tx_suffix }; the byte fields
// (`proof`, `aux_tx_suffix`) arrive as JSON arrays of integers, not hex strings.
let claim_proof: serde_json::Value = pod_provider
.raw_request(
"pod_getBridgeClaimProof".into(),
vec![withdraw_receipt.transaction_hash],
)
.await?;
let proof: Vec<u8> = serde_json::from_value(claim_proof["proof"].clone())?;
let aux_tx_suffix: Vec<u8> = serde_json::from_value(claim_proof["aux_tx_suffix"].clone())?;

// 3. Claim on Ethereum
let eth_bridge = EthBridge::new(
Expand All @@ -153,8 +161,8 @@ eth_bridge
eth_token,
amount,
eth_recipient,
claim_proof["proof"].as_str().unwrap().parse()?,
claim_proof["auxTxSuffix"].as_str().unwrap().parse()?,
proof.into(),
aux_tx_suffix.into(),
)
.send().await?
.watch().await?;
Expand Down
10 changes: 6 additions & 4 deletions doc/api-reference/guides/bridge-to-pod.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ const provider = new ethers.JsonRpcProvider("https://eth.llamarpc.com");
const wallet = new ethers.Wallet(PRIVATE_KEY, provider);

const BRIDGE = "ETHEREUM_BRIDGE_ADDRESS";
const TOKEN = "TOKEN_ADDRESS"; // use 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE for native token
const TOKEN = "TOKEN_ADDRESS"; // must be an ERC-20 whitelisted on the bridge (there is no native-token deposit path)
const amount = ethers.parseUnits("100", 6);
const podRecipient = wallet.address;

// Sign an EIP-2612 permit for gasless approval.
// Sign an EIP-2612 permit for gasless approval. The bridge expects the permit
// as exactly 97 tightly-packed bytes: deadline(32) || v(1) || r(32) || s(32).
// If the token does not support permit, set permit to "0x" and
// send a separate approval transaction:
// const token = new ethers.Contract(TOKEN, ["function approve(address,uint256)"], wallet);
Expand Down Expand Up @@ -65,11 +66,12 @@ let provider = ProviderBuilder::new()
.on_http("https://eth.llamarpc.com".parse()?);

let bridge_address = "ETHEREUM_BRIDGE_ADDRESS".parse()?;
let token_address = "TOKEN_ADDRESS".parse()?; // use 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE for native token
let token_address = "TOKEN_ADDRESS".parse()?; // must be an ERC-20 whitelisted on the bridge (there is no native-token deposit path)
let amount = U256::from(100_000_000u64); // e.g. 100 USDC
let pod_recipient = signer.address();

// Sign an EIP-2612 permit for gasless approval.
// Sign an EIP-2612 permit for gasless approval. The bridge expects the permit
// as exactly 97 tightly-packed bytes: deadline(32) || v(1) || r(32) || s(32).
// If the token does not support permit, set permit to empty bytes and
// send a separate approval transaction first.
let permit = vec![];
Expand Down
2 changes: 1 addition & 1 deletion doc/api-reference/guides/place-a-perpetual-order.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This guide walks through opening a leveraged perpetual position on one of Pod's perp markets. For background, see [Perpetuals](https://docs.v2.pod.network/documentation/markets/perpetuals) and [Market Configurations](../market-configurations.md) for the live perp market list.

Perpetual markets are quoted in **USD** and use cross-margin: a single USD deposit serves as collateral for all open perp positions on the account. `size` is the order quantity in **base-asset units** and is signed — positive opens a long, negative opens a short. Margin is computed by the market from `|size| × price / maxLeverage`.
Perpetual markets are quoted in **USD** and use cross-margin: a single USD deposit serves as collateral for all open perp positions on the account. `size` is the order quantity in **base-asset units** and is signed — positive opens a long, negative opens a short. Margin is computed by the market from `|size| × markPrice / maxLeverage` (the mark price, not the order's limit price), adjusted by the mark-to-limit difference.

See the [Orderbook precompile reference](../applications-precompiles/orderbook.md) for the timestamp unit, deadline-alignment, and TTL rules that apply to every call below.

Expand Down
17 changes: 11 additions & 6 deletions doc/api-reference/guides/read-market-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const markets = await provider.send("ob_getMarkets", []);

```javascript
const orderbookId = "0x0000000000000000000000000000000000000000000000000000000000000001"; // NVDAx-USD spot
const depth = 20; // price levels per side
const depth = 10; // price levels per side (the served snapshot retains at most 10)

const snapshot = await provider.send("ob_getOrderbook", [orderbookId, depth]);
// Returns: { buys: { price: { volume } }, sells: { ... }, timestamp }
Expand All @@ -24,9 +24,12 @@ const snapshot = await provider.send("ob_getOrderbook", [orderbookId, depth]);
```javascript
const candles = await provider.send("ob_getCandles", [
orderbookId,
startTimestamp, // microseconds
endTimestamp, // microseconds
interval, // candle interval
{
resolution: "1m", // "1m" | "5m" | "15m" | "30m" | "1h" | "4h" | "1d" | "1w" | "1M"
from_ts: startTimestamp, // microseconds
to_ts: endTimestamp, // microseconds, optional
limit: 100, // optional
},
]);
```

Expand All @@ -35,9 +38,11 @@ const candles = await provider.send("ob_getCandles", [
```javascript
const orders = await provider.send("ob_getOrders", [
walletAddress,
{ clob_ids: [orderbookId] },
{ orderbook_id: orderbookId },
]);
// Returns: [{ hash, side, status, price, remainingBase, filledBase, filledQuote, ... }]
// Returns: { orders: [{ order_id, tx_hash, side, status, price, initial_size,
// filled_base_amount, filled_quote_amount, ... }],
// total_count, next_cursor }
```

## Get positions
Expand Down
6 changes: 4 additions & 2 deletions doc/api-reference/guides/recover-locked-account.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ const abi = ["function recover(bytes32 txHash, uint64 nonce) public"];
const recovery = new ethers.Contract(RECOVERY, abi, wallet);

// 1. Get the recovery target for the locked account
const { txHash: targetTxHash, nonce } = await provider.send("pod_getRecoveryTargetTx", [wallet.address]);
// (the response is `{ hash, nonce }`; it is `null` if the account is not locked)
const { hash: targetTxHash, nonce } = await provider.send("pod_getRecoveryTargetTx", [wallet.address]);

// 2. Call the recovery precompile
const tx = await recovery.recover(targetTxHash, nonce);
Expand Down Expand Up @@ -54,13 +55,14 @@ let recovery = Recovery::new(
);

// 1. Get the recovery target for the locked account
// (the response is `{ hash, nonce }`; it is `null` if the account is not locked)
let target: TargetTx = provider
.raw_request("pod_getRecoveryTargetTx".into(), vec![account_address])
.await?;

// 2. Call the recovery precompile
let receipt = recovery
.recover(target.tx_hash, target.nonce)
.recover(target.hash, target.nonce)
.send()
.await?
.watch()
Expand Down
4 changes: 2 additions & 2 deletions doc/api-reference/json-rpc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ Pod is not a blockchain and has no blocks. Most `eth_` methods work as expected,

| RPC Method | Ethereum | Pod |
| ------------------------- | ------------------------------------ | ------------------------------------------------------------ |
| **eth\_blockNumber** | Returns the most recent block number | Returns the latest past perfection timestamp in microseconds |
| **eth\_blockNumber** | Returns the most recent block number | Returns the node's current wall-clock time as a Unix timestamp in seconds |
| **eth\_getBlockByHash** | Returns block information by hash | Returns an empty block structure |
| **eth\_getBlockByNumber** | Returns block information by number | Returns an empty block structure |

**Timestamps are in microseconds.** Pod uses microsecond-precision Unix timestamps wherever Ethereum uses block numbers - including `eth_blockNumber`, transaction deadlines, and TTLs.
**Timestamps are in microseconds.** Pod uses microsecond-precision Unix timestamps for transaction deadlines and TTLs. Block-number-shaped values are different: `eth_blockNumber` and the `blockNumber` in receipts and block responses carry Unix timestamps in whole **seconds**.

**Block-related fields are zeroed.** Since Pod has no blocks, EVM opcodes that reference block properties (`block.number`, `block.coinbase`, `block.difficulty`, `block.basefee`) return 0. `block.timestamp` returns the local validator's timestamp at execution time.

Expand Down
Loading
Loading