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

# Send transactions with TypeScript

> Prepare, fund, execute, and track OMS Wallet transactions from TypeScript.

`sendTransaction` and `callContract` require an active wallet session. They accept different transaction inputs but run the same lifecycle.

## Follow the transaction lifecycle

1. The SDK prepares the transaction for the selected `Network`. The default mode is `TransactionMode.Relayer`.
2. OMS marks the prepared transaction as sponsored or returns fee-token options. Sponsored transactions need no fee selection. Unsponsored transactions need one option.
3. The SDK executes the prepared OMS transaction and receives its latest status.
4. Unless `waitForStatus` is `false`, the SDK polls for a transaction hash or terminal status and returns the latest result.

OMS sponsors transaction fees on testnets. On mainnet, the preparation response reports whether the transaction is sponsored or requires a fee option.

## Choose a transaction input

Use `sendTransaction` for a native transfer, pre-encoded calldata, or a viem-compatible ABI call. Use `callContract` when your input is a method signature plus typed argument objects.

<CodeGroup>
  ```typescript Native transfer theme={null}
  import { Networks } from '@polygonlabs/oms-wallet'
  import { parseUnits } from 'viem'

  const transaction = await omsWallet.wallet.sendTransaction({
    network: Networks.amoy,
    to: '0x1111111111111111111111111111111111111111',
    value: parseUnits('0.001', 18),
  })
  ```

  ```typescript Encoded calldata theme={null}
  import { Networks } from '@polygonlabs/oms-wallet'
  import type { Hex } from 'viem'

  const data = '0xa9059cbb000000000000000000000000111111111111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000f4240' as Hex

  const transaction = await omsWallet.wallet.sendTransaction({
    network: Networks.amoy,
    to: '0x2222222222222222222222222222222222222222',
    data,
  })
  ```

  ```typescript ABI call theme={null}
  import { Networks } from '@polygonlabs/oms-wallet'
  import { parseUnits } from 'viem'

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

  const transaction = await omsWallet.wallet.sendTransaction({
    network: Networks.amoy,
    to: '0x2222222222222222222222222222222222222222',
    abi: erc20Abi,
    functionName: 'transfer',
    args: [
      '0x1111111111111111111111111111111111111111',
      parseUnits('1', 6),
    ],
  })
  ```

  ```typescript Method and arguments theme={null}
  import { Networks } from '@polygonlabs/oms-wallet'
  import { parseUnits } from 'viem'

  const transaction = await omsWallet.wallet.callContract({
    network: Networks.amoy,
    contractAddress: '0x2222222222222222222222222222222222222222',
    method: 'transfer(address,uint256)',
    args: [
      {
        type: 'address',
        value: '0x1111111111111111111111111111111111111111',
      },
      {
        type: 'uint256',
        value: parseUnits('1', 6).toString(),
      },
    ],
  })
  ```
</CodeGroup>

Native `value` amounts are `bigint` values in raw base units. A calldata or ABI transaction can also include `value`. All forms require a recipient or contract address; these methods do not expose a recipient-free contract deployment input.

## Select fees accurately

Testnet transactions are sponsored and do not call your selector. On mainnet, a preparation with `sponsored: true` also sends no fee option and does not call your selector.

For an unsponsored transaction:

* With no selector, the SDK chooses the first fee option returned by OMS. It does not check whether the wallet can afford that option.
* `FeeOptionSelector.firstAvailable` queries indexer balances and chooses the first returned option whose raw balance covers its raw fee value.
* A custom selector receives the same enriched `FeeOptionWithBalance[]` and returns one option's `selection`.

`firstAvailable` returns no selection when balance data is unavailable or no option is affordable. The transaction then fails before execution with a fee-selection error.

```typescript theme={null}
const transaction = await omsWallet.wallet.sendTransaction({
  network: Networks.polygon,
  to: '0x1111111111111111111111111111111111111111',
  value: 0n,
  selectFeeOption: async (options) => {
    const affordableUsdc = options.find((option) =>
      option.feeOption.token.symbol === 'USDC' &&
      option.availableRaw !== undefined &&
      BigInt(option.availableRaw) >= BigInt(option.feeOption.value),
    )

    return affordableUsdc?.selection
  },
})
```

Each enriched option includes the original `feeOption`, its ready-to-return `selection`, and optional balance fields. `feeOption.value` and `availableRaw` are raw integer strings. `available` is formatted with known token decimals. `balance`, `available`, `availableRaw`, and `decimals` can be `undefined` when the indexer or token metadata cannot supply them.

## Interpret the result

```typescript theme={null}
console.log(transaction.txnId)
console.log(transaction.status)
console.log(transaction.txnHash)
console.log(transaction.statusResolution)
```

`SendTransactionResponse` contains:

* `txnId`: the OMS transaction ID returned during preparation. Keep it for later status checks.
* `status`: the latest OMS status: `quoted`, `pending`, `executed`, `failed`, or `unknown`.
* `txnHash`: the EVM transaction hash when OMS has one. It is optional.
* `statusResolution`: `resolved`, `timed-out`, or `not-requested`.

`resolved` means polling observed `executed`, `failed`, or a transaction hash. It does not mean the chain has reached application-specific confirmation depth. A response can have `status: 'pending'` and `statusResolution: 'resolved'` once a hash exists.

`timed-out` returns the last observed status without treating the transaction as failed. The transaction can still progress after the SDK returns.

## Control status waiting

Waiting is enabled by default. The SDK performs a status request immediately, waits 400 ms between early polls, then 2 seconds between later polls, with a 60-second total timeout. These defaults correspond to:

```typescript theme={null}
statusPolling: {
  timeoutMs: 60_000,
  fastIntervalMs: 400,
  fastPollCount: 5,
  intervalMs: 2_000,
}
```

Override only the values your application needs:

```typescript theme={null}
const transaction = await omsWallet.wallet.sendTransaction({
  network: Networks.amoy,
  to: '0x1111111111111111111111111111111111111111',
  value: parseUnits('0.001', 18),
  statusPolling: {
    timeoutMs: 30_000,
    intervalMs: 1_000,
  },
})
```

Return immediately after execution when your application will track the OMS transaction itself:

```typescript theme={null}
const transaction = await omsWallet.wallet.sendTransaction({
  network: Networks.amoy,
  to: '0x1111111111111111111111111111111111111111',
  value: parseUnits('0.001', 18),
  waitForStatus: false,
})

console.log(transaction.statusResolution) // 'not-requested'

const latest = await omsWallet.wallet.getTransactionStatus({
  txnId: transaction.txnId,
})
```

The immediate response does not include `txnHash`. Use `getTransactionStatus` until OMS returns the status your application requires.

## Handle uncertain execution

If the execute request fails after preparation, the SDK throws `OMS_TRANSACTION_EXECUTION_UNCONFIRMED` and includes `txnId`. The SDK cannot confirm whether submission occurred, so do not blindly send an identical transaction again.

If execution succeeds but automatic status lookup fails, the SDK throws `OMS_TRANSACTION_STATUS_LOOKUP_FAILED` with `txnId`. Retry `getTransactionStatus` for that ID. An onchain `failed` status is returned as a normal transaction result rather than thrown as an SDK request error.
