> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://sdk.railnet.org/api/mcp` to find what you need.

# Call builders

Every write is a `build*Call` builder. It is synchronous, takes no client and returns the contract
call. Simulating, batching, or feeding it to a step engine is yours.

The SDK sends nothing, in React either. It keeps the protocol's rules, such as the salt derivation
and the output asset a deposit must name, and the encoding.

## Import

```ts
import { buildSpawnConduitCall, randomSalt } from '@railnetorg/railnet-sdk'
```

## Usage

```ts
import { simulateContract, writeContract } from 'viem/actions'
import { buildSpawnConduitCall, randomSalt } from '@railnetorg/railnet-sdk'

const call = buildSpawnConduitCall({
  factory: '0x...',
  name: 'My Conduit',
  symbol: 'COND',
  vehicle: '0x...',
  initialExpectedSupply: 1000000n,
  transferEnabled: true,
  accessControl: '0x...',
  feeManager: '0x...',
  accountList: '0x...',
  ownerRegistry: '0x...',
  querySalt: randomSalt(),
  deploymentSalt: randomSalt(),
})

const { request } = await simulateContract(client, { ...call, account })
const hash = await writeContract(client, request)
```

## Returns

`{ address, abi, functionName, args }`. Spread it into viem's `simulateContract`, `writeContract`
or `encodeFunctionData`, into wagmi's `useSimulateContract`, or through
[`toCall`](/utilities/toCall) into `sendCalls`. The ABI and args stay literal, so viem keeps full
type inference.

A builder takes only what the call needs to be encoded: no client, no transaction overrides, and no
`sender` unless the encoding depends on it.

## No approvals, no address lookups

The builders emit only the Railnet call, and they resolve nothing from chain.
`buildDepositConduitCall` requires `vehicle` and `buildRedeemConduitCall` requires `outputAsset`.
[`getDepositConduitCall`](/actions/getDepositConduitCall) and
[`getRedeemConduitCall`](/actions/getRedeemConduitCall) read those for you and hand back the
identity of the query the call will create.

A deposit also needs an ERC-20 approval first, and the allowance is spent by the deposit. Send it
yourself, or batch the pair with [`toCall`](/utilities/toCall). A redeem needs none; the conduit
burns the caller's shares internally.

The `spawn*` builders are the same; the factories pull an initial deposit, so the caller approves
the factory beforehand.

## `sender` is required for deposits and redeems

`buildDepositConduitCall` and `buildRedeemConduitCall` take `sender`, with `receiver` defaulting to
it. No other builder does.

For a deposit, `conduit.create()` binds the query salt to the caller,
`keccak256(abi.encode(msg.sender, salt))`, so the call cannot be encoded without the sender. Any
other account reverts `InvalidQuerySalt`.

A redeem goes through `createRedeemFromConduitShares()`, which takes the salt raw and derives the
query salt from `msg.sender` itself, so there `sender` only sets the default `receiver`.

Through a Safe, an EIP-5792 batch or a relayer, `msg.sender` is that contract. See [The sender](/guides/depositingAndRedeeming#the-sender).

## Salts are required

Every builder that takes a salt requires it, so a `build*Call` is a pure function of its inputs.
Generate them with [`randomSalt`](/utilities/randomSalt) and keep them. See [The salt](/guides/depositingAndRedeeming#the-salt) and
[Query identity](/utilities/queryIdentity).

```ts
import { buildSpawnConduitCall, randomSalt } from '@railnetorg/railnet-sdk'

const call = buildSpawnConduitCall({
  // ...
  querySalt: randomSalt(),
  deploymentSalt: randomSalt(),
})
```

## Sending them

From a script or a server, `simulateContract` then `writeContract`. From React, wagmi's
`useSimulateContract` and `useWriteContract`. See [Sending from React](/guides/sendingFromReact).
