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

# How to make your Monad contract clear-signable (ERC-7730)

export const CopyToClipboard = ({value, children}) => {
  const [copied, setCopied] = useState(false);
  const handleCopy = async () => {
    try {
      await navigator.clipboard.writeText(value);
      setCopied(true);
      setTimeout(() => setCopied(false), 1000);
    } catch {
      const textarea = document.createElement("textarea");
      textarea.value = value;
      textarea.style.position = "fixed";
      textarea.style.opacity = "0";
      document.body.appendChild(textarea);
      textarea.select();
      document.execCommand("copy");
      document.body.removeChild(textarea);
      setCopied(true);
      setTimeout(() => setCopied(false), 1000);
    }
  };
  return <span style={{
    display: "inline",
    whiteSpace: "nowrap"
  }}>
      {children}
      <button onClick={handleCopy} title={copied ? "Copied!" : "Copy to clipboard"} style={{
    background: "none",
    border: "none",
    cursor: "pointer",
    padding: "2px",
    display: "inline-flex",
    alignItems: "center",
    verticalAlign: "middle",
    marginLeft: "4px",
    opacity: copied ? 1 : 0.4,
    transition: "opacity 0.15s"
  }} onMouseEnter={e => {
    if (!copied) e.currentTarget.style.opacity = "0.8";
  }} onMouseLeave={e => {
    if (!copied) e.currentTarget.style.opacity = "0.4";
  }}>
        {copied ? <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#22c55e" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
            <polyline points="20 6 9 17 4 12" />
          </svg> : <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
            <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
          </svg>}
      </button>
    </span>;
};

ERC-7730 descriptors tell wallets how to display your contract calls and typed-data signatures in plain language. For Monad, the process is the same as any EVM chain: write the descriptor, bind it to `chainId` 143 and your contract address, then add it to the public registry.

## What is clear signing?

[ERC-7730](https://eips.ethereum.org/EIPS/eip-7730) is a JSON format for describing how a wallet should render contract calldata and EIP-712 messages. The descriptor lives outside your contract. It maps functions, message types, and fields to labels and formatters a wallet can show before a user signs.

The lookup key is `chainId` plus contract address, so Monad contracts do not need a Monad-specific version of the standard.

Without a descriptor, a wallet may only have raw calldata:

```text theme={null}
0x095ea7b3000000000000000000000000fe9c9ca3eed0fb3e6a5c0bf42ad6f1a0d1c7b2a40000000000000000000000000000000000000000000000000000000003b9aca00
```

With a descriptor, the same approval can be shown as fields a user can check:

```text theme={null}
Approve USDC
Spender:  Uniswap V3 Router
Amount:   1,000 USDC
Network:  Monad
```

## Why it matters for Monad

Users should not have to trust the site that assembled a transaction. A spoofed frontend can ask for one action while submitting another, and raw calldata gives most users nothing useful to inspect. Clear signing moves the important details into the wallet prompt: the action, recipient, amount, token, deadline, or any other field that matters for the call.

Adding a descriptor does not require a contract change. You publish a JSON file, validate it, and submit it to the registry used by wallets that support ERC-7730.

> Monad mainnet `chainId` is `143` (testnet `10143`). See [Network Information](/developer-essentials/network-information).

## Quickstart

Install [`uv`](https://docs.astral.sh/uv/) first. The `uvx` command runs the `erc7730` tool without a separate global install.

1. Generate a starting descriptor from your ABI:

```bash theme={null}
uvx erc7730 generate \
  --chain-id 143 \
  --address 0xYourContractOnMonad \
  --abi ./abi/YourContract.json \
  --owner "Your Protocol" \
  --url "https://yourprotocol.xyz" \
  > calldata-yourcontract.json
```

2. Edit the intents and field labels so they read like English (see [Editing the descriptor](#editing-the-descriptor)).

3. Validate the descriptor:

```bash theme={null}
uvx erc7730 lint calldata-yourcontract.json
```

4. Preview how a wallet renders your fields at [clear-signing.sourcify.dev](https://clear-signing.sourcify.dev).

5. Open a PR to the [ERC-7730 registry](https://github.com/ethereum/clear-signing-erc7730-registry). CI validates it against the schema, a maintainer reviews, and once merged, supporting wallets can use it.

Open the PR from an account tied to the protocol or contract owner. Registry maintainers may ask for proof that you control the deployment.

## Keep the ABI inline

The `generate` command embeds your contract ABI under `context.contract.abi`, which makes the descriptor self-contained. Leave it there: with the ABI in the file, the fields validate locally and nothing has to reach an explorer to make sense of the call.

While linting you may see a warning like this:

```text theme={null}
warning: Could not fetch ABI: Fetching reference ABI for chain id 143 failed, display fields will not be validated against ABI: ... Missing/Invalid API Key
```

The warning is harmless. On top of your inline ABI, `erc7730` also tries to fetch the deployed contract's ABI from Etherscan to cross-check your display fields, and that request needs an `ETHERSCAN_API_KEY`. Set one (`export ETHERSCAN_API_KEY=...`) and lint passes clean; without it, that cross-check is skipped. Either way, keep the inline `abi`.

```json theme={null}
{
  "context": {
    "contract": {
      "deployments": [
        { "chainId": 143, "address": "0xYourContractOnMonad" }
      ],
      "abi": [
        {
          "type": "function",
          "name": "approve",
          "inputs": [
            { "name": "spender", "type": "address" },
            { "name": "amount", "type": "uint256" }
          ]
        }
      ]
    }
  }
}
```

## Editing the descriptor

`generate` gives you a skeleton with one entry per function. Most of the hand-editing is in two places:

* **`intent`**: a short action label, such as `Approve USDC` or `Wrap MON`. Keep it to 30 characters or fewer; some hardware wallet screens truncate longer strings.
* **`fields`**: the parameters you want users to review, each with a `label` and `format`.

Use the most specific formatter that fits the value:

| `format`      | Renders                                       | Notes                                                                                               |
| ------------- | --------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| `tokenAmount` | `1,000 USDC`                                  | Applies decimals and ticker. Set a `threshold` with a `message` like `Unlimited` for max approvals. |
| `amount`      | Native MON with ticker                        | Reads the transaction `value`.                                                                      |
| `addressName` | A known name, otherwise a checksummed address | Use `types` (eoa, contract, token) and `sources` (local, ens).                                      |
| `raw`         | A number, string, or address as-is            |                                                                                                     |
| `date`        | A readable date from a unix timestamp         | Set `encoding`.                                                                                     |

Field paths use three roots: `#.` for decoded calldata or message fields, `$.` for this descriptor's own metadata, and `@.` for the transaction container, such as `@.value`.

## Monad examples

These Monad descriptors are useful references when writing your own:

<ul>
  <li>
    <strong>Permit2</strong>, the EIP-712 approval contract used across many dApps, at <CopyToClipboard value="0x000000000022D473030F116dDEE9F6B43aC78BA3">[`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://monadvision.com/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)</CopyToClipboard>, added to the canonical Uniswap descriptor in [registry PR #2611](https://github.com/ethereum/clear-signing-erc7730-registry/pull/2611).
  </li>

  <li>
    <strong>The Monad staking precompile</strong> (delegate, undelegate, claim rewards) at <CopyToClipboard value="0x0000000000000000000000000000000000001000">[`0x0000000000000000000000000000000000001000`](https://monadvision.com/address/0x0000000000000000000000000000000000001000)</CopyToClipboard>, in [registry PR #2589](https://github.com/ethereum/clear-signing-erc7730-registry/pull/2589).
  </li>

  <li>
    <strong>Wrapped MON</strong> (wrap, unwrap, and ERC-20 calls) at <CopyToClipboard value="0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A">[`0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A`](https://monadvision.com/address/0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A)</CopyToClipboard>.
  </li>
</ul>

## How a wallet uses the descriptor

At a high level, a supporting wallet does this:

1. A user starts a transaction or signs an EIP-712 message.
2. The wallet computes the 4-byte selector (or the EIP-712 type hash).
3. It looks up the descriptor by `chainId` and address.
4. It checks that the descriptor's `context` binding matches the actual target.
5. It decodes the parameters with the ABI and resolves token and name metadata.
6. It applies each field formatter and shows the `intent` with the formatted fields.

If no descriptor is found, the wallet falls back to its default signing view.

## Resources

* [ERC-7730 specification](https://eips.ethereum.org/EIPS/eip-7730)
* [ERC-7730 registry](https://github.com/ethereum/clear-signing-erc7730-registry)
* [Sourcify preview tool](https://clear-signing.sourcify.dev)
* [clearsigning.org](https://clearsigning.org)
* [Network Information (chainId 143)](/developer-essentials/network-information)
* [Monad Developer Discord](https://discord.gg/monaddev)
