# Approval

Before Kima can move user funds, the origin asset must be authorized through the mechanism that applies to that flow:

* Regular ERC-20 tokens use an on-chain `approve()` transaction
* Permit2 tokens use a signature-based approval flow
* BTC-origin flows use an HTLC lock instead of ERC-20 approval

For regular ERC-20 approvals, there are 3 core components:

* **Owner address**: User
* **Spender address**: Kima Pool
* **Total amount**: origin token amount plus gas fees

## Getting the Kima Pool Address

### Using the Kima Backend

`GET /chains/pool` returns a list of pool addresses and balances for all supported chains.

```json
[
  {
    "chainName": "SOL",
    "poolAddress": "5tvyUUqPMWVGaVsRXHoQWqGw6h9uifM45BHCTQgzwSdr",
    "balance": [
      {
        "amount": "99684.841000000000000000",
        "tokenSymbol": "USDK",
        "decimal": "6"
      }
    ],
    "nativeGasAmount": "70.119964040000000000"
  },
  {
    "chainName": "TRX",
    "poolAddress": "TQ3qmAgUgMwrY9prMiHLZmF43G4Jk8bxNF",
    "balance": [],
    "nativeGasAmount": ""
  }
]
```

### Using the Kima API

| Chain  | Type           | Pool Address                                 |
| ------ | -------------- | -------------------------------------------- |
| EVM    | ecdsa          | 0x9a721c664f9d69e4da24f91386086fbd81da23c1   |
| Solana | eddsa          | 5tvyUUqPMWVGaVsRXHoQWqGw6h9uifM45BHCTQgzwSdr |
| Tron   | ecdsa (base58) | TQ3qmAgUgMwrY9prMiHLZmF43G4Jk8bxNF           |

You can also obtain the pool addresses from the TSS endpoint. Use the `type` in the chart above to determine which one to use.

Use the environment-appropriate API base URL:

* Mainnet: `GET https://api.kima.network/kima-finance/kima-blockchain/kima/tss_pubkey`
* Testnet: `GET https://api.sardis.kima.network/kima-finance/kima-blockchain/kima/tss_pubkey`

Bitcoin is also supported. For BTC, prefer `GET /chains/pool` from the backend; the backend derives the BTC pool address from the TSS data (`reserved` or `ecdsaPubkey`) for the active environment.

Success Response:

* `tssPubkey` (array object)
  * tssPubKey: string
  * ecdsa: string
  * eddsa: string
  * reserved: string
* `pagination` (object)
  * nextKey: string | null
  * total: number

Note: for Tron, the hex `ecdsa` address must be converted to a base 58 checksum address.

## Getting the Service Fee

The fees and token authorization amounts are calculated by an endpoint exposed by Kima chain. This endpoint also returns a `feeId` which must be included when submitting the transaction.

### Using the Kima Backend

`GET /submit/fees`

Query Params (all required):

* `amount` (number): the amount of tokens to transfer
* `originAddress` (string): the source user address
* `originChain` (string): the chain the user tokens will come from
* `originSymbol` (string): the token symbol the user will pay with (or bridge from)
* `targetAddress` (string): the receiving address
* `targetChain` (string): the destination chain
* `targetSymbol` (string): the token symbol being delivered

Example:

```
http://localhost:3001/submit/fees?amount=1337&originAddress=0x742d35Cc6634C0532925a3b844Bc454e4438f44e&originChain=BSC&originSymbol=USDK&targetAddress=5FHwkrdxkjF7xoL2ncGh4AEYs1KyJzz5MeiaHGz8h8GA&targetChain=SOL&targetSymbol=USDK
```

```
http://localhost:3001/submit/fees?amount=10&originAddress=TQ3qmAgUgMwrY9prMiHLZmF43G4Jk8bxNF&originChain=TRX&originSymbol=USDK&targetAddress=0x742d35Cc6634C0532925a3b844Bc454e4438f44e&targetChain=ARB&targetSymbol=USDK
```

Port note: these direct backend examples use the backend's default local port `3001`. If your frontend or local proxy exposes the backend on another port such as `4000`, use that URL instead.

Example note: these sample fee requests and responses use `USDK`, which is a testnet token. If you are integrating against mainnet, replace it with a supported mainnet token such as `USDC`.

Success Response:

Amounts are returned as both numbers representing the amount in whole tokens and as a string representing the integer amount in the smallest unit of the token (e.g. USDC is 6 decimals).

* `feeId` (string): a unique identifier for the fee; include this in the submit request (`/submit/transfer` or `/submit/swap`)
* `feeOrigin*` the gas fee for the origin chain
* `feeKimaProcessing*` The processing fee (currently 0.05%)
* `feeTarget*` the gas fee for the target chain
* `feeTotal*` the total fee
* `transactionValues`: amounts used for token authorization and for submitting the transaction
  * `feeFromOrigin`: amounts used when the user selects to pay fees from the origin amount
    * `allowanceAmount`: the token amount to authorize. For regular ERC-20 tokens this is the value to pass to `approve()`. For Permit2 tokens, use it when building the Permit2 authorization/signature flow.
    * `submitAmount`: the amount that needs to be passed to submit endpoints
  * `feeFromTarget`: amounts used when the user selects to pay fees from the target amount

The response field is still named `allowanceAmount`, but treat it as the origin token authorization amount. There are 2 variants returned depending on whether the user chooses to pay fees from the origin or target amount.

* When paying fees from the origin, use `transactionValues.feeFromOrigin` values.
* When paying fees from the target, use `transactionValues.feeFromTarget`.

Example:

Transferring 10 USDK from ARB to SOL with `0.07371` USD in total fees.

When paying fees from the origin

* The total fees are added to the amount
* User signs the message `I approve the transfer of 10.07371 USDK from ARB to 5FHwkrdxkjF7xoL2ncGh4AEYs1KyJzz5MeiaHGz8h8GA on SOL.` (`feeFromOrigin.message`)
* The authorization amount is `10.07371` USDK (`feeFromOrigin.allowanceAmount`)
* For regular ERC-20 tokens, use that value in `approve()`. For Permit2 tokens, use it in the Permit2 authorization flow.
* The amount sent to submit endpoints is amount RECEIVED on the target chain
* The amount sent to submit endpoints is `10` USDK (`feeFromOrigin.submitAmount`)
  * Also included is the `feeId` and signed message
* The user receives `10` USDK on Solana

When paying fees from the target chain

* The user does not pay fees on Arbitrum
* User signs the message `I approve the transfer of 10 USDK from ARB to 5FHwkrdxkjF7xoL2ncGh4AEYs1KyJzz5MeiaHGz8h8GA on SOL.` (`feeFromTarget.message`)
* The authorization amount is `10` USDK (`feeFromTarget.allowanceAmount`)
* For regular ERC-20 tokens, use that value in `approve()`. For Permit2 tokens, use it in the Permit2 authorization flow.
* The amount sent to submit endpoints is `9.926290` USDK (`feeFromTarget.submitAmount`)
* The fees are deducted from the amount received on Solana so the user receives `9.926290` USDK on Solana

Save the corresponding `submitAmount`, `message` and `feeId` for later as they are needed to submit the transaction.

```json
{
  "feeId": "5af06c68-44d6-4079-8e5b-bec1cfa154c7",
  "feeOriginGasFiat": "0.009105",
  "feeOriginGasBigInt": {
    "value": 9105204603000000,
    "decimals": 18
  },
  "feeKimaProcessingFiat": "0.05",
  "feeKimaProcessingBigInt": {
    "value": 50000,
    "decimals": 6
  },
  "feeTargetGasFiat": "0.014605",
  "feeTargetGasBigInt": {
    "value": 14605,
    "decimals": 6
  },
  "feeTotalFiat": "0.07371",
  "feeTotalBigInt": {
    "value": 73710,
    "decimals": 6
  },
  "transactionValues": {
    "feeFromOrigin": {
      "allowanceAmount": {
        "value": 10073710000000000000,
        "decimals": 18
      },
      "submitAmount": {
        "value": 10000000000000000000,
        "decimals": 18
      },
      "message": "I approve the transfer of 10.07371 USDK from ARB to 5FHwkrdxkjF7xoL2ncGh4AEYs1KyJzz5MeiaHGz8h8GA on SOL."
    },
    "feeFromTarget": {
      "allowanceAmount": {
        "value": 10000000,
        "decimals": 6
      },
      "submitAmount": {
        "value": 9926290,
        "decimals": 6
      },
      "message": "I approve the transfer of 10 USDK from ARB to 5FHwkrdxkjF7xoL2ncGh4AEYs1KyJzz5MeiaHGz8h8GA on SOL."
    }
  },
  "peggedTo": "USD",
  "expiration": "2025-05-07T00:24:24Z"
}
```

### Chain Names

See the short names in the [Supported Assets](https://docs.kima.network/kima-network/supported-assets) section.

### Using the Kima API

There is also an endpoint that can be called directly.

`POST {{baseUrl}}/v3/fees/calculate`

It has the same input/output purpose as the Backend `/submit/fees` endpoint except:

* The params are in the body as JSON
* There is an extra param `creator` which is the Kima address of the developer wallet sending the transaction- `kima1...`

For most integrations, `GET /submit/fees` remains the recommended wrapper because it keeps your app aligned with the backend contract.

### Permit2 Tokens

Read the selected origin token metadata from `GET /chains`.

* `isPermit2=false`: use the standard `approve` flow shown below.
* `isPermit2=true`: signature is still required, and submit with `options.permit2` payload (`r`, `s`, `v`, `deadline`) in addition.
* Permit2 tokens are not supported in `mode=light`.

## BTC-origin flow (approval equivalent)

BTC-origin transactions do not use ERC20 `approve`. Instead, funds are first locked in a BTC HTLC output and then submitted to Kima with HTLC metadata.

You should still call `GET /submit/fees` first and keep `feeId` for the final submit step.

### Step 1: Create HTLC lock intent

`POST /btc/htlc/lock-intent`

Request body:

* `senderAddress` (string): BTC sender address
* `senderPubkey` (string): BTC sender public key (hex)
* `recipientAddress` (string): recipient address used to construct the lock script
* `amountSats` (string): lock amount in satoshis
* `poolAddress` (string, optional): explicit BTC recipient in script
* `lockBlocks` (number, optional): timeout as block delta
* `timeout` (number, optional): explicit timeout value

Response includes:

* `lockId`
* `htlcAddress`
* `timeoutHeight`
* `amountSats`
* `senderPubkey`

### Step 2: Broadcast BTC lock transaction

Use the user's BTC wallet/provider to send `amountSats` to the returned `htlcAddress`. Save the broadcast transaction hash (`txid`).

### Step 3: Record the broadcast transaction

`POST /btc/htlc/record`

Request body:

* `lockId`
* `txid`

Response returns submit-ready HTLC fields:

* `htlcCreationHash`
* `htlcCreationVout`
* `htlcExpirationTimestamp`
* `htlcVersion` (currently `p2wsh-sha256-cltv-v1`)
* `senderPubKey`

Use these together with:

* `htlcAddress` and `htlcLockId` from `lock-intent`
* `htlcAmountSats` from the selected lock amount

These values are then sent to `POST /submit/transfer` or `POST /submit/swap`.

## Calling Approve

Once all the info has been collected it’s time to make the on chain call. The exact details of how this is done depends on the origin chain and library used.

```typescript
import {
  getClientsForChain,
  getPoolAddressesForChain,
  getServiceFee,
} from "../utils";

export async function approve(
  chain: ChainName,
  userAddress: Address,
  amount: string
) {
  const { publicClient, walletClient } = getClientsForChain(chain);
  const { contractAddress, poolAddress } = getPoolAddressesForChain(chain);

  // simulate approval call
  const { request } = await publicClient.simulateContract({
    account: userAddress,
    address: contractAddress,
    abi: erc20Abi,
    functionName: "approve",
    args: [poolAddress, amount],
  });

  const hash = await walletClient.writeContract(request);

  const receipt = await publicClient.waitForTransactionReceipt({ hash });

  return receipt;
}
```

The `getClientsForChain()` and `getPoolAddressesForChain()` would be utility functions defined elsewhere.
