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

# Claimable transfers

# Claimable Transfers (Gifts)

Send shielded funds to an Ethereum wallet address that has not registered with
Privacy Boost yet. The recipient claims by proving ownership of that address; you
reclaim the funds if they never do. See
[Claimable Transfers](/sdk/concepts/claimable-transfers) for the concept and
trust model.

<Warning>
  **Preview feature — pending external audit.** Claimable transfers are
  implemented across the protocol and SDK but have not completed external
  security audit, and the feature is enabled per deployment. Gift support is
  advertised by the server's `/info` (through its gift refund-delay bounds); gate
  production use on your own review.
</Warning>

## Sending a gift

The most common case: you know the recipient's wallet address but they have not
joined Privacy Boost. Use `giftFundToWallet`. The refund deadline is a block
height after which you can reclaim an unclaimed gift; the min/max delay is
advertised from the server's `/info` endpoint. Pass the current chain head as
`currentBlock` so the SDK can pre-validate the refund delay before submitting.

```kotlin theme={null}
try {
    val result = withContext(Dispatchers.IO) {
        sdk.giftFundToWallet(
            tokenAddress = "0x...token-address",
            amount = "1000000000000000000", // 1 token, in wei
            recipientWallet = "0xRecipientEoaAddress",
            refundAfterBlock = currentBlock + 50_000UL,
            currentBlock = currentBlock
        )
    }
    println("Funded gift: ${result.txHash}")
    println("Share this claim link with the recipient: ${result.claimLink}")

    // Persist result.giftRecord so you can refund later (see "Refunding" below).
    saveGiftRecord(result.giftRecord)
} catch (e: SDKError) {
    println("Gift funding failed: $e")
}
```

If the recipient is already a Privacy Boost user, fund with their privacy address
too (`giftFund`) so the gift ciphertext is sealed to their viewing key for the
smoothest discovery:

```kotlin theme={null}
val result = withContext(Dispatchers.IO) {
    sdk.giftFund(
        tokenAddress = "0x...token-address",
        amount = "1000000000000000000",
        recipientPrivacyAddress = "0x04...recipient-privacy-address", // 194-char privacy address
        recipientWallet = "0xRecipientEoaAddress",
        refundAfterBlock = currentBlock + 50_000UL,
        currentBlock = currentBlock
    )
}
```

### Funding parameters

| Parameter                 | Type     | Required        | Description                                                          |
| ------------------------- | -------- | --------------- | -------------------------------------------------------------------- |
| `tokenAddress`            | `String` | Yes             | Token contract address                                               |
| `amount`                  | `String` | Yes             | Amount in wei (smallest unit)                                        |
| `recipientPrivacyAddress` | `String` | `giftFund` only | Recipient's 194-char privacy address (the ciphertext's ECDH target)  |
| `recipientWallet`         | `String` | Yes             | Recipient's Ethereum address the gift binds to (may be unregistered) |
| `refundAfterBlock`        | `ULong`  | Yes             | Block height after which you may reclaim an unclaimed gift           |
| `currentBlock`            | `ULong`  | Yes             | Current chain head; pre-validates the refund delay before submitting |

<Warning>
  The gift binds **irrevocably** to `recipientWallet`. A wrong-but-valid address
  can be claimed by whoever controls it, and only an unclaimed gift is
  refundable. Show the exact checksummed address and require explicit
  confirmation before funding.
</Warning>

### Funding result

`giftFund` / `giftFundToWallet` return a `TransferResult` carrying two gift
fields in addition to the usual transfer fields:

```kotlin theme={null}
data class TransferResult(
    val requestId: String,
    val txHash: String,
    val fee: String,
    val giftRecord: GiftRecord?, // persist this — it makes the gift refundable
    val claimLink: String?       // pbgift:v1:... — share with the recipient
)
```

## Sharing the claim link

`result.claimLink` is a `pbgift:v1:...` string that carries the encrypted opening
the recipient needs. Share it out of band (message, QR code). Treat it like a
password — it reveals the gift's amount and recipient to anyone who reads it.

## Claiming a gift

The recipient registers Privacy Boost with the same wallet address, then claims.

List the pending gifts addressed to the authenticated wallet:

```kotlin theme={null}
val pending = withContext(Dispatchers.IO) {
    sdk.getPendingGifts()
}
for (gift in pending) {
    println("#${gift.index}: ${gift.amount} of token ${gift.tokenId}")
}
```

Claim one by its position in that list, or — more robustly — by its stable
commitment `cGift` (the list can shift as gifts settle):

```kotlin theme={null}
// By index (positional):
withContext(Dispatchers.IO) {
    sdk.giftClaim(index = 0u, acknowledgeUnknownSender = true)
}

// By stable commitment (preferred when the list may change):
withContext(Dispatchers.IO) {
    sdk.giftClaimByCGift(cGift = pending[0].cGift, acknowledgeUnknownSender = true)
}
```

Or claim straight from a claim link, without a server lookup first:

```kotlin theme={null}
withContext(Dispatchers.IO) {
    sdk.giftClaimFromLink(link = "pbgift:v1:...", acknowledgeUnknownSender = true)
}
```

<Info>
  `acknowledgeUnknownSender` must be `true` to claim. The hidden-sender model
  cannot reveal who funded a gift, so the recipient explicitly acknowledges
  accepting funds from an unknown sender. Surface this as a consent prompt.
</Info>

A successful claim re-mints the gift into a normal shielded note — from then on it
behaves like any other note in the recipient's balance.

## Refunding an unclaimed gift

A claim and a refund spend the same nullifier, so only one can ever settle. After
the refund deadline passes, reclaim an unclaimed gift using the local record you
saved at funding time. `giftRefundByRecord` takes the record's index:

```kotlin theme={null}
// Records the SDK persisted for gifts you funded:
val records = withContext(Dispatchers.IO) {
    sdk.getGiftRecords()
}

// Refund the one at index 0:
withContext(Dispatchers.IO) {
    sdk.giftRefundByRecord(index = 0u)
}
```

<Info>
  Gift records are what make a gift refundable. They are included in an
  [exported session](/sdk/android/guides/session-storage), so persisting and restoring
  the session keeps your unclaimed gifts refundable across restarts. If you ever
  lose the record, you can still refund by supplying every field manually via
  `giftRefund(recipientWallet, blind, refundAfterBlock, tokenId, amount, fundingTreeNumber, giftLeafIndex)`.
</Info>

## Previewing a claim link offline

Decode a claim link into a preview without any network call — useful to show the
recipient what they are about to accept. `decodeGiftLink` is synchronous and
throwing (it works fully offline), so it does not need `withContext`:

```kotlin theme={null}
val preview = sdk.decodeGiftLink(link = "pbgift:v1:...")
println("${preview.amount} of token ${preview.tokenId} to ${preview.recipientWallet}, refundable after ${preview.refundAfterBlock}")
```

## Types

```kotlin theme={null}
data class GiftRecord(
    val recipientWallet: String,
    val blind: String,
    val refundAfterBlock: ULong,
    val tokenId: UShort,
    val amount: String,
    val cGift: String,
    val giftNpk: String,
    val requestId: String,
    val fundingTreeNumber: ULong?,
    val giftLeafIndex: ULong?,
    val status: String?
)

data class PendingGift(
    val index: UInt,
    val treeNumber: ULong,
    val leafIndex: ULong,
    val cGift: String,
    val tokenId: UShort,
    val amount: String,
    val refundAfterBlock: ULong,
    val recipientWallet: String
)

data class GiftLinkPreview(
    val server: String,
    val chainId: ULong,
    val cGift: String,
    val recipientWallet: String,
    val tokenId: UShort,
    val amount: String,
    val refundAfterBlock: ULong
)
```

<Info>
  A third funding mode, `giftFundSecretBearer`, binds a gift to a secret instead
  of a wallet so it can be claimed before the recipient has any wallet. It is
  **experimental, disabled by default, and a bearer instrument** (whoever holds
  the link can claim). See the [concept page](/sdk/concepts/claimable-transfers)
  before considering it.
</Info>
