Skip to main content
This guide walks you through integrating a deployed Conduit into your platform. By the end, you’ll be able to show balances, handle deposits and withdrawals, preview operations with fees, and monitor positions through the Railnet API.
This guide assumes a Conduit has already been deployed for your platform. If you need to deploy one first, see Create a Conduit.

Prerequisites

  • Conduit address — the deployed Conduit contract on your target network
  • RPC endpoint — an Ethereum JSON-RPC provider (Alchemy, Infura, etc.)
  • Railnet API endpointhttps://query.railnet-testnet.defi.testnet.kiln.fi/v1/graphql (testnet)

Read Conduit state

A Conduit exposes view functions you can call directly on-chain to display balances, share prices, and product status in your UI.

Product info

// The underlying asset (e.g., USDC)
address asset = conduit.asset();

// The underlying Vehicle (strategy) the Conduit wraps
IVehicle vehicle = conduit.getVehicle();

// Whether the Conduit is open for deposits
bool enabled = conduit.isEnabled();

Balances and share price

// Total underlying assets managed by the Conduit
uint256 tvl = conduit.totalAssets();

// Total Conduit shares in circulation
uint256 supply = conduit.totalSupply();

// A specific user's share balance
uint256 userShares = conduit.balanceOf(userAddress);

// Share price: assets per share (no fees applied)
Asset[] memory shareValue = conduit.convert(
    toAssetArray(address(conduit), 1e18),  // 1 share
    true                                     // shares → assets
);

Preview operations

Use estimate() to show users what they’ll receive after fees before they commit to a transaction. Use convert() for a pure conversion without fees (e.g., displaying portfolio value).
// How many shares will the user get for 100 USDC? (includes fees)
Asset[] memory input = new Asset[](1);
input[0] = Asset({asset: address(usdc), value: 100e6});

Asset[] memory estimated = conduit.estimate(
    input,
    Mode.DEPOSIT,
    EstimationType.OUTPUT
);
// estimated[0].value = shares the user will receive

// How much USDC for redeeming 50 shares? (includes fees)
Asset[] memory redeemInput = new Asset[](1);
redeemInput[0] = Asset({asset: address(conduit), value: 50e18});

Asset[] memory redeemEstimate = conduit.estimate(
    redeemInput,
    Mode.REDEEM,
    EstimationType.OUTPUT
);
// redeemEstimate[0].value = USDC the user will receive
estimate() includes all fee types (deposit, redeem, management, performance). Use it for transaction previews. Use convert() for display-only share-to-asset conversions where fees don’t apply.

Handle deposits

Users deposit base assets (e.g., USDC) and receive Conduit shares representing proportional ownership. The Conduit handles all interaction with the underlying strategy.
1

Approve the Conduit

The user approves the Conduit to spend their tokens.
IERC20(usdc).approve(address(conduit), amount);
2

Create the deposit

Query memory query = Query({
    owner: address(conduit),
    receiver: address(conduit),
    mode: Mode.DEPOSIT,
    input: new Asset[](1),
    output: new Asset[](0),
    salt: bytes32(uint256(nonce)),
    data: ""
});
query.input[0] = Asset({
    asset: address(usdc),
    value: amount
});

// Create the deposit — auto-processes for sync strategies
conduit.create(query, userAddress);
For sync strategies (e.g., Aave, Compound), the deposit settles in the same transaction. The user receives shares immediately.For async strategies (e.g., Ethena, Syrup), the query enters PROCESSING. A keeper calls process() automatically when the underlying protocol is ready — the user doesn’t need to take any further action.

Handle withdrawals

Users return Conduit shares and receive the underlying asset.
1

Approve Conduit shares

conduit.approve(address(conduit), shareAmount);
2

Create the withdrawal

Query memory query = Query({
    owner: address(conduit),
    receiver: address(conduit),
    mode: Mode.REDEEM,
    input: new Asset[](1),
    output: new Asset[](0),
    salt: bytes32(uint256(nonce)),
    data: ""
});
query.input[0] = Asset({
    asset: address(conduit),
    value: shareAmount
});

// Create the withdrawal — auto-processes for sync strategies
conduit.create(query, userAddress);
Same behavior as deposits: sync strategies settle immediately, async strategies are settled automatically by keepers.
For async strategies, keepers monitor active queries and call process() when the underlying protocol is ready to settle. Your platform doesn’t need to build monitoring infrastructure, and users never need to return for a second transaction. The experience is identical for sync and async strategies from the user’s perspective.

Monitor with the Railnet API

The Railnet GraphQL API provides indexed on-chain data for building dashboards, tracking operations, and generating reports. Use it alongside on-chain view calls for a complete picture.
https://query.railnet-testnet.defi.testnet.kiln.fi/v1/graphql
This is the testnet endpoint. The mainnet endpoint will be provided when available.

Query positions

Retrieve the current allocation breakdown for a strategy backing your Conduit.
query GetMultiVehiclePositions($address: String!) {
  multiVehicle(where: { address: { _eq: $address } }) {
    address
    totalAssets
    totalSupply
    vehicles {
      vehicleAddress
      totalAssets
      sector
    }
  }
}

Track operation lifecycle

Monitor deposit and withdrawal queries as they move through the STEAM state machine.
query GetQueryState($queryId: String!) {
  steamQuery(where: { id: { _eq: $queryId } }) {
    id
    state
    mode
    owner
    receiver
    inputAssets {
      asset
      value
    }
    outputAssets {
      asset
      value
    }
    createdAt
    updatedAt
  }
}

Monitor keeper automation

Check the status of automated operations for your Conduit.
query GetKeeperJobs($target: String!) {
  keeperJobs(where: { target: { _eq: $target } }) {
    jobId
    status
    lastExecutedAt
    executionCount
  }
}

Poll for updates

Fetch position data on a regular interval to keep your UI current.
const ENDPOINT = "https://query.railnet-testnet.defi.testnet.kiln.fi/v1/graphql";

async function getPositions(address: string) {
  const response = await fetch(ENDPOINT, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      query: `
        query ($address: String!) {
          multiVehicle(where: { address: { _eq: $address } }) {
            totalAssets
            totalSupply
            vehicles { vehicleAddress totalAssets }
          }
        }
      `,
      variables: { address }
    })
  });
  return response.json();
}
The API also supports GraphQL subscriptions for real-time updates on query state changes:
subscription OnQueryUpdate($owner: String!) {
  steamQuery(where: { owner: { _eq: $owner } }) {
    id
    state
    updatedAt
  }
}

Next steps

Configure fees

Set up management, performance, deposit, and redeem fees for your Conduit.

Set up compliance

Configure allowlists, blocklists, and sanctions screening.

Conduit reference

Full technical reference for the Conduit contract.

Keeper automation

How keepers automate async operations for your users.