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

# Kotlin SDK send transactions

> Send native value, encoded calldata, and contract calls through one transaction lifecycle.

`sendTransaction` and `callContract` are two input forms for the same lifecycle. Both require an active selected wallet and a supported `Network`.

1. The SDK prepares the request with OMS.
2. The preparation response either marks it sponsored or supplies fee options.
3. The SDK executes the prepared transaction with the applicable fee selection.
4. By default, the SDK polls until status resolves or the polling timeout expires.

## Send native value

Values are non-negative raw base-unit integers. Use `parseUnits` to convert a display amount.

```kotlin theme={null}
import technology.polygon.omswallet.Network
import technology.polygon.omswallet.utils.parseUnits

val result = omsWallet.wallet.sendTransaction(
    network = Network.AMOY,
    to = "0x1111111111111111111111111111111111111111",
    value = parseUnits("0.01", 18),
)

println("OMS transaction: ${result.txnId}")
println("Status: ${result.status}")
println("Hash: ${result.txnHash}")
```

`parseUnits` rounds fractional precision beyond `decimals` to the nearest base unit and requires a non-negative decimal count.

## Send encoded calldata

Use the request overload when you already have calldata or need to set the transaction mode:

```kotlin theme={null}
import technology.polygon.omswallet.Network
import technology.polygon.omswallet.models.SendTransactionRequest
import java.math.BigInteger

val result = omsWallet.wallet.sendTransaction(
    network = Network.AMOY,
    request = SendTransactionRequest(
        to = "0x2222222222222222222222222222222222222222",
        value = BigInteger.ZERO,
        data = "0x1234",
    ),
)
```

`SendTransactionRequest.mode` defaults to `TransactionMode.Relayer`. This mode is an execution input; it does not by itself promise sponsorship.

## Call a contract by method and arguments

Use `callContract` when you have an ABI method signature and argument values rather than encoded calldata:

```kotlin theme={null}
import kotlinx.serialization.json.JsonPrimitive
import technology.polygon.omswallet.Network
import technology.polygon.omswallet.models.AbiArg
import technology.polygon.omswallet.utils.parseUnits

val result = omsWallet.wallet.callContract(
    network = Network.AMOY,
    contract = "0x2222222222222222222222222222222222222222",
    method = "transfer(address,uint256)",
    args = listOf(
        AbiArg(
            type = "address",
            value = JsonPrimitive("0x1111111111111111111111111111111111111111"),
        ),
        AbiArg(
            type = "uint256",
            value = JsonPrimitive(parseUnits("1", 6).toString()),
        ),
    ),
)
```

The call is state-changing and follows the same prepare, fee, execute, and wait steps as `sendTransaction`.

## Resolve sponsorship and fees

Testnet transaction fees are sponsored. On mainnet, OMS reports whether each prepared transaction is sponsored or requires a fee option.

When the response is sponsored, the SDK executes without a fee selection and does not call your selector. For an unsponsored response:

* No selector: the SDK submits the first returned fee option.
* `FeeOptionSelector.firstAvailable`: the SDK chooses the first option whose known raw balance covers its quoted fee.
* Custom selector: your callback returns a `FeeOptionSelection` from the available options.
* No fee options, or a selector returning `null`: the SDK fails before execution.

```kotlin theme={null}
import technology.polygon.omswallet.Network
import technology.polygon.omswallet.models.FeeOptionSelector
import technology.polygon.omswallet.utils.parseUnits

val result = omsWallet.wallet.sendTransaction(
    network = Network.POLYGON,
    to = "0x1111111111111111111111111111111111111111",
    value = parseUnits("0", 18),
    selectFeeOption = FeeOptionSelector.firstAvailable,
)
```

A custom selector receives `FeeOptionWithBalance` values:

```kotlin theme={null}
import technology.polygon.omswallet.Network
import technology.polygon.omswallet.models.FeeOptionSelector
import technology.polygon.omswallet.utils.parseUnits

val result = omsWallet.wallet.sendTransaction(
    network = Network.POLYGON,
    to = "0x1111111111111111111111111111111111111111",
    value = parseUnits("0", 18),
    selectFeeOption = FeeOptionSelector { options ->
        options.firstOrNull {
            it.feeOption.token.symbol == "USDC"
        }?.selection
    },
)
```

Balance enrichment is best effort. The SDK asks the indexer for matching balances, but selector items can have null `balance`, `available`, `availableRaw`, or `decimals` when data is unavailable or the enrichment request fails. `firstAvailable` only selects an option when `availableRaw` is present and sufficient. It does not make an independent balance request or guarantee that execution will accept the fee.

## Understand the returned status

`SendTransactionResponse` contains:

| Field              | Meaning                                                                 |
| ------------------ | ----------------------------------------------------------------------- |
| `txnId`            | OMS transaction ID used for later status lookup                         |
| `status`           | Latest OMS status, such as `Quoted`, `Pending`, `Executed`, or `Failed` |
| `txnHash`          | Onchain hash when OMS has returned one                                  |
| `statusResolution` | Whether waiting was skipped, resolved, or timed out                     |

With the default `waitForStatus = true`, the SDK polls immediately after execution. It sets `statusResolution = Resolved` when status becomes `Executed` or `Failed`, or when OMS returns a nonblank transaction hash. A resolved response can therefore still report `Pending` when its hash is already available.

If the timeout expires first, the response uses `TimedOut` and preserves the latest status and any hash. Timeout does not mean failure. Continue with `getTransactionStatus(txnId)`.

Set `waitForStatus = false` to return directly after execute:

```kotlin theme={null}
import technology.polygon.omswallet.Network
import technology.polygon.omswallet.models.TransactionStatusResolution
import technology.polygon.omswallet.utils.parseUnits

val submitted = omsWallet.wallet.sendTransaction(
    network = Network.AMOY,
    to = "0x1111111111111111111111111111111111111111",
    value = parseUnits("0", 18),
    waitForStatus = false,
)

check(submitted.statusResolution == TransactionStatusResolution.NotRequested)
val current = omsWallet.wallet.getTransactionStatus(submitted.txnId)
```

An immediate response intentionally has `txnHash = null`; fetch status to obtain a later hash. `getTransactionStatus` returns the current `status` and nullable `txnHash` but does not add a `statusResolution` value.

To tune waiting, pass `TransactionStatusPollingOptions`. Its defaults are `fastPollCount = 5`, `fastPollIntervalMillis = 400`, `pollIntervalMillis = 2_000`, and `timeoutMillis = 60_000`.

## Handle interruption and uncertain execution

Coroutine cancellation stops the local SDK call or status polling. It is not an onchain cancellation and does not cancel a transaction that OMS may already have accepted for execution. Use `waitForStatus = false` when you need the SDK to return after execute instead of canceling a waiting coroutine.

When execute fails before the SDK can confirm submission, it throws `OMSWalletTransactionException` with `code = TransactionExecutionUnconfirmed` and the prepared `txnId`. Do not automatically submit the write again because the first attempt may have reached OMS.

When post-submit status polling fails, the exception uses `TransactionStatusLookupFailed`, includes `txnId`, and is retryable through `getTransactionStatus`.
