Conversation
…refund (#64) register_agent previously required `attached_deposit >= storage_cost` and kept any excess, so an agent overpaying for its storage deposit silently lost the difference. Require the deposit to match the storage cost exactly instead: the exact cost for a first-time registration, and exactly zero for a re-registration (which reuses the existing slot). The contract never holds more than it needs and never has to refund. Removal still does not refund the deposit, so a removed-then-re-registering agent pays the storage cost again — kept as-is for simplicity and now documented on remove_agent. Update shade-agent-js's default register deposit from 0.005 to the exact 0.00486 NEAR so register() keeps working against the exact check, and update the agent-contract / api docs accordingly.
There was a problem hiding this comment.
Pull request overview
This PR updates the agent registration flow in shade-contract-template to require an exact attached deposit (no overpayment/no refund) and aligns the JS client + reference docs with the new contract rule, addressing issue #64 (point 1).
Changes:
- Contract:
register_agentnow requiresattached_deposit == required_deposit(exact storage cost for first-time registration, exactly0for re-registration). - JS client: updates the default register deposit to
0.00486 NEAR(4860000000000000000000 yocto) soregister()continues to work against the updated contract. - Tests/docs: unit tests and reference docs updated to reflect the exact-deposit semantics and the “re-register attaches 0” rule.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| shade-contract-template/src/lib.rs | Enforces exact attached deposit and centralizes storage-cost calculation. |
| shade-contract-template/src/internal/unit_tests.rs | Updates registration deposit fixtures and adds regression tests for overpay / re-register deposit rules. |
| shade-agent-js/src/api.ts | Updates default register deposit constant + JSDoc to match contract behavior. |
| shade-agent-js/tests/unit/api.test.ts | Updates unit test fixture values to the new default deposit. |
| docs/reference/agent-contract.md | Updates contract reference to document and illustrate exact-deposit behavior. |
| docs/reference/api.md | Updates API docs for register() deposit defaults and behavior table. |
| &format!( | ||
| "Attached deposit must be exactly the storage cost {}", | ||
| required_deposit.exact_amount_display() | ||
| ) | ||
| ); |
There was a problem hiding this comment.
Fixed in 6b4ae88. Reworded to "Attached deposit must be exactly {}" — accurate for re-registration too, where required = 0 (reads "…exactly 0 NEAR").
| // New agents pay the exact storage cost; re-registration reuses the existing slot and must | ||
| // attach nothing. The deposit must match exactly — the contract never refunds. | ||
| let required_deposit = if already_registered { | ||
| NearToken::from_yoctonear(0) | ||
| } else { |
There was a problem hiding this comment.
Fixed in 6b4ae88. The sandbox integration tests now use the exact deposit: tests/helpers/mod.rs DEPOSIT_005_NEAR → EXACT_STORAGE_DEPOSIT (0.00486 NEAR), with all helpers::-referencing calls updated, and README.md updated. Since the deposit check runs before verify_attestation, this matters for the measurement/PPID failure-path tests too. (Note: the sandbox suite isn't run in the PR gate — flagged for the maintainer e2e run.)
| // First-time registration requires the exact storage stake: zero attached deposit must fail | ||
| #[test] | ||
| #[should_panic(expected = "Attached deposit must be greater than storage cost")] | ||
| #[should_panic(expected = "Attached deposit must be exactly the storage cost")] |
There was a problem hiding this comment.
Fixed in 6b4ae88. Expectation updated to "Attached deposit must be exactly" to match the reworded message.
| // First-time registration: attached deposit below storage cost must fail | ||
| #[test] | ||
| #[should_panic(expected = "Attached deposit must be greater than storage cost")] | ||
| #[should_panic(expected = "Attached deposit must be exactly the storage cost")] |
There was a problem hiding this comment.
Fixed in 6b4ae88. Expectation updated to "Attached deposit must be exactly" to match the reworded message.
|
|
||
| // First-time registration: attached deposit above storage cost must fail (no overpayment, no refund) | ||
| #[test] | ||
| #[should_panic(expected = "Attached deposit must be exactly the storage cost")] |
There was a problem hiding this comment.
Fixed in 6b4ae88. Expectation updated to "Attached deposit must be exactly" to match the reworded message.
|
|
||
| // Re-registration must attach exactly zero: any deposit on an already-registered agent must fail | ||
| #[test] | ||
| #[should_panic(expected = "Attached deposit must be exactly the storage cost")] |
There was a problem hiding this comment.
Fixed in 6b4ae88. Expectation updated to "Attached deposit must be exactly" to match the reworded message.
|
/claude-review |
|
/claude-review |
Code reviewSummarized: Found 3 issues:
shade-agent-framework/tests-in-tee/test-script.js Lines 948 to 959 in 8adec7f
shade-agent-framework/shade-agent-js/src/api.ts Lines 188 to 205 in 8adec7f
shade-agent-framework/shade-agent-js/src/api.ts Lines 50 to 51 in 8adec7f |
- Update the sandbox integration tests and tests-in-tee scenario that still
attached 0.005 NEAR to register_agent. Since the exact-deposit check runs
before verify_attestation, the old 0.005 deposit now panics before reaching
the intended path: the contract integration tests (helpers DEPOSIT_005_NEAR
-> EXACT_STORAGE_DEPOSIT) failed their success/measurement/PPID assertions,
and tests-in-tee test-different-account-id no longer reached the report_data
mismatch it asserts. Use the exact 0.00486 NEAR. (Claude HIGH, Copilot)
- Reword the deposit panic from "...exactly the storage cost {}" to
"...exactly {}" so it reads correctly for re-registration (required = 0),
and update the dependent unit-test expectations and the agent-contract doc
snippet. (Copilot)
- Document that register({ forceDeposit: true }) is first-registration-only:
it always attaches a non-zero deposit and now fails on re-registration,
which the default contract requires to attach exactly 0. (Claude MEDIUM, Copilot)
- Cross-reference the contract STORAGE_BYTES_TO_REGISTER and the shade-agent-js
DEFAULT_REGISTER_DEPOSIT_YOCTO so the exact-match coupling is documented. (Claude LOW)
Update shade-contract-template/README.md for the exact deposit.
|
/claude-review |
| // register_agent requires this exact storage cost. The shade-agent-js client hardcodes the | ||
| // matching default deposit (DEFAULT_REGISTER_DEPOSIT_YOCTO); changing this value must be mirrored there. | ||
| const STORAGE_BYTES_TO_REGISTER: u128 = 486; |
There was a problem hiding this comment.
Fixed in 4535748 — reworded to "A first-time register_agent must attach exactly this storage cost; re-registration attaches 0." so the comment no longer implies the deposit is always required.
Code reviewOverall this is a clean, well-synced change: Found 4 issues:
shade-agent-framework/shade-contract-template/src/lib.rs Lines 98 to 104 in 6b4ae88
shade-agent-framework/docs/reference/api.md Lines 122 to 124 in 6b4ae88
|
…istration (#95) Address Copilot review finding: the comment above STORAGE_BYTES_TO_REGISTER read as if register_agent always requires the deposit, but re-registration requires exactly 0. Reword to "first-time registration … re-registration attaches 0".
|
Thanks — addressing the 4 findings from the latest review (head now
|
|
/claude-review |
| #[allow(dead_code)] | ||
| pub const DEPOSIT_005_NEAR: NearToken = NearToken::from_yoctonear(5_000_000_000_000_000_000_000); // 0.005 NEAR | ||
| pub const EXACT_STORAGE_DEPOSIT: NearToken = | ||
| NearToken::from_yoctonear(4_860_000_000_000_000_000_000); |
There was a problem hiding this comment.
Keeping #[allow(dead_code)] — it's required, not removable. The helpers module is compiled into every integration-test binary, and owner_operations_tests / update_contract_tests include it but don't reference EXACT_STORAGE_DEPOSIT. Verified: removing the attribute produces constant EXACT_STORAGE_DEPOSIT is never used warnings in both of those test crates.
Code reviewThis PR tightens Security, access-control, attestation-verification ordering (the deposit Found 3 issues:
shade-agent-framework/shade-contract-template/src/lib.rs Lines 99 to 105 in 4535748
shade-agent-framework/shade-contract-template/src/lib.rs Lines 142 to 146 in 4535748
shade-agent-framework/shade-agent-js/src/api.ts Lines 191 to 194 in 4535748 |
…ther (#95) Address Claude review finding [MED:75]: the exact-deposit check means an old client (0.005 default) fails first-time registration against the new contract. Add an upgrade-coordination callout to the register-agent reference.
|
Thanks — addressing the 3 findings (head now
|
|
/claude-review |
Code reviewFound 3 issues (all LOW — no CRITICAL/HIGH/MEDIUM survived scrutiny). The core change is solid: the attestation verification path in
|
…n note (#95) Address Claude review finding [LOW:55]: the previous note implied an expired attestation evicts the agent, but removal is lazy (only in require_valid_agent on an agent-gated call) or manual. An expired-but-still-mapped agent re-registers with a 0 deposit; the storage cost is only re-charged after the agent is actually removed from the map.
|
Thanks — addressing the 3 LOW findings (head now
|
|
/claude-review |
Code reviewFound 2 issues (both low/medium, well-mitigated — no blocking concerns):
shade-agent-framework/shade-contract-template/src/lib.rs Lines 100 to 104 in e5d6c9c Everything else checks out. Reviewed for security, architecture/cross-package sync, bugs, and production concerns:
|
|
|
||
| > [!NOTE] | ||
| > A removed agent can re-register by calling `register_agent` with a valid attestation. | ||
| > A removed agent can re-register with a valid attestation. Because removal does not refund the storage deposit, re-registering an agent that was *actually removed from the map* is treated as a first-time registration and must attach the storage cost again. Removal is either manual (`remove_agent`) or lazy: an invalid agent (expired attestation, un-approved measurements/PPID) is evicted only when it next calls an agent-gated method — not by `register_agent` itself. An agent that is still in the map (e.g. its attestation expired but it hasn't been evicted yet) re-registers with a `0` deposit, since its slot already exists. |
There was a problem hiding this comment.
Keep the previous statement, A removed agent can re-register by calling register_agent with a valid attestation.
| An agent must attach 0.00486 NEAR to cover its own storage cost in the contract. If you change how much data is stored per agent, update the `STORAGE_BYTES_TO_REGISTER` constant accordingly. | ||
| For a first-time registration an agent must attach **exactly** 0.00486 NEAR to cover its own storage cost in the contract. The deposit must match the storage cost exactly — the contract never refunds an overpayment, so attaching more is rejected. Re-registering an already-registered agent uses no new storage, so it must attach exactly `0`. If you change how much data is stored per agent, update the `STORAGE_BYTES_TO_REGISTER` constant accordingly. | ||
|
|
||
| > [!IMPORTANT] |
|
Superseded by #103. Closing this in favor of a require-≥-and-refund-the-excess design instead of the exact |
Closes #64
What & why
register_agentrequiredattached_deposit >= storage_costand kept any excess, so an agent that overpaid its storage deposit silently lost the difference (issue #64, point 1). This changes the check to require the deposit to match the storage cost exactly — no overpayment is possible, and the contract never has to refund:STORAGE_BYTES_TO_REGISTER × storage_byte_cost= 0.00486 NEAR.Issue #64 point 2 (a removed agent re-registering pays the storage deposit again, since removal doesn't refund) is intentionally kept as-is for simplicity per discussion — now explicitly documented on
remove_agent.Because the contract now rejects any deposit other than the exact amount,
shade-agent-js's default register deposit had to move from0.005→0.00486NEAR soregister()keeps working.Files changed
shade-contract-template/src/lib.rs—register_agentnow computesrequired_deposit(exact storage cost for new agents, 0 for re-registration) and requiresattached_deposit == required_deposit; added aContract::agent_storage_cost()helper as the single source of truth for the cost.shade-contract-template/src/internal/unit_tests.rs— test deposit constants reworked (EXACT_STORAGE_DEPOSIT,DEPOSIT_BELOW_COST,DEPOSIT_ABOVE_COST); registration setups now attach the exact amount; updated the two insufficient-deposit panic-message expectations.shade-agent-js/src/api.ts—DEFAULT_REGISTER_DEPOSIT_YOCTO→4860000000000000000000, JSDoc updated.shade-agent-js/tests/unit/api.test.ts— default-deposit fixture updated to the exact amount.docs/reference/agent-contract.md—register_agentblock + storage note rewritten for the exact-deposit/no-refund rule;remove_agentnote now states re-registration pays again.docs/reference/api.md—register()default deposit, parameter description, example, and deposit-selection table updated.Tests added/updated
New unit tests in
shade-contract-template(run undercargo test --lib):test_register_agent_errors_when_storage_deposit_exceeds_cost— regression for Fix agent deposits #64: overpaying (0.005) is now rejected rather than silently kept.test_register_agent_errors_when_reregister_attaches_deposit— re-registration must attach exactly 0.test_agent_storage_cost_matches_expected— guards the hardcodedEXACT_STORAGE_DEPOSITagainst storage-byte-cost drift.Verified locally:
shade-contract-templatecargo fmt/cargo clippy --all-targets(no new warnings) /cargo test --lib(54 pass);shade-agent-jsnpm run build+npm test(275 pass);shade-agent-templatetscclean.Follow-up / maintainer notes
tests-in-tee/(/run-e2e) must be run by a maintainer — it exercises the live registration flow under attestation (thesuccessful-registrationscenario), which is the only place this change is proven end-to-end. The e2e image buildsshade-agent-jsfrom the local workspace, so the contract + library changes stay consistent there.shade-agent-jschanges must ship together — an old publishedshade-agent-js(0.005 default) would fail registration against the new exact check. Version bump for@neardefi/shade-agent-js(currently 2.2.0) left to the release process.==check is sensitive to NEAR's per-byte storage price (today1e19yocto/byte). If that protocol parameter ever changes, the exact amount changes and clients must update; this is the trade-off accepted in choosing exact-deposit over a>=+refund design.🤖 Generated with Claude Code
Release impact
@neardefi/shade-agent-js— minor. The default register deposit changed (0.005 → 0.00486 NEAR). Backward-compatible for library upgraders (0.00486 satisfies both the old>=contract and the new==contract), and it signals the coupled contract change. Version bump is handled onmainper the release process (not in this PR).