feat(near): name every intent, and flag the ones that hand over authority - #460
Open
shahan-khatchadourian-anchorage wants to merge 1 commit into
Conversation
shahan-khatchadourian-anchorage
force-pushed
the
shahankhatchadourian/near-g-sanitize-intent-strings
branch
from
August 7, 2026 03:29
e8db95f to
11c0de9
Compare
shahan-khatchadourian-anchorage
force-pushed
the
shahankhatchadourian/near-h-intent-type
branch
from
August 7, 2026 03:29
33821a3 to
b374c87
Compare
shahan-khatchadourian-anchorage
force-pushed
the
shahankhatchadourian/near-g-sanitize-intent-strings
branch
from
August 7, 2026 23:22
11c0de9 to
c941470
Compare
…rity
The intent type never reached the screen. serde consumes the `intent` tag
to select the variant, so the value was known and dropped, leaving
`transfer` and `ft_withdraw` distinguishable only by the presence of one
`Token` field under an identical title -- an instant, irreversible internal
transfer reading like a withdraw. A batch had no boundary either, so a
second intent's fields ran on from the first's.
Emit an `Intent` field naming the variant, carrying its position when the
envelope holds more than one, and title a single-intent envelope after it
(`NEAR Intent: Transfer`). This mirrors `action_boundary_field` and
`title_for` on the transaction path, with one deliberate difference: the
field renders for a lone intent too, because a signed batch nests intents
inside per-payload sections and a section holding one intent has nothing
else to name its type.
Intent: 1 of 2: Transfer
To: bob.near
Amount: 1 wNEAR
Intent: 2 of 2: Add Public Key
Add Public Key: ed25519:8rVv...
[warn] account-control: this key gains permanent authority over the
account's entire intents balance, until it is explicitly removed
The four account-control intents each rendered as one unremarkable line
while handing over authority rather than moving a named amount. Appended
to a legitimate swap they read as part of it. Each now carries a warning
naming the consequence, the treatment `actions.rs` already gives `AddKey`.
Two intents that cannot execute also rendered clean. An empty `intents`
list does nothing but still spends the signer's nonce, and a transfer to
the signer's own account is refused by `Transfer::execute_intent` -- the
same class as the expired-deadline check beside them.
`try_render_single_intent` returns the title alongside the fields rather
than making the caller re-parse the envelope to derive it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
shahan-khatchadourian-anchorage
force-pushed
the
shahankhatchadourian/near-h-intent-type
branch
from
August 7, 2026 23:22
b374c87 to
5e3c34b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #459.
The intent type reaches the screen
serde consumes the
intenttag to select the variant, so the type is known and was dropped. Without it,transferandft_withdrawdiffer only by the presence of oneTokenfield under an identical title — an instant, irreversible internal transfer reads like a withdraw. A batch had no boundary either, so a second intent's fields ran on from the first's.A single-intent envelope titles after its type:
NEAR Intent: FT Withdraw.This mirrors
action_boundary_fieldandtitle_foron the transaction path, with one deliberate difference: theIntentfield renders for a lone intent too, whereaction_boundary_fieldomits itself because the title covers it. A signed batch nests intents inside per-payload sections, so a section holding one intent has nothing else naming its type — its title comes from the enclosing borsh transaction.Intents that hand over authority
add_public_key,remove_public_key,set_auth_by_predecessor_idandauth_calleach rendered as one unremarkable line while granting authority rather than moving a named amount. Appended to a legitimate swap they read as part of it. Each now carries a warning naming the consequence — the treatmentactions.rsalready gives theAddKeyaction, which it breaks out field by field because "their absent forms widen the grant".Two intents that cannot execute
Both rendered clean, the same way an expired deadline would without the check beside them:
intentslist does nothing but still spends the signer's nonce — a real no-op nonce burn a user can be tricked into signing.Transfer::execute_intentwithInvalidIntent.API
try_render_single_intentreturns aRenderedEnvelopecarrying the title alongside the fields, rather than making the caller re-parse the envelope to derive it.Payload shape
This changes the emitted payload, so wallet renderers and any downstream snapshots need to expect:
TextV2labelledIntentper intentTitlefor single-intent envelopesUpdated here: the e2e expected payload, the CLI display fixture, a CLI title assertion, and the two docs pages showing rendered output.
Scope
Purely additive to the field list. P9 — the
Networkfield and network cross-check on the intents path — is deliberately not here, since it needs wallet-side coordination on a different axis; it follows as its own PR.Testing
203 tests pass with default features, 187 with
--no-default-features, clippy clean, fullmake testgreen.🤖 Generated with Claude Code