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

# Call a token from your contract

> Move confidential tokens from another contract by sharing encrypted amounts with the token and receiving what it returns

When your contract moves confidential tokens, it passes encrypted amounts to the token and gets encrypted amounts back. Both directions use `sharedEuint64`, a handle tagged with who shared it and with whom. This page shows the pattern on a vault, and it applies to [FHERC20](/fhe-library/confidential-contracts/fherc20/overview) and [ERC20Confidential](/fhe-library/confidential-contracts/dual-mode/overview) alike.

## Why amounts cross as shared values

FHE operations check the permission of the contract performing them, not of whoever called it. Suppose a token accepted a bare `euint64` from any caller. That caller could pass a handle only the token may use, such as someone's balance, and get back a value derived from it. A share records the sharer and the intended receiver, and lasts one transaction, so the receiver can check who handed the value over. [Passing encrypted values between contracts](/fhe-library/core-concepts/inputs#passing-encrypted-values-between-contracts) covers the mechanism.

For a token call, that means two steps around every call:

1. Share the amount with the token: `FHE.shareEuint64(amount, address(token))`.
2. Receive the result from the token: `FHE.receiveEuint64FromCall(result, address(token))`.

Name the token in both. In `receiveEuint64FromCall`, the address must be the contract you called in that same expression. Naming any other trusted address checks who created the share, not who handed it to you.

## A vault that holds deposits

This vault takes deposits with `confidentialTransferFrom` and pays out with `confidentialTransfer`. Each deposit is kept as an encrypted balance the depositor can decrypt:

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

import { FHE, euint64, externalEuint64, sharedEuint64 } from "@fhenixprotocol/cofhe-contracts/FHE.sol";
import { IERC7984 } from "fhenix-confidential-contracts/contracts/interfaces/IERC7984.sol";

contract ConfidentialVault {
    IERC7984 public immutable token;
    mapping(address => euint64) private _deposits;

    constructor(IERC7984 token_) {
        token = token_;
    }

    function deposit(externalEuint64 encryptedAmount, bytes calldata inputProof) external {
        euint64 amount = FHE.asEuint64(encryptedAmount, inputProof);

        sharedEuint64 moved = token.confidentialTransferFrom(
            msg.sender,
            address(this),
            FHE.shareEuint64(amount, address(token))
        );
        euint64 received = FHE.receiveEuint64FromCall(moved, address(token));

        euint64 updated = FHE.add(_deposits[msg.sender], received);
        FHE.allowThis(updated);
        FHE.allow(updated, msg.sender);
        _deposits[msg.sender] = updated;
    }

    function withdraw(externalEuint64 encryptedAmount, bytes calldata inputProof) external {
        euint64 requested = FHE.asEuint64(encryptedAmount, inputProof);
        euint64 amount = FHE.select(FHE.lte(requested, _deposits[msg.sender]), requested, FHE.asEuint64(0));

        sharedEuint64 moved = token.confidentialTransfer(msg.sender, FHE.shareEuint64(amount, address(token)));
        euint64 sent = FHE.receiveEuint64FromCall(moved, address(token));

        euint64 updated = FHE.sub(_deposits[msg.sender], sent);
        FHE.allowThis(updated);
        FHE.allow(updated, msg.sender);
        _deposits[msg.sender] = updated;
    }

    function depositOf(address account) external view returns (euint64) {
        return _deposits[account];
    }
}
```

Three details carry the design:

* **Credit what moved, not what was asked.** A transfer that exceeds the sender's balance moves zero instead of reverting. The vault credits `received`, the amount the token returns, so a failed deposit credits nothing.
* **Clamp before you send.** `withdraw` uses `FHE.select` to replace an over-deposit request with zero. The contract cannot revert on an encrypted comparison without revealing it.
* **Persist with `allowThis`.** A received handle carries access for this transaction only. Values you store must get `FHE.allowThis`, and `FHE.allow` for the account that should decrypt them. See [access control](/fhe-library/core-concepts/access-control).

`IERC7984` is the interface both token families implement, so the vault works with either. The shared overloads of `confidentialTransfer` and `confidentialTransferFrom` are selected by the `sharedEuint64` argument type.

## Call the vault from your app

`confidentialTransferFrom` runs with the vault as `msg.sender`, so the depositor must first make the vault an [operator](/fhe-library/confidential-contracts/fherc20/operators). Without it, the deposit reverts with `FHERC20UnauthorizedSpender` on FHERC20, or `ERC20ConfidentialUnauthorizedSpender` on ERC20Confidential.

Encrypt the amount with the vault as the consuming contract, because the vault calls `FHE.asEuint64`, not the token:

```typescript theme={null}
import { Encryptable } from '@cofhe/sdk';

const vaultAddress = await vault.getAddress();
const until = Math.floor(Date.now() / 1000) + 600;
await token.setOperator(vaultAddress, until);

const [amount, inputProof] = await client
  .encryptInputs([Encryptable.uint64(40_000_000n)])
  .setConsumingContract(vaultAddress)
  .execute();

await vault.deposit(amount, inputProof);
```

An input encrypted for the token instead of the vault fails with `InvalidSigner`. The same happens when one account encrypts and another sends the transaction: encrypt with the client of the account that calls the vault. See [encrypting inputs](/client-sdk/guides/encrypting-inputs).

## What goes wrong

| Error | Cause |
| - | - |
| `NotShared` | The receiver found no share for it. An EOA calling a `sharedEuint64` overload directly hits this, because only a contract can share a value. Wallets use the `externalEuint64` overloads |
| `UnexpectedSharer` | The share came from a different address than the receiver checked for |
| `SenderNotAllowed` | Your contract shared a handle it is not allowed to use |
| `InvalidSigner` | The input was encrypted for a different contract or account |

These errors come from the TaskManager. See [common errors](/fhe-library/core-concepts/common-errors).

## Receive tokens with a callback

To react when tokens arrive, rather than pulling them, implement `IERC7984Receiver`. The token calls it during `confidentialTransferAndCall` with the amount as a `sharedEuint64`, which you receive with `FHE.receiveEuint64Param`. See [transfer callbacks](/fhe-library/confidential-contracts/fherc20/transfer-callbacks).


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