> ## 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 EVM transactions or Solana transfers 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.

Whether a transaction is sponsored is decided by your project's settings, not by your application. See [Gas Sponsorship](/wallets/gas-sponsorship).

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

## Send a Solana transfer

Use `sendSolanaTransfer` with a selected Solana wallet. Set `asset` to `SOL` for a native transfer or to an SPL token mint address. `amount` is a `bigint` in the asset's smallest unit: lamports for SOL or base units for an SPL token.

```typescript theme={null}
import { SolanaNetworks } from '@polygonlabs/oms-wallet'

const transaction = await omsWallet.wallet.sendSolanaTransfer({
  network: SolanaNetworks.devnet,
  asset: 'SOL',
  to: '3gFktQX6vki5M2DzN8Y1ESPUJ4fJ8o6hVQWf8vYvPypD',
  amount: 1_000_000n,
})
```

For an SPL transfer, `to` is the recipient wallet address, not an associated token account. WaaS uses an existing associated token account or creates one when required. Solana transfers use the same fee selector, execution mode, status waiting, and result shape as EVM transactions.

## Select fees accurately

When you provide a selector, a sponsored transaction calls it with an empty array. Return `undefined` to continue with the sponsored transaction, or throw to stop execution. `FeeOptionSelector.firstAvailable` handles the empty array by returning `undefined`.

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.
