Skip to content

feat(near): port NEAR Intents preset (decode + render) and merge converters - #428

Draft
shahan-khatchadourian-anchorage wants to merge 1 commit into
shahankhatchadourian/near-a2-chainfrom
shahankhatchadourian/near-a3-intents
Draft

feat(near): port NEAR Intents preset (decode + render) and merge converters#428
shahan-khatchadourian-anchorage wants to merge 1 commit into
shahankhatchadourian/near-a2-chainfrom
shahankhatchadourian/near-a3-intents

Conversation

@shahan-khatchadourian-anchorage

@shahan-khatchadourian-anchorage shahan-khatchadourian-anchorage commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Third PR of the NEAR-consolidation stack (stacked on #426). Adds the NEAR Intents preset -- decode + render for the signed-envelope and pre-signature flows -- and merges the crate's two input-format handlers into one.

Stack: A1 (#425) -> A2 (#426) -> A3 (this) -> A4 (signature verification) -> B (identity + wiring) -> C (docs site).

What this PR does

Adds chain_parsers/visualsign-near/src/presets/intents/:

  • mod.rs: entry points (try_decode_execute_intents, try_render_single_intent), NearIntentsError, NearTokenRegistry/TokenMeta.
  • args.rs: JSON deserialization of execute_intents args.
  • tokens.rs: seeded NEP-141 token table + amount formatting.
  • render.rs: renders all 11 intent variants (TokenDiff, Transfer, FtWithdraw, NftWithdraw, MtWithdraw, NativeWithdraw, AddPublicKey, RemovePublicKey, SetAuthByPredecessorId, StorageDeposit, AuthCall) plus the signature-status section.
  • verify.rs: partial -- only verify_and_extract and the secp256k1 recovery-id guard (render.rs calls these directly to compile). The dedicated signed-vector test module is scoped to A4 (signature verification specifically), since it needs fixture files and dev-dependencies (k256, bs58) not yet added.

near_env_shim.rs does not carry over -- a prior investigation this session found near-sdk 5.27+ (what this workspace resolves) already fixes the native-linking gap that module worked around, so visualsign-near keeps the workspace's unsafe_code = "forbid" outright, no carve-out.

The converter merge (the one non-mechanical step)

tx.rs's NearTransaction becomes an enum:

pub enum NearTransaction {
    OnChain(near_primitives::transaction::Transaction),
    Intent(String), // validated DefusePayload JSON, kept as text
}

from_string tries borsh decode first (via the existing hex/base64 detection); on failure, it validates the input as a DefusePayload JSON envelope. Input that is neither is rejected. This replaces two separate converters -- one per input format, each registered under its own custom chain name -- with a single type discriminating by format, matching the target design (one chain identity, format-discriminated).

convert.rs dispatches on the enum: OnChain renders as before (A2's baseline fields) plus the execute_intents decode branch A2 deliberately omitted, now calling the real preset when the action is a FunctionCall to intents.near/execute_intents; Intent renders via try_render_single_intent, titled "NEAR Intent", with no signature section (nothing is signed yet at that stage).

Security fix folded in

Self-review of this port surfaced a signing-integrity gap: FtWithdraw/NftWithdraw/MtWithdraw (verified against the pinned defuse-core source) carry an optional storage_deposit field -- an unconditional, unrefundable wNEAR debit alongside the withdraw -- and an optional msg field that switches the call into its _transfer_call form. Neither was rendered: a user signing "withdraw 1 wNEAR" could be authorizing an additional, invisible debit of arbitrary size with zero indication.

Fixed: both fields now render on all three withdraw renderers via a shared helper, with new regression tests (*_renders_storage_deposit_and_message, ft_withdraw_omits_message_and_storage_deposit_when_absent) covering both presence and absence.

Verification

  • cargo test -p visualsign-near -- 45/45 pass, under both default and --features diagnostics.
  • cargo clippy -p visualsign-near --all-targets -- -D warnings, cargo fmt --check -- clean.
  • cargo check/clippy -p parser_app --no-default-features --features near -- clean (narrow-build variant).
  • make -C src lint, make -C src test -- clean (default features; stock builds unaffected).
  • Internal code review and security review both run against this diff; the one finding (storage_deposit/msg) is fixed above with test coverage.

@shahan-khatchadourian-anchorage shahan-khatchadourian-anchorage changed the title shahankhatchadourian/near a3 intents feat(near): port NEAR Intents preset (decode + render) and merge converters Jul 30, 2026
…erters

Ports the NEAR Intents protocol decoder into
chain_parsers/visualsign-near/src/presets/intents/: envelope parsing,
all 11 intent renderers, execute_intents dispatch, and the
pre-signature single-intent render. crate:: references become super::
since the module moves from a standalone crate root into a nested
presets::intents module.

near_env_shim.rs does not carry over -- this session's earlier
investigation found near-sdk 5.27+ (what this workspace resolves)
already fixes the native-linking gap it worked around, so
visualsign-near keeps the workspace's unsafe_code = "forbid" with no
carve-out. verify.rs ports only its core functions (verify_and_extract,
the secp256k1 recovery-id guard), since render.rs calls them directly
to compile; dedicated signed-vector test coverage for verification is
scoped to the next PR in the stack.

The converter merge (the one non-mechanical step): tx.rs's
NearTransaction becomes an enum, OnChain(Transaction) or Intent(String),
discriminating borsh-vs-JSON input -- borsh decode attempted first,
falling through to DefusePayload JSON validation only on failure, input
that is neither rejected. This replaces two separate converters (one
per input format, each its own custom chain) with one type.
convert.rs re-adds the execute_intents decode branch A2 omitted, now
calling the real preset, plus a new branch rendering the Intent variant.

Security review of this port found a signing-integrity gap:
FtWithdraw/NftWithdraw/MtWithdraw carry an optional storage_deposit (an
unconditional, unrefundable wNEAR debit alongside the withdraw) and msg
(switches the call into its _transfer_call form) that were never
surfaced -- a user could sign a withdraw and see only the nominal
amount while authorizing an invisible additional debit. Fixed here:
both fields now render on all three withdraw variants, with regression
tests covering presence and absence.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds NEAR Intents support to visualsign-near by introducing an intents “preset” (decode + render) and merging the crate’s previously separate input-format handling into a single NearTransaction enum that discriminates between on-chain borsh transactions and pre-signature intents JSON envelopes.

Changes:

  • Introduces presets::intents with JSON args decoding, token metadata/formatting, rendering for supported intent variants, and partial signature verification plumbing.
  • Merges NEAR parsing into NearTransaction::{OnChain, Intent} and updates the converter to dispatch/render accordingly (including execute_intents decoding for intents.near).
  • Adds crate feature wiring for diagnostics passthrough (visualsign/diagnostics) and updates tests to cover the new paths.

Reviewed changes

Copilot reviewed 10 out of 11 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/chain_parsers/visualsign-near/src/tx.rs Parses either borsh NEAR transactions or validates/stores DefusePayload JSON for intent signing flows.
src/chain_parsers/visualsign-near/src/convert.rs Dispatches conversion based on NearTransaction variant and decodes/renders execute_intents calls.
src/chain_parsers/visualsign-near/src/lib.rs Exposes the new presets module.
src/chain_parsers/visualsign-near/src/presets/mod.rs Adds the presets module root and exports intents.
src/chain_parsers/visualsign-near/src/presets/intents/mod.rs Defines the intents preset API, registry types, errors, and integration tests.
src/chain_parsers/visualsign-near/src/presets/intents/args.rs Deserializes execute_intents JSON args into MultiPayload list.
src/chain_parsers/visualsign-near/src/presets/intents/render.rs Renders intents, envelope fields, and signature status into VisualSign fields.
src/chain_parsers/visualsign-near/src/presets/intents/tokens.rs Provides seeded NEP-141 token metadata and unit formatting helpers.
src/chain_parsers/visualsign-near/src/presets/intents/verify.rs Adds signature verification + payload extraction helpers (partial, used by rendering).
src/chain_parsers/visualsign-near/Cargo.toml Adds thiserror and introduces a diagnostics feature wired to visualsign/diagnostics.
src/Cargo.lock Pulls in thiserror resolution for the NEAR crate.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +19 to +21
pub enum NearIntentsError {
#[error("execute_intents args were not valid JSON: {0}")]
ArgsNotJson(String),
Comment on lines +103 to +106
fields.extend(render_signature(standard_name(mp), &check)?);
if let Some(payload) = &extracted {
fields.extend(render_single(payload, registry)?);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants