> For the complete documentation index, see [llms.txt](https://plabs.gitbook.io/plabs-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://plabs.gitbook.io/plabs-docs/privacy-core/shield-erc20/operations.md).

# Operations: shield / unshield / transfer

Official infrastructure SHOULD screen the **depositor** before building shield calldata. See [Compliance implementation](/plabs-docs/privacy-core/compliance-implementation.md).

## shield (permissionless deposit)

```solidity
function shield(uint256 amountUnits, PrivacyCall calldata call) external;
```

Deposits `amountUnits * scale` wei of underlying from `msg.sender` and creates a shielded note of `amountUnits`.

| Property          | Value                                                        |
| ----------------- | ------------------------------------------------------------ |
| Permission        | Anyone                                                       |
| `valueBalance`    | `amountUnits \| (1 << 255)` (same encoding as pERC20 `mint`) |
| `recipientMeta`   | `bytes32(0)` — recipient is the output note                  |
| `executor`        | `address(0)`                                                 |
| Public accounting | `shieldedSupply += amountUnits`                              |
| Event             | `Shielded(depositor, amountUnits, weiAmount)`                |

### Preconditions

* `amountUnits != 0` and `amountUnits < SUBGROUP_ORDER`
* Caller has approved at least `amountUnits * scale` on the underlying token
* Underlying transfer increases pool balance by **exactly** `weiAmount` (rejects non-standard tokens)

### Proof bundle

* Typically a **single output action** with a DUMMY input (`nfOld` unique, non-zero) — same witness shape as pERC20 `mint`
* Binding signature binds `valueBalance = amountUnits | (1 << 255)`

### Call chain

```
shield → safeTransferFrom → _executeBundle(vb, executor=0) → shieldedSupply +=
```

***

Official infrastructure SHOULD screen the **recipient** before submitting unshield transactions. See [Compliance implementation](/plabs-docs/privacy-core/compliance-implementation.md).

## unshield (withdraw to public address)

```solidity
function unshield(uint256 amountUnits, address recipient, PrivacyCall calldata call) external;
```

Spends shielded notes worth `amountUnits` and releases `amountUnits * scale` wei of underlying to `recipient`.

| Property          | Value                                                                 |
| ----------------- | --------------------------------------------------------------------- |
| Permission        | Note holder (via spend-auth sig)                                      |
| `valueBalance`    | `amountUnits` (bit 255 = 0; same as pERC20 `burn`)                    |
| `recipientMeta`   | `bytes32(uint256(uint160(recipient)))` — **bound in binding sighash** |
| `executor`        | `address(0)`                                                          |
| Public accounting | `shieldedSupply -= amountUnits` (before transfer)                     |
| Event             | `Unshielded(recipient, amountUnits, weiAmount)`                       |

### Recipient binding

`recipient` is hashed into the binding sighash as `recipientMeta`. A relayer submitting the transaction **cannot redirect** the ERC-20 payout — the proof must match the declared recipient.

### Partial unshield

Bundle may include a **change output note** back to the user (multi-action bundle), same as partial burn on pERC20.

### CEI + reentrancy

1. `_executeBundle` (verify proofs, spend nullifiers, insert outputs)
2. `shieldedSupply -= amountUnits`
3. `safeTransfer(underlying, recipient, weiAmount)`

Both `shield` and `unshield` use `nonReentrant`.

***

## transfer (private note→note)

```solidity
function transfer(PrivacyCall calldata call) external returns (bool);
function transfer(address executor, PrivacyCall calldata call) external returns (bool);
```

Identical to [pERC20 transfer](/plabs-docs/privacy-core/perc20/operations.md):

| Property          | Value                                                                          |
| ----------------- | ------------------------------------------------------------------------------ |
| `valueBalance`    | `0`                                                                            |
| Custody           | Unchanged — no underlying movement                                             |
| `shieldedSupply`  | Unchanged                                                                      |
| Executor overload | When `executor != address(0)`, only that address may submit (atomic swap legs) |

***

## Disabled operations

```solidity
function mint(uint256, PrivacyCall calldata) external pure { revert MintDisabled(); }
function burn(uint256, PrivacyCall calldata) external pure { revert BurnDisabled(); }
```

Unbacked issuance would break the custody invariant. Use `shield` / `unshield` instead.

***

## valueBalance encoding

| Operation  | valueBalance                     | Underlying movement |
| ---------- | -------------------------------- | ------------------- |
| `transfer` | `0`                              | none                |
| `unshield` | bit255=0, low bits=`amountUnits` | pool → `recipient`  |
| `shield`   | bit255=1, low bits=`amountUnits` | `msg.sender` → pool |

Same sign-bit scheme as pERC20 — see [Signatures](/plabs-docs/privacy-core/signatures.md) and [pERC20 operations](/plabs-docs/privacy-core/perc20/operations.md).

***

## Error reference

| Error                           | When                                       |
| ------------------------------- | ------------------------------------------ |
| `MintDisabled` / `BurnDisabled` | Calling disabled `mint`/`burn`             |
| `ZeroAmount`                    | `amountUnits == 0`                         |
| `AmountTooLarge`                | `amountUnits >= SUBGROUP_ORDER`            |
| `ZeroRecipient`                 | `unshield` to `address(0)`                 |
| `SupplyUnderflow`               | `shieldedSupply < amountUnits` on unshield |
| `NonStandardToken`              | Received underlying ≠ expected on shield   |
| `Reentrancy`                    | Reentrant call during shield/unshield      |
| `UnauthorizedExecutor`          | Swap leg submitted by wrong executor       |

***

## Call chain summary

```
IPERC20.transfer        → _executeBundle(vb=0)           → OrchardVerifier
IERC20Shield.shield     → pull ERC20 + _executeBundle     → shieldedSupply +=
IERC20Shield.unshield   → _executeBundle + transfer ERC20 → shieldedSupply -=
```

## Next

* [Compliance implementation](/plabs-docs/privacy-core/compliance-implementation.md)
* [Deployment & factory](/plabs-docs/privacy-core/shield-erc20/deployment.md)
* [pERC20 operations](/plabs-docs/privacy-core/perc20/operations.md) (shared transfer semantics)
* [Universal call format](/plabs-docs/privacy-core/universal-call-format.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://plabs.gitbook.io/plabs-docs/privacy-core/shield-erc20/operations.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
