> For the complete documentation index, see [llms.txt](https://docs.gensyn.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.gensyn.ai/tech/delphi-sdk/methods.md).

# On-Chain Methods

## Overview

On-chain methods send transactions or read directly from the Gensyn blockchain via a Gateway contract.

### Market Deployments & Routing

Testnet and mainnet have automated-settlement and legacy deployments. Each market belongs to one of those deployments.

The client checks both factories with `marketProxiesExist`. It caches the result, then routes market-scoped calls to the owning gateway. This applies to buys, sells, quotes, redemptions, and liquidations.

Use `resolveGateway()` before making direct Gateway calls:

```typescript
const gateway = await client.resolveGateway(marketAddress);
```

Setting `gatewayAddress` or `DELPHI_GATEWAY_CONTRACT` pins every call. Pinning disables automatic routing. If you incorrectly pin a gateway, you can revert with `MarketProxyNotDeployedByFactory`.

### Contract Addresses

You can find Testnet and mainnet RPC endpoints, contract addresses, and more by visiting [Network Information](https://docs.gensyn.network/network-information) on the Gensyn Foundation docs.

### Trading

#### Pricing Mechanism (DPM)

Delphi app markets on testnet and mainnet use *Dynamic Parimutuel (DPM)* markets. This means:

* Prices shift continuously with every trade (no fixed order book)
* `spotImpliedProbability` reflects the market's current consensus probability
* For a binary market: `prob[0] + prob[1] = 1e18` (100%)
* A spot price of 0.65 collateral tokens per share means \~65% implied probability

{% hint style="info" %}
`competition-testnet` is separate from the Delphi app deployments. Its agent-competition markets use LMSR contracts. The SDK exposes the same market-scoped trading interfaces across both mechanisms.
{% endhint %}

### Quoting Trades

These are read-only and therefore do not consume any *gas.*

#### quoteBuy

The cost to receive an exact number of shares.

```typescript
const { tokensIn } = await client.quoteBuy({
  marketAddress: "0x..." as `0x${string}`,
  outcomeIdx: 0,
  sharesOut: BigInt(Math.round(10 * 1e18)),
});
const costUsdc = Number(tokensIn) / 1e6;
```

#### quoteSell

The payout for selling an exact number of shares.

```typescript
const { tokensOut } = await client.quoteSell({
  marketAddress: "0x..." as `0x${string}`,
  outcomeIdx: 0,
  sharesIn: BigInt(Math.round(5 * 1e18)),
});
const payoutUsdc = Number(tokensOut) / 1e6;
```

#### quoteRedeem and quoteLiquidate

`quoteRedeem()` estimates redemption for a settled market. `quoteLiquidate()` estimates exit value for an expired or failed market.

Use `redeemMarket()` only after settlement. Use `liquidate()` for `expired` and `failed` markets.

### Buying Shares

Token approval must exist before buying. The full flow with approval and slippage looks like this:

```typescript
const marketAddress = "0x..." as `0x${string}`;
const outcomeIdx = 0;
const sharesOut = BigInt(Math.round(10 * 1e18));

// 1. Quote the cost
const { tokensIn } = await client.quoteBuy({ marketAddress, outcomeIdx, sharesOut });

// 2. Ensure collateral-token approval (no-op if already approved)
await client.ensureTokenApproval({ marketAddress, minimumAmount: tokensIn });

// 3. Execute with 2% slippage
const maxTokensIn = tokensIn * 102n / 100n;
const { transactionHash } = await client.buyShares({
  marketAddress,
  outcomeIdx,
  sharesOut,
  maxTokensIn,
});
```

#### BuyShares (*params*)

| Field           | Type                | Description                                          |
| --------------- | ------------------- | ---------------------------------------------------- |
| `marketAddress` | `` `0x${string}` `` | Market proxy address                                 |
| `outcomeIdx`    | `number`            | Outcome index to buy                                 |
| `sharesOut`     | `bigint`            | Exact shares to receive (18 decimals)                |
| `maxTokensIn`   | `bigint`            | Maximum collateral spend — slippage cap (6 decimals) |

### Selling Shares

```typescript
const sharesIn = BigInt(Math.round(5 * 1e18));
const { tokensOut } = await client.quoteSell({ marketAddress, outcomeIdx, sharesIn });

const minTokensOut = tokensOut * 98n / 100n; // 2% slippage
const { transactionHash } = await client.sellShares({
  marketAddress,
  outcomeIdx,
  sharesIn,
  minTokensOut,
});
```

#### SellShares (*params*)

| Field           | Type                | Description                                               |
| --------------- | ------------------- | --------------------------------------------------------- |
| `marketAddress` | `` `0x${string}` `` | Market proxy address                                      |
| `outcomeIdx`    | `number`            | Outcome index to sell                                     |
| `sharesIn`      | `bigint`            | Exact shares to sell (18 decimals)                        |
| `minTokensOut`  | `bigint`            | Minimum collateral received — slippage floor (6 decimals) |

### Liquidating Positions

Liquidation exits positions without a winning outcome. It applies to expired and failed markets.

```typescript
const status = await client.getMarketStatus(marketAddress);
if (status === "expired" || status === "failed") {
  const { transactionHash } = await client.liquidate({
    marketAddress,
    outcomeIndices: [0, 1], // Covers a binary market.
  });
}
```

#### liquidate(*params*)

| Field            | Type                | Description           |
| ---------------- | ------------------- | --------------------- |
| `marketAddress`  | `` `0x${string}` `` | Market proxy address  |
| `outcomeIndices` | `number[]`          | Outcomes to liquidate |

Pass every outcome index where you hold shares. Omitted indices are not liquidated, and their funds remain locked. `[0, 1]` covers a binary market.

#### getMarketStatus(marketAddress)

Reads a market's lifecycle status from the owning gateway. This value is fresher than the REST API.

```typescript
const status = await client.getMarketStatus(marketAddress);
// "open" | "awaiting_settlement" | "settled" | "expired" | "failed"
```

### Slippage Guidelines

| Scenario                 | Recommended slippage |
| ------------------------ | -------------------- |
| Quiet market             | 1–2%                 |
| Active market            | 2–5%                 |
| Large trade (>$100)      | 5–10%                |
| Time-sensitive execution | 5%                   |

Here's an example of using integer arithmetic to avoid floating point:

```typescript
const slippageBps = 200n; // 200 = 2%
const maxTokensIn  = tokensIn  * (10000n + slippageBps) / 10000n;
const minTokensOut = tokensOut * (10000n - slippageBps) / 10000n
```

#### Common Contract Errors

| Error                             | Cause                           | Fix                                               |
| --------------------------------- | ------------------------------- | ------------------------------------------------- |
| `TokensInExceedsMax`              | Actual cost > maxTokensIn       | Re-quote and increase slippage                    |
| `TokensOutBelowMin`               | Actual payout < minTokensOut    | Re-quote and increase slippage                    |
| `MarketNotOpen`                   | Market closed or settled        | Cannot trade; check status                        |
| `SharesInExceedSupply`            | Selling more than held          | Query balance first                               |
| `GrossTokensOutNotPositive`       | Nothing to sell                 | Position is empty                                 |
| `ZeroTokensIn`                    | sharesOut too small             | Use a larger share amount                         |
| `MarketProxyNotDeployedByFactory` | Gateway does not own the market | Resolve the gateway or remove the pinned override |

### Token Approvals

#### getTokenAllowance(*params*)

Read the current ERC-20 allowance your wallet has granted to a market.

```typescript
const { allowance } = await client.getTokenAllowance({
  marketAddress: "0xMarket",
});
```

#### approveToken(*params*)

Approve the ERC-20 token for spending by a market.

{% hint style="info" %}
This defaults to unlimited (`uint256` max).
{% endhint %}

```typescript
// Approve unlimited
await client.approveToken({ marketAddress });

// Approve exact amount (e.g. 100 tokens; $TEST on testnet)
await client.approveToken({ marketAddress, amount: 100_000_000n });
```

#### ensureTokenApproval(*params*)

Check if sufficient allowance exists and only sends an approval transaction if needed. *Recommended before buying shares.*

```typescript
const { approvalNeeded, allowance, transactionHash } = await client.ensureTokenApproval({
  marketAddress: "0xMarket",
  minimumAmount: tokensIn,
  approveAmount: tokensIn * 10n, // optional; defaults to unlimited
});
```

{% hint style="info" %}
The SDK uses the configured token address from `DELPHI_TOKEN_ADDRESS` or the network default. It does not resolve a token address per market.
{% endhint %}

| Network             | Collateral token |
| ------------------- | ---------------- |
| Testnet             | $TEST            |
| Mainnet             | USDC             |
| Competition testnet | TST              |

### Gateway Contract Reference

This section is for advanced users who want to call the Gateway contract directly via `viem`.

The SDK uses `DYNAMIC_PARIMUTUEL_GATEWAY_ABI` on every network, including `competition-testnet`. The competition LMSR gateway exposes identical call signatures.

Competition differs in its underlying pricing math. Its `MarketSettled` event also omits market-creator economics fields.

#### viem Client Setup

Set up your viem client like this:

```typescript
import { createPublicClient, http, defineChain, type Abi } from "viem";
import {
  DELPHI_FACTORY_ABI,
  DYNAMIC_PARIMUTUEL_GATEWAY_ABI,
} from "@gensyn-ai/gensyn-delphi-sdk";

const chain = defineChain({
  id: Number(process.env.GENSYN_CHAIN_ID),
  name: "Gensyn Testnet",
  nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
  rpcUrls: { default: { http: [process.env.GENSYN_RPC_URL!] } },
});

const publicClient = createPublicClient({ chain, transport: http(process.env.GENSYN_RPC_URL!) });
const gateway = await client.resolveGateway(marketProxy);
```

`resolveGateway()` creates a signer-backed client internally. Configure `WALLET_PRIVATE_KEY` or CDP credentials, even for read-only use.

{% hint style="warning" %}
Do not use `DELPHI_GATEWAY_CONTRACT` for direct market calls unless it is the owning gateway. `resolveGateway()` selects the correct deployment.
{% endhint %}

`DELPHI_FACTORY_ABI` is also exported for direct Factory reads, including `marketProxiesExist`.

#### Gateway Functions

All of the gateway functions, broken down into tables by **\[1]** read and **\[2]** write.

#### 1. *Read* Functions

| Function                   | Args                                      | Returns              | Notes                                                         |
| -------------------------- | ----------------------------------------- | -------------------- | ------------------------------------------------------------- |
| `quoteBuyExactOut`         | marketProxy, outcomeIdx, sharesOut        | `tokensIn: uint256`  | Collateral cost (6 dec)                                       |
| `quoteSellExactIn`         | marketProxy, outcomeIdx, sharesIn         | `tokensOut: uint256` | Collateral payout (6 dec)                                     |
| `spotImpliedProbability`   | marketProxy, outcomeIdx                   | `uint256`            | 1e18 = 100%                                                   |
| `spotImpliedProbabilities` | marketProxy, outcomeIndices\[]            | `uint256[]`          | Batch                                                         |
| `spotPrice`                | marketProxy, outcomeIdx                   | `uint256`            | 1e18 = 1.0 collateral tokens per share                        |
| `spotPrices`               | marketProxy, outcomeIndices\[]            | `uint256[]`          | Batch                                                         |
| `balanceOf`                | marketProxy, owner, outcomeIdx            | `uint256`            | Shares (18 dec)                                               |
| `batchBalanceOf`           | marketProxy, owners\[], outcomeIndices\[] | `uint256[]`          | Batch                                                         |
| `totalSupply`              | marketProxy, outcomeIdx                   | `uint256`            | Total shares (18 dec)                                         |
| `totalSupplies`            | marketProxy, outcomeIndices\[]            | `uint256[]`          | Batch                                                         |
| `getMarket`                | marketProxy                               | Market struct        | Full on-chain state                                           |
| `marketStatus`             | marketProxy                               | `uint8`              | 0=Open, 1=Awaiting settlement, 2=Settled, 3=Expired, 4=Failed |
| `token`                    | marketProxy                               | `address`            | Collateral token address                                      |

#### 2. *Write* Functions

Prefer the SDK methods over calling the Gateway directly, as they handle simulation, approval, and receipt waiting.

| Function      | Args                                            | Notes                             |
| ------------- | ----------------------------------------------- | --------------------------------- |
| `buyExactOut` | marketProxy, outcomeIdx, sharesOut, maxTokensIn | Use `DelphiClient.buyShares()`    |
| `sellExactIn` | marketProxy, outcomeIdx, sharesIn, minTokensOut | Use `DelphiClient.sellShares()`   |
| `redeem`      | marketProxy                                     | Use `DelphiClient.redeemMarket()` |
| `liquidate`   | marketProxy, outcomeIndices                     | Use `DelphiClient.liquidate()`    |

### Direct Read Examples

#### Implied Probability for All Outcomes

```typescript
const probs = await publicClient.readContract({
  address: gateway,
  abi: DYNAMIC_PARIMUTUEL_GATEWAY_ABI as Abi,
  functionName: "spotImpliedProbabilities",
  args: [marketProxy, [0n, 1n]],
}) as bigint[];
// probs[i] / 1e18 * 100 = implied probability %
```

#### Spot Prices

```typescript
const prices = await publicClient.readContract({
  address: gateway,
  abi: DYNAMIC_PARIMUTUEL_GATEWAY_ABI as Abi,
  functionName: "spotPrices",
  args: [marketProxy, [0n, 1n]],
}) as bigint[];
// prices[i] / 1e18 = collateral tokens per share
```

#### Full Market State

```typescript
const onchainMarket = await publicClient.readContract({
  address: gateway,
  abi: DYNAMIC_PARIMUTUEL_GATEWAY_ABI as Abi,
  functionName: "getMarket",
  args: [marketProxy],
}) as {
  config: {
    outcomeCount: bigint;
    k: bigint;
    tradingFee: bigint;         // 1e18 = 100%
    tradingDeadline: bigint;    // Unix timestamp
    settlementDeadline: bigint; // Unix timestamp
  };
  pool: bigint;
  tradingFees: bigint;
};

const feePercent = Number(onchainMarket.config.tradingFee) / 1e18 * 100;
const deadline = new Date(Number(onchainMarket.config.tradingDeadline) * 1000);
```

#### Share Balance for a Wallet

```typescript
const balance = await publicClient.readContract({
  address: gateway,
  abi: DYNAMIC_PARIMUTUEL_GATEWAY_ABI as Abi,
  functionName: "balanceOf",
  args: [marketProxy, walletAddress as `0x${string}`, BigInt(outcomeIdx)],
}) as bigint;
// balance / 1e18 = shares
```

#### Price Impact Estimation

```typescript
const spotPrices = await publicClient.readContract({
  address: gateway,
  abi: DYNAMIC_PARIMUTUEL_GATEWAY_ABI as Abi,
  functionName: "spotPrices",
  args: [marketProxy, [0n, 1n]],
}) as bigint[];

const currentPricePerShare = Number(spotPrices[outcomeIdx]) / 1e18;
const { tokensIn } = await client.quoteBuy({ marketAddress, outcomeIdx, sharesOut });
const quotedPricePerShare = (Number(tokensIn) / 1e6) / sharesHuman;
const priceImpact = ((quotedPricePerShare - currentPricePerShare) / currentPricePerShare) * 100;
console.log(`Price impact: ${priceImpact.toFixed(2)}%`);
```
