DomFiDomination Finance

Quick Start

From zero to your first DomFi contract interaction in under 10 minutes. Read prices, open trades, and deposit into the vault.

Prerequisites

  • JavaScript path: Node.js 20+ (required by @domfi/sdk; any runtime with a global fetch works)
  • Solidity / CLI path: Foundry (includes forge, cast, and anvil)
  • A wallet with ETH on Base for gas
  • USDC on Base for collateral and oracle fees
  • An RPC endpoint for Base: https://mainnet.base.org for public access, or Alchemy/Infura for production

For most integrations, the published @domfi/sdk is the fastest path. It gives you one typed client for REST reads, viem-backed RPC reads, wallet-signed trading, token approvals, position valuation, and vault flows — with mainnet/testnet contract addresses and API roots resolved for you. Calling the contracts directly (the rest of this page) remains the lower-level alternative for Foundry/Solidity users or teams that prefer raw ABIs.

Install the SDK

npm install @domfi/sdk viem
pnpm add @domfi/sdk viem
yarn add @domfi/sdk viem
bun add @domfi/sdk viem

viem is a peer dependency your application supplies. Requirements: Node.js >= 20 (or any runtime with global fetch), viem >= 2.21, TypeScript 5.6+ recommended. The package ships dual ESM + CJS builds with types, is side-effect-free / tree-shakeable, and runs in Node and modern browsers. Wallets stay application-owned — the SDK never holds private keys or relays transactions.

Read-only quick start

REST reads need no wallet or RPC. The built-in testnet / mainnet chain profiles ship default REST API roots (https://testnet-api.domination.finance/api/v2 and https://api.domination.finance/api/v2) and resolved contract addresses, so no address wiring is needed:

import { createDomfiClient } from "@domfi/sdk";

const domfi = createDomfiClient({ chain: "testnet" });

const status = await domfi.system.apiStatus();
const markets = await domfi.api.markets.list();
const headPrices = await domfi.api.prices.head();

Write-enabled mode

Both the REST and RPC layers are optional. Add viem publicClient / walletClient / account to enable RPC reads and wallet-signed writes:

import { createDomfiClient } from "@domfi/sdk";
import { createPublicClient, createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { base } from "viem/chains";

const account = privateKeyToAccount("0xYOUR_PRIVATE_KEY");
const publicClient = createPublicClient({ chain: base, transport: http("https://mainnet.base.org") });
const walletClient = createWalletClient({ account, chain: base, transport: http("https://mainnet.base.org") });

const domfi = createDomfiClient({
  chain: "mainnet",
  publicClient,
  walletClient,
  account: account.address,
});

See the npm README for the full write-enabled example (open a trade, approve tokens, deposit into the vault).

What's in the client

NamespacePurpose
domfi.apiTyped REST reads (markets, prices, positions, history)
domfi.actionsHigh-level trade & vault actions
domfi.tradingPrepare → simulate → send → track a trade
domfi.tokenToken approvals
domfi.valuationPnL / liquidation / watch
domfi.vaultLP deposit & withdrawal flows
domfi.delegationEIP-712 delegated trading
domfi.referralsReferral program
domfi.pointsPoints program
domfi.chainResolved addresses / config
@domfi/sdk/mathPure bigint protocol math

Full API documentation lives on the npm page.

Install

npm install viem
npm install ethers
curl -L https://foundry.paradigm.xyz | bash
foundryup

Includes forge (build/test/script), cast (CLI calls), and anvil (local node). No Node.js required.

Connect to the Protocol

Start from the deployed Base mainnet addresses below. DomfiRegistry is the discovery entry point, but most integrations pin the current proxy addresses directly.

ContractAddressUse For
DomfiRegistry0xe438360464EaDa40b7921C993322bD4dA8881103Discover all other contracts
DomfiTrading0x7447cb5350a096364A13bEAf77916dfB35db9445Open and close trades
DomfiOracle0x1aB9c3A2E1A09f2D06bf4A75d1721c7e113B8D4DRead dominance prices
DomfiVault0xA194082Aabb75Dd1Ca9Dc1BA573A5528BeB8c2FbLP deposits and withdrawals
DomfiTradingStorage0x608ff95777F419040a3b1E42ed73dD3EFf42Cc24Query open positions
import { createPublicClient, http } from 'viem';
import { base } from 'viem/chains';

const client = createPublicClient({
  chain: base,
  transport: http('https://mainnet.base.org'),
});

Or verify your connection with cast (included with Foundry):

cast chain-id --rpc-url https://mainnet.base.org
# Expected: 8453

Use minimal ABI fragments for the specific calls you make. For full verified ABIs, use BaseScan:

Read a Dominance Price

const price = await client.readContract({
  address: '0x1aB9c3A2E1A09f2D06bf4A75d1721c7e113B8D4D',
  abi: [{
    name: 'getPrice',
    type: 'function',
    stateMutability: 'view',
    inputs: [{ name: 'pairIndex', type: 'uint16' }],
    outputs: [{ name: '', type: 'uint256' }],
  }],
  functionName: 'getPrice',
  args: [0], // 0 = BTCDOM
});

console.log('BTCDOM price:', Number(price) / 1e18);
// Expected: ~52.5 (Bitcoin dominance percentage)
import { Contract, JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://mainnet.base.org');
const oracle = new Contract(
  '0x1aB9c3A2E1A09f2D06bf4A75d1721c7e113B8D4D',
  [{
    name: 'getPrice',
    type: 'function',
    stateMutability: 'view',
    inputs: [{ name: 'pairIndex', type: 'uint16' }],
    outputs: [{ name: '', type: 'uint256' }],
  }],
  provider,
);

const price = await oracle.getPrice(0);

console.log('BTCDOM price:', Number(price) / 1e18);
// Expected: ~52.5 (Bitcoin dominance percentage)
// ReadPrice.s.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Script, console} from "forge-std/Script.sol";

interface IDomfiOracle {
    function getPrice(uint16 pairIndex) external view returns (uint256);
}

contract ReadPrice is Script {
    function run() external view {
        IDomfiOracle oracle = IDomfiOracle(
            0x1aB9c3A2E1A09f2D06bf4A75d1721c7e113B8D4D
        );

        uint256 price = oracle.getPrice(0); // 0 = BTCDOM
        console.log("BTCDOM price (18 decimals):", price);
        // Expected: ~52500000000000000000 (52.5%)
    }
}
forge script ReadPrice.s.sol --rpc-url https://mainnet.base.org

No setup required — read a price in one command:

cast call 0x1aB9c3A2E1A09f2D06bf4A75d1721c7e113B8D4D \
  "getPrice(uint16)(uint256)" \
  0 \
  --rpc-url https://mainnet.base.org
# Returns: 52500000000000000000 (18 decimals → 52.5% BTCDOM)
IndexPair
0BTCDOM
1ETHDOM
2USDTDOM
3BNBDOM
4SOLDOM

Open a Trade (Write)

openTrade() submits a real on-chain order. Approve USDC to DomfiTradingStorage, not DomfiTrading, then wait for the approval receipt before you submit the trade. Both steps spend real gas. The oracle fee is charged in USDC and pulled together with your collateral, so approve collateral + oracle fee — no ETH msg.value is needed.

Before you send the transaction:

  • USDC must be approved to DomfiTradingStorage, not DomfiTrading. This is the most common integration mistake.
  • openTrade() charges a flat 0.10 USDC oracle fee, pulled together with your collateral. Read the per-pair fee from DomfiPairsStorage.pairOracleFee(pairIndex) and approve collateral + oracleFee — no ETH msg.value is required.
  • Collateral uses 6 decimals because USDC on Base is 6-decimal native USDC.
  • Leverage uses 2-decimal precision: 100 = 1x, 1000 = 10x, 5000 = 50x, 25000 = 250x.
  • Execution is asynchronous. openTrade() queues the order, then an off-chain bot fulfills the price.
import { createPublicClient, createWalletClient, http, parseUnits } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { base } from 'viem/chains';

const USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
const TRADING_ADDRESS = '0x7447cb5350a096364A13bEAf77916dfB35db9445';
const TRADING_STORAGE_ADDRESS = '0x608ff95777F419040a3b1E42ed73dD3EFf42Cc24';
const PAIRS_STORAGE_ADDRESS = '0x444079DDCaFd4feE3812E2fF79c5F74a1F4f9Be1';

const erc20Abi = [{
  name: 'approve',
  type: 'function',
  stateMutability: 'nonpayable',
  inputs: [
    { name: 'spender', type: 'address' },
    { name: 'amount', type: 'uint256' },
  ],
  outputs: [{ name: '', type: 'bool' }],
}] as const;

const pairsStorageAbi = [{
  name: 'pairOracleFee',
  type: 'function',
  stateMutability: 'view',
  inputs: [{ name: 'pairIndex', type: 'uint16' }],
  outputs: [{ name: '', type: 'uint256' }],
}] as const;

const tradingAbi = [{
  name: 'openTrade',
  type: 'function',
  stateMutability: 'nonpayable',
  inputs: [
    {
      name: 't',
      type: 'tuple',
      components: [
        { name: 'collateral', type: 'uint256' },
        { name: 'openPrice', type: 'uint192' },
        { name: 'tp', type: 'uint192' },
        { name: 'sl', type: 'uint192' },
        { name: 'trader', type: 'address' },
        { name: 'leverage', type: 'uint32' },
        { name: 'pairIndex', type: 'uint16' },
        { name: 'index', type: 'uint8' },
        { name: 'buy', type: 'bool' },
      ],
    },
    { name: 'orderType', type: 'uint8' },
    { name: 'slippageP', type: 'uint256' },
  ],
  outputs: [],
}] as const;

const account = privateKeyToAccount('0xYOUR_PRIVATE_KEY');
const publicClient = createPublicClient({
  chain: base,
  transport: http('https://mainnet.base.org'),
});
const walletClient = createWalletClient({
  account,
  chain: base,
  transport: http('https://mainnet.base.org'),
});

const collateral = parseUnits('100', 6);

// Step 1: Read the per-pair oracle fee (USDC, 6 decimals)
const oracleFee = await publicClient.readContract({
  address: PAIRS_STORAGE_ADDRESS,
  abi: pairsStorageAbi,
  functionName: 'pairOracleFee',
  args: [0], // 0 = BTCDOM
});

// Step 2: Approve collateral + oracle fee to TradingStorage (not Trading)
const approvalHash = await walletClient.writeContract({
  address: USDC_ADDRESS,
  abi: erc20Abi,
  functionName: 'approve',
  args: [TRADING_STORAGE_ADDRESS, collateral + oracleFee],
});

await publicClient.waitForTransactionReceipt({ hash: approvalHash });

// Step 3: Submit a 10x long on BTCDOM — the oracle fee is pulled from the USDC approval
await walletClient.writeContract({
  address: TRADING_ADDRESS,
  abi: tradingAbi,
  functionName: 'openTrade',
  args: [
    {
      trader: account.address,
      pairIndex: 0,
      index: 0,
      collateral,
      openPrice: 0n,
      buy: true,
      leverage: 1000,
      tp: 0n,
      sl: 0n,
    },
    0,
    50,
  ],
});
// OpenTrade.s.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Script} from "forge-std/Script.sol";

interface IERC20 {
    function approve(address spender, uint256 amount) external returns (bool);
}

interface IDomfiTrading {
    struct Trade {
        uint256 collateral;
        uint192 openPrice;
        uint192 tp;
        uint192 sl;
        address trader;
        uint32 leverage;
        uint16 pairIndex;
        uint8 index;
        bool buy;
    }
    function openTrade(Trade calldata t, uint8 orderType, uint256 slippageP) external;
}

interface IDomfiPairsStorage {
    function pairOracleFee(uint16 pairIndex) external view returns (uint256);
}

contract OpenTrade is Script {
    address constant USDC = 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913;
    address constant TRADING = 0x7447cb5350a096364A13bEAf77916dfB35db9445;
    address constant TRADING_STORAGE = 0x608ff95777F419040a3b1E42ed73dD3EFf42Cc24;
    address constant PAIRS_STORAGE = 0x444079DDCaFd4feE3812E2fF79c5F74a1F4f9Be1;

    function run() external {
        vm.startBroadcast();

        // Step 1: Read the per-pair oracle fee (USDC, 6 decimals)
        uint256 oracleFee = IDomfiPairsStorage(PAIRS_STORAGE).pairOracleFee(0); // 0 = BTCDOM

        // Step 2: Approve collateral + oracle fee to TradingStorage (not Trading)
        IERC20(USDC).approve(TRADING_STORAGE, 100e6 + oracleFee); // 100 USDC + oracle fee

        // Step 3: Submit a 10x long on BTCDOM (oracle fee taken from the USDC approval)
        IDomfiTrading(TRADING).openTrade(
            IDomfiTrading.Trade({
                collateral: 100e6,   // 100 USDC (6 decimals)
                openPrice: 0,        // market order — filled by oracle
                tp: 0,               // no take profit
                sl: 0,               // no stop loss
                trader: msg.sender,
                leverage: 1000,      // 10x (2 decimal precision)
                pairIndex: 0,        // BTCDOM
                index: 0,            // first trade slot
                buy: true            // long
            }),
            0,  // 0 = market order
            50  // 0.5% slippage (2 decimals)
        );

        vm.stopBroadcast();
    }
}
forge script OpenTrade.s.sol \
  --rpc-url https://mainnet.base.org \
  --private-key 0xYOUR_PRIVATE_KEY \
  --broadcast

The order is not opened immediately. Wait for the price bot to fulfill it, then read the resulting position from DomfiTradingStorage or watch the MarketOpenExecuted event.

Decimal Reference

ValuePrecisionExample
USDC amounts (collateral)6 decimals$100 = 100000000
Prices (dominance)18 decimals52.5% = 52500000000000000000
Leverage2 decimals10x = 1000, 250x = 25000
Slippage2 decimals0.5% = 50
Fee rates2 decimals0.1% = 10