Skip to content

feat: allow withdrawing assets from pool to owner#12

Open
aleksuss wants to merge 17 commits intomainfrom
feat/withdraw-from-pool
Open

feat: allow withdrawing assets from pool to owner#12
aleksuss wants to merge 17 commits intomainfrom
feat/withdraw-from-pool

Conversation

@aleksuss
Copy link
Copy Markdown
Member

@aleksuss aleksuss commented Sep 29, 2025

It's a duplicate of #9

We need a function to withdraw assets from TEE solver's pool back to owner before the adding/removing liquidity pool features are ready.

This is not a problem for the non-TEE AMM solver because the person who runs the AMM solver owns the private key of the solver account, but no one has access to the TEE solver's key pairs. We need to allow the solver registry's owner to withdraw assets from pool when needed.

Summary by CodeRabbit

  • New Features

    • Admin withdrawal flow now supports cross-contract token withdrawals; public key add/remove and token-withdraw endpoints added.
    • Multi-token balances and TokenId parsing/formatting added to the mock environment.
  • Changes

    • Events now use owned pool_id/timestamp values; deposit/withdraw flows use unified minimal-deposit handling, callbacks, and improved error handling.
  • Breaking

    • On-chain upgrade/migrate endpoints removed.
  • Tests

    • Integration tests for withdraw success and error scenarios; new test helpers added.
  • CI/Build

    • Reproducible-build image, workflows, and makefile build/test ordering updated.

think-in-universe and others added 11 commits September 14, 2025 00:49
# Conflicts:
#	contracts/intents-vault/res/intents_vault.wasm
#	contracts/intents-vault/src/lib.rs
#	contracts/mock-intents/res/mock_intents.wasm
#	contracts/mock-intents/src/lib.rs
#	contracts/solver-registry/res/solver_registry.wasm
#	contracts/solver-registry/src/admin.rs
#	contracts/solver-registry/src/events.rs
#	contracts/solver-registry/src/ext/intents_vault.rs
@aleksuss aleksuss requested review from birchmd and mrLSD September 29, 2025 15:32
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Sep 29, 2025

Walkthrough

Adds multi-token accounting and FT withdrawal paths, a TokenId type, PromiseOrValue ft_withdraw signatures and callbacks, ONE_YOCTO constant, event payloads changed to owned values, removal of the upgrade module, new integration tests, and CI/Makefile/Cargo metadata updates.

Changes

Cohort / File(s) Summary
Intents Vault interface & entrypoint
contracts/intents-vault/src/lib.rs
Adds public methods add_public_key, remove_public_key, and ft_withdraw(...)->PromiseOrValue<U128]; implements Contract::ft_withdraw asserting 1 yocto and delegating to external intents contract; imports PromiseOrValue and json_types::U128.
Mock Intents — multi-token support & TokenId
contracts/mock-intents/src/lib.rs, contracts/mock-intents/src/token.rs, contracts/mock-intents/src/token_receiver.rs
Adds mt_balances: LookupMap<(TokenId, AccountId), Balance> and Prefix::MultiTokenBalances; introduces TokenId type with parsing/formatting; implements multi-token helpers, ft_withdraw, on_ft_withdraw, mt_balance_of, mt_batch_balance_of; updates ft_on_transfer to use TokenId and deposit into mt_balances.
Solver Registry — external bindings
contracts/solver-registry/src/ext/intents_vault.rs
Adds external method IntentsVaultContract::ft_withdraw(...)->PromiseOrValue<U128> and imports PromiseOrValue and json_types::U128.
Solver Registry — admin, pool flows & events
contracts/solver-registry/src/admin.rs, contracts/solver-registry/src/pool.rs, contracts/solver-registry/src/lib.rs, contracts/solver-registry/src/events.rs
Adds withdraw_from_pool(...) -> PromiseOrValue<U128> and callback on_withdraw_from_pool; makes some functions payable; refactors deposit callback signature and gas; introduces ONE_YOCTO constant and replaces inline yocto constructions; changes event fields from references to owned values; adds FullAccessKeyAdded, AssetDeposited, AssetWithdrawn events; adds validations for pool creation and deposit error handling.
Solver Registry — remove upgrade module
contracts/solver-registry/src/upgrade.rs
Removes the NEAR upgrade/migrate implementation and related imports.
Solver Registry — tests & test utils
contracts/solver-registry/tests/common/utils.rs, contracts/solver-registry/tests/test_withdraw_assets.rs
Adds test helpers to query mock-intents balances and to call withdraw_from_pool; adds integration tests covering successful withdrawals and error cases.
Makefile & CI / Cargo metadata
Makefile, .github/workflows/*, contracts/*/Cargo.toml
Adjusts Makefile targets and adds recursive clean; consolidates GitHub Actions into matrix jobs and updates cargo-near URL/version; bumps reproducible-build Docker images and digests in multiple Cargo.toml files.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Owner
  participant SR as Solver Registry
  participant IV as Intents Vault
  participant IC as Intents Contract

  Owner->>SR: withdraw_from_pool(pool_id, receiver_id, token_id, amount) [+1 yocto]
  SR->>SR: validate pool, token, permissions
  SR->>IV: ft_withdraw(intents_id, token, receiver, amount, memo, msg) [+1 yocto]
  IV->>IV: assert 1 yocto && caller == parent
  IV->>IC: ft_withdraw(token, receiver, amount, memo, msg)
  IC-->>IV: PromiseOrValue<U128> (used_amount)
  IV-->>SR: PromiseOrValue<U128> (used_amount)
  SR->>SR: on_withdraw_from_pool callback(result)
  alt Ok
    SR->>Owner: return used_amount
    SR-->>SR: emit AssetWithdrawn
  else Err
    SR->>Owner: panic / error
  end
Loading
sequenceDiagram
  autonumber
  actor Funder
  participant SR as Solver Registry
  participant IV as Intents Vault
  participant IC as Intents Contract

  Funder->>SR: deposit_into_pool(pool_id, token_id, amount) [+1 yocto]
  SR->>IV: ft_on_transfer / deposit flow
  IV->>IV: internal_deposit_mt_balance(TokenId, receiver, amount)
  IV->>IC: external FT ops (transfer/resolve)
  IC-->>IV: PromiseOrValue<U128> (used_amount)
  IV-->>SR: used_amount
  SR->>SR: on_deposit_into_pool(pool_id, token_id, amount, result)
  alt Ok and used_amount>0
    SR-->>SR: emit AssetDeposited
    SR->>Funder: refund amount - used_amount
  else Ok and used_amount==0
    SR->>Funder: refund full amount
  else Err
    SR->>Funder: panic / error
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

I hop through promises with a twitching nose,
ONE_YOCTO snug where the pathway goes.
Tokens named and balances spread,
Callbacks sing while events are said.
Tests crunch carrots, CI lights aglow. 🐇

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title concisely and accurately describes the primary feature change—adding the ability for the contract owner to withdraw assets from a pool—using a clear conventional commit prefix and specific language that aligns with the pull request’s objectives.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/withdraw-from-pool

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🧪 Early access (Sonnet 4.5): enabled

We are currently testing the Sonnet 4.5 model, which is expected to improve code review quality. However, this model may lead to increased noise levels in the review comments. Please disable the early access features if the noise level causes any inconvenience.

Note:

  • Public repositories are always opted into early access features.
  • You can enable or disable early access features from the CodeRabbit UI or by updating the CodeRabbit configuration file.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
contracts/solver-registry/tests/common/utils.rs (1)

285-334: Critical: ensure owner is FT-registered before withdrawals

Withdraw sends FT to receiver_id; NEP‑141 requires receiver storage registration. Owner isn’t registered in setup, so withdrawals to owner will fail when FT transfers execute.

Apply in setup_test_environment:

-    // Register accounts for NEP-141 tokens
-    register_accounts_for_tokens(&wnear, &usdc, &[&alice, &bob]).await?;
+    // Register accounts for NEP-141 tokens (owner must be able to receive withdrawals)
+    register_accounts_for_tokens(&wnear, &usdc, &[&owner, &alice, &bob]).await?;
🧹 Nitpick comments (6)
contracts/mock-intents/src/token.rs (2)

12-19: Minor: avoid magic string for default standard

Use a constant to prevent typos and centralize the default.

+const NEP141: &str = "nep141";
@@
 impl From<AccountId> for TokenId {
     fn from(account_id: AccountId) -> Self {
         Self {
             account_id,
-            standard: "nep141".to_string(),
+            standard: NEP141.to_string(),
         }
     }
 }

5-10: Future-proof JSON shape if exposed externally

If TokenId is ever part of the public JSON API, consider serializing as a single "standard:account_id" string (custom Serialize/Deserialize) to match parsing and logs. Current derive will emit an object. Optional now.

Also applies to: 21-31, 33-43

contracts/solver-registry/tests/test_withdraw_assets.rs (1)

81-93: Optional: assert receiver balance increases

For stronger E2E validation, also assert owner’s FT balances increased by the withdrawal amounts (after registering owner storage as suggested).

Also applies to: 119-131

contracts/solver-registry/src/pool.rs (2)

17-17: Callback gas likely too low if you add any follow-up actions

5 Tgas is tight even for JSON event emission; if you later forward refunds or update storage, you will run out. Recommend 15–20 Tgas for safety.

-const GAS_DEPOSIT_INTO_POOL_CALLBACK: Gas = Gas::from_tgas(5);
+const GAS_DEPOSIT_INTO_POOL_CALLBACK: Gas = Gas::from_tgas(20);

Based on learnings.


27-47: Minor docs: ‘NEAR’ amounts wording is misleading for multi-token pools

These vectors track per-token balances, not NEAR. Please update doc comments here and in PoolInfo to avoid confusion.

-    /// How much NEAR this contract has.
+    /// Per-token balances tracked for the pool (indexed by `token_ids`).
contracts/intents-vault/src/lib.rs (1)

96-103: Parent-account check can panic on top-level accounts; add a clearer error or fallback

split_once('.') will panic for single-segment accounts. If this contract might be deployed outside a subaccount, prefer an explicit check and error message.

-    let (_, parent_account_id) = contract_id.split_once('.').expect("Invalid contract ID");
+    let Some((_, parent_account_id)) = contract_id.split_once('.') else {
+        env::panic_str("Contract must be deployed as a subaccount for parent-only access control");
+    };

Based on learnings.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3db2244 and 3311504.

⛔ Files ignored due to path filters (4)
  • contracts/intents-vault/res/intents_vault.wasm is excluded by !**/*.wasm
  • contracts/mock-ft/res/mock_ft.wasm is excluded by !**/*.wasm
  • contracts/mock-intents/res/mock_intents.wasm is excluded by !**/*.wasm
  • contracts/solver-registry/res/solver_registry.wasm is excluded by !**/*.wasm
📒 Files selected for processing (12)
  • contracts/intents-vault/src/lib.rs (2 hunks)
  • contracts/mock-intents/src/lib.rs (4 hunks)
  • contracts/mock-intents/src/token.rs (1 hunks)
  • contracts/mock-intents/src/token_receiver.rs (2 hunks)
  • contracts/solver-registry/src/admin.rs (3 hunks)
  • contracts/solver-registry/src/events.rs (2 hunks)
  • contracts/solver-registry/src/ext/intents_vault.rs (1 hunks)
  • contracts/solver-registry/src/lib.rs (6 hunks)
  • contracts/solver-registry/src/pool.rs (4 hunks)
  • contracts/solver-registry/src/upgrade.rs (0 hunks)
  • contracts/solver-registry/tests/common/utils.rs (2 hunks)
  • contracts/solver-registry/tests/test_withdraw_assets.rs (1 hunks)
💤 Files with no reviewable changes (1)
  • contracts/solver-registry/src/upgrade.rs
🧰 Additional context used
🧬 Code graph analysis (7)
contracts/solver-registry/tests/common/utils.rs (1)
contracts/solver-registry/src/admin.rs (1)
  • withdraw_from_pool (61-93)
contracts/solver-registry/tests/test_withdraw_assets.rs (3)
contracts/solver-registry/tests/common/utils.rs (11)
  • setup_test_environment (285-334)
  • create_liquidity_pool (337-359)
  • approve_compose_hash (362-380)
  • register_worker_alice (411-426)
  • create_account (22-34)
  • storage_deposit (97-112)
  • ft_transfer (114-132)
  • deposit_into_pool (187-212)
  • get_pool_account_id (533-537)
  • get_mock_intents_balances (559-576)
  • withdraw_from_pool (579-601)
contracts/solver-registry/src/pool.rs (3)
  • create_liquidity_pool (90-115)
  • deposit_into_pool (181-210)
  • get_pool_account_id (175-179)
contracts/solver-registry/src/admin.rs (2)
  • approve_compose_hash (17-27)
  • withdraw_from_pool (61-93)
contracts/intents-vault/src/lib.rs (2)
contracts/mock-intents/src/lib.rs (3)
  • add_public_key (53-60)
  • remove_public_key (63-69)
  • ft_withdraw (72-91)
contracts/solver-registry/src/ext/intents_vault.rs (3)
  • add_public_key (6-6)
  • remove_public_key (7-7)
  • ft_withdraw (8-15)
contracts/solver-registry/src/admin.rs (2)
contracts/solver-registry/src/pool.rs (2)
  • new (59-77)
  • get_pool_account_id (175-179)
contracts/solver-registry/tests/common/utils.rs (2)
  • withdraw_from_pool (579-601)
  • get_pool_account_id (533-537)
contracts/mock-intents/src/lib.rs (4)
contracts/solver-registry/tests/common/utils.rs (1)
  • ft_transfer (114-132)
contracts/mock-ft/src/lib.rs (2)
  • ft_transfer (82-84)
  • new (59-76)
contracts/intents-vault/src/lib.rs (2)
  • ft_withdraw (32-38)
  • ft_withdraw (77-93)
contracts/solver-registry/src/ext/intents_vault.rs (1)
  • ft_withdraw (8-15)
contracts/solver-registry/src/pool.rs (1)
contracts/solver-registry/tests/common/utils.rs (1)
  • get_pool_account_id (533-537)
contracts/solver-registry/src/ext/intents_vault.rs (2)
contracts/mock-intents/src/lib.rs (1)
  • ft_withdraw (72-91)
contracts/intents-vault/src/lib.rs (2)
  • ft_withdraw (32-38)
  • ft_withdraw (77-93)
🪛 GitHub Actions: Test
contracts/solver-registry/tests/common/utils.rs

[error] 1-1: Cargo build failed for solver-registry: could not compile due to previous error (include_bytes missing wasm).

contracts/solver-registry/tests/test_withdraw_assets.rs

[error] 1-1: Cargo build failed for solver-registry: could not compile due to previous error (include_bytes missing wasm).

contracts/solver-registry/src/admin.rs

[error] 1-1: Cargo build failed for solver-registry: could not compile due to previous error (include_bytes missing wasm).

contracts/solver-registry/src/pool.rs

[error] 1-1: Cargo build failed for solver-registry: could not compile due to previous error (include_bytes missing wasm).

contracts/solver-registry/src/events.rs

[error] 1-1: Cargo build failed for solver-registry: could not compile due to previous error (include_bytes missing wasm).

contracts/solver-registry/src/ext/intents_vault.rs

[error] 1-1: Cargo build failed for solver-registry: could not compile due to previous error (include_bytes missing wasm).

contracts/solver-registry/src/lib.rs

[error] 1-1: Cargo build failed for solver-registry: could not compile due to previous error (include_bytes missing wasm).

🪛 GitHub Actions: Build
contracts/solver-registry/src/pool.rs

[error] 108-108: couldn't read contracts/solver-registry/src/../../intents-vault/res/intents_vault.wasm: No such file or directory (os error 2) while expanding include_bytes! macro at this location.

🔇 Additional comments (10)
contracts/mock-intents/src/token_receiver.rs (1)

22-25: LGTM: correct TokenId usage and MT deposit

Converting predecessor to TokenId and depositing before logging is sound. Return U128(0) complies with NEP-141 expectations.

Please confirm internal_deposit_mt_balance initializes (account_id, token_id) entries to 0 on first use to avoid panics on empty state.

contracts/solver-registry/tests/common/utils.rs (4)

368-369: Verify 1 yocto on approve_compose_hash

Contract snippet shows approve_compose_hash without assert_one_yocto; if not required, drop the deposit to save gas. If required elsewhere, keep.


539-556: Nice: single‑token MT balance helper

Helper is clean and consistent with TokenId string format.


558-576: Batch MT balances helper looks good

Efficient conversion to "nep141:{id}" and mapping to u128.


578-601: Admin withdraw helper matches contract API

1 yocto deposit and gas are appropriate.

contracts/solver-registry/src/ext/intents_vault.rs (1)

1-16: LGTM: ext interface matches withdraw flow

Return type and params align with intents-vault and admin usage; ONE_YOCTO is attached at the call site.

contracts/solver-registry/src/pool.rs (2)

181-210: Good: ONE_YOCTO on ft_transfer_call and input validation

The minimal deposit and basic guards look correct for NEP‑141 transfer calls and pool membership checks. No action needed.


108-109: Stale CI blocker – no include_bytes! call or intents-vault/res directory exists

Likely an incorrect or invalid review comment.

contracts/intents-vault/src/lib.rs (1)

76-93: ft_withdraw forwarding looks correct with access control and 1 yⓃ

Enforces 1 yⓃ and parent-only, forwards to external contract, and preserves the return value. LGTM.

contracts/solver-registry/src/events.rs (1)

50-62: Event additions/shape changes are consistent; amount should reflect used value

New variants and by-value fields look good. Ensure deposit emits the used amount (not requested) to keep event logs semantically correct — see pool.rs callback note.

Run tests that assert AssetDeposited.amount equals the actually used amount from ft_transfer_call.

Comment on lines +18 to +23
fn ft_transfer(
receiver_id: AccountId,
amount: U128,
memo: Option<String>,
) -> PromiseOrValue<U128>;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Correct ext_ft::ft_transfer return type to match NEP-141

Line 18 defines ft_transfer as returning PromiseOrValue<U128>, but the NEP-141 standard (and our mock-ft implementation) return (). Expecting a serialized PromiseOrValue<U128> causes the promise to deserialize garbage and fail every withdrawal. This makes the new owner withdrawal path unusable. Please update the trait to return () so the promise resolves successfully.

 trait FungibleTokenContract {
     fn ft_transfer(
         receiver_id: AccountId,
         amount: U128,
         memo: Option<String>,
-    ) -> PromiseOrValue<U128>;
+    );
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fn ft_transfer(
receiver_id: AccountId,
amount: U128,
memo: Option<String>,
) -> PromiseOrValue<U128>;
}
trait FungibleTokenContract {
fn ft_transfer(
receiver_id: AccountId,
amount: U128,
memo: Option<String>,
);
}
🤖 Prompt for AI Agents
In contracts/mock-intents/src/lib.rs around lines 18 to 23, the
ext_ft::ft_transfer trait method currently returns PromiseOrValue<U128>, but
NEP-141 (and our mock-ft) expect a unit () return; change the method signature
to return () instead of PromiseOrValue<U128> and update any corresponding
implementations/call sites to return/resolve unit so the promise deserializes
correctly and withdrawals succeed.

Comment on lines +84 to 104
self.internal_withdraw_mt_balance(&sender_id, &token.clone().into(), amount.0);

ext_ft::ext(token)
.with_attached_deposit(NearToken::from_yoctonear(1))
.ft_transfer(receiver_id, amount, memo)
.then(Self::ext(env::current_account_id()).on_ft_withdraw(amount))
.into()
}

#[private]
pub const fn on_ft_withdraw(
&mut self,
amount: U128,
#[callback_result] call_result: Result<(), PromiseError>,
) -> U128 {
if call_result.is_ok() {
U128(amount.0)
} else {
U128(0)
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Debit mt_balances only after the FT transfer succeeds

Line 84 removes balance with internal_withdraw_mt_balance before the external ft_transfer resolves, and the callback (lines 94-103) never restores state on failure—it just returns U128(0). Any error (including the serialization bug above or a downstream FT panic) silently burns assets from the vault’s ledger even though no tokens left the FT contract. We need to move the balance mutation into the callback (after confirming call_result.is_ok()) or at least pass sender_id/token_id into the callback and re-credit plus panic on failure so the whole withdraw rolls back. Right now this is a critical correctness breach that can strand user funds.

-        self.internal_withdraw_mt_balance(&sender_id, &token.clone().into(), amount.0);
-
-        ext_ft::ext(token)
+        let token_id: TokenId = token.clone().into();
+        ext_ft::ext(token)
             .with_attached_deposit(NearToken::from_yoctonear(1))
             .ft_transfer(receiver_id, amount, memo)
-            .then(Self::ext(env::current_account_id()).on_ft_withdraw(amount))
+            .then(
+                Self::ext(env::current_account_id())
+                    .on_ft_withdraw(sender_id, token_id, amount),
+            )
             .into()
     }
 
     #[private]
-    pub const fn on_ft_withdraw(
+    pub fn on_ft_withdraw(
         &mut self,
+        sender_id: AccountId,
+        token_id: TokenId,
         amount: U128,
         #[callback_result] call_result: Result<(), PromiseError>,
     ) -> U128 {
-        if call_result.is_ok() {
-            U128(amount.0)
-        } else {
-            U128(0)
-        }
+        match call_result {
+            Ok(()) => {
+                self.internal_withdraw_mt_balance(&sender_id, &token_id, amount.0);
+                U128(amount.0)
+            }
+            Err(_) => env::panic_str("FT transfer failed; state reverted"),
+        }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
self.internal_withdraw_mt_balance(&sender_id, &token.clone().into(), amount.0);
ext_ft::ext(token)
.with_attached_deposit(NearToken::from_yoctonear(1))
.ft_transfer(receiver_id, amount, memo)
.then(Self::ext(env::current_account_id()).on_ft_withdraw(amount))
.into()
}
#[private]
pub const fn on_ft_withdraw(
&mut self,
amount: U128,
#[callback_result] call_result: Result<(), PromiseError>,
) -> U128 {
if call_result.is_ok() {
U128(amount.0)
} else {
U128(0)
}
}
// Defer debiting our internal MT balance until after the FT transfer succeeds.
let token_id: TokenId = token.clone().into();
ext_ft::ext(token)
.with_attached_deposit(NearToken::from_yoctonear(1))
.ft_transfer(receiver_id, amount, memo)
.then(
Self::ext(env::current_account_id())
.on_ft_withdraw(sender_id, token_id, amount),
)
.into()
}
#[private]
pub fn on_ft_withdraw(
&mut self,
sender_id: AccountId,
token_id: TokenId,
amount: U128,
#[callback_result] call_result: Result<(), PromiseError>,
) -> U128 {
match call_result {
Ok(()) => {
// Only now debit the MT balance, since the FT transfer succeeded.
self.internal_withdraw_mt_balance(&sender_id, &token_id, amount.0);
U128(amount.0)
}
Err(_) => {
// On failure, panic to revert the entire promise chain and avoid stranded funds.
env::panic_str("FT transfer failed; state reverted")
}
}
}
🤖 Prompt for AI Agents
In contracts/mock-intents/src/lib.rs around lines 84 to 104, the code debits
mt_balances before the external ft_transfer resolves causing possible permanent
loss on failure; change the flow so the balance mutation happens only after
confirming call_result.is_ok(): remove the pre-transfer
internal_withdraw_mt_balance call, pass the sender_id and token (or token_id)
into the callback (or store them in the promise args), and in on_ft_withdraw, if
call_result.is_ok() then perform internal_withdraw_mt_balance and return the
amount, otherwise re-credit the balance (or do not debit) and panic/return an
error so the withdraw fully rolls back and user funds cannot be silently burned.

Comment on lines +145 to 171
pub fn on_deposit_into_pool(
&mut self,
pool_id: u32,
token_id: &AccountId,
amount: U128,
#[callback_result] used_fund: Result<U128, PromiseError>,
#[callback_result] result: Result<U128, PromiseError>,
) -> U128 {
if let Ok(used_fund) = used_fund {
// Refund the unused amount.
// ft_transfer_call() returns the used fund
U128(amount.0.saturating_sub(used_fund.0))
} else {
amount
match result {
Ok(used_fund) => {
if used_fund.0 > 0 {
Event::AssetDeposited {
pool_id,
token_id,
amount: &amount,
}
.emit();
}

// Refund the unused amount.
// ft_transfer_call() returns the used fund
U128(amount.0.saturating_sub(used_fund.0))
}
Err(e) => {
env::panic_str(&format!("Error depositing into pool: {e:?}"));
}
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Deposit callback emits wrong amount, doesn’t update local accounting, and doesn’t forward refunds

  • Event emits the requested amount, not the used amount. It should log used_fund.
  • Pool state isn’t updated, so balances drift from the Intents vault.
  • Unused tokens are refunded by the FT back to this contract (sender of ft_transfer_call), not to the original caller; you’re returning a number but not transferring the tokens to the user. You already had _sender_id in the deposit API — use it.

Proposed fix (adjust signatures, update state, and forward unused tokens):

-    pub fn on_deposit_into_pool(
+    pub fn on_deposit_into_pool(
         &mut self,
-        pool_id: u32,
-        token_id: &AccountId,
-        amount: U128,
+        pool_id: u32,
+        token_id: &AccountId,
+        amount: U128,
+        sender_id: AccountId,
         #[callback_result] result: Result<U128, PromiseError>,
     ) -> U128 {
         match result {
-            Ok(used_fund) => {
-                if used_fund.0 > 0 {
+            Ok(used_fund) => {
+                // Update local pool accounting.
+                if used_fund.0 > 0 {
+                    let mut pool = self.pools.get(pool_id).expect(ERR_POOL_NOT_FOUND);
+                    if let Some(idx) = pool.token_ids.iter().position(|t| t == token_id) {
+                        pool.amounts[idx] = pool.amounts[idx].saturating_add(used_fund.0);
+                        self.pools.replace(pool_id, &pool);
+                        self.pools.flush();
+                    }
                     Event::AssetDeposited {
-                        pool_id,
-                        token_id,
-                        amount: &amount,
+                        pool_id,
+                        token_id,
+                        amount: &used_fund,
                     }
                     .emit();
                 }
 
-                // Refund the unused amount.
-                // ft_transfer_call() returns the used fund
-                U128(amount.0.saturating_sub(used_fund.0))
+                // Forward unused amount (already refunded by FT back to this contract) to the original sender.
+                let unused = amount.0.saturating_sub(used_fund.0);
+                if unused > 0 {
+                    ext_ft::ext(token_id.clone())
+                        .with_attached_deposit(ONE_YOCTO)
+                        .ft_transfer(sender_id.clone(), U128(unused), Some("refund unused deposit".to_string()));
+                }
+                U128(unused)
             }
             Err(e) => {
                 env::panic_str(&format!("Error depositing into pool: {e:?}"));
             }
         }
     }

And pass the sender into the callback:

-    pub(crate) fn deposit_into_pool(
-        &self,
+    pub(crate) fn deposit_into_pool(
+        &self,
         pool_id: u32,
         token_id: &AccountId,
-        _sender_id: &AccountId,
+        sender_id: &AccountId,
         amount: Balance,
     ) -> PromiseOrValue<U128> {
@@
             .then(
                 Self::ext(env::current_account_id())
                     .with_static_gas(GAS_DEPOSIT_INTO_POOL_CALLBACK)
                     .with_unused_gas_weight(0)
-                    .on_deposit_into_pool(pool_id, token_id, U128(amount)),
+                    .on_deposit_into_pool(pool_id, token_id, U128(amount), sender_id.clone()),
             )

Note: Increase GAS_DEPOSIT_INTO_POOL_CALLBACK as suggested, since we update storage and may forward refunds. Based on learnings.


🏁 Script executed:

#!/bin/bash
# Find all call sites to update callback signature
rg -nP 'on_deposit_into_pool\s*\(' -C2
rg -nP 'deposit_into_pool\s*\(' -C3

Length of output: 6403


🏁 Script executed:

#!/bin/bash
rg -nP 'fn deposit_into_pool' -C2 contracts/solver-registry/src/token_receiver.rs

Length of output: 0


🏁 Script executed:

#!/bin/bash
sed -n '1,200p' contracts/solver-registry/src/token_receiver.rs

Length of output: 1045


🏁 Script executed:

#!/bin/bash
sed -n '175,250p' contracts/solver-registry/src/pool.rs

Length of output: 1379


🏁 Script executed:

#!/bin/bash
rg -nP 'pub async fn deposit_into_pool' -C3 contracts/solver-registry/tests/common/utils.rs

Length of output: 165


Fix deposit callback to log correct amount, sync state, and refund unused tokens

  • In on_deposit_into_pool (pool.rs 145–171): emit used_fund instead of requested amount, update self.pools by adding used_fund.0 to the pool’s token balance (get → modify → replace → flush).
  • Extend on_deposit_into_pool signature to take sender_id: AccountId and after computing unused = amount.0 – used_fund.0, call ext_ft::ft_transfer(sender_id, U128(unused), …) to forward the refund.
  • In deposit_into_pool (pool.rs 181–210), pass sender_id.clone() into .on_deposit_into_pool(…) instead of only (pool_id, token_id, U128(amount)).
  • Bump GAS_DEPOSIT_INTO_POOL_CALLBACK to account for the extra storage writes and the refund transfer.
🤖 Prompt for AI Agents
In contracts/solver-registry/src/pool.rs around lines 145–171, the deposit
callback currently emits the requested amount, does not update pool state with
the actually used_fund, and fails to refund unused tokens; change the
on_deposit_into_pool signature to accept sender_id: AccountId, emit used_fund
(not the original requested amount), update self.pools by retrieving the pool
entry, adding used_fund.0 to the pool’s token balance (modify → replace →
flush), compute unused = amount.0.saturating_sub(used_fund.0) and call
ext_ft::ft_transfer(sender_id, U128(unused), ...) to refund the caller, and
panic only on Err; then update deposit_into_pool (around lines 181–210) to pass
sender_id.clone() into .on_deposit_into_pool(...), and increase
GAS_DEPOSIT_INTO_POOL_CALLBACK to cover the extra storage writes and the refund
transfer.

Comment on lines +19 to +21
// Create a liquidity pool
create_liquidity_pool(&solver_registry, &wnear, &usdc).await?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Build is red: include_bytes! for intents_vault.wasm not found

Workspace build fails before tests due to solver-registry/src/pool.rs embedding a missing WASM at:
../../intents-vault/res/intents_vault.wasm.

Two options:

  • Quick: commit the WASM and point include_bytes! to an existing path in the repo.
  • Robust: add a build.rs in solver-registry to copy/build the intents-vault WASM into a known location and set an env var used by include_bytes!.

Example (robust) in solver-registry:

// src/pool.rs
- .deploy_contract(include_bytes!("../../intents-vault/res/intents_vault.wasm").to_vec())
+ .deploy_contract(include_bytes!(env!("INTENTS_VAULT_WASM_PATH")).to_vec())

build.rs:

use std::{env, fs, path::PathBuf};
fn main() {
    // Adjust relative paths as needed for your workspace layout
    let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
    let wasm = manifest_dir.join("../intents-vault/res/intents_vault.wasm");
    // Fail early with a clear error if missing
    if !wasm.exists() {
        panic!("Missing intents_vault.wasm at {}", wasm.display());
    }
    println!("cargo:rustc-env=INTENTS_VAULT_WASM_PATH={}", wasm.display());
}

🏁 Script executed:

#!/bin/bash
# Verify presence of the embedded WASM and referenced path(s)
set -euo pipefail
echo "Checking for intents_vault.wasm under workspace..."
fd -a 'intents_vault.wasm'
echo "Grepping include_bytes usage and env var..."
rg -n 'include_bytes!\(' -C2 contracts/solver-registry/src | sed -n '1,120p'

Length of output: 191


Please run the following to verify the presence of intents_vault.wasm and any include_bytes! calls:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "Listing files in contracts/solver-registry/src:"
find contracts/solver-registry/src -type f
echo
echo "Searching for any reference to intents_vault (.wasm file):"
rg -n 'intents[-_]vault\.wasm' -C2 .
echo
echo "Searching for include_bytes! macro usage in the repo:"
rg -n 'include_bytes!' -C2 .

Length of output: 1957


Please run:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "Contents of contracts/intents-vault/res:"
find contracts/intents-vault/res -maxdepth 1 -type f -printf '%f\n'

Length of output: 103


Fix missing intents_vault.wasm import in solver-registry
Build is failing because pool.rs calls

.deploy_contract(include_bytes!("../../intents-vault/res/intents_vault.wasm").to_vec())

but contracts/intents-vault/res/intents_vault.wasm doesn’t exist. Choose one:

  • Commit the compiled WASM at contracts/intents-vault/res/intents_vault.wasm.
  • Add a build.rs in contracts/solver-registry that locates/copies the WASM and sets an env var for include_bytes!(env!("INTENTS_VAULT_WASM_PATH")).
    Location: contracts/solver-registry/src/pool.rs:108
🤖 Prompt for AI Agents
In contracts/solver-registry/tests/test_withdraw_assets.rs around lines 19-21
and specifically addressing contracts/solver-registry/src/pool.rs line 108, the
test fails because pool.rs calls
include_bytes!("../../intents-vault/res/intents_vault.wasm") but that file isn’t
committed; fix by adding a build script in contracts/solver-registry (build.rs)
that locates the compiled intents-vault wasm (e.g.,
../intents-vault/res/intents_vault.wasm or target path), copies or verifies it
into the solver-registry crate (or sets an absolute path), and sets an
environment variable INTENTS_VAULT_WASM_PATH pointing to the wasm file so
pool.rs can use include_bytes!(env!("INTENTS_VAULT_WASM_PATH")); ensure the
build.rs emits rerun-if-changed for the source wasm and that the CI/build
process either checks in the compiled WASM or builds intents-vault before
running solver-registry tests.

Comment thread contracts/solver-registry/tests/test_withdraw_assets.rs
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
makefile (2)

3-15: Avoid coupling lint to the intents-vault build

Having lint depend on intents-vault means every formatting or clippy pass now compiles and copies the vault WASM, which slows down feedback and introduces unrelated build breakages into linting. Please drop the dependency so make lint only runs cargo fmt/cargo clippy.


1-33: Declare the phony targets explicitly

all, test, and clean (and the other helper targets) don’t correspond to real files; without a .PHONY stanza they can be skipped if a file with the same name exists. Add a single .PHONY: all lint test clean solver-registry intents-vault mock-intents mock-ft block near the top to keep them reliable.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3311504 and 43d5550.

📒 Files selected for processing (1)
  • makefile (2 hunks)
🧰 Additional context used
🪛 checkmake (0.2.2)
makefile

[warning] 1-1: Missing required phony target "all"

(minphony)


[warning] 1-1: Missing required phony target "clean"

(minphony)


[warning] 1-1: Missing required phony target "test"

(minphony)


[warning] 1-1: Target "all" should be declared PHONY.

(phonydeclared)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Tests

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
.github/workflows/test.yml (1)

21-24: Rename lint step for clarity.

The step title still says “Run cargo clippy,” but it now executes make lint. Please update the name so the workflow log reflects what’s actually happening.

Apply this diff:

-      - name: Run cargo clippy
+      - name: Run lint
         run: make lint
contracts/mock-intents/Cargo.toml (1)

15-17: Image/digest consistency verified across workspace

All Cargo.toml files with a [package.metadata.near.reproducible_build] section use the image sourcescan/cargo-near:0.17.0-rust-1.86.0 and digest sha256:1784ca6310f3496f0048356ce420921c8f5fdf71ee8124d43a2e1ceb1f70db8a. No mismatches detected. Consider switching to “reproducible-wasm” in container_build_command for full NEP-330 determinism.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 43d5550 and bccfb97.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • contracts/intents-vault/res/intents_vault.wasm is excluded by !**/*.wasm
📒 Files selected for processing (6)
  • .github/workflows/bulid.yml (1 hunks)
  • .github/workflows/test.yml (2 hunks)
  • contracts/intents-vault/Cargo.toml (1 hunks)
  • contracts/mock-ft/Cargo.toml (1 hunks)
  • contracts/mock-intents/Cargo.toml (1 hunks)
  • contracts/solver-registry/Cargo.toml (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • .github/workflows/bulid.yml
  • contracts/intents-vault/Cargo.toml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build Solver Registry (solver-registry)
  • GitHub Check: Tests
🔇 Additional comments (2)
contracts/solver-registry/Cargo.toml (1)

15-17: Image/digest pin looks proper; keep all crates in lockstep.

No issues spotted. Recommend enforcing alignment across contracts and validating the digest in CI.

You can reuse the verification script from the other comment. Optionally add a lightweight CI check that fails if any Cargo.toml has a different image or digest.

contracts/mock-ft/Cargo.toml (1)

15-17: Confirm tag↔digest mapping and repo-wide consistency

  • Manually verify that sourcescan/cargo-near:0.17.0-rust-1.86.0 resolves to sha256:1784ca6310f3496f0048356ce420921c8f5fdf71ee8124d43a2e1ceb1f70db8a (e.g. via skopeo inspect, Docker, or registry API).
  • Ensure every contracts/**/Cargo.toml reproducible_build block pins this exact image and digest.

@aleksuss aleksuss force-pushed the feat/withdraw-from-pool branch from bccfb97 to 71a9018 Compare September 29, 2025 16:45
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
.github/workflows/test.yml (4)

17-18: Matrix job looks good; add least-privilege perms and cancel in-progress runs.

Keeps both lint/test running; tighten security and CI efficiency with job-level permissions and concurrency.

 code-checker:
-  name: Run ${{ matrix.command }}
+  name: Run ${{ matrix.command }}
+  permissions:
+    contents: read
+  concurrency:
+    group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.command }}
+    cancel-in-progress: true

20-23: Non‑fail‑fast is fine; add a sensible timeout.

Prevent hung jobs from burning minutes.

 strategy:
   fail-fast: false
   matrix:
     command: [ lint, test ]
+timeout-minutes: 30

28-28: Avoid bare curl|sh; verify installer integrity and pin artifact.

Pipe‑to‑shell without checksum is risky. Download, verify SHA256, then run. Provide checksum from the release assets.

-      - name: Install cargo-near CLI
-        run: curl --proto '=https' --tlsv1.2 -LsSf https://github.com/near/cargo-near/releases/download/cargo-near-v0.17.0/cargo-near-installer.sh | sh
+      - name: Install cargo-near CLI
+        run: |
+          set -euo pipefail
+          curl -LsSf \
+            -o cargo-near-installer.sh \
+            https://github.com/near/cargo-near/releases/download/cargo-near-v0.17.0/cargo-near-installer.sh
+          # TODO: replace <SHA256SUM> with the value from the release checksum file
+          echo "<SHA256SUM>  cargo-near-installer.sh" | sha256sum -c -
+          bash cargo-near-installer.sh

Please confirm the SHA256 used.


29-30: Ensure Rust toolchain (and wasm target) is installed before make lint/test.

Unless your Makefile installs Rust, this will fail on ubuntu‑latest. Install toolchain + rustfmt/clippy + wasm target explicitly.

       - name: Checkout repository
         uses: actions/checkout@v4
+      - name: Install Rust toolchain
+        uses: dtolnay/rust-toolchain@stable
+        with:
+          components: rustfmt, clippy
+          targets: wasm32-unknown-unknown
       - name: Install cargo-near CLI
         run: |
           set -euo pipefail
           curl -LsSf \
             -o cargo-near-installer.sh \
             https://github.com/near/cargo-near/releases/download/cargo-near-v0.17.0/cargo-near-installer.sh
           echo "<SHA256SUM>  cargo-near-installer.sh" | sha256sum -c -
           bash cargo-near-installer.sh
       - name: Run ${{ matrix.command }}
         run: make ${{ matrix.command }}

If your Makefile bootstraps Rust, ignore this and confirm.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bccfb97 and 71a9018.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • contracts/intents-vault/res/intents_vault.wasm is excluded by !**/*.wasm
📒 Files selected for processing (6)
  • .github/workflows/bulid.yml (1 hunks)
  • .github/workflows/test.yml (1 hunks)
  • contracts/intents-vault/Cargo.toml (1 hunks)
  • contracts/mock-ft/Cargo.toml (1 hunks)
  • contracts/mock-intents/Cargo.toml (1 hunks)
  • contracts/solver-registry/Cargo.toml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • contracts/mock-ft/Cargo.toml
  • contracts/mock-intents/Cargo.toml
  • contracts/solver-registry/Cargo.toml
  • .github/workflows/bulid.yml
  • contracts/intents-vault/Cargo.toml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Run test
  • GitHub Check: Build solver-registry
  • GitHub Check: Run lint

Comment thread contracts/solver-registry/src/admin.rs Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
contracts/solver-registry/src/admin.rs (1)

69-72: Consider using Near SDK panic for consistency.

The pool lookup uses env::panic_str with an inline message. For consistency with the codebase, consider following the pattern suggested in a previous review: use a constant error message (e.g., ERR_POOL_NOT_FOUND from pool.rs) and the Near SDK panic mechanism.

Based on past review comments.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9065747 and e808fae.

📒 Files selected for processing (1)
  • contracts/solver-registry/src/admin.rs (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
contracts/solver-registry/src/admin.rs (3)
contracts/solver-registry/src/pool.rs (2)
  • new (59-70)
  • get_pool_account_id (175-179)
contracts/solver-registry/src/lib.rs (1)
  • new (93-112)
contracts/solver-registry/tests/common/utils.rs (2)
  • withdraw_from_pool (579-601)
  • get_pool_account_id (533-537)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Run test
  • GitHub Check: Run lint
  • GitHub Check: Build solver-registry
🔇 Additional comments (4)
contracts/solver-registry/src/admin.rs (4)

2-10: LGTM!

The new imports are appropriate for the cross-contract withdrawal functionality. The callback gas allocation of 5 Tgas is reasonable for handling the withdrawal result.


73-76: LGTM!

The validation logic is sound:

  • Token ID membership is verified
  • Amount is checked for > 0
  • The comment appropriately explains why real-time balance checking is skipped

78-96: LGTM!

The cross-contract call structure is well-implemented:

  • Proper 1 yoctoNEAR deposit attached for the ft_withdraw call
  • Callback correctly chained with appropriate gas allocation
  • Unused gas weight set to 0 to ensure deterministic gas distribution
  • Returns PromiseOrValue<U128> as specified in the function signature

98-118: LGTM!

The callback handler is correctly implemented:

  • #[private] ensures only the contract can invoke it
  • Success path emits the AssetWithdrawn event after confirming the withdrawal succeeded
  • Error path panics with a descriptive message, which is appropriate for cross-contract call failures
  • Returns the withdrawn amount on success

impl Contract {
/// Approve a docker compose hash for worker registration
///
#[payable]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Validate attached deposit or remove #[payable] decorator.

The function is marked #[payable] but doesn't validate the attached deposit with assert_one_yocto(). This allows callers to accidentally attach NEAR tokens that would be locked in the contract. Either add assert_one_yocto() at the start of the function (if 1 yoctoNEAR is required for authorization/security), or remove the #[payable] decorator if no deposit is needed.

Apply this diff if 1 yoctoNEAR validation is intended:

 #[payable]
 #[access_control_any(roles(Role::Owner))]
 pub fn approve_compose_hash(&mut self, compose_hash: String) {
+    assert_one_yocto();
     DockerComposeHash::try_from_hex(&compose_hash)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#[payable]
#[payable]
#[access_control_any(roles(Role::Owner))]
pub fn approve_compose_hash(&mut self, compose_hash: String) {
assert_one_yocto();
DockerComposeHash::try_from_hex(&compose_hash)
// ... rest of the implementation
}
🤖 Prompt for AI Agents
In contracts/solver-registry/src/admin.rs at line 15 the function is marked
#[payable] but does not validate attached deposit; either remove the #[payable]
attribute if no deposit is allowed, or keep #[payable] and add
assert_one_yocto() as the first statement in the function body to require a 1
yoctoNEAR deposit for authorization; choose the intended behavior and update the
attribute or insert the assert_one_yocto() call accordingly (ensure the function
imports or has access to assert_one_yocto()).


/// Remove an approved docker compose has
/// Remove an approved docker compose hash
#[payable]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Validate attached deposit or remove #[payable] decorator.

Same issue as approve_compose_hash - the function is marked #[payable] but doesn't validate the attached deposit. Add assert_one_yocto() or remove the decorator.

Apply this diff if 1 yoctoNEAR validation is intended:

 #[payable]
 #[access_control_any(roles(Role::Owner))]
 pub fn remove_compose_hash(&mut self, compose_hash: String) {
+    assert_one_yocto();
     DockerComposeHash::try_from_hex(&compose_hash)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#[payable]
#[payable]
#[access_control_any(roles(Role::Owner))]
pub fn remove_compose_hash(&mut self, compose_hash: String) {
assert_one_yocto();
DockerComposeHash::try_from_hex(&compose_hash)
// …rest of implementation…
}
🤖 Prompt for AI Agents
In contracts/solver-registry/src/admin.rs around line 30, the function is marked
#[payable] but does not validate the attached deposit; either remove the
#[payable] attribute or add an assert_one_yocto() check at the start of the
function (or equivalent deposit validation) to enforce a 1 yoctoNEAR attachment;
if you intend to require 1 yocto, insert assert_one_yocto() as the first
statement and keep #[payable], otherwise delete #[payable] to prevent accidental
deposits.

Comment on lines +51 to 55
Event::FullAccessKeyAdded {
public_key: &public_key,
}
.emit();
Promise::new(env::current_account_id()).add_full_access_key(public_key)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Event emitted before key addition completes.

The FullAccessKeyAdded event is now emitted before the promise to add the key executes. If the promise fails, the event log will incorrectly indicate that the key was added. Consider whether this ordering is intentional, as it creates a potential inconsistency between logged events and actual state.

If the event should only be emitted on success, this requires a callback pattern similar to withdraw_from_pool. Otherwise, document that this event indicates intent rather than completion.

🤖 Prompt for AI Agents
In contracts/solver-registry/src/admin.rs around lines 51 to 55, the
FullAccessKeyAdded event is emitted before the Promise to add the full access
key executes, causing a mismatch if the promise fails; change the flow so the
event is emitted only after the key addition succeeds by converting the current
Promise call into an async callback pattern (similar to withdraw_from_pool):
return a Promise that calls add_full_access_key and then schedules a callback
(e.g., on_add_full_access_key) on success which inspects the callback result and
emits FullAccessKeyAdded only when the result indicates success; alternatively,
if you intend the event to represent intent rather than confirmation, update the
comment/docs at this location to state that the event is emitted optimistically
and may not reflect completion.

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