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

# NEAR

> NEAR transactions and NEAR Intents (Defuse Protocol) signing

The NEAR module decodes raw NEAR transactions into VisualSign payloads, and renders both halves of the [NEAR Intents](https://docs.near-intents.org) protocol: the pre-signature intent a user is about to sign, and the signed batch an `execute_intents` call submits on-chain.

## Architecture overview

### Two input formats, one chain identity

NEAR carries two distinct payload shapes under a single `CHAIN_NEAR` identity, discriminated purely by input format:

* **A borsh-encoded transaction** (`near::sign_transaction`) — the standard NEAR wire format (`near_primitives::transaction::Transaction` or `SignedTransaction`), hex or base64 encoded.
* **A pre-signature intents envelope** (`near::sign_intent`) — a bare `DefusePayload` JSON object, the message a user signs before it is wrapped in a signature standard and submitted to solvers.

Borsh bytes are never valid JSON, so the two formats never collide: the parser attempts a borsh decode first, and only falls back to validating the input as a JSON envelope if that fails. Input that is neither is rejected — never guessed at or partially reinterpreted.

### Account model

NEAR is account-based. Accounts are either **named** (`alice.near`) or **implicit** — derived deterministically from a public key, with no on-chain registration step. HSM-generated keys always produce implicit accounts: a 64-character hex string for ed25519 keys, an EVM-style `0x…` address for secp256k1 keys. A single user signing on multiple chains therefore appears as multiple, unlinked implicit accounts on `intents.near` — one per key.

### Key components

For a borsh transaction, the parser produces:

* Top-level fields: `Network`, `From`, `To`.
* Per-action fields — a native `Transfer` renders an `Amount`; a `FunctionCall` renders `Method` plus, for the three token-movement methods the parser understands (`ft_transfer`, `ft_transfer_call`, `ft_withdraw`), the resolved recipient and amount. An `execute_intents` call to `intents.near` additionally decodes and renders the full signed intent batch inline. Any other action or method renders its generic label only — a partially understood call never masquerades as a fully decoded one.

For an intents envelope (either pre-signature or inside a decoded `execute_intents` batch), the parser produces:

* Envelope fields: `Signer`, `Verifying Contract`, `Deadline`, `Nonce`. A pre-signature envelope is preceded by the same `Network` field the borsh path renders, since the resolved network is part of every token-metadata signature scope.
* For a signed batch specifically, a `Standard` and `Signature` (or a signature-invalid warning) per entry, since verification and structural decode are independent — a bad signature still renders the intents it wraps.
* Per-intent fields, one section per intent in the batch.

## Supported intent types

| Intent                       | Fields surfaced                                                     |
| ---------------------------- | ------------------------------------------------------------------- |
| `token_diff`                 | `Send`/`Receive` per token, Memo, Referral                          |
| `transfer`                   | To, Amount per token, Memo                                          |
| `ft_withdraw`                | Token, To, Amount, Memo, Message, Storage Deposit                   |
| `nft_withdraw`               | Token, To, NFT Token Id, Memo, Message, Storage Deposit             |
| `mt_withdraw`                | Token, To, MT Token (per token id/amount), Message, Storage Deposit |
| `native_withdraw`            | To, Amount                                                          |
| `add_public_key`             | Add Public Key                                                      |
| `remove_public_key`          | Remove Public Key                                                   |
| `set_auth_by_predecessor_id` | Auth By Predecessor                                                 |
| `storage_deposit`            | Contract, For Account, Amount                                       |
| `auth_call`                  | Contract, Message, Attached Deposit                                 |

`ft_withdraw`, `nft_withdraw`, and `mt_withdraw` each carry an optional `storage_deposit` (a second, unconditional wNEAR debit alongside the withdrawal, never refunded on failure) and `msg` (which switches the call into its `_transfer_call` form) — both render when present, since either changes what the withdrawal actually does beyond moving the named asset.

## Signature standards

The intents preset verifies all seven signature standards the protocol defines — NEP-413, ERC-191, TIP-191, raw ed25519, WebAuthn, TonConnect, and SEP-53 — using the protocol's own canonical implementation ([`near/intents`](https://github.com/near/intents)) rather than a reimplementation, so new intent variants and signing standards stay correct as the protocol evolves. Verification recovers a signing key; whether that key is checked against the claimed account depends on the account's shape. For an implicit account, the account id is itself derived from the key, so the recovered key is compared against it directly — a mismatch renders as a hard finding, not a hedge. For a named account (`alice.near`), that key-to-account relationship lives in on-chain access-key state the parser doesn't have, so it renders as an explicit "not verified" caveat instead.

ERC-191 and TIP-191 recover a secp256k1 key rather than pass/fail: a well-formed signature always recovers *some* key, so a tampered message recovers a different key rather than failing outright. A malformed recovery byte is rejected before it reaches verification, since the native `ecrecover` backend otherwise panics on out-of-range input — a malformed payload must be treated as invalid input, never a crash.

## Visualization strategy

* **Amounts in native units where resolvable** — a `wrap.near` (wNEAR) transfer renders as `1 wNEAR`, not `1000000000000000000000000` raw units. Unresolved assets render the raw base-unit amount tagged with the asset id, rather than guessing at decimals — a wrong decimals value would silently misrender an amount. Beyond the compiled-in seed table, a wallet can supply symbol/decimals for additional asset ids per request, optionally signed by a curator key whose curve is chosen by the asset's origin chain (NEAR, Ethereum, or Solana) rather than a single NEAR-wide curve — see [Chain Metadata](/wallet-integration/core-concepts/chain-metadata#near-token-metadata).
* **Fail-closed method-args decoding** — `ft_transfer`, `ft_transfer_call`, and `ft_withdraw` args decode only when they parse as exactly the known shape (`deny_unknown_fields`); anything else falls back to the generic `FunctionCall` view rather than rendering a partial, possibly misleading subset of fields.
* **Deadlines and invalid signatures render as soft findings alongside the content they qualify** — the intents still render even when a deadline has passed or a signature fails verification, with the problem surfaced as a warning field rather than blocking the render outright.

## Using parser\_cli

The CLI accepts either input format directly — hex/base64 for a transaction, or raw JSON for an intents envelope:

```bash theme={null}
cargo run --bin parser_cli -- decode \
  --chain near \
  --output human \
  -t 0a000000616c6963652e6e656172000000000000000000000000000000000000000000000000000000000000000000010000000000000008000000626f622e6e65617200000000000000000000000000000000000000000000000000000000000000000100000003000000a1edccce1bc2d3000000000000
```

Output:

```
┌─ Transaction: Transfer
│  Version: 0
│  Type: NearTx
│
└─ Fields:
   ├─ Network: NEAR Mainnet
   ├─ From: alice.near
   ├─ To: bob.near
   └─ Amount: 1 NEAR
```

An intents envelope decodes the same way:

```bash theme={null}
cargo run --bin parser_cli -- decode \
  --chain near \
  --output human \
  -t '{"signer_id":"alice.near","verifying_contract":"intents.near","deadline":"2100-01-01T00:00:00Z","nonce":"XVoKfmScb3G+XqH9ke/fSlJ/3xO59sNhCxhpG821BH8=","intents":[{"intent":"ft_withdraw","token":"wrap.near","receiver_id":"bob.near","amount":"1000000000000000000000000"}]}'
```

Output:

```
┌─ Transaction: NEAR Intent
│  Version: 0
│  Type: NearTx
│
└─ Fields:
   ├─ Network: NEAR Mainnet
   ├─ Signer: alice.near
   ├─ Verifying Contract: intents.near
   ├─ Deadline: 2100-01-01T00:00:00+00:00
   ├─ Nonce: 0x5d5a0a7e649c6f71be5ea1fd91efdf4a527fdf13b9f6c3610b18691bcdb5047f
   ├─ Token: wrap.near
   ├─ To: bob.near
   └─ Amount: 1 wNEAR
```

The `--network` flag selects the rendered `Network` header — `NEAR_MAINNET` (the default) or `NEAR_TESTNET`. An unrecognized value errors before anything renders, rather than silently falling back to mainnet.

The resolved network also has to agree with the accounts being rendered: an account whose suffix contradicts it (`.testnet` under mainnet, or `.near` under testnet) is refused rather than displayed under a network it does not belong to. This covers the transaction's own accounts and every rendered envelope's `signer_id`/`verifying_contract`. Since the default is mainnet, decoding a `.testnet` envelope needs `--network NEAR_TESTNET` explicitly. Implicit (64-hex) accounts carry no suffix and are not constrained.

### Supplying token metadata locally

Assets the compiled-in seed table doesn't cover render their raw base-unit amount tagged `unresolved <asset id>`. To give the CLI a symbol and decimals for one, pass `--near-token-metadata-mappings`:

```bash theme={null}
echo '{"symbol":"MYTOKEN","decimals":8}' > mytoken.json

cargo run --bin parser_cli -- decode \
  --chain near \
  --output human \
  --near-token-metadata-mappings 'MyToken@mytoken.json@nep141:my-token.near' \
  -t '{"signer_id":"alice.near","verifying_contract":"intents.near","deadline":"2100-01-01T00:00:00Z","nonce":"XVoKfmScb3G+XqH9ke/fSlJ/3xO59sNhCxhpG821BH8=","intents":[{"intent":"ft_withdraw","token":"my-token.near","receiver_id":"bob.near","amount":"100000000"}]}'
```

The envelope withdraws `my-token.near`, the asset the mapping covers, so the
`Amount` field renders as `1 MYTOKEN` rather than `100000000 (unresolved
nep141:my-token.near)`.

Two details differ from the Ethereum and Solana mapping flags:

* **`@` separates the fields, not `:`.** NEAR Intents asset ids contain their own colons (`nep141:wrap.near`), so a colon-delimited format would truncate the id at its first embedded colon.
* **The CLI signs each entry it loads** with its local development key, because it runs the require-signed posture — an unsigned entry is dropped rather than trusted. That key is allowlisted only in builds that enable the `dev-signing` feature, which `parser_cli` does and the enclave binary does not. The posture is a per-deployment choice, not a property of the parser: `parser_cli` pins require-signed, while `parser_app` registers the converter with the permissive default.

Repeat the flag for each asset. A mapping that fails to parse or whose file can't be read is reported and skipped; the rest still load.

### Configuring curator keys

Signer identity is resolved per origin chain from three environment variables, each a comma-separated list of hex public keys:

| Variable                        | Curve     | Vouches for                         |
| ------------------------------- | --------- | ----------------------------------- |
| `VISUALSIGN_NEAR_TOKEN_SIGNERS` | ed25519   | NEAR-native assets                  |
| `VISUALSIGN_ETH_TOKEN_SIGNERS`  | secp256k1 | Ethereum-origin and EVM-twin assets |
| `VISUALSIGN_SOL_TOKEN_SIGNERS`  | ed25519   | Solana-origin and SVM-twin assets   |

Each populates only its own domain, so a key trusted for Ethereum-origin assets does not thereby vouch for NEAR-native ones. A variable left unset leaves that domain with no enrolled curators, and a signature checked against it is never recognized.

An unset domain is fail-closed but not inert: under the permissive posture the entry is still accepted as a gap fill for an asset the seed table does not cover, and marked unverified. It cannot override a curated seed. Under the require-signed posture it is refused outright. So a deployment that wants signed token metadata to count as verified has to enrol the curator keys; without them, supplying a signature has the same effect as omitting one.

## Implementation details

Source code available at:

* [NEAR Parser](https://github.com/anchorageoss/visualsign-parser/tree/main/src/chain_parsers/visualsign-near)
* [Intents preset](https://github.com/anchorageoss/visualsign-parser/tree/main/src/chain_parsers/visualsign-near/src/presets/intents)
* [parser\_cli NEAR plugin](https://github.com/anchorageoss/visualsign-parser/tree/main/src/chain_parsers/visualsign-near/src/cli_plugin.rs)

## Resources

* [NEAR Intents documentation](https://docs.near-intents.org)
* [near/intents (Defuse Protocol) repository](https://github.com/near/intents)
* [NEAR Protocol documentation](https://docs.near.org)
