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

# FHERC20 Wrappers

> Shield standard ERC20 or native tokens into confidential FHERC20 tokens

## Overview

FHERC20 Wrappers enable you to convert standard ERC20 tokens or native tokens (e.g., ETH) into confidential FHERC20 tokens and vice versa. This creates a privacy layer on top of existing tokens, allowing users to transact privately while maintaining interoperability with the broader DeFi ecosystem.

<CardGroup cols={2}>
  <Card title="Privacy Layer" icon="mask">
    Transform transparent ERC20 or native token balances into encrypted FHERC20 balances for confidential transactions.
  </Card>

  <Card title="Reversible" icon="rotate">
    Unshield confidential tokens back to standard ERC20 or native tokens at any time through a secure claim process.
  </Card>

  <Card title="1:1 Backing" icon="scale-balanced">
    Each shielded token is backed by the underlying token held in the wrapper contract, with a rate-based conversion for decimal normalization.
  </Card>

  <Card title="DeFi Bridge" icon="bridge">
    Bridge between transparent DeFi protocols and confidential trading/transfers.
  </Card>
</CardGroup>

***

## Wrapper Types

FHERC20 provides two wrapper contracts:

| Wrapper                  | Purpose               | Shield Function                                       | Underlying                          |
| ------------------------ | --------------------- | ----------------------------------------------------- | ----------------------------------- |
| **FHERC20ERC20Wrapper**  | Shields ERC20 tokens  | `shield(to, amount)`                                  | Any standard ERC20                  |
| **FHERC20NativeWrapper** | Shields native tokens | `shieldNative(to)` / `shieldWrappedNative(to, value)` | Native currency (e.g., ETH) or WETH |

Both wrappers share the same unshielding flow via the `FHERC20WrapperClaimHelper`.

***

## Rate and Decimal Conversion

Wrappers normalize token decimals to fit within the `euint64` type. The maximum confidential decimals default to **6** (configurable via `_maxDecimals()`).

```solidity theme={null}
// Conversion rate between underlying and confidential precision
function rate() external view returns (uint256);
```

For an ERC20 with 18 decimals:

* `rate() = 10^(18 - 6) = 10^12`
* Shielding `1,000,000,000,000` (1e12) underlying tokens produces `1` confidential token
* Amounts are truncated to the nearest multiple of `rate()` during shielding

<Note>
  Some non-standard tokens such as fee-on-transfer or other deflationary-type tokens are not supported by the wrapper.
</Note>

***

## ERC20 Wrapper (FHERC20ERC20Wrapper)

### How It Works

<Steps>
  <Step title="Shield Tokens">
    User deposits standard ERC20 tokens into the wrapper contract, which mints an equivalent amount of confidential FHERC20 tokens (divided by the rate).
  </Step>

  <Step title="Confidential Transfers">
    User can now transfer the shielded tokens confidentially using all FHERC20 features while balances remain encrypted.
  </Step>

  <Step title="Unshield Request">
    When ready to exit, user burns confidential tokens. The burned amount is marked as publicly decryptable via `FHE.allowPublic`, and a claim is created.
  </Step>

  <Step title="Decrypt Off-Chain">
    The user (or anyone) requests decryption of the burned amount off-chain via `decryptForTx`, receiving the plaintext and a decryption proof.
  </Step>

  <Step title="Claim Tokens">
    The user submits the plaintext and proof on-chain via `claimUnshielded`. The contract verifies the proof and transfers the underlying ERC20 tokens (multiplied by the rate).
  </Step>
</Steps>

### Shielding Tokens

```solidity theme={null}
function shield(address to, uint256 amount) external returns (euint64);
```

**Parameters:**

* `to`: Address to receive the shielded (confidential) tokens
* `amount`: Amount of ERC20 tokens to shield (will be truncated to nearest multiple of `rate()`)

**Returns:** The encrypted amount of confidential tokens minted.

```javascript theme={null}
// 1. Approve wrapper to spend your tokens
await erc20Token.approve(wrapperAddress, amount);

// 2. Shield tokens (receives confidential tokens)
await wrapper.shield(recipientAddress, amount);

// 3. Check confidential balance (encrypted)
const encBalance = await wrapper.confidentialBalanceOf(recipientAddress);

// 4. Transfer confidentially
const [encAmount] = await cofheClient
  .encryptInputs([Encryptable.uint64(100n)])
  .execute();
await wrapper.confidentialTransfer(anotherAddress, encAmount);
```

### ERC1363 Direct Shielding

The ERC20 wrapper also implements the ERC1363 `onTransferReceived` callback, allowing users to shield tokens in a single transaction without a separate approval step:

```javascript theme={null}
// Direct transfer-and-shield in one transaction (if the ERC20 supports ERC1363)
await erc20Token.transferAndCall(wrapperAddress, amount, encodedRecipient);
```

***

## Native Wrapper (FHERC20NativeWrapper)

The native wrapper shields native tokens (e.g., ETH) or WETH into confidential FHERC20 tokens.

### Shield Native Tokens

```solidity theme={null}
// Shield native currency directly (e.g., ETH)
function shieldNative(address to) external payable returns (euint64);

// Shield WETH tokens
function shieldWrappedNative(address to, uint256 value) external returns (euint64);
```

**Parameters:**

* `to`: Address to receive the shielded tokens
* `value`: Amount of WETH to shield (for `shieldWrappedNative`)
* `msg.value`: Amount of native currency to shield (for `shieldNative`)

Amounts are truncated to the nearest multiple of `rate()`. Any dust below the threshold is refunded to the caller.

```javascript theme={null}
// Shield ETH directly
await nativeWrapper.shieldNative(recipientAddress, { value: ethers.parseEther("1.0") });

// Or shield WETH
await weth.approve(nativeWrapperAddress, amount);
await nativeWrapper.shieldWrappedNative(recipientAddress, amount);
```

***

## Unshielding Tokens

Unshielding is a three-step process shared by both wrapper types: burn on-chain, decrypt off-chain, then claim with proof.

### Step 1: Unshield (Burn and Create Claim)

```solidity theme={null}
function unshield(address from, address to, uint64 amount) external returns (euint64);
```

**Parameters:**

* `from`: Address whose confidential tokens to burn (caller must be `from` or an operator for `from`)
* `to`: Address to receive the underlying tokens after claiming
* `amount`: Amount of confidential tokens to unshield

This function:

1. Burns the specified amount of confidential tokens from `from`
2. Calls `FHE.allowPublic(burned)` so anyone can request decryption of the burned amount
3. Creates a claim for the recipient

```javascript theme={null}
// Unshield 100 confidential tokens
await wrapper.unshield(myAddress, myAddress, 100);

// A claim is created, but underlying tokens aren't sent yet
// The burned amount is now publicly decryptable
```

<Note>
  Due to the [zero-replacement behavior](/fhe-library/confidential-contracts/fherc20/core-features#the-update-function), if you attempt to unshield more than your balance, zero tokens will be burned and you'll have a claim for zero tokens.
</Note>

### Step 2: Decrypt Off-Chain

Retrieve the claim's `ctHash` using `getUserClaims`, then request decryption via `decryptForTx`. Since `FHE.allowPublic` was called, no permit is needed:

```typescript theme={null}
// Get the user's pending claims
const claims = await wrapper.getUserClaims(myAddress);
const claimCtHash = claims[0].ctHash;

// Request decryption off-chain (no permit needed)
const decryptResult = await client
  .decryptForTx(claimCtHash)
  .withoutPermit()
  .execute();

// decryptResult.decryptedValue — the plaintext amount
// decryptResult.signature      — the decryption proof
```

### Step 3: Claim Unshielded Tokens

Submit the plaintext and proof to the contract. The contract verifies the proof via `FHE.verifyDecryptResult` and transfers the underlying tokens (multiplied by the rate):

```solidity theme={null}
function claimUnshielded(
    bytes32 unshieldRequestId,
    uint64 unshieldAmountCleartext,
    bytes calldata decryptionProof
) external;
```

**Parameters:**

* `unshieldRequestId`: The ciphertext hash identifying the claim
* `unshieldAmountCleartext`: The plaintext value returned by `decryptForTx`
* `decryptionProof`: The proof proving the plaintext is authentic

```typescript theme={null}
// Claim your underlying tokens by submitting the proof
const tx = await wrapper.claimUnshielded(
  decryptResult.ctHash,
  decryptResult.decryptedValue,
  decryptResult.signature
);
await tx.wait();

// Check underlying token balance
const balance = await erc20Token.balanceOf(myAddress);
```

### Batch Claiming

You can claim multiple unshield requests in a single transaction:

```solidity theme={null}
function claimUnshieldedBatch(
    bytes32[] memory unshieldRequestIds,
    uint64[] memory unshieldAmountCleartexts,
    bytes[] memory decryptionProofs
) external;
```

```typescript theme={null}
// Batch claim all pending unshields
const claims = await wrapper.getUserClaims(myAddress);
const ids = [];
const amounts = [];
const proofs = [];

for (const claim of claims) {
    const result = await client
      .decryptForTx(claim.ctHash)
      .withoutPermit()
      .execute();
    ids.push(result.ctHash);
    amounts.push(result.decryptedValue);
    proofs.push(result.signature);
}

await wrapper.claimUnshieldedBatch(ids, amounts, proofs);
```

***

## Claim Management

### Claim Structure

```solidity theme={null}
struct Claim {
    address to;              // Recipient address
    bytes32 ctHash;          // Ciphertext hash identifying the claim
    uint64 requestedAmount;  // Original requested unshield amount
    uint64 decryptedAmount;  // Actual decrypted amount (set after claim)
    bool claimed;            // Whether underlying tokens have been claimed
}
```

### Getting Claim Information

```solidity theme={null}
function getClaim(bytes32 ctHash) public view returns (Claim memory);
```

```typescript theme={null}
// Get claim info
const claim = await wrapper.getClaim(ctHash);

console.log(`To: ${claim.to}`);
console.log(`Requested: ${claim.requestedAmount}`);
console.log(`Decrypted: ${claim.decryptedAmount}`);
console.log(`Claimed: ${claim.claimed}`);
```

### Getting User Claims

```solidity theme={null}
function getUserClaims(address user) public view returns (Claim[] memory);
```

Returns all pending (unclaimed) claims for a user:

```typescript theme={null}
// Get all pending claims
const claims = await wrapper.getUserClaims(myAddress);

console.log(`You have ${claims.length} pending claims`);

for (const claim of claims) {
    console.log(`Claim ${claim.ctHash} - requested: ${claim.requestedAmount}`);
}
```

***

## Events

```solidity theme={null}
// Emitted when an unshield request is created
event Unshielded(address indexed to, euint64 indexed amount);

// Emitted when an unshield request is claimed
event ClaimedUnshielded(
    address indexed to,
    bytes32 indexed unshieldRequestId,
    euint64 indexed unshieldAmount,
    uint64 unshieldAmountCleartext
);

// Emitted when native tokens are shielded (NativeWrapper only)
event ShieldedNative(address indexed from, address indexed to, uint256 value);
```

***

## Complete Examples

### ERC20 Wrapper

```solidity theme={null}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { FHERC20ERC20Wrapper } from "@fhenixprotocol/confidential-contracts/contracts/FHERC20/extensions/FHERC20ERC20Wrapper.sol";

// Deploy a wrapper for an existing ERC20 token
contract MyTokenWrapper is FHERC20ERC20Wrapper {
    constructor(IERC20 underlyingToken)
        FHERC20ERC20Wrapper(underlyingToken, "Shielded MTK", "sMTK")
    {}
}
```

### Native Wrapper

```solidity theme={null}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import { FHERC20NativeWrapper } from "@fhenixprotocol/confidential-contracts/contracts/FHERC20/extensions/FHERC20NativeWrapper.sol";

// Deploy a wrapper for native ETH
contract ShieldedETH is FHERC20NativeWrapper {
    constructor(address wethAddress)
        FHERC20NativeWrapper(wethAddress, "Shielded ETH", "sETH")
    {}
}
```

***

## Security Considerations

<AccordionGroup>
  <Accordion title="Token Compatibility" icon="plug" defaultOpen>
    <Warning>
      FHERC20ERC20Wrapper only works with standard ERC20 tokens. It will NOT work with:

      * Rebasing tokens (token balance changes automatically)
      * Fee-on-transfer tokens (tokens that charge fees)
      * Already-encrypted FHERC20 tokens
    </Warning>

    Always test with the specific token before deploying to production.
  </Accordion>

  <Accordion title="Claim Flow" icon="clock" defaultOpen>
    Claiming requires completing the off-chain decryption step first:

    ```typescript theme={null}
    // 1. Unshield burns tokens and allows public decryption
    await wrapper.unshield(user.address, user.address, amount);

    // 2. Get the claim's ctHash
    const claims = await wrapper.getUserClaims(user.address);
    const ctHash = claims[0].ctHash;

    // 3. Decrypt off-chain
    const decryptResult = await client
      .decryptForTx(ctHash)
      .withoutPermit()
      .execute();

    // 4. Claim with proof
    await wrapper.claimUnshielded(
      decryptResult.ctHash,
      decryptResult.decryptedValue,
      decryptResult.signature
    ); // ✅ Success
    ```

    Implement proper UI feedback for the decryption step.
  </Accordion>

  <Accordion title="Zero-Replacement" icon="circle-xmark" defaultOpen>
    If you unshield more than your balance, you get zero:

    ```javascript theme={null}
    // Balance: 100 confidential tokens
    const encBalance = await wrapper.confidentialBalanceOf(user.address);

    // Try to unshield 200
    await wrapper.unshield(user.address, user.address, 200);

    // Claim will give you 0 underlying tokens
    await waitAndClaim(ctHash);
    const received = 0; // Not 100, not 200, but 0
    ```

    Always ensure sufficient balance before unshielding.
  </Accordion>

  <Accordion title="Rate Conversion" icon="calculator" defaultOpen>
    Be aware of the rate conversion when shielding and unshielding:

    ```javascript theme={null}
    // For an ERC20 with 18 decimals, rate = 1e12
    const rate = await wrapper.rate();

    // Shielding 1.5 tokens (1.5e18 wei)
    // → 1.5e18 / 1e12 = 1,500,000 confidential units (1.5 with 6 decimals)
    await wrapper.shield(user.address, ethers.parseUnits("1.5", 18));

    // Amounts not aligned to rate are truncated
    // Shielding 1.5000005e18 → only 1.5e18 is shielded
    ```
  </Accordion>

  <Accordion title="Claim Management" icon="list-check" defaultOpen>
    Users can accumulate multiple pending claims. Use batch claiming for efficiency:

    ```typescript theme={null}
    // Multiple unshields
    await wrapper.unshield(user.address, user.address, 50);  // Claim 1
    await wrapper.unshield(user.address, user.address, 30);  // Claim 2
    await wrapper.unshield(user.address, user.address, 20);  // Claim 3

    // Batch claim all at once
    const claims = await wrapper.getUserClaims(user.address);
    const ids = [], amounts = [], proofs = [];

    for (const claim of claims) {
        const result = await client
          .decryptForTx(claim.ctHash)
          .withoutPermit()
          .execute();
        ids.push(result.ctHash);
        amounts.push(result.decryptedValue);
        proofs.push(result.signature);
    }

    await wrapper.claimUnshieldedBatch(ids, amounts, proofs);
    ```

    Provide UI to track and manage multiple claims.
  </Accordion>
</AccordionGroup>

***

## Use Cases

<CardGroup cols={2}>
  <Card title="DEX Privacy" icon="arrow-right-arrow-left">
    Shield tokens before trading on a confidential DEX, then unshield profits. Your trading activity and positions remain private.
  </Card>

  <Card title="Private Payments" icon="hand-holding-dollar">
    Shield stablecoins for confidential payments, then unshield to cash out to bank accounts or fiat on-ramps.
  </Card>

  <Card title="Confidential Payroll" icon="money-check-dollar">
    Companies can shield tokens, distribute salaries confidentially, and employees unshield to receive standard tokens.
  </Card>

  <Card title="Privacy Pools" icon="users">
    Create pools where users deposit tokens for privacy, transact confidentially, and withdraw when desired.
  </Card>
</CardGroup>

***

## Related Topics

* Learn about [Core Features](/fhe-library/confidential-contracts/fherc20/core-features) for confidential transfers
* Review [Best Practices](/fhe-library/confidential-contracts/fherc20/best-practices) for secure implementations
