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

<Info>
  Gift methods are called directly on the WASM `PrivacyBoost` instance (`sdk`
  below). They may not be present in the checked-in generated types, so the code
  blocks here are illustrative and not type-checked against the published build.
</Info>

## Sending a gift

The most common case: you know the recipient's wallet address but they have not
joined Privacy Boost. Use `giftFundToWallet`. Every fund/claim/refund call
returns a `TransferResult`.

The bindings read every funding field as a string and treat a JS number as
missing, so pass block heights as decimal strings.

```typescript theme={null}
// Refund deadline: a block height after which you can reclaim an unclaimed gift.
// The min/max delay is advertised from the server's /info endpoint.
const currentBlock = await publicClient.getBlockNumber();
const refundAfterBlock = currentBlock + 50_000n;

const result = await sdk.giftFundToWallet({
  tokenAddress: '0x...token-address',
  amount: '1000000000000000000', // 1 token, in wei
  recipientWallet: '0xRecipientEoaAddress',
  refundAfterBlock: refundAfterBlock.toString(),
  currentBlock: currentBlock.toString(),
});

console.log('Funded gift:', result.txHash);
console.log('Share this claim link with the recipient:', result.claimLink);

// Persist result.giftRecord so you can refund later (see "Refunding" below).
saveGiftRecord(result.giftRecord);
```

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. Note the field is `recipient` — the 194-char privacy
address:

```typescript theme={null}
const result = await sdk.giftFund({
  tokenAddress: '0x...token-address',
  amount: '1000000000000000000',
  recipient: '0x04...recipient-privacy-address', // 194-char privacy address
  recipientWallet: '0xRecipientEoaAddress',
  refundAfterBlock: refundAfterBlock.toString(),
  currentBlock: currentBlock.toString(),
});
```

### Funding parameters

| Parameter | Type | Required | Description |
| - | - | - | - |
| `tokenAddress` | `string` | Yes | Token contract address |
| `amount` | `string` | Yes | Amount in wei (smallest unit) |
| `recipient` | `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` | `string` | Yes | Block height after which you may reclaim an unclaimed gift, decimal string |
| `currentBlock` | `string` | No | Current chain head, decimal string. When present, the refund delay is checked locally before submitting; the server re-validates either way. Required by `giftFundSecretBearer` |

<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:

```typescript theme={null}
interface TransferResult {
  requestId: string;
  txHash: string;
  giftRecord?: GiftRecord; // persist this — it makes the gift refundable
  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:

```typescript theme={null}
const pending = await sdk.getPendingGifts();
for (const gift of pending) {
  console.log(`#${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):

```typescript theme={null}
// By index (positional):
await sdk.giftClaim({ index: 0, acknowledgeUnknownSender: true });

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

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

```typescript theme={null}
await 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.

```typescript theme={null}
// Records the SDK persisted for gifts you funded:
const records = sdk.getGiftRecords();

// Refund the one at index 0 (a positional u32, NOT an object):
await sdk.giftRefundByRecord(0);
```

<Info>
  Gift records are what make a gift refundable, and `getGiftRecords()` holds
  them in memory only: they are gone after a reload, so store each
  `giftRecord` yourself when the gift is funded. It throws once the session can
  no longer read the account, including after an auto-lock that ends it,
  rather than answering an empty list. Merge what it returns into your stored
  records by `cGift` rather than replacing them. If you ever lose
  the record, you can still refund by supplying every field manually via
  `giftRefund({ recipientWallet, blind, refundAfterBlock, tokenId, amount, treeNumber, leafIndex })`.
</Info>

## Previewing a claim link offline

Decode a claim link into a preview with no network call — useful to show the
recipient what they are about to accept. `decodeGiftLink` is synchronous and
offline, so there is no `await`:

```typescript theme={null}
const preview = sdk.decodeGiftLink('pbgift:v1:...');
console.log(preview.amount, preview.tokenId, preview.recipientWallet, preview.refundAfterBlock);
```

## Types

```typescript theme={null}
interface GiftRecord {
  recipientWallet: string;
  blind: string;
  refundAfterBlock: number;
  tokenId: number;
  amount: string;
  cGift: string;
  giftNpk: string;
  requestId: string;
  fundingTreeNumber?: number;
  giftLeafIndex?: number;
  status?: string;
}

interface PendingGift {
  index: number;
  treeNumber: number;
  leafIndex: number;
  cGift: string;
  tokenId: number;
  amount: string;
  refundAfterBlock: number;
  recipientWallet: string;
}

interface GiftLinkPreview {
  server: string;
  chainId: number;
  cGift: string;
  recipientWallet: string;
  tokenId: number;
  amount: string;
  refundAfterBlock: number;
}
```

<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 a
  **bearer instrument** (whoever holds the link can claim), selected only by
  that explicit method. See the [concept
  page](/sdk/concepts/claimable-transfers) before considering it.
</Info>

It takes `tokenAddress`, `amount`, `refundAfterBlock`, and `currentBlock`, all
strings, with `currentBlock` required because the refund delay is validated
locally before funding. No `giftRecord` is returned and `getGiftRecords()` never
lists the gift: the returned `claimLink` is the recipient's claim credential and
the funder's refund credential alike.

```typescript theme={null}
const result = await sdk.giftFundSecretBearer({
  tokenAddress: '0x...token-address',
  amount: '1000000000000000000',
  refundAfterBlock: refundAfterBlock.toString(),
  currentBlock: currentBlock.toString(),
});
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.