> ## Documentation Index
> Fetch the complete documentation index at: https://docs.railnet.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Integrate a Conduit

> Build an earn experience on top of Railnet — deposits, withdrawals, balances, and reporting

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.

<Note>
  This guide assumes a Conduit has already been deployed for your platform. If you need to deploy one first, see [Create a Conduit](/developers/contracts/conduit-deployment).
</Note>

## Prerequisites

* **Conduit address** — the deployed Conduit contract on your target network
* **RPC endpoint** — an Ethereum JSON-RPC provider (Alchemy, Infura, etc.)
* **Railnet API endpoint** — `https://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

<CodeGroup>
  ```solidity Solidity theme={null}
  // 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();
  ```

  ```typescript TypeScript theme={null}
  // Coming soon
  ```
</CodeGroup>

### Balances and share price

<CodeGroup>
  ```solidity Solidity theme={null}
  // 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
  );
  ```

  ```typescript TypeScript theme={null}
  // Coming soon
  ```
</CodeGroup>

### 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).

<CodeGroup>
  ```solidity Solidity theme={null}
  // 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
  ```

  ```typescript TypeScript theme={null}
  // Coming soon
  ```
</CodeGroup>

<Tip>
  `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.
</Tip>

## 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.

<Steps>
  <Step title="Approve the Conduit">
    The user approves the Conduit to spend their tokens.

    <CodeGroup>
      ```solidity Solidity theme={null}
      IERC20(usdc).approve(address(conduit), amount);
      ```

      ```typescript TypeScript theme={null}
      // Coming soon
      ```
    </CodeGroup>
  </Step>

  <Step title="Create the deposit">
    <CodeGroup>
      ```solidity Solidity theme={null}
      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);
      ```

      ```typescript TypeScript theme={null}
      // Coming soon
      ```
    </CodeGroup>

    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.
  </Step>
</Steps>

## Handle withdrawals

Users return Conduit shares and receive the underlying asset.

<Steps>
  <Step title="Approve Conduit shares">
    <CodeGroup>
      ```solidity Solidity theme={null}
      conduit.approve(address(conduit), shareAmount);
      ```

      ```typescript TypeScript theme={null}
      // Coming soon
      ```
    </CodeGroup>
  </Step>

  <Step title="Create the withdrawal">
    <CodeGroup>
      ```solidity Solidity theme={null}
      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);
      ```

      ```typescript TypeScript theme={null}
      // Coming soon
      ```
    </CodeGroup>

    Same behavior as deposits: sync strategies settle immediately, async strategies are settled automatically by keepers.
  </Step>
</Steps>

<Info>
  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.
</Info>

## 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
```

<Note>
  This is the **testnet** endpoint. The mainnet endpoint will be provided when available.
</Note>

### Query positions

Retrieve the current allocation breakdown for a strategy backing your Conduit.

```graphql theme={null}
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.

```graphql theme={null}
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.

```graphql theme={null}
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.

```typescript theme={null}
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:

```graphql theme={null}
subscription OnQueryUpdate($owner: String!) {
  steamQuery(where: { owner: { _eq: $owner } }) {
    id
    state
    updatedAt
  }
}
```

## Next steps

<CardGroup cols={2}>
  <Card title="Configure fees" icon="percent" href="/conduits/configure-fees">
    Set up management, performance, deposit, and redeem fees for your Conduit.
  </Card>

  <Card title="Set up compliance" icon="shield-check" href="/conduits/compliance">
    Configure allowlists, blocklists, and sanctions screening.
  </Card>

  <Card title="Conduit reference" icon="book" href="/developers/contracts/conduit">
    Full technical reference for the Conduit contract.
  </Card>

  <Card title="Keeper automation" icon="robot" href="/developers/contracts/keeper">
    How keepers automate async operations for your users.
  </Card>
</CardGroup>
