Skip to content

feat(gas-keys): charge gas from gas key balance, deposit from account#14969

Merged
darioush merged 6 commits into
masterfrom
darioush/gaskey-balances
Feb 4, 2026
Merged

feat(gas-keys): charge gas from gas key balance, deposit from account#14969
darioush merged 6 commits into
masterfrom
darioush/gaskey-balances

Conversation

@darioush

@darioush darioush commented Feb 3, 2026

Copy link
Copy Markdown
Contributor
  • Add gas_cost and deposit_cost fields to TransactionCost (precomputed in calculate_tx_cost)
  • Split transaction cost charging for gas key transactions.
  • Add NotEnoughGasKeyBalance error variant for insufficient gas key balance.
  • Change verify_and_charge_gas_key_tx_ephemeral signature to take &mut AccessKey so it can modify the gas key balance

Testing changes:

  • Split test_gas_key_tx_not_enough_balance into test_gas_key_tx_not_enough_gas_key_balance and test_gas_key_tx_not_enough_account_balance_for_deposit,
  • Updated apply & test-loop tests to fund gas keys and assert balances.

Will update protocol_schema.toml and openapi.json after review.

Out of scope for this PR:

  • Processing refunds to gas keys (for now, they will go to account balance).
  • Charging for failed transaction.

@darioush darioush marked this pull request as ready for review February 3, 2026 00:57
@darioush darioush requested a review from a team as a code owner February 3, 2026 00:57
Copilot AI review requested due to automatic review settings February 3, 2026 00:57
@codecov

codecov Bot commented Feb 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.52055% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.84%. Comparing base (3886ec4) to head (a9956b4).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
core/primitives/src/errors.rs 0.00% 5 Missing ⚠️
runtime/runtime/src/config.rs 83.33% 0 Missing and 2 partials ⚠️
runtime/runtime/src/verifier.rs 99.21% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           master   #14969       +/-   ##
===========================================
+ Coverage    1.30%   68.84%   +67.54%     
===========================================
  Files         725      919      +194     
  Lines      140594   201273    +60679     
  Branches   140594   201273    +60679     
===========================================
+ Hits         1835   138569   +136734     
+ Misses     138706    56739    -81967     
- Partials       53     5965     +5912     
Flag Coverage Δ
pytests-nightly 1.30% <0.00%> (-0.01%) ⬇️
unittests 68.46% <93.15%> (?)
unittests-nightly 68.40% <94.52%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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

Implements the new gas-keys charging model by splitting transaction costs into gas vs deposit components, charging gas from the gas key balance while charging deposits from the signer account balance, and updating tests accordingly.

Changes:

  • Extend TransactionCost with gas_cost and deposit_cost computed in calculate_tx_cost.
  • Update gas key verification to deduct gas_cost from the gas key balance and deposit_cost from the account balance, adding NotEnoughGasKeyBalance.
  • Update runtime/test-loop tests to fund gas keys and assert the split balance effects.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
test-loop-tests/src/tests/gas_keys.rs Funds gas keys in test-loop and asserts deposit vs gas charging split using RPC views/outcomes.
runtime/runtime/src/verifier.rs Changes gas key verification to mutate access key balance and charge split costs; adds/updates tests.
runtime/runtime/src/tests/apply.rs Updates apply test to fund gas key and assert account/deposit vs gas-key/gas cost split; uses tx_cost.
runtime/runtime/src/config.rs Adds gas_cost/deposit_cost to TransactionCost and computes them in calculate_tx_cost.
core/primitives/src/views.rs Adds helpers for reading gas key balance from permission view and summing tokens burnt in outcomes.
core/primitives/src/errors.rs Adds InvalidTxError::NotEnoughGasKeyBalance and its Display formatting.

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

Comment thread core/primitives/src/views.rs Outdated
Ok(TransactionCost { gas_burnt, gas_remaining, receipt_gas_price, total_cost, burnt_amount })
let gas_cost = safe_add_balance(burnt_amount, remaining_gas_amount)?;
let deposit_cost = total_deposit(actions)?;
let total_cost = safe_add_balance(gas_cost, deposit_cost)?;

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.

nit: I think having it as a member function on TransactionCost is cleaner

@darioush darioush Feb 4, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think it's better to do the addition and overflow check as part of calculate_tx_cost.

If we move to member functions, we have awkward choices:

  1. Methods return Result -- callers now deal with errors from what looks like a getter, and the overflow rejection no longer happens at construction time. (i.e., we still should check total_cost does not overflow for gas keys transactions, even though we charge gas and deposit separately).
  2. Check overflow at construction, then recompute in methods, doing the work twice.

Let me know if you have a good solution here.

Comment thread core/primitives/src/views.rs Outdated
}
}

impl FinalExecutionOutcomeView {

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.

I'd rather avoid adding such helper functions as part of primitives since it is part of the public API now and we have to maintain it forever

@darioush darioush requested a review from frol as a code owner February 4, 2026 14:10
@darioush darioush enabled auto-merge February 4, 2026 14:15
@darioush darioush added this pull request to the merge queue Feb 4, 2026
Merged via the queue into master with commit fb83c9a Feb 4, 2026
31 checks passed
@darioush darioush deleted the darioush/gaskey-balances branch February 4, 2026 14:45
pull Bot pushed a commit to See887777/nearcore that referenced this pull request Jun 3, 2026
# Gas Keys ([NEP-611](near/NEPs#611))
Gas keys are a new type of access key that carries a pre-funded NEAR
balance and multiple independent nonce sequences. When a transaction is
signed with a gas key, gas costs are deducted from the gas key's balance
rather than the signer's account balance (deposits like NEAR transfers
still come from the account).

## Why do we need gas keys?
- Enabling SPICE without a DoS vector. The SPICE project (Separation of
Execution from Consensus) decouples transaction execution from block
production: transactions are included in blocks first, and executed
asynchronously later. This means chunk producers can no longer verify
account balances at inclusion time. An attacker could drain an account's
balance in one transaction, then flood the chain with many transactions
signed by that account. All would be accepted (since execution hasn't
caught up), consuming block space for free when they inevitably fail.
Gas keys close this gap by requiring gas to be pre-committed in the key
itself, so chunk producers can verify gas availability without access to
the latest state.

- Parallel transaction submission. Today, programmatic users who submit
many transactions in parallel must manage multiple access keys (each
with its own nonce sequence). A gas key supports up to 1,024 independent
nonce slots via a single key, removing that complexity.

# Context

- NEP (if exists): near/NEPs#611
- Implementation: 
 - near#14868
 - near#14869
 - near#14883
 - near#14891
 - near#14892
 - near#14911
 - near#14924
 - near#14925
 - near#14930
 - near#14933
 - near#14969
 - near#14985
 - near#15033
 - near#15034
 - near#15067
 - near#15068
 - near#15100
 - near#15101
 - near#15186

# Testing and QA
- unit tests
- test-loop:
https://github.com/near/nearcore/blob/master/test-loop-tests/src/tests/gas_keys.rs
- forknet (automated): near#15207
- forknet (manual testing, planned)

# Checklist
- [x] Link to nightly nayduck run:
https://nayduck.nearone.org/#/run/4756
- [x] Update CHANGELOG.md to include this protocol feature in the
`Unreleased` section.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants