Skip to content

feat!: partial implementation of builtin liquidity pool - #1628

Merged
sdbondi merged 4 commits into
tari-project:developmentfrom
sdbondi:wip-liquidity-pool
Nov 6, 2025
Merged

feat!: partial implementation of builtin liquidity pool#1628
sdbondi merged 4 commits into
tari-project:developmentfrom
sdbondi:wip-liquidity-pool

Conversation

@sdbondi

@sdbondi sdbondi commented Nov 3, 2025

Copy link
Copy Markdown
Member

Description

feat: partial implementation of builtin liquidity pool
feat!: Adds ability to get divisibility from ResourceManager
feat!: adds drop_empty and is empty calls to bucket

Motivation and Context

Liquidity pools are generally useful (e.g. for swap implementations, fee paying fees using other resources, etc)

The ABI missed a way to get the resource divisibility

How Has This Been Tested?

Partialy in existing unit tests, more tests to follow once a more complete implementation is done

What process can a PR reviewer use to test or verify this change?

Breaking Changes

  • None
  • Requires data directory to be deleted
  • Other - Please specify

BREAKING CHANGE: template ABI changes will require all templates using resource_type calls to be recompiled

Summary by CodeRabbit

  • New Features

    • Two-resource liquidity pool (contribute/redeem, LP tokens)
    • Resource queries now return divisibility info
    • Bucket APIs: empty-check, locked-amount query, and explicit drop-empty action
    • Metadata: get_or_insert and remove helpers
  • Improvements

    • Flows now distinguish unlocked vs locked balances
    • Test tooling: simpler faucet helpers and state utilities
    • Extra arithmetic ops (pow, checked_pow, optional sqrt)
    • Reduced WASM module footprint
  • Bug Fixes

    • Automatic empty-bucket cleanup after withdrawals
  • Version

    • v0.16.0

@coderabbitai

coderabbitai Bot commented Nov 3, 2025

Copy link
Copy Markdown
Contributor

Caution

Review failed

The pull request is closed.

Walkthrough

Renames GetResourceType→GetResourceInfo and exposes ResourceInfo (resource_type + divisibility); replaces amount() semantics with unlocked_amount()/is_empty(), adds BucketAction::DropEmpty and BucketGetAmountArg with automatic post-take cleanup, adds Amount arithmetic helpers, implements a two-resource liquidity pool, and bumps workspace/version and features.

Changes

Cohort / File(s) Summary
Resource Info & Actions
crates/template_lib/src/args/types.rs, crates/template_lib/src/resource/manager.rs, crates/template_lib_types/src/resource_type.rs
GetResourceTypeGetResourceInfo; new public ResourceInfo { resource_type, divisibility }; ResourceManager gains resource_info() and divisibility(); resource_type() delegates to resource_info().
ResourceContainer & Balance API
crates/engine_types/src/resource_container.rs, crates/engine/src/runtime/impl.rs, crates/engine/src/runtime/working_state.rs, crates/engine/src/fee_state.rs, crates/engine_types/src/vault.rs
fungible(...) renamed to public_fungible(...); amount() replaced by unlocked_amount() across logic; is_empty() added; balances, supply, fee accounting, and validations updated to use unlocked amounts.
Bucket API & Runtime Bucket Cleanup
crates/engine_types/src/bucket.rs, crates/template_lib/src/models/bucket.rs, crates/template_lib/src/args/types.rs, crates/engine/src/transaction/processor.rs
Added BucketGetAmountArg enum; added BucketAction::DropEmpty; Bucket::drop_empty(), Bucket::is_empty(), Bucket::locked_amount() added; transaction processor queries GetAmount after take and invokes DropEmpty when unlocked amount == 0.
Amount arithmetic & features
crates/template_lib_types/src/amount/amount.rs, crates/template_lib/Cargo.toml, crates/template_lib_types/Cargo.toml, Cargo.toml
Added pow, checked_pow, optional checked_sqrt (feature-gated); checked_mul signature changed; operator macros extended; new extra-arith feature and num-integer wiring; workspace version bumped to 0.16.0.
Liquidity Pool Template
crates/template_builtin/templates/liquidity_pool/src/lib.rs, crates/template_builtin/templates/liquidity_pool/Cargo.toml, crates/template_builtin/tests/liquidity_pool.rs
New TwoResourceLiquidityPool template with instantiate, contribute, redeem, add/remove liquidity, pool balance queries; tests and Cargo config added.
Faucet & Test tooling refactor
crates/template_builtin/templates/faucet/src/lib.rs, crates/template_test_tooling/src/*
Replaced test_faucet_component with xtr_faucet_component; removed stored resource_manager field from faucet; added create_test_faucet_component() helper; PackageBuilder and read-only store helpers updated.
Wasm module storage change
crates/engine/src/wasm/module.rs, crates/template_test_tooling/src/package_builder.rs
WasmModule.code: Vec<u8>Box<[u8]>; from_code accepts impl Into<Box<[u8]>>; into_code returns Box<[u8]>; PackageBuilder adapted.
Events / Metadata / Resource helpers
crates/engine_types/src/events.rs, crates/engine_types/src/resource.rs, crates/template_lib/src/models/metadata.rs
Simplified payload/metadata accessors to return &str; Metadata::get now Option<&str>; added get_or_insert and remove.
Tests & examples updates
crates/engine/tests/*, crates/template_builtin/*, crates/template_test_tooling/*, applications/tari_indexer/README.md
Tests updated for faucet API, unlocked_amount semantics, payload changes; builtins and README formatting updates; various test helpers refactored.
ABI / versioning & validation
crates/template_abi/src/version.rs, crates/engine/src/wasm/process.rs, crates/engine/tests/templates/buggy/src/lib.rs
LATEST_TEMPLATE_VERSION → "0.15.0"; added MINIMUM_SUPPORTED_WASM_ABI_VERSION and use it in template validation; embedded test ABI version updated.
Misc / Ergonomics
crates/engine_types/src/commit_result.rs, crates/template_test_tooling/src/support/assert_error.rs, lints.toml, crates/template_builtin/templates/account/src/lib.rs
Added #[track_caller] to many test/commit helpers; clippy allow for drop_non_drop; account::deposit early-return on empty buckets; other small logging/message edits.

Sequence Diagram(s)

sequenceDiagram
    participant Processor
    participant Engine
    participant BucketActor

    Processor->>Engine: Invoke BucketAction::Take(bucket_id, amount)
    Engine->>BucketActor: Perform take, then Invoke GetAmount(BucketGetAmountArg::Everything)
    BucketActor-->>Engine: Return Amount (unlocked_amount)
    alt unlocked_amount == 0
        Engine->>BucketActor: Invoke BucketAction::DropEmpty(bucket_id)
        BucketActor-->>Engine: Confirm drop
        Engine-->>Processor: Return success (bucket dropped)
    else unlocked_amount > 0
        Engine-->>Processor: Return success (bucket still exists)
    end
Loading
sequenceDiagram
    participant User
    participant LiquidityPool
    participant VaultA
    participant VaultB
    participant LPToken

    User->>LiquidityPool: contribute(bucket_a, bucket_b)
    LiquidityPool->>LiquidityPool: validate, compute lp_mint (sqrt/proportional)
    LiquidityPool->>LPToken: mint(lp_mint)
    LiquidityPool->>VaultA: deposit(contribution_a)
    LiquidityPool->>VaultB: deposit(contribution_b)
    LiquidityPool-->>User: return lp_bucket

    User->>LiquidityPool: redeem(lp_bucket)
    LiquidityPool->>LiquidityPool: compute redemption amounts
    LiquidityPool->>VaultA: withdraw(amount_a)
    LiquidityPool->>VaultB: withdraw(amount_b)
    LiquidityPool->>LPToken: burn(lp_bucket)
    LiquidityPool-->>User: return (bucket_a, bucket_b)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing extra attention:
    • crates/template_builtin/templates/liquidity_pool/src/lib.rs — numeric math, overflow guards, edge-case invariants.
    • Cross-cutting replacement of amount() → unlocked_amount()/is_empty() across engine, proofs, fees, vaults, and supply accounting.
    • Post-take DropEmpty flow in crates/engine/src/transaction/processor.rs — ensure no races or incorrect state transitions.
    • ResourceInfo/GetResourceInfo API encoding/decoding in engine↔template_lib interactions.

Possibly related PRs

Poem

🐰 I hopped through code with careful paws,
Exposed ResourceInfo and checked divisibility's laws,
Unlocked amounts set free from their lock,
Empty buckets dropped — click of a clock,
Liquidity pools bloom, LPs minted anew — carrots for you! 🥕

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main change: a partial implementation of a built-in liquidity pool template, which is the primary focus of this changeset.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4732060 and 2fbe3dd.

📒 Files selected for processing (1)
  • crates/engine/src/runtime/impl.rs (13 hunks)

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

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

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 5

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e01b553 and ad9abef.

📒 Files selected for processing (16)
  • crates/engine/src/runtime/impl.rs (5 hunks)
  • crates/engine/src/runtime/working_state.rs (2 hunks)
  • crates/engine/src/transaction/processor.rs (2 hunks)
  • crates/engine/tests/events.rs (2 hunks)
  • crates/engine_types/src/bucket.rs (1 hunks)
  • crates/engine_types/src/events.rs (1 hunks)
  • crates/engine_types/src/resource.rs (1 hunks)
  • crates/engine_types/src/resource_container.rs (6 hunks)
  • crates/template_builtin/templates/pool/Cargo.toml (1 hunks)
  • crates/template_builtin/templates/pool/src/lib.rs (1 hunks)
  • crates/template_lib/src/args/types.rs (2 hunks)
  • crates/template_lib/src/models/bucket.rs (1 hunks)
  • crates/template_lib/src/models/metadata.rs (2 hunks)
  • crates/template_lib/src/resource/manager.rs (2 hunks)
  • crates/template_lib_types/src/amount/amount.rs (3 hunks)
  • crates/template_lib_types/src/resource_type.rs (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (11)
crates/template_lib/src/models/bucket.rs (2)
crates/template_lib/src/resource/manager.rs (1)
  • resp (144-144)
crates/template_lib/src/models/proof.rs (3)
  • resp (132-132)
  • resp (175-175)
  • resp (195-195)
crates/engine_types/src/bucket.rs (1)
crates/engine_types/src/resource_container.rs (1)
  • is_empty (866-868)
crates/engine_types/src/resource_container.rs (2)
crates/template_lib/src/resource/builder/mod.rs (1)
  • public_fungible (54-56)
crates/engine_types/src/bucket.rs (2)
  • amount (55-57)
  • is_empty (51-53)
crates/engine/src/runtime/working_state.rs (2)
crates/engine_types/src/resource_container.rs (3)
  • public_fungible (55-63)
  • resource_address (175-182)
  • amount (134-141)
crates/engine_types/src/bucket.rs (2)
  • resource_address (67-69)
  • amount (55-57)
crates/template_lib/src/models/metadata.rs (2)
crates/engine_types/src/indexed_value.rs (2)
  • metadata (105-107)
  • metadata (313-315)
crates/engine_types/src/resource.rs (1)
  • metadata (203-205)
crates/template_lib_types/src/amount/amount.rs (1)
crates/template_lib/src/models/stealth.rs (1)
  • new (69-80)
crates/engine/src/transaction/processor.rs (5)
bindings/src/types/Amount.ts (1)
  • Amount (12-12)
crates/engine_types/src/bucket.rs (1)
  • amount (55-57)
crates/engine_types/src/resource_container.rs (1)
  • amount (134-141)
crates/template_lib/src/models/bucket.rs (1)
  • amount (187-195)
crates/template_lib/src/models/proof.rs (1)
  • amount (104-112)
crates/template_lib/src/resource/manager.rs (7)
bindings/src/types/StealthValueProof.ts (1)
  • StealthValueProof (9-15)
crates/engine_types/src/resource.rs (2)
  • resource_type (107-109)
  • divisibility (211-213)
crates/engine_types/src/resource_container.rs (1)
  • resource_type (184-191)
crates/template_lib/src/models/bucket.rs (1)
  • resource_type (88-97)
crates/template_lib/src/models/vault.rs (1)
  • resource_type (364-366)
crates/template_lib/src/models/proof.rs (4)
  • resource_type (82-91)
  • resp (132-132)
  • resp (175-175)
  • resp (195-195)
bindings/src/types/ResourceType.ts (1)
  • ResourceType (17-17)
crates/template_builtin/templates/pool/src/lib.rs (3)
crates/template_lib/src/resource/manager.rs (4)
  • get (104-106)
  • from (993-995)
  • resource_address (109-111)
  • resource_type (119-121)
crates/template_lib/src/models/bucket.rs (4)
  • from (50-52)
  • resource_address (76-85)
  • amount (187-195)
  • resource_type (88-97)
crates/engine/src/runtime/impl.rs (1)
  • emit_event (435-458)
crates/engine/src/runtime/impl.rs (4)
crates/engine_types/src/resource.rs (2)
  • divisibility (211-213)
  • resource_type (107-109)
crates/engine_types/src/resource_container.rs (3)
  • resource_type (184-191)
  • public_fungible (55-63)
  • resource_address (175-182)
crates/template_lib/src/models/bucket.rs (2)
  • resource_type (88-97)
  • resource_address (76-85)
crates/template_lib/src/args/types.rs (3)
  • resource_address (448-453)
  • resource_address (645-650)
  • bucket_id (455-460)
crates/engine/tests/events.rs (2)
crates/engine_types/src/events.rs (1)
  • payload (119-121)
crates/template_lib/src/models/metadata.rs (1)
  • get (49-51)
⏰ 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). (6)
  • GitHub Check: file licenses
  • GitHub Check: clippy
  • GitHub Check: check stable
  • GitHub Check: machete
  • GitHub Check: check nightly
  • GitHub Check: test
🔇 Additional comments (16)
crates/template_builtin/templates/pool/Cargo.toml (1)

1-18: Well-structured manifest for liquidity pool template crate.

The Cargo.toml is properly configured for a compiled template with appropriate defaults:

  • Dual crate-type (cdylib + lib) is correct for template compilation and library usage
  • Dependency on tari_template_lib with minimal feature set (macro, alloc) aligns with embedded/low-footprint design
  • Release profile settings (opt-level='s', lto=true, codegen-units=1, panic='abort', strip='debuginfo') are appropriate for optimizing compiled template size and performance

Please verify that the relative path dependency ../../../../crates/template_lib correctly resolves to the intended location in your monorepo structure, and confirm that the "macro" and "alloc" features are consistent with how other template crates in the project declare tari_template_lib dependencies.

crates/engine_types/src/resource.rs (1)

207-209: The change is correct and improves the code.

The original comment assumes that removing .map(|s| s.as_str()) would break the API, but this is incorrect. The Metadata::get() method already returns Option<&str> (it internally applies .map() to convert from Option<&String> to Option<&str>). The original code in token_symbol() was applying a redundant .map() on an already-unwrapped type. All existing callers handle Option<&str> correctly, including those that further convert to owned strings or use the value directly.

crates/engine_types/src/events.rs (1)

115-117: LGTM! Simplified payload access aligns with updated Metadata API.

The change correctly removes the redundant .map(|s| s.as_str()) since Metadata::get now returns Option<&str> directly instead of Option<&String>.

crates/engine/tests/events.rs (1)

118-119: LGTM! Test assertions correctly updated for new Metadata API.

The removal of dereference operators aligns with Metadata::get now returning Option<&str> directly instead of Option<&String>.

Also applies to: 131-135

crates/template_lib/src/models/metadata.rs (4)

49-51: LGTM! Improved ergonomics with direct &str return.

Changing from Option<&String> to Option<&str> reduces unnecessary indirection and aligns with common Rust patterns for string accessors.


53-56: LGTM! Useful addition following standard library patterns.

The get_or_insert method follows the BTreeMap::entry().or_insert() pattern, providing a convenient way to retrieve or create default values.


58-60: LGTM! Completes the mutation API surface.

The remove method provides necessary functionality for metadata cleanup operations, returning the removed value for potential reuse.


182-186: LGTM! Test expectations correctly updated.

Test assertions properly reflect the new Option<&str> return type from get().

crates/engine_types/src/resource_container.rs (2)

55-63: LGTM! Constructor rename clarifies resource visibility.

Renaming from fungible to public_fungible helps distinguish from confidential and stealth fungible resources, improving API clarity.


866-868: LGTM! Useful emptiness check for resource containers.

The is_empty() method correctly checks both revealed amounts and confidential commitments, providing a comprehensive emptiness indicator.

crates/engine_types/src/bucket.rs (1)

51-53: LGTM! Properly exposes resource container emptiness check.

The method correctly delegates to the underlying ResourceContainer::is_empty(), maintaining consistency with other bucket delegation patterns.

crates/template_lib/src/models/bucket.rs (1)

172-182: LGTM! Well-documented bucket cleanup method.

The drop_empty() method follows established patterns for bucket operations and clearly documents that it will panic if the bucket is not empty. This provides an explicit cleanup path for empty buckets.

crates/engine/src/runtime/working_state.rs (2)

595-595: LGTM! Consistent use of renamed constructor.

The change from fungible to public_fungible aligns with the resource container API update.


400-405: Validation change is intentional and sound; templates are compatible.

The stricter bucket validation (checking !self.buckets.is_empty() instead of checking only positive amounts) is a design improvement that enforces better transaction accounting. The drop_empty() API provides an escape hatch for explicit empty bucket handling, though it's not called anywhere in the codebase because well-formed templates already consume all buckets completely (via deposit(), burn(), or join()).

Manifest examples like tariswap_do_swap.rs demonstrate the correct pattern: buckets are withdrawn, passed through operations, and immediately deposited—leaving no dangling buckets. The stricter validation prevents accidentally leaving buckets unconsumed, which is a positive change. No template updates are required since the existing codebase already follows sound bucket lifecycle practices.

crates/template_lib/src/args/types.rs (2)

232-233: LGTM! Enum variant rename reflects expanded information.

Renaming GetResourceType to GetResourceInfo indicates the action now returns additional resource metadata (such as divisibility) beyond just the type, improving the API's expressiveness.


486-486: LGTM! New action supports explicit bucket cleanup.

The DropEmpty variant enables the new Bucket::drop_empty() functionality, providing a structured way to dispose of empty buckets.

Comment thread crates/engine/src/runtime/impl.rs
Comment thread crates/template_builtin/templates/pool/src/lib.rs Outdated
Comment thread crates/template_builtin/templates/pool/src/lib.rs Outdated
Comment thread crates/template_builtin/templates/pool/src/lib.rs Outdated
Comment thread crates/template_lib_types/src/resource_type.rs
@github-actions

github-actions Bot commented Nov 3, 2025

Copy link
Copy Markdown

Test Results (CI)

465 tests  +2   465 ✅ +2   54m 27s ⏱️ + 2m 55s
 64 suites +1     0 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit 4732060. ± Comparison against base commit 9dddeb7.

♻️ This comment has been updated with latest results.

@sdbondi
sdbondi force-pushed the wip-liquidity-pool branch from ad9abef to a2b6c85 Compare November 4, 2025 10:26

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

♻️ Duplicate comments (4)
crates/engine/src/runtime/impl.rs (1)

2133-2151: DropEmpty consumes non-empty buckets before erroring—critical data loss.

This implementation calls take_bucket immediately, removing the bucket from state. If the bucket is non-empty, an error is returned, but the bucket and its contents have already been removed and are silently lost forever. This is the exact issue flagged in the previous review and remains unfixed.

Apply this diff to check emptiness before removing the bucket:

             BucketAction::DropEmpty => {
                 let bucket_id = bucket_ref.bucket_id().ok_or_else(|| RuntimeError::InvalidArgument {
                     argument: "bucket_ref",
                     reason: "DropEmpty bucket action requires a bucket id".to_string(),
                 })?;
                 args.assert_no_args("Bucket::DropEmpty")?;

                 self.tracker.write_with(|state| {
-                    let bucket = state.take_bucket(bucket_id)?;
-                    if !bucket.is_empty() {
-                        return Err(RuntimeError::InvalidArgument {
-                            argument: "bucket_ref",
-                            reason: "Cannot drop a non-empty bucket".to_string(),
-                        });
-                    }
-                    // Drop
-                    Ok(InvokeResult::unit())
+                    {
+                        let bucket = state.get_bucket(bucket_id)?;
+                        if !bucket.is_empty() {
+                            return Err(RuntimeError::InvalidArgument {
+                                argument: "bucket_ref",
+                                reason: "Cannot drop a non-empty bucket".to_string(),
+                            });
+                        }
+                    }
+                    let _ = state.take_bucket(bucket_id)?;
+                    Ok(InvokeResult::unit())
                 })
             },
crates/template_builtin/templates/pool/src/lib.rs (3)

147-172: LP minting breaks the invariant—allows manipulation.

The implementation deposits funds before reading vault balances (lines 160-161), then uses post-deposit balances to calculate ratios. This breaks the proportionality invariant and allows contributors to mint arbitrary LP tokens by depositing unbalanced amounts. Additionally, the formula a_ratio * a_amount + b_ratio * b_amount (line 168) incorrectly adds quantities that represent different resources.

This is the exact issue flagged in the previous review and remains unfixed.

Apply this diff to capture pre-deposit balances and mint proportionally to the tighter leg:

         pub fn contribute(&mut self, bucket_a: Bucket, bucket_b: Bucket) -> Bucket {
             // check that the buckets are correct
             let resource_a = bucket_a.resource_address();
             let resource_b = bucket_b.resource_address();
             self.assert_pool_resource(resource_a);
             self.assert_pool_resource(resource_b);
             assert_ne!(resource_a, resource_b, "The resources must be different");

             // extract the bucket amounts for later
             let a_amount = bucket_a.amount();
             let b_amount = bucket_b.amount();

-            // add the liquidity to the pool
-            self.vault_a.deposit(bucket_a);
-            self.vault_b.deposit(bucket_b);
-
-            // get the bucket/pool ratios
-            let a_ratio = self.get_a_ratio(a_amount);
-            let b_ratio = self.get_b_ratio(b_amount);
-
-            // the amount of new lp tokens are proportional to the bucket-pool ratios
-            let new_lp_amount = a_ratio * a_amount + b_ratio * b_amount;
-
-            // mint and return the new lp tokens
-            self.lp_resource.mint_fungible(new_lp_amount)
+            let current_a = self.vault_a.balance();
+            let current_b = self.vault_b.balance();
+            let lp_supply = self.lp_total_supply();
+
+            let mint_amount = if lp_supply.is_zero() {
+                assert!(!a_amount.is_zero() && !b_amount.is_zero(), "Cannot bootstrap the pool with a zero amount");
+                // For initial deposit, mint based on the smaller amount to establish price
+                if a_amount < b_amount { a_amount } else { b_amount }
+            } else {
+                // Mint proportionally to share added, using the tighter constraint
+                let minted_from_a = (lp_supply * a_amount).checked_div(current_a).expect("Division by current_a");
+                let minted_from_b = (lp_supply * b_amount).checked_div(current_b).expect("Division by current_b");
+                if minted_from_a < minted_from_b { minted_from_a } else { minted_from_b }
+            };
+            assert!(!mint_amount.is_zero(), "Liquidity contribution mints zero LP tokens");
+
+            self.vault_a.deposit(bucket_a);
+            self.vault_b.deposit(bucket_b);
+            self.lp_resource.mint_fungible(mint_amount)
         }

174-195: Redeem returns zero for partial withdrawals—precision bug.

The scaled-division pattern at lines 182-187 multiplies both numerator and denominator by the same decimals factor, which cancels out and provides no precision benefit. When lp_amount < total_supply, the integer division yields lp_ratio = 0, so redeemers receive zero tokens. This renders partial redemptions completely broken.

This is the exact issue flagged in the previous review and remains unfixed.

Apply this diff to compute the share directly:

         pub fn redeem(&mut self, lp_bucket: Bucket) -> (Bucket, Bucket) {
             let lp_amount = lp_bucket.amount();

             // get the pool information
             let a_balance = self.vault_a.balance();
             let b_balance = self.vault_b.balance();

-            // calculate the amount of tokens to take from each pool
-            let decimals = Amount::from(1_000_000u64);
-            let lp_ratio = (lp_amount * decimals) / (self.lp_total_supply() * decimals);
-
-            // TODO: div_ceil is probably not a great rounding function
-            let a_amount = (lp_ratio.div_ceil(decimals)) * a_balance;
-            let b_amount = (lp_ratio.div_ceil(decimals)) * b_balance;
+            let total_supply = self.lp_total_supply();
+            assert!(!total_supply.is_zero(), "Cannot redeem liquidity when LP supply is zero");
+            
+            let a_amount = (a_balance * lp_amount).checked_div(total_supply).expect("Division by total_supply in redeem");
+            let b_amount = (b_balance * lp_amount).checked_div(total_supply).expect("Division by total_supply in redeem");

             // burn the LP tokens
             lp_bucket.burn();
             // return the pool tokens
             let a_bucket = self.vault_a.withdraw(a_amount);
             let b_bucket = self.vault_b.withdraw(b_amount);
             (a_bucket, b_bucket)
         }

220-233: Redemption preview mirrors the same precision bug.

get_redemption_value repeats the scaled-division pattern from redeem, so it also reports zero for fractional LP amounts. If redeem is fixed but this preview function is not, they will report inconsistent values.

This is the exact issue flagged in the previous review and remains unfixed.

Apply this diff to match the corrected redemption calculation:

         pub fn get_redemption_value(&self, lp_amount: Amount) -> (Amount, Amount) {
             // get the pool information
             let a_balance = self.vault_a.balance();
-            let a_decimals = self.vault_a.to_resource_manager().divisibility();
             let b_balance = self.vault_b.balance();
-            let b_decimals = self.vault_b.to_resource_manager().divisibility();

-            // calculate the amount of tokens to take from each pool
-            let decimals = Amount::TEN.pow(u8::max(a_decimals, b_decimals) as u32);
-            let lp_ratio = (lp_amount * decimals) / (self.lp_total_supply() * decimals);
-            let a_amount = (lp_ratio.div_ceil(decimals)) * a_balance;
-            let b_amount = (lp_ratio.div_ceil(decimals)) * b_balance;
+            let total_supply = self.lp_total_supply();
+            if total_supply.is_zero() {
+                return (Amount::zero(), Amount::zero());
+            }
+            
+            let a_amount = (a_balance * lp_amount).checked_div(total_supply).expect("Division by total_supply in get_redemption_value");
+            let b_amount = (b_balance * lp_amount).checked_div(total_supply).expect("Division by total_supply in get_redemption_value");
             (a_amount, b_amount)
         }
🧹 Nitpick comments (8)
crates/template_lib/src/models/metadata.rs (2)

53-56: Add test coverage for the new get_or_insert method.

The implementation looks correct, but this new public API method lacks test coverage. Consider adding tests to verify:

  • Inserting a new key-value pair and returning the value
  • Returning an existing value without modifying the map

58-60: Add test coverage for the new remove method.

This new public API method lacks test coverage. Consider adding tests to verify:

  • Removing an existing key returns Some(value)
  • Removing a non-existent key returns None
crates/template_lib/src/resource/manager.rs (1)

113-121: Consider: Resource type accessor now makes additional engine call.

The resource_type() method now delegates to resource_info(), which queries both resource_type and divisibility from the engine. While this is cleaner architecturally, it means:

  1. Callers who only need the type still fetch divisibility
  2. Repeated calls to resource_type() make multiple engine calls (no caching)

The inline NOTE is helpful, but consider whether frequently-called code paths might see a performance impact.

crates/engine/src/runtime/impl.rs (1)

866-888: Update error message to reflect new action name.

The implementation correctly returns ResourceInfo with both resource_type and divisibility. However, the error message at line 872 still references the old action name.

Apply this diff:

                 let resource_address =
                     resource_ref
                         .as_resource_address()
                         .ok_or_else(|| RuntimeError::InvalidArgument {
                             argument: "resource_ref",
-                            reason: "GetResourceType resource action requires a resource address".to_string(),
+                            reason: "GetResourceInfo resource action requires a resource address".to_string(),
                         })?;
crates/template_builtin/templates/pool/src/lib.rs (4)

68-69: Consider higher divisibility for LP tokens.

LP tokens are created with divisibility(0), meaning they cannot be fractionally owned. This severely limits precision for liquidity providers and may cause rounding issues in contribute/redeem calculations. Standard practice is to use higher divisibility (e.g., 6-18) to enable fine-grained ownership and better price discovery.

Consider changing:

             let lp_resource = ResourceBuilder::public_fungible()
-                .with_divisibility(0)
+                .with_divisibility(18)  // or another appropriate value

197-208: Clarify add_liquidity vs contribute semantics.

The add_liquidity method allows depositing a single asset without minting LP tokens, which can break the pool's constant product invariant. This appears to be an administrative function, but the naming overlaps with contribute (which mints LP tokens for balanced adds). Consider:

  1. Renaming to admin_add_liquidity or similar to clarify intent
  2. Documenting when this should be used vs. contribute
  3. Adding a check that the pool maintains reasonable balance ratios after single-sided adds

210-218: Clarify remove_liquidity vs redeem semantics.

Similar to add_liquidity, the remove_liquidity method allows withdrawing assets without burning LP tokens, which breaks the proportionality invariant. This differs from redeem, which properly burns LP tokens. The same concerns apply: rename for clarity, document usage, and ensure the pool maintains valid state.


264-282: Inconsistent zero checks between get_a_ratio and get_b_ratio.

get_a_ratio (line 267) uses balance == 0 while get_b_ratio (line 277) correctly uses balance.is_zero(). For consistency and to follow Rust best practices with custom types, both should use the is_zero() method.

Apply this diff:

         pub fn get_a_ratio(&self, amount: Amount) -> Amount {
             let balance = self.vault_a.balance();

-            if balance == 0 {
+            if balance.is_zero() {
                 Amount::ONE
             } else {
                 amount.checked_div(balance).expect("Division by zero in get_a_ratio")
             }
         }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ad9abef and a2b6c85.

📒 Files selected for processing (16)
  • crates/engine/src/runtime/impl.rs (5 hunks)
  • crates/engine/src/runtime/working_state.rs (2 hunks)
  • crates/engine/src/transaction/processor.rs (2 hunks)
  • crates/engine/tests/events.rs (2 hunks)
  • crates/engine_types/src/bucket.rs (1 hunks)
  • crates/engine_types/src/events.rs (1 hunks)
  • crates/engine_types/src/resource.rs (1 hunks)
  • crates/engine_types/src/resource_container.rs (6 hunks)
  • crates/template_builtin/templates/pool/Cargo.toml (1 hunks)
  • crates/template_builtin/templates/pool/src/lib.rs (1 hunks)
  • crates/template_lib/src/args/types.rs (2 hunks)
  • crates/template_lib/src/models/bucket.rs (1 hunks)
  • crates/template_lib/src/models/metadata.rs (2 hunks)
  • crates/template_lib/src/resource/manager.rs (2 hunks)
  • crates/template_lib_types/src/amount/amount.rs (3 hunks)
  • crates/template_lib_types/src/resource_type.rs (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • crates/template_builtin/templates/pool/Cargo.toml
🚧 Files skipped from review as they are similar to previous changes (4)
  • crates/template_lib/src/args/types.rs
  • crates/template_lib/src/models/bucket.rs
  • crates/engine_types/src/resource_container.rs
  • crates/engine_types/src/events.rs
🧰 Additional context used
🧬 Code graph analysis (10)
crates/engine_types/src/bucket.rs (1)
crates/engine_types/src/resource_container.rs (1)
  • is_empty (866-868)
crates/engine/src/runtime/impl.rs (4)
crates/engine_types/src/resource.rs (2)
  • divisibility (211-213)
  • resource_type (107-109)
crates/template_lib/src/resource/manager.rs (3)
  • divisibility (129-131)
  • resource_type (119-121)
  • resource_address (109-111)
crates/engine_types/src/resource_container.rs (3)
  • resource_type (184-191)
  • public_fungible (55-63)
  • resource_address (175-182)
crates/template_lib/src/args/types.rs (3)
  • resource_address (448-453)
  • resource_address (645-650)
  • bucket_id (455-460)
crates/engine/src/transaction/processor.rs (5)
bindings/src/types/Amount.ts (1)
  • Amount (12-12)
crates/engine_types/src/bucket.rs (1)
  • amount (55-57)
crates/engine_types/src/resource_container.rs (1)
  • amount (134-141)
crates/template_lib/src/models/bucket.rs (1)
  • amount (187-195)
crates/template_lib/src/models/proof.rs (1)
  • amount (104-112)
crates/template_lib_types/src/resource_type.rs (1)
bindings/src/types/ResourceType.ts (1)
  • ResourceType (17-17)
crates/engine/src/runtime/working_state.rs (2)
crates/engine_types/src/resource_container.rs (3)
  • public_fungible (55-63)
  • resource_address (175-182)
  • amount (134-141)
crates/engine_types/src/bucket.rs (2)
  • resource_address (67-69)
  • amount (55-57)
crates/template_lib/src/models/metadata.rs (2)
crates/engine_types/src/indexed_value.rs (2)
  • metadata (105-107)
  • metadata (313-315)
crates/engine_types/src/resource.rs (1)
  • metadata (203-205)
crates/engine/tests/events.rs (2)
crates/engine_types/src/events.rs (1)
  • payload (119-121)
crates/template_lib/src/models/metadata.rs (1)
  • get (49-51)
crates/template_lib_types/src/amount/amount.rs (2)
crates/template_lib/src/models/stealth.rs (1)
  • new (69-80)
crates/common_types/src/vote_power.rs (3)
  • sub (73-75)
  • mul (81-83)
  • div (89-91)
crates/template_builtin/templates/pool/src/lib.rs (5)
crates/template_lib/src/models/metadata.rs (5)
  • new (38-40)
  • get (49-51)
  • from (92-94)
  • from (98-100)
  • from (104-106)
crates/template_lib_types/src/amount/amount.rs (2)
  • new (52-54)
  • is_zero (62-64)
crates/engine_types/src/resource_container.rs (4)
  • public_fungible (55-63)
  • resource_address (175-182)
  • amount (134-141)
  • resource_type (184-191)
crates/template_lib/src/resource/manager.rs (4)
  • get (104-106)
  • from (993-995)
  • resource_address (109-111)
  • resource_type (119-121)
crates/engine/src/runtime/impl.rs (1)
  • emit_event (435-458)
crates/template_lib/src/resource/manager.rs (5)
crates/engine_types/src/resource.rs (2)
  • resource_type (107-109)
  • divisibility (211-213)
crates/engine_types/src/resource_container.rs (1)
  • resource_type (184-191)
crates/template_lib/src/models/bucket.rs (1)
  • resource_type (88-97)
crates/template_lib/src/models/vault.rs (1)
  • resource_type (364-366)
crates/template_lib/src/models/proof.rs (4)
  • resource_type (82-91)
  • resp (132-132)
  • resp (175-175)
  • resp (195-195)
⏰ 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). (5)
  • GitHub Check: check nightly
  • GitHub Check: test
  • GitHub Check: check stable
  • GitHub Check: clippy
  • GitHub Check: machete
🔇 Additional comments (22)
crates/template_lib/src/models/metadata.rs (2)

49-51: LGTM! More idiomatic return type.

Returning Option<&str> instead of Option<&String> is more idiomatic and flexible for callers. The implementation correctly uses map(|s| s.as_str()) to convert the reference.


182-185: LGTM! Tests correctly updated.

The test assertions correctly expect &str values, aligning with the updated get method signature.

crates/template_lib_types/src/amount/amount.rs (3)

7-7: LGTM: Import cleanup aligns with macro expansion.

Removing ops::Neg from the import list is appropriate since the newtype_ops! macro now generates the Neg implementation automatically.


297-305: LGTM: Exponentiation methods follow existing patterns.

The pow and pow_checked methods are consistent with other arithmetic operations on Amount. The checked variant properly handles overflow by returning Option<Self>.


394-395: LGTM: Extended arithmetic operations.

Adding rem and neg to the newtype_ops! macro provides complete arithmetic functionality for Amount. This is a non-breaking public API extension.

crates/engine_types/src/resource.rs (1)

207-209: LGTM: Simplified token symbol accessor.

Removing the redundant .map(|s| s.as_str()) is correct since metadata.get() already returns Option<&str> directly.

crates/engine_types/src/bucket.rs (1)

51-53: LGTM: Emptiness check delegation.

The is_empty() method properly delegates to the underlying resource_container, consistent with other accessor methods in this struct.

crates/engine/tests/events.rs (2)

118-119: LGTM: Removed unnecessary dereferences.

The assertion is cleaner without the dereference operators since payload().get() already returns &str (when unwrapped from Option<&str>).


131-135: LGTM: Consistent dereference cleanup.

Same improvement as the previous assertion block—removing unnecessary dereferences for cleaner test code.

crates/engine/src/runtime/working_state.rs (2)

595-595: LGTM: Renamed to public_fungible for clarity.

The rename from fungible to public_fungible aligns with the ResourceType::is_public_fungible() naming convention, improving API consistency.


400-402: Validation logic is correct—no action required.

Based on verification, the stricter bucket validation (treating any remaining bucket as dangling) is the intended design and works correctly:

  1. Empty buckets ARE dropped: After Take operations in processor.rs (line 341), when a bucket becomes zero-valued, BucketAction::DropEmpty is explicitly invoked.

  2. DropEmpty removes from state: The DropEmpty action calls take_bucket(), which removes the bucket from the HashMap entirely via .remove(&bucket_id) (working_state.rs line 482).

  3. Validation expectation is sound: At finalization, the check if !self.buckets.is_empty() correctly expects zero buckets—any remaining bucket indicates a logic error, not just an empty one.

The code follows the intended behavior: empty buckets are cleaned up before finalization, so the validation can safely treat any remaining bucket as an error condition.

crates/engine/src/transaction/processor.rs (2)

45-45: LGTM: Added required import for Amount.

The Amount import is necessary for decoding the GetAmount result in the new post-take cleanup logic.


328-342: Review: Post-take empty bucket cleanup.

The cleanup logic automatically drops empty buckets after Take operations to prevent dangling buckets at finalization. The flow is:

  1. Invoke Take on the bucket
  2. Query remaining amount via GetAmount
  3. If zero, invoke DropEmpty

Minor concern: The code uses .decode::<Amount>()? which will propagate errors, but there's no explicit error handling if the decode fails. Consider whether a more descriptive error message would help debugging if this fails unexpectedly.

However, the logic itself appears sound and aligns with the stricter bucket validation in working_state.rs.

crates/template_lib/src/resource/manager.rs (3)

46-46: LGTM: Added ResourceInfo import.

The new ResourceInfo type is needed for the resource_info() method introduced below.


123-131: LGTM: Divisibility accessor added.

The new divisibility() method complements the existing API and follows the same pattern as resource_type().


133-145: LGTM: Centralized resource info query.

The resource_info() method is the primary accessor for resource metadata, returning both type and divisibility in a single engine call. This is more efficient than separate queries.

crates/template_lib_types/src/resource_type.rs (3)

4-4: LGTM: Import simplification.

Removing FromStr from the top-level imports is appropriate since it's now conditionally imported within the std-gated parsing module.


64-96: LGTM: Feature-gated parsing module with proper re-export.

The FromStr implementation and ParseResourceTypeError are correctly placed in a std-gated module, and the error type is properly re-exported at line 96, resolving the concern from the previous review.


98-102: LGTM: ResourceInfo struct added.

The new ResourceInfo struct provides a unified way to query both resource type and divisibility, supporting the updated GetResourceInfo engine action. Public fields are appropriate for this simple data structure.

crates/engine/src/runtime/impl.rs (2)

120-120: LGTM: ResourceInfo import added.

The import is necessary for the new GetResourceInfo action that returns both resource type and divisibility.


1383-1383: LGTM: Updated to use public_fungible.

The change from fungible to public_fungible aligns with the broader refactoring to clarify resource container types.

crates/template_builtin/templates/pool/src/lib.rs (1)

312-322: LGTM: Resource fungibility validation.

The check_resource_is_fungible method correctly validates that resources are one of the fungible-like types (Fungible, Confidential, or Stealth) and properly rejects NonFungible resources.

Comment thread crates/template_builtin/templates/pool/src/lib.rs Outdated
@sdbondi
sdbondi force-pushed the wip-liquidity-pool branch from a2b6c85 to 43bd29c Compare November 4, 2025 10:37

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 3

♻️ Duplicate comments (1)
crates/engine/src/runtime/impl.rs (1)

2133-2151: CRITICAL: DropEmpty destroys non-empty buckets before validation.

take_bucket is called on Line 2141 before checking if the bucket is empty. If the bucket contains funds, the error on Line 2143 is returned but the bucket has already been removed from state—the funds are permanently lost.

This issue was previously flagged but remains unresolved.

Apply this diff to check emptiness before removal:

             BucketAction::DropEmpty => {
                 let bucket_id = bucket_ref.bucket_id().ok_or_else(|| RuntimeError::InvalidArgument {
                     argument: "bucket_ref",
                     reason: "DropEmpty bucket action requires a bucket id".to_string(),
                 })?;
                 args.assert_no_args("Bucket::DropEmpty")?;

                 self.tracker.write_with(|state| {
-                    let bucket = state.take_bucket(bucket_id)?;
-                    if !bucket.is_empty() {
-                        return Err(RuntimeError::InvalidArgument {
-                            argument: "bucket_ref",
-                            reason: "Cannot drop a non-empty bucket".to_string(),
-                        });
-                    }
-                    // Drop
-                    Ok(InvokeResult::unit())
+                    {
+                        let bucket = state.get_bucket(bucket_id)?;
+                        if !bucket.is_empty() {
+                            return Err(RuntimeError::InvalidArgument {
+                                argument: "bucket_ref",
+                                reason: "Cannot drop a non-empty bucket".to_string(),
+                            });
+                        }
+                    }
+                    let _ = state.take_bucket(bucket_id)?;
+                    Ok(InvokeResult::unit())
                 })
             },
🧹 Nitpick comments (1)
crates/engine/src/transaction/processor.rs (1)

327-342: Rename variable for clarity.

The variable prev_bucket_val is misleading—it holds the bucket's amount after the take operation, not before. Consider renaming to remaining_amount or bucket_amount_after_take for clarity.

Apply this diff:

-                let prev_bucket_val = runtime
+                let remaining_amount = runtime
                     .interface()
                     .bucket_invoke(bucket_ref, BucketAction::GetAmount, invoke_args![].into())?
                     .decode::<Amount>()?;
-                if prev_bucket_val.is_zero() {
+                if remaining_amount.is_zero() {
                     // Drop the bucket to prevent a dangling (empty) bucket
                     runtime
                         .interface()
                         .bucket_invoke(bucket_ref, BucketAction::DropEmpty, invoke_args![].into())?;
                 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a2b6c85 and 43bd29c.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (18)
  • Cargo.toml (1 hunks)
  • crates/engine/src/runtime/impl.rs (5 hunks)
  • crates/engine/src/runtime/working_state.rs (2 hunks)
  • crates/engine/src/transaction/processor.rs (2 hunks)
  • crates/engine/tests/events.rs (2 hunks)
  • crates/engine_types/src/bucket.rs (1 hunks)
  • crates/engine_types/src/events.rs (1 hunks)
  • crates/engine_types/src/resource.rs (1 hunks)
  • crates/engine_types/src/resource_container.rs (6 hunks)
  • crates/template_builtin/templates/pool/Cargo.toml (1 hunks)
  • crates/template_builtin/templates/pool/src/lib.rs (1 hunks)
  • crates/template_lib/src/args/types.rs (2 hunks)
  • crates/template_lib/src/models/bucket.rs (1 hunks)
  • crates/template_lib/src/models/metadata.rs (2 hunks)
  • crates/template_lib/src/resource/manager.rs (2 hunks)
  • crates/template_lib_types/src/amount/amount.rs (3 hunks)
  • crates/template_lib_types/src/resource_type.rs (2 hunks)
  • integration_tests/Cargo.toml (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • crates/template_lib/src/args/types.rs
  • crates/engine_types/src/bucket.rs
  • crates/engine_types/src/resource.rs
  • crates/engine_types/src/events.rs
  • crates/template_builtin/templates/pool/src/lib.rs
  • crates/engine/src/runtime/working_state.rs
  • crates/template_builtin/templates/pool/Cargo.toml
🧰 Additional context used
🧬 Code graph analysis (8)
crates/template_lib/src/models/bucket.rs (2)
crates/template_lib/src/resource/manager.rs (1)
  • resp (144-144)
crates/template_lib/src/models/proof.rs (3)
  • resp (132-132)
  • resp (175-175)
  • resp (195-195)
crates/template_lib_types/src/resource_type.rs (1)
bindings/src/types/ResourceType.ts (1)
  • ResourceType (17-17)
crates/template_lib/src/models/metadata.rs (2)
crates/engine_types/src/indexed_value.rs (2)
  • metadata (105-107)
  • metadata (313-315)
crates/engine_types/src/resource.rs (1)
  • metadata (203-205)
crates/engine/src/runtime/impl.rs (3)
crates/engine_types/src/resource.rs (2)
  • divisibility (211-213)
  • resource_type (107-109)
crates/engine_types/src/resource_container.rs (3)
  • resource_type (184-191)
  • public_fungible (55-63)
  • resource_address (175-182)
crates/template_lib/src/args/types.rs (3)
  • resource_address (448-453)
  • resource_address (645-650)
  • bucket_id (455-460)
crates/engine/src/transaction/processor.rs (5)
bindings/src/types/Amount.ts (1)
  • Amount (12-12)
crates/engine_types/src/bucket.rs (1)
  • amount (55-57)
crates/engine_types/src/resource_container.rs (1)
  • amount (134-141)
crates/template_lib/src/models/bucket.rs (1)
  • amount (187-195)
crates/template_lib/src/models/proof.rs (1)
  • amount (104-112)
crates/engine_types/src/resource_container.rs (2)
crates/template_lib/src/resource/builder/mod.rs (1)
  • public_fungible (54-56)
crates/engine_types/src/bucket.rs (3)
  • amount (55-57)
  • resource_address (67-69)
  • is_empty (51-53)
crates/template_lib/src/resource/manager.rs (7)
crates/engine_types/src/bucket.rs (1)
  • resource_type (71-73)
crates/engine_types/src/resource.rs (2)
  • resource_type (107-109)
  • divisibility (211-213)
crates/engine_types/src/resource_container.rs (1)
  • resource_type (184-191)
crates/template_lib/src/models/bucket.rs (1)
  • resource_type (88-97)
crates/template_lib/src/models/vault.rs (1)
  • resource_type (364-366)
crates/template_lib/src/models/proof.rs (4)
  • resource_type (82-91)
  • resp (132-132)
  • resp (175-175)
  • resp (195-195)
bindings/src/types/ResourceType.ts (1)
  • ResourceType (17-17)
crates/engine/tests/events.rs (2)
crates/engine_types/src/events.rs (1)
  • payload (119-121)
crates/template_lib/src/models/metadata.rs (1)
  • get (49-51)
⏰ 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). (5)
  • GitHub Check: machete
  • GitHub Check: test
  • GitHub Check: check stable
  • GitHub Check: check nightly
  • GitHub Check: clippy
🔇 Additional comments (17)
crates/template_lib/src/models/metadata.rs (4)

49-51: LGTM: Idiomatic API improvement.

Changing the return type to Option<&str> is more idiomatic than returning Option<&String> and provides better flexibility for callers.


53-56: Add test coverage for the new method.

The get_or_insert implementation is correct and follows Rust conventions. However, there's no test coverage for this new method.

Consider adding a test case to verify:

  • Insertion behavior when key doesn't exist
  • Return of existing value when key exists
  • Correct return of &str reference
#[test]
fn test_get_or_insert() {
    let mut metadata = Metadata::new();
    
    // Insert when key doesn't exist
    let value = metadata.get_or_insert("key1", "default");
    assert_eq!(value, "default");
    assert_eq!(metadata.get("key1"), Some("default"));
    
    // Return existing value when key exists
    let value = metadata.get_or_insert("key1", "other");
    assert_eq!(value, "default");
}

58-60: Add test coverage for the new method.

The remove implementation is correct. However, there's no test coverage for this new method.

Consider adding a test case:

#[test]
fn test_remove() {
    let mut metadata = metadata!("key1" => "value1", "key2" => "value2");
    
    assert_eq!(metadata.remove("key1"), Some("value1".to_string()));
    assert_eq!(metadata.get("key1"), None);
    assert_eq!(metadata.remove("nonexistent"), None);
}

182-186: LGTM: Tests properly updated.

The test assertions correctly validate the new Option<&str> return type.

crates/template_lib_types/src/amount/amount.rs (2)

7-7: LGTM! Import cleanup aligns with macro-generated negation.

The removal of ops::Neg from imports is consistent with the updated newtype_ops! macro now generating the negation implementation.


394-395: I need to verify the behavior of the newtype_ops macro and confirm edge-case handling in bnum.Now let me search for bnum behavior with edge cases:

Let me verify the exact behavior when newtype_ops generates operators and what trait implementations bnum::BInt provides:Perfect! This is the critical finding. Now I have all the information I need.

Based on my verification, I can now provide a definitive assessment of the review comment.

Add test coverage for rem and neg operations, particularly edge cases.

The macro-generated operators expose real safety concerns:

  1. Modulus by zero or overflow: The Rem trait will panic if divisor is zero or if self / other results in overflow. For Amount % Amount::ZERO or negation-induced overflow conditions, this will panic.

  2. Division/remainder with MIN/-1: The operations /, % for arguments INT_MIN and -1 will unconditionally panic. This applies to Amount::MIN % Amount::from(-1).

  3. Negation of MIN: Negating MIN causes either undefined behavior (unchecked) or a panic (strict). No tests exist for (-Amount::MIN).

Current state:

  • No tests found for the newly added rem operator
  • No tests for negation edge cases (e.g., Amount::MIN)
  • Existing negation tests only use normal values, not boundary conditions

Add explicit test cases for:

  • amount % Amount::zero() (should panic)
  • Amount::MIN % Amount::from(-1) (should panic)
  • -Amount::MIN (should panic or return controlled result)
  • rem behavior with negative operands to document expected sign behavior
crates/engine/src/transaction/processor.rs (1)

45-45: LGTM – Import addition supports new bucket cleanup logic.

The Amount import is required for decoding and comparing bucket amounts in the new post-take cleanup flow.

crates/template_lib_types/src/resource_type.rs (3)

4-4: LGTM – Import cleanup aligns with module reorganization.

Removing the top-level FromStr import is correct since it's now only used within the parsing module.


64-96: LGTM – Error type is now properly exported.

The parsing module correctly encapsulates FromStr functionality and publicly re-exports ParseResourceTypeError, addressing the previous concern about the error type being unreachable.


98-102: LGTM – ResourceInfo provides clean API for resource metadata.

The new ResourceInfo struct appropriately bundles resource_type and divisibility, enabling callers to retrieve both properties in a single engine call.

crates/engine/src/runtime/impl.rs (3)

120-120: LGTM – Import supports ResourceInfo return type.


866-887: LGTM – GetResourceInfo consolidates resource metadata retrieval.

Returning ResourceInfo instead of just ResourceType allows callers to retrieve both resource_type and divisibility in a single engine call, improving efficiency.


1383-1383: LGTM – Naming clarifies fungible resource visibility.

Using public_fungible instead of fungible makes the distinction from confidential and stealth resources explicit.

crates/template_lib/src/resource/manager.rs (4)

46-46: LGTM – Import enables ResourceInfo usage.


113-121: LGTM – Clean delegation reduces redundant engine calls.

Delegating to resource_info() avoids duplicate logic, and the documentation helpfully suggests calling resource_info() directly when both type and divisibility are needed.


123-131: LGTM – Divisibility accessor complements resource_type().

The new divisibility() method provides a convenient accessor while recommending resource_info() for callers needing both properties.


133-145: LGTM – Central resource info retrieval method.

The resource_info() method provides a single point for retrieving both resource_type and divisibility, efficiently replacing the prior GetResourceType call.

Comment thread crates/engine_types/src/resource_container.rs
Comment thread crates/engine/tests/events.rs
Comment thread crates/template_lib_types/src/amount/amount.rs

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

♻️ Duplicate comments (1)
crates/engine/src/runtime/impl.rs (1)

2133-2150: DropEmpty still deletes non-empty buckets before erroring
We’re still calling take_bucket up front, so a non-empty bucket is removed from state and its contents are lost before we return the error. Please re-check the bucket first, bail out if it’s non-empty, and only then remove it. This is the same critical issue previously flagged.

Apply this patch:

                 self.tracker.write_with(|state| {
-                    let bucket = state.take_bucket(bucket_id)?;
-                    if !bucket.is_empty() {
-                        return Err(RuntimeError::InvalidArgument {
-                            argument: "bucket_ref",
-                            reason: "Cannot drop a non-empty bucket".to_string(),
-                        });
-                    }
-                    // Drop
-                    Ok(InvokeResult::unit())
+                    {
+                        let bucket = state.get_bucket(bucket_id)?;
+                        if !bucket.is_empty() {
+                            return Err(RuntimeError::InvalidArgument {
+                                argument: "bucket_ref",
+                                reason: "Cannot drop a non-empty bucket".to_string(),
+                            });
+                        }
+                    }
+                    let _ = state.take_bucket(bucket_id)?;
+                    Ok(InvokeResult::unit())
                 })
🧹 Nitpick comments (3)
crates/template_lib/src/models/account.rs (1)

38-41: LGTM! Clean iterator accessor for vault resource addresses.

The implementation correctly delegates to BTreeMap::keys() and the return type properly captures the iterator semantics. This is a useful addition for enumerating resources in liquidity pool scenarios.

Optional: Consider enhancing the documentation to note that iteration order is deterministic (sorted by BTreeMap):

-    /// Returns an iterator over all resource addresses in the account.
+    /// Returns an iterator over all resource addresses in the account.
+    /// The addresses are yielded in sorted order as determined by the BTreeMap.
     pub fn all_resources_iter(&self) -> impl Iterator<Item = &ResourceAddress> {
crates/template_test_tooling/src/template_test.rs (1)

501-540: Consider documenting the single-vault assumption.

The method assumes the faucet component has exactly one vault (Line 533 uses .first()). If the TestFaucet template is modified to include multiple vaults, this could silently return an unexpected resource address.

Consider adding a comment or assertion:

 let vault_id = indexed
     .vault_ids()
     .first()
-    .expect("No vault id found in faucet component state");
+    .expect("TestFaucet component must have exactly one vault");

Alternatively, assert that there is exactly one vault:

let vault_ids = indexed.vault_ids();
assert_eq!(vault_ids.len(), 1, "TestFaucet component must have exactly one vault");
let vault_id = &vault_ids[0];
crates/template_test_tooling/src/builtin_component_state.rs (1)

82-115: Good simplification.

The refactored initialization is cleaner with constants replacing dynamically-derived IDs, and removing the template address parameter simplifies the API.

The comment at Line 96 could be updated for clarity:

-    // This must mirror the test faucet component
+    // This must mirror the XTR faucet component structure
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 43bd29c and ab89896.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (38)
  • Cargo.toml (2 hunks)
  • applications/tari_indexer/README.md (1 hunks)
  • applications/tari_validator_node/src/genesis_state.rs (2 hunks)
  • crates/engine/src/runtime/fee_state.rs (1 hunks)
  • crates/engine/src/runtime/impl.rs (12 hunks)
  • crates/engine/src/runtime/working_state.rs (9 hunks)
  • crates/engine/src/wasm/module.rs (2 hunks)
  • crates/engine/tests/access_rules.rs (1 hunks)
  • crates/engine/tests/account.rs (3 hunks)
  • crates/engine/tests/composability.rs (2 hunks)
  • crates/engine/tests/fees.rs (4 hunks)
  • crates/engine/tests/test.rs (1 hunks)
  • crates/engine_types/src/bucket.rs (1 hunks)
  • crates/engine_types/src/commit_result.rs (7 hunks)
  • crates/engine_types/src/proof.rs (1 hunks)
  • crates/engine_types/src/resource_container.rs (12 hunks)
  • crates/engine_types/src/vault.rs (1 hunks)
  • crates/template_builtin/build.rs (1 hunks)
  • crates/template_builtin/src/lib.rs (2 hunks)
  • crates/template_builtin/templates/account/Cargo.toml (1 hunks)
  • crates/template_builtin/templates/account/src/lib.rs (1 hunks)
  • crates/template_builtin/templates/faucet/Cargo.toml (1 hunks)
  • crates/template_builtin/templates/faucet/src/lib.rs (1 hunks)
  • crates/template_builtin/templates/liquidity_pool/Cargo.toml (1 hunks)
  • crates/template_builtin/templates/liquidity_pool/src/lib.rs (1 hunks)
  • crates/template_builtin/tests/liquidity_pool.rs (1 hunks)
  • crates/template_lib/Cargo.toml (1 hunks)
  • crates/template_lib/src/models/account.rs (1 hunks)
  • crates/template_lib/src/models/bucket.rs (1 hunks)
  • crates/template_lib_types/Cargo.toml (2 hunks)
  • crates/template_lib_types/src/amount/amount.rs (6 hunks)
  • crates/template_test_tooling/src/builtin_component_state.rs (2 hunks)
  • crates/template_test_tooling/src/lib.rs (1 hunks)
  • crates/template_test_tooling/src/package_builder.rs (2 hunks)
  • crates/template_test_tooling/src/read_only_state_store.rs (2 hunks)
  • crates/template_test_tooling/src/support/assert_error.rs (2 hunks)
  • crates/template_test_tooling/src/template_test.rs (9 hunks)
  • lints.toml (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • crates/engine/tests/access_rules.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/engine_types/src/resource_container.rs
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-04T10:10:24.249Z
Learnt from: sdbondi
Repo: tari-project/tari-ootle PR: 1629
File: applications/tari_walletd/src/handlers/accounts.rs:1001-1002
Timestamp: 2025-11-04T10:10:24.249Z
Learning: In applications/tari_walletd/src/handlers/accounts.rs, the expect() on Memo::new_pay_ref_and_bytes_truncate at line 1002 is safe and intentional. PayRef is validated to be at most 64 bytes during address decoding (PayRef::MAX_LEN = 64), and the function only returns None if payref exceeds 252 bytes (Memo::MAX_BYTES_LENGTH - 1). Since 64 < 252, None is impossible with a valid PayRef.

Applied to files:

  • crates/engine/tests/fees.rs
  • crates/engine/tests/composability.rs
🧬 Code graph analysis (20)
crates/template_test_tooling/src/lib.rs (1)
crates/template_test_tooling/src/template_test.rs (1)
  • xtr_faucet_component (71-73)
crates/engine_types/src/commit_result.rs (1)
crates/engine_types/src/fees.rs (1)
  • is_paid_in_full (58-60)
crates/template_lib/src/models/account.rs (1)
bindings/src/types/ResourceAddress.ts (1)
  • ResourceAddress (6-6)
crates/engine/src/runtime/fee_state.rs (1)
crates/engine_types/src/proof.rs (2)
  • amount (42-44)
  • amount (84-86)
crates/engine_types/src/bucket.rs (3)
crates/engine_types/src/resource_container.rs (3)
  • is_empty (872-876)
  • unlocked_amount (138-145)
  • balance (134-136)
crates/engine/src/runtime/engine_args.rs (1)
  • is_empty (65-67)
crates/engine_types/src/vault.rs (1)
  • balance (105-107)
crates/template_test_tooling/src/read_only_state_store.rs (3)
crates/engine_types/src/events.rs (1)
  • template_address (107-109)
bindings/src/types/ComponentHeader.ts (1)
  • ComponentHeader (9-17)
crates/engine_types/src/substate.rs (1)
  • component (590-595)
crates/engine/src/runtime/impl.rs (3)
crates/engine_types/src/resource.rs (2)
  • divisibility (211-213)
  • resource_type (107-109)
crates/engine_types/src/resource_container.rs (3)
  • resource_type (188-195)
  • public_fungible (55-63)
  • resource_address (179-186)
crates/template_lib/src/models/bucket.rs (2)
  • resource_type (88-97)
  • resource_address (76-85)
crates/template_lib/src/models/bucket.rs (2)
crates/engine_types/src/bucket.rs (1)
  • is_empty (51-53)
crates/engine_types/src/resource_container.rs (1)
  • is_empty (872-876)
crates/template_test_tooling/src/package_builder.rs (2)
crates/template_builtin/src/lib.rs (1)
  • get_template_builtin (43-45)
crates/engine/src/wasm/module.rs (1)
  • from_code (61-63)
crates/template_builtin/src/lib.rs (1)
crates/template_lib/src/models/component.rs (1)
  • from_array (67-69)
crates/engine/src/runtime/working_state.rs (4)
bindings/src/types/ResourceContainer.ts (1)
  • ResourceContainer (11-29)
crates/engine_types/src/resource_container.rs (3)
  • public_fungible (55-63)
  • resource_address (179-186)
  • unlocked_amount (138-145)
crates/engine_types/src/bucket.rs (2)
  • resource_address (72-74)
  • unlocked_amount (55-57)
crates/engine_types/src/proof.rs (4)
  • resource_address (46-48)
  • resource_address (88-90)
  • amount (42-44)
  • amount (84-86)
crates/engine/tests/test.rs (2)
bindings/src/helpers/consts.ts (1)
  • XTR (10-10)
bindings/src/types/Amount.ts (1)
  • Amount (12-12)
crates/engine/tests/fees.rs (2)
crates/template_test_tooling/src/template_test.rs (1)
  • xtr_faucet_component (71-73)
crates/engine_types/src/fees.rs (1)
  • total_fees_paid (46-48)
crates/template_builtin/tests/liquidity_pool.rs (2)
bindings/src/helpers/consts.ts (1)
  • XTR (10-10)
crates/template_test_tooling/src/template_test.rs (2)
  • xtr_faucet_component (71-73)
  • new (98-100)
crates/engine/tests/account.rs (1)
crates/template_test_tooling/src/template_test.rs (1)
  • xtr_faucet_component (71-73)
crates/template_test_tooling/src/support/assert_error.rs (1)
bindings/src/types/RejectReason.ts (1)
  • RejectReason (4-12)
applications/tari_validator_node/src/genesis_state.rs (4)
bindings/src/types/ResourceType.ts (1)
  • ResourceType (17-17)
bindings/src/types/Vault.ts (1)
  • Vault (5-5)
crates/engine_types/src/vault.rs (1)
  • new (49-54)
bindings/src/types/ResourceContainer.ts (1)
  • ResourceContainer (11-29)
crates/template_test_tooling/src/builtin_component_state.rs (4)
crates/template_test_tooling/src/template_test.rs (2)
  • test_nft_faucet_component (75-77)
  • xtr_faucet_component (71-73)
crates/engine_types/src/resource_container.rs (1)
  • stealth (91-101)
crates/template_lib/src/models/vault.rs (1)
  • for_test (478-480)
bindings/src/types/ComponentHeader.ts (1)
  • ComponentHeader (9-17)
crates/template_test_tooling/src/template_test.rs (3)
bindings/src/types/IndexedWellKnownTypes.ts (1)
  • IndexedWellKnownTypes (15-30)
crates/template_builtin/src/lib.rs (1)
  • all_builtin_templates (51-72)
crates/template_test_tooling/src/builtin_component_state.rs (1)
  • initialize_builtin_faucet_state (82-115)
crates/template_builtin/templates/liquidity_pool/src/lib.rs (4)
crates/engine_types/src/resource_container.rs (3)
  • public_fungible (55-63)
  • resource_address (179-186)
  • resource_type (188-195)
crates/engine_types/src/vault.rs (3)
  • new (49-54)
  • resource_address (121-123)
  • resource_type (125-127)
crates/engine/src/runtime/impl.rs (1)
  • emit_event (435-458)
crates/template_lib/src/models/bucket.rs (3)
  • resource_address (76-85)
  • amount (193-201)
  • resource_type (88-97)
⏰ 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: test
🔇 Additional comments (46)
crates/template_test_tooling/src/support/assert_error.rs (1)

9-9: Excellent addition of #[track_caller] for better panic diagnostics.

Adding #[track_caller] to these assertion helpers will show the test call site in panic messages rather than the helper's internal panic location, significantly improving the debugging experience when tests fail.

Also applies to: 19-19, 26-26

applications/tari_indexer/README.md (1)

56-110: Documentation verification confirms all CLI options are accurate and properly implemented.

I verified all documented CLI options against the implementation in applications/tari_indexer/src/cli.rs and their usage in main.rs. Every option listed in the README has a corresponding implementation with correct short flags, long flags, and environment variable annotations where applicable. The organization into functional categories (Core Options, Configuration, Logging, API Server, Blockchain Scanning, P2P Networking, Epoch Oracle) accurately reflects the grouping of related CLI parameters. The example usage section demonstrates realistic command patterns that align with the actual CLI structure.

lints.toml (1)

73-75: LGTM: Reasonable lint exception with clear justification.

The clippy::drop_non_drop allowance is well-justified for legitimate use cases involving borrow checker constraints.

crates/template_builtin/templates/account/src/lib.rs (1)

115-119: LGTM: Efficient early return for empty buckets.

The early return avoids unnecessary vault operations when the bucket is empty, improving efficiency and aligning with the new bucket lifecycle handling introduced in this PR.

crates/template_builtin/templates/account/Cargo.toml (1)

11-18: LGTM: Build configuration aligns with builtin template requirements.

The switch to opt-level = 3 for builtin templates prioritizes performance over binary size, which is appropriate. The dual cdylib and lib crate types enable both WASM deployment and integration testing.

crates/template_builtin/templates/faucet/Cargo.toml (1)

12-20: LGTM: Consistent build configuration across builtin templates.

The build configuration changes match those in the account template, ensuring consistency across all builtin templates in the codebase.

crates/engine_types/src/commit_result.rs (1)

62-143: LGTM: Enhanced panic diagnostics with track_caller.

Adding #[track_caller] to test helper methods improves debugging by showing the actual call site in panic messages rather than the helper method location.

Cargo.toml (2)

3-3: LGTM: Version bump reflects breaking changes.

The minor version bump from 0.15.1 to 0.16.0 appropriately indicates the breaking changes noted in the PR (ABI changes, data directory deletion required).


213-213: LGTM: Added dependency supports new arithmetic features.

The num-integer dependency enables the new checked_sqrt functionality in the Amount type.

crates/engine/src/runtime/working_state.rs (6)

595-595: LGTM: API migration to public_fungible.

The change from fungible() to public_fungible() aligns with the ResourceContainer API updates across the codebase.


496-498: LGTM: Consistent use of unlocked_amount for balance checks.

Using unlocked_amount() for the zero check is consistent with the API migration throughout this PR.


656-662: LGTM: Supply tracking uses unlocked_amount consistently.

The total supply calculations correctly use unlocked_amount() for both increase and overflow checks.


1336-1399: LGTM: Fee collection migrated to unlocked_amount.

All fee collection and refund logic consistently uses unlocked_amount() throughout the refactored fee finalization flow.


1528-1534: LGTM: Stealth transfer validation uses unlocked_amount.

The revealed funds validation correctly compares bucket.unlocked_amount() against the statement's revealed input amount.


400-405: Review comment is based on a misunderstanding of the validation logic.

The condition !b.is_empty() explicitly targets non-empty buckets, not empty ones:

  • Empty buckets (where is_empty() returns true) satisfy the condition and pass validation
  • Non-empty buckets (where is_empty() returns false) fail the condition and trigger the DanglingBuckets error

The validation correctly enforces that all buckets must be empty at transaction finalization. Empty buckets are not flagged as dangling; only buckets with remaining funds are.

Likely an incorrect or invalid review comment.

crates/template_lib_types/src/amount/amount.rs (5)

152-157: LGTM: Signature change improves efficiency.

Changing checked_mul to take other: Self (owned) instead of &Self eliminates a copy operation since Self is Copy.


279-291: LGTM: Square root implementation handles edge cases.

The feature-gated checked_sqrt correctly returns None for negative inputs and handles zero as a special case.


311-319: LGTM: Power operations with proper overflow handling.

Both pow and checked_pow are well-implemented, delegating to the underlying I192 operations. Test coverage at lines 520-537 addresses the previous review concern.


408-409: LGTM: Extended operator support via macros.

Adding rem and neg to the newtype_ops! macro provides complete arithmetic operator coverage for Amount.


512-548: LGTM: Comprehensive test coverage added.

Tests now cover:

  • Basic arithmetic including new checked_pow (lines 520-521)
  • Overflow detection for checked_pow (lines 535-536)
  • Square root operations including negative input handling (lines 539-548)

This addresses the previous review comment requesting test coverage for power operations.

applications/tari_validator_node/src/genesis_state.rs (1)

126-133: LGTM: Vault initialization refactored cleanly.

The vault is now created before the component header and properly referenced in the component state. This removes the need for ResourceManager in genesis state initialization.

crates/engine_types/src/vault.rs (1)

105-111: LGTM: Clear separation of locked vs unlocked balances.

The balance() method now correctly returns only unlocked amounts, while locked_balance() (line 109) provides separate access to locked amounts. This is a logical and consistent API design.

crates/template_builtin/build.rs (1)

13-18: LGTM: New liquidity pool template added to build.

The liquidity_pool template is now included in the build process alongside the existing templates.

crates/template_test_tooling/src/lib.rs (1)

14-14: LGTM: Test utility renamed for clarity.

The rename from test_faucet_component to xtr_faucet_component provides more specific naming. Note this is a breaking change for any code using the test tooling.

crates/engine/tests/composability.rs (2)

227-247: LGTM: Empty account sufficient for composability test.

The test focuses on invalid method calls and error handling, not account balances. Using create_empty_account() simplifies the test setup without affecting the test's validity.


316-352: LGTM: Empty account sufficient for recursion limit test.

The test validates recursion depth limits, which doesn't require funded accounts. The change simplifies test setup while preserving test coverage.

crates/template_lib/Cargo.toml (1)

30-30: LGTM: New feature flag for extended arithmetic.

The extra-arith feature enables extended arithmetic capabilities from tari_template_lib_types, supporting the new liquidity pool implementation and other advanced use cases.

crates/engine/src/runtime/fee_state.rs (1)

26-62: LGTM: Fee validation now uses unlocked amounts.

The change to unlocked_amount() is consistent with the broader API shift and makes semantic sense—fee payments should use unlocked resources. Error messages are correctly updated to reference the unlocked amounts.

crates/engine_types/src/proof.rs (1)

84-86: Semantic change verified and isolated to one caller.

The search found only one caller: the runtime's ProofAction::GetAmount handler at crates/engine/src/runtime/impl.rs:2179. The method now returns unlocked amounts exclusively (via LockedResource::amount()self.locked.unlocked_amount()). The change flows through Proof::amount()LockedResource::amount() and is returned directly to external callers via the GetAmount action.

This is a breaking change if external callers previously relied on total amounts (locked + unlocked). No other callers exist in the codebase.

crates/template_builtin/src/lib.rs (2)

39-41: LGTM: New liquidity pool template address constant.

The constant follows the established naming convention and pattern used by other builtin template addresses.


65-69: Appropriate deferral of template inclusion.

The commented-out liquidity pool template entry aligns with the PR's partial implementation approach. The path and structure are correct for when the template is ready to be enabled.

crates/template_builtin/templates/liquidity_pool/Cargo.toml (1)

1-19: LGTM: Well-configured template crate.

The Cargo.toml follows the established pattern for template crates with appropriate features enabled (macro, alloc, extra-arith) and an optimized release profile for WASM binary size.

crates/engine/tests/test.rs (1)

37-40: LGTM: Import additions for XTR constant and Amount type.

These imports support the updated test interactions with resources and amounts. The changes are minimal and appropriate.

crates/template_builtin/templates/faucet/src/lib.rs (1)

43-45: LGTM: Cleaner resource manager access.

The change to obtain the ResourceManager at call time via self.vault.to_resource_manager() rather than storing it as a field reduces redundant state. This aligns with the broader API surface changes in the PR.

crates/template_lib_types/Cargo.toml (1)

12-13: LGTM: Well-structured optional arithmetic feature.

The extra-arith feature is properly gated with optional num-integer dependency. The ?/ syntax in the std feature correctly handles conditional inclusion of num-integer/std. The configuration maintains no_std compatibility by default.

Also applies to: 23-23

crates/template_test_tooling/src/read_only_state_store.rs (2)

15-18: LGTM: Import addition for new method.

The TemplateAddress import is necessary for the new get_components_by_template_address method.


33-48: LGTM: Useful helper method for finding components by template.

The implementation correctly filters all components matching the provided template address. The approach is straightforward with proper error handling.

crates/engine/tests/fees.rs (3)

8-8: LGTM: Updated to use new faucet helper.

The import change from test_faucet_component to xtr_faucet_component aligns with the broader test tooling updates in the PR.


74-80: LGTM: Updated faucet interaction pattern.

The change to use xtr_faucet_component() with explicit take method calls is more explicit and aligns with the new faucet API.


94-94: LGTM: Consistent faucet usage updates.

The balance assertions and faucet calls are updated consistently throughout the test file to use the new xtr_faucet_component() helper with explicit amounts.

Also applies to: 119-119

crates/engine_types/src/bucket.rs (1)

51-62: LGTM: More explicit bucket balance API.

The replacement of amount() with three specific methods (is_empty(), unlocked_amount(), balance()) provides clearer semantics. The distinction between unlocked and total balance is important for locked resource scenarios. The doc comment on balance() is helpful.

crates/template_test_tooling/src/template_test.rs (4)

30-30: LGTM!

The new imports support the dynamic template loading and the create_test_faucet_component helper method.

Also applies to: 41-41, 44-44


71-73: Good refactoring.

The function rename aligns with the XTR faucet refactoring, and extracting the initial balance as a constant improves code maintainability.

Also applies to: 95-96


126-128: LGTM!

Dynamic template loading via all_builtin_templates() is more maintainable than hardcoded static addresses.


440-466: LGTM!

The refactored methods correctly use the XTR faucet component's take method, and the #[track_caller] annotations improve debugging experience.

Also applies to: 468-499

crates/template_test_tooling/src/builtin_component_state.rs (1)

14-14: LGTM!

The import changes correctly support the XTR faucet refactoring and remove unused imports.

Also applies to: 22-22, 26-26, 29-29, 32-32

Comment thread crates/template_builtin/templates/liquidity_pool/src/lib.rs
Comment thread crates/template_lib/src/models/bucket.rs
@sdbondi
sdbondi force-pushed the wip-liquidity-pool branch from ab89896 to 4732060 Compare November 5, 2025 13:02

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (3)
crates/engine/src/runtime/impl.rs (2)

867-889: Fix action names in error messages and asserts for GetResourceInfo

Error/label strings still refer to GetResourceType, which is confusing.

-            ResourceAction::GetResourceInfo => {
+            ResourceAction::GetResourceInfo => {
                 let resource_address =
                     resource_ref
                         .as_resource_address()
                         .ok_or_else(|| RuntimeError::InvalidArgument {
                             argument: "resource_ref",
-                            reason: "GetResourceType resource action requires a resource address".to_string(),
+                            reason: "GetResourceInfo resource action requires a resource address".to_string(),
                         })?;
 
-                args.assert_no_args("ResourceAction::GetResourceType")?;
+                args.assert_no_args("ResourceAction::GetResourceInfo")?;

Also fix the similar message in GetTotalSupply:

-                            reason: "GetResourceType resource action requires a resource address".to_string(),
+                            reason: "GetTotalSupply resource action requires a resource address".to_string(),

1701-1712: Typo in argument label (“TakeFeesArg”)

This path validates PayFee; the argument label should be consistent.

-                    if container.unlocked_amount().is_zero() {
-                        return Err(RuntimeError::InvalidArgument {
-                            argument: "TakeFeesArg",
+                    if container.unlocked_amount().is_zero() {
+                        return Err(RuntimeError::InvalidArgument {
+                            argument: "PayFeeArg",
                             reason: "Fee payment has zero value".to_string(),
                         });
                     }
crates/engine_types/src/resource_container.rs (1)

610-626: Stealth lock_all returns wrong variant (type mismatch)

The Stealth branch of lock_all() at line 626 incorrectly returns Self::public_fungible(...), creating a Fungible variant instead of Stealth. This violates the type invariant enforced by unlock() at line 631, which checks if self.resource_type() != container.resource_type() and will fail for stealth resources. All other branches (Fungible, NonFungible, Confidential) correctly preserve their variant type.

             Self::Stealth {
                 revealed_amount,
                 locked_amount,
                 ..
             } => {
                 if revealed_amount.is_zero() {
                     return Err(ResourceError::InsufficientBalance {
                         details: "lock_all: resource container contained no funds".to_string(),
                     });
                 }

                 // Sets to zero and returns the amount
                 let newly_locked_amount = mem::take(revealed_amount);
                 *locked_amount += newly_locked_amount;
-                Ok(Self::public_fungible(resource_address, newly_locked_amount))
+                Ok(Self::stealth(resource_address, newly_locked_amount))
             },
♻️ Duplicate comments (3)
crates/engine_types/src/resource_container.rs (1)

868-872: is_empty implementation looks correct now

Unlocked + locked + commitments all considered; addresses the prior locked-only buckets case.

crates/engine/src/runtime/impl.rs (1)

2163-2181: DropEmpty removes the bucket before checking emptiness (funds loss on error path)

take_bucket removes the bucket; if it’s non‑empty you error after removal, losing contents.

Apply the safe pattern: inspect in place, then remove.

             BucketAction::DropEmpty => {
                 let bucket_id = bucket_ref.bucket_id().ok_or_else(|| RuntimeError::InvalidArgument {
                     argument: "bucket_ref",
                     reason: "DropEmpty bucket action requires a bucket id".to_string(),
                 })?;
                 args.assert_no_args("Bucket::DropEmpty")?;

-                self.tracker.write_with(|state| {
-                    let bucket = state.take_bucket(bucket_id)?;
-                    if !bucket.is_empty() {
-                        return Err(RuntimeError::InvalidArgument {
-                            argument: "bucket_ref",
-                            reason: "Cannot drop a non-empty bucket".to_string(),
-                        });
-                    }
-                    // Drop
-                    Ok(InvokeResult::unit())
-                })
+                self.tracker.write_with(|state| {
+                    {
+                        let bucket = state.get_bucket(bucket_id)?;
+                        if !bucket.is_empty() {
+                            return Err(RuntimeError::InvalidArgument {
+                                argument: "bucket_ref",
+                                reason: "Cannot drop a non-empty bucket".to_string(),
+                            });
+                        }
+                    }
+                    let _ = state.take_bucket(bucket_id)?;
+                    Ok(InvokeResult::unit())
+                })
             },

Please add a test that calling DropEmpty on a non‑empty bucket leaves it intact.

crates/template_builtin/templates/liquidity_pool/src/lib.rs (1)

97-219: contribute drops residual buckets — return minted LP plus leftover buckets

contribute() partially consumes the input buckets via .take(...) but does not return or explicitly handle the remaining contents; the leftover buckets are dropped on scope exit which, per the Bucket API, will cause a dangling‑bucket panic or asset loss. Change the API to return the minted LP bucket plus the two residual buckets so callers can reclaim leftovers and update call sites/events.

-        pub fn contribute(&mut self, mut bucket_a: Bucket, mut bucket_b: Bucket) -> Bucket {
+        pub fn contribute(
+            &mut self,
+            mut bucket_a: Bucket,
+            mut bucket_b: Bucket,
+        ) -> (Bucket, Bucket, Bucket) {
             …
             let contributed_a = bucket_a.take(a_contribution);
             let contributed_b = bucket_b.take(b_contribution);
             …
-            mint_lp_tokens
+            (mint_lp_tokens, bucket_a, bucket_b)
         }

Add unit/integration tests that call contribute() with off‑ratio inputs to verify leftovers are returned and no dangling buckets occur.

🧹 Nitpick comments (4)
crates/template_lib/src/models/metadata.rs (1)

53-56: Consider making the string conversion explicit.

The method relies on implicit coercion from &mut String (returned by or_insert_with) to &str. While this works correctly, explicitly calling .as_str() would improve readability.

Apply this diff to make the conversion explicit:

     pub fn get_or_insert<K: Into<String>, V: Into<String>>(&mut self, key: K, default: V) -> &str {
         let key = key.into();
-        self.0.entry(key).or_insert_with(|| default.into())
+        self.0.entry(key).or_insert_with(|| default.into()).as_str()
     }
crates/engine_types/src/proof.rs (1)

84-86: Consider: Naming inconsistency in LockedResource::amount().

The method amount() on LockedResource now returns unlocked_amount(), which is semantically confusing—one would expect LockedResource.amount() to return the locked amount, not the unlocked amount. While this aligns with the broader refactor, consider whether this method should be renamed to unlocked_amount() for clarity, or whether it should actually return the locked amount instead.

crates/engine/src/transaction/processor.rs (1)

333-346: Post-take cleanup: prefer clearer naming and explicit intent

The emptiness probe is correct (Everything covers locked + commitments). Consider a small refactor to clarify semantics and reduce confusion:

-                let prev_bucket_val = runtime
+                // Remaining total after take (unlocked + locked + commitments)
+                let remaining_total = runtime
                     .interface()
                     .bucket_invoke(
                         bucket_ref,
                         BucketAction::GetAmount,
                         invoke_args![BucketGetAmountArg::Everything].into(),
                     )?
                     .decode::<Amount>()?;
-                if prev_bucket_val.is_zero() {
+                if remaining_total.is_zero() {
                     // Drop the bucket to prevent a dangling (empty) bucket
                     runtime
                         .interface()
                         .bucket_invoke(bucket_ref, BucketAction::DropEmpty, invoke_args![].into())?;
                 }

Please confirm this path is exercised for confidential/stealth buckets (commitments-only) in tests so we don’t regress emptiness detection.

crates/engine/src/runtime/working_state.rs (1)

399-405: DanglingBuckets count misreports when only some buckets are non‑empty

You error if any non‑empty bucket exists, but report the total bucket count. Report only the non‑empty ones to aid debugging.

-        if self.buckets.iter().any(|(_, b)| !b.is_empty()) {
-            return Err(TransactionCommitError::DanglingBuckets {
-                count: self.buckets.len(),
-            }
+        let non_empty = self.buckets.values().filter(|b| !b.is_empty()).count();
+        if non_empty > 0 {
+            return Err(TransactionCommitError::DanglingBuckets { count: non_empty }
             .into());
         }

If the intent is to forbid any leftover buckets (even empty), change the predicate to !self.buckets.is_empty() and keep count: self.buckets.len(). Please confirm desired policy.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ab89896 and 4732060.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (50)
  • Cargo.toml (2 hunks)
  • applications/tari_indexer/README.md (1 hunks)
  • applications/tari_validator_node/src/genesis_state.rs (2 hunks)
  • crates/engine/src/runtime/fee_state.rs (1 hunks)
  • crates/engine/src/runtime/impl.rs (13 hunks)
  • crates/engine/src/runtime/working_state.rs (9 hunks)
  • crates/engine/src/transaction/processor.rs (2 hunks)
  • crates/engine/src/wasm/module.rs (2 hunks)
  • crates/engine/src/wasm/process.rs (1 hunks)
  • crates/engine/tests/access_rules.rs (1 hunks)
  • crates/engine/tests/account.rs (3 hunks)
  • crates/engine/tests/composability.rs (2 hunks)
  • crates/engine/tests/events.rs (2 hunks)
  • crates/engine/tests/fees.rs (4 hunks)
  • crates/engine/tests/templates/buggy/src/lib.rs (1 hunks)
  • crates/engine/tests/test.rs (2 hunks)
  • crates/engine_types/src/bucket.rs (1 hunks)
  • crates/engine_types/src/commit_result.rs (7 hunks)
  • crates/engine_types/src/events.rs (1 hunks)
  • crates/engine_types/src/proof.rs (1 hunks)
  • crates/engine_types/src/resource.rs (1 hunks)
  • crates/engine_types/src/resource_container.rs (12 hunks)
  • crates/engine_types/src/vault.rs (1 hunks)
  • crates/template_abi/src/version.rs (1 hunks)
  • crates/template_builtin/build.rs (1 hunks)
  • crates/template_builtin/src/lib.rs (2 hunks)
  • crates/template_builtin/templates/account/Cargo.toml (1 hunks)
  • crates/template_builtin/templates/account/src/lib.rs (1 hunks)
  • crates/template_builtin/templates/faucet/Cargo.toml (1 hunks)
  • crates/template_builtin/templates/faucet/src/lib.rs (1 hunks)
  • crates/template_builtin/templates/liquidity_pool/Cargo.toml (1 hunks)
  • crates/template_builtin/templates/liquidity_pool/src/lib.rs (1 hunks)
  • crates/template_builtin/tests/liquidity_pool.rs (1 hunks)
  • crates/template_lib/Cargo.toml (1 hunks)
  • crates/template_lib/src/args/types.rs (3 hunks)
  • crates/template_lib/src/models/account.rs (1 hunks)
  • crates/template_lib/src/models/bucket.rs (2 hunks)
  • crates/template_lib/src/models/metadata.rs (2 hunks)
  • crates/template_lib/src/resource/manager.rs (2 hunks)
  • crates/template_lib_types/Cargo.toml (2 hunks)
  • crates/template_lib_types/src/amount/amount.rs (6 hunks)
  • crates/template_lib_types/src/resource_type.rs (2 hunks)
  • crates/template_test_tooling/src/builtin_component_state.rs (2 hunks)
  • crates/template_test_tooling/src/lib.rs (1 hunks)
  • crates/template_test_tooling/src/package_builder.rs (2 hunks)
  • crates/template_test_tooling/src/read_only_state_store.rs (2 hunks)
  • crates/template_test_tooling/src/support/assert_error.rs (2 hunks)
  • crates/template_test_tooling/src/template_test.rs (19 hunks)
  • integration_tests/Cargo.toml (2 hunks)
  • lints.toml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (24)
  • crates/template_builtin/templates/account/src/lib.rs
  • crates/engine_types/src/vault.rs
  • crates/template_lib/src/models/account.rs
  • crates/template_lib/src/args/types.rs
  • crates/engine/tests/fees.rs
  • crates/template_test_tooling/src/read_only_state_store.rs
  • crates/template_builtin/templates/faucet/src/lib.rs
  • crates/template_lib_types/Cargo.toml
  • crates/engine/src/wasm/module.rs
  • applications/tari_indexer/README.md
  • crates/engine/tests/composability.rs
  • crates/template_lib/src/resource/manager.rs
  • crates/template_lib/Cargo.toml
  • crates/engine/tests/access_rules.rs
  • crates/template_builtin/templates/account/Cargo.toml
  • crates/template_builtin/tests/liquidity_pool.rs
  • crates/engine/src/runtime/fee_state.rs
  • applications/tari_validator_node/src/genesis_state.rs
  • lints.toml
  • crates/engine/tests/events.rs
  • crates/template_test_tooling/src/support/assert_error.rs
  • crates/engine/tests/test.rs
  • crates/template_builtin/src/lib.rs
  • crates/template_builtin/templates/faucet/Cargo.toml
🧰 Additional context used
🧬 Code graph analysis (15)
crates/template_test_tooling/src/template_test.rs (2)
crates/template_builtin/src/lib.rs (1)
  • all_builtin_templates (51-72)
crates/template_test_tooling/src/builtin_component_state.rs (1)
  • initialize_builtin_faucet_state (82-115)
crates/engine_types/src/commit_result.rs (1)
crates/engine_types/src/fees.rs (1)
  • is_paid_in_full (58-60)
crates/engine/tests/account.rs (1)
crates/template_test_tooling/src/template_test.rs (1)
  • xtr_faucet_component (71-73)
crates/engine/src/runtime/working_state.rs (3)
crates/engine_types/src/resource_container.rs (3)
  • public_fungible (55-63)
  • resource_address (175-182)
  • unlocked_amount (134-141)
crates/engine_types/src/bucket.rs (2)
  • resource_address (67-69)
  • unlocked_amount (55-57)
crates/engine_types/src/proof.rs (4)
  • resource_address (46-48)
  • resource_address (88-90)
  • amount (42-44)
  • amount (84-86)
crates/template_lib/src/models/bucket.rs (3)
crates/template_lib/src/models/proof.rs (4)
  • resp (132-132)
  • resp (175-175)
  • resp (195-195)
  • amount (104-112)
crates/engine_types/src/bucket.rs (2)
  • is_empty (51-53)
  • locked_amount (63-65)
crates/engine_types/src/resource_container.rs (2)
  • is_empty (868-872)
  • locked_amount (154-163)
crates/template_lib/src/models/metadata.rs (2)
crates/engine_types/src/indexed_value.rs (2)
  • metadata (105-107)
  • metadata (313-315)
crates/engine_types/src/resource.rs (1)
  • metadata (203-205)
crates/engine/src/transaction/processor.rs (2)
bindings/src/types/Amount.ts (1)
  • Amount (12-12)
crates/template_lib/src/models/bucket.rs (1)
  • amount (206-214)
crates/template_test_tooling/src/package_builder.rs (2)
crates/template_builtin/src/lib.rs (1)
  • get_template_builtin (43-45)
crates/engine/src/wasm/module.rs (1)
  • from_code (61-63)
crates/template_test_tooling/src/lib.rs (1)
crates/template_test_tooling/src/template_test.rs (1)
  • xtr_faucet_component (71-73)
crates/engine/src/runtime/impl.rs (4)
crates/engine_types/src/resource.rs (2)
  • divisibility (211-213)
  • resource_type (107-109)
crates/template_lib/src/resource/manager.rs (2)
  • divisibility (129-131)
  • resource_type (119-121)
crates/engine_types/src/resource_container.rs (2)
  • resource_type (184-191)
  • public_fungible (55-63)
crates/template_lib/src/models/bucket.rs (2)
  • resource_type (88-97)
  • amount (206-214)
crates/engine_types/src/resource_container.rs (5)
crates/template_lib/src/resource/builder/mod.rs (1)
  • public_fungible (54-56)
crates/template_lib/src/models/bucket.rs (4)
  • amount (206-214)
  • resource_address (76-85)
  • locked_amount (219-227)
  • is_empty (185-194)
crates/engine_types/src/proof.rs (6)
  • amount (42-44)
  • amount (84-86)
  • resource_address (46-48)
  • resource_address (88-90)
  • container (58-60)
  • container (100-102)
crates/template_lib/src/models/proof.rs (2)
  • amount (104-112)
  • resource_address (71-80)
crates/engine_types/src/bucket.rs (4)
  • unlocked_amount (55-57)
  • resource_address (67-69)
  • locked_amount (63-65)
  • is_empty (51-53)
crates/template_test_tooling/src/builtin_component_state.rs (4)
crates/template_test_tooling/src/template_test.rs (2)
  • test_nft_faucet_component (75-77)
  • xtr_faucet_component (71-73)
crates/engine_types/src/resource_container.rs (1)
  • stealth (91-101)
crates/template_lib/src/models/vault.rs (1)
  • for_test (478-480)
bindings/src/types/ComponentHeader.ts (1)
  • ComponentHeader (9-17)
crates/template_builtin/templates/liquidity_pool/src/lib.rs (4)
crates/engine_types/src/resource_container.rs (3)
  • public_fungible (55-63)
  • resource_address (175-182)
  • resource_type (184-191)
crates/engine_types/src/vault.rs (3)
  • new (49-54)
  • resource_address (121-123)
  • resource_type (125-127)
crates/engine/src/runtime/impl.rs (1)
  • emit_event (436-459)
crates/template_lib/src/models/bucket.rs (3)
  • resource_address (76-85)
  • amount (206-214)
  • resource_type (88-97)
crates/engine_types/src/bucket.rs (2)
crates/engine_types/src/resource_container.rs (2)
  • is_empty (868-872)
  • unlocked_amount (134-141)
crates/template_lib/src/models/bucket.rs (1)
  • is_empty (185-194)
crates/template_lib_types/src/resource_type.rs (1)
bindings/src/types/ResourceType.ts (1)
  • ResourceType (17-17)
⏰ 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). (6)
  • GitHub Check: check nightly
  • GitHub Check: check stable
  • GitHub Check: test
  • GitHub Check: clippy
  • GitHub Check: machete
  • GitHub Check: fmt
🔇 Additional comments (48)
crates/engine/tests/templates/buggy/src/lib.rs (1)

46-50: LGTM! Version bump aligns with breaking changes.

The embedded ABI template version has been correctly updated to reflect the new template ABI version introduced in this PR. This change is expected given the breaking changes to the template ABI mentioned in the PR objectives.

crates/engine_types/src/commit_result.rs (1)

62-62: LGTM! Excellent improvement to test helper ergonomics.

Adding #[track_caller] to these test assertion helpers will significantly improve the developer experience by showing the actual test location where assertions fail, rather than the internal helper method location. This is a standard Rust pattern for methods that panic and is correctly applied to all relevant methods.

Also applies to: 69-69, 81-81, 90-90, 99-99, 110-110, 119-119, 128-128, 135-135

crates/template_builtin/build.rs (1)

13-18: LGTM! Template structure verified.

The addition of the liquidity_pool template to the build list is correct. The template directory has the required structure with Cargo.toml and src/lib.rs in place.

crates/engine_types/src/resource.rs (1)

207-209: Clean simplification—change is correct and consistent with metadata API.

The Metadata::get() method signature is pub fn get(&self, key: &str) -> Option<&str>, confirming that it returns Option<&str> directly. The removal of .map(|s| s.as_str()) is appropriate and eliminates redundant mapping. The public API remains stable with the unchanged method signature.

crates/engine/src/wasm/process.rs (1)

276-290: LGTM! Version constant migration is consistent.

The migration from MINIMUM_SUPPORTED_TEMPLATE_LIB_VERSION to MINIMUM_SUPPORTED_WASM_ABI_VERSION is applied consistently across all references (lines 279, 282, 284), and the updated log messages accurately reflect the ABI-centric versioning approach.

crates/template_abi/src/version.rs (2)

5-5: Version bump looks good.

The update to "0.15.0" aligns with the breaking changes and new features introduced in this PR.


6-7: Verification passed: old constant completely removed and new constant properly integrated.

The old MINIMUM_SUPPORTED_TEMPLATE_LIB_VERSION constant has been completely removed with no orphaned references remaining in the codebase. The new MINIMUM_SUPPORTED_WASM_ABI_VERSION constant is consistently used across all necessary locations in process.rs. The breaking change (setting minimum = latest at "0.15.0") is intentional and complete.

crates/template_lib/src/models/metadata.rs (3)

49-51: LGTM! Idiomatic API improvement.

Changing the return type from Option<&String> to Option<&str> is more idiomatic and provides better ergonomics for callers. The implementation correctly uses map(|s| s.as_str()) to perform the conversion.


58-60: LGTM! Clean implementation.

The remove method correctly delegates to the inner BTreeMap and returns the removed value, providing a useful addition to the API.


182-186: LGTM! Tests properly updated.

The test assertions correctly reflect the new return type of Option<&str> for the get() method.

crates/template_test_tooling/src/package_builder.rs (3)

66-69: Breaking change to add_template return type acknowledged.

The method now returns TemplateAddress instead of &mut Self, breaking the fluent API. This is documented in the PR objectives and allows callers to directly obtain the template address. Users who need method chaining can use add_template_with_envs or call add_template_opts and handle the address separately.


106-109: Good addition of add_template_from_code helper.

This new public method provides a clean abstraction for loading templates from raw WASM code. The signature aligns well with WasmModule::from_code, and the use of unwrap() is consistent with existing error handling in this test tooling crate.


101-104: No issues found. TemplateAddress is a type alias for Hash, which explicitly derives Copy. Taking TemplateAddress by value in add_builtin_template() is safe and allows the caller to continue using the value after the call.

crates/template_test_tooling/src/lib.rs (1)

14-14: LGTM! Clean rename to align with XTR faucet naming convention.

The export rename from test_faucet_component to xtr_faucet_component is consistent with the broader refactoring across the PR to standardize faucet naming.

crates/template_test_tooling/src/template_test.rs (7)

30-30: LGTM! Imports support the new dynamic template loading and vault extraction features.

The added imports for IndexedWellKnownTypes, all_builtin_templates, and ResourceAddress enable the dynamic builtin template loading and the new faucet component creation helper.

Also applies to: 41-41, 44-44


71-73: LGTM! Function rename aligns with XTR faucet naming.

The rename from test_faucet_component to xtr_faucet_component is consistent with the broader refactoring to standardize XTR faucet references.


95-96: LGTM! Extracting the initial balance as a constant improves maintainability.

The new FUNDED_ACCOUNT_INITIAL_BALANCE constant makes the test configuration explicit and easier to adjust. Good practice to avoid magic numbers.


126-128: LGTM! Dynamic template loading improves maintainability.

Using all_builtin_templates() instead of hardcoded addresses makes the codebase more maintainable and flexible for adding new builtin templates.


320-320: LGTM! The #[track_caller] attributes improve debugging experience.

Adding #[track_caller] to test helper methods that may panic provides better error context by preserving the actual caller location. This is especially valuable in test tooling.

Also applies to: 345-345, 375-375, 426-426, 444-444, 472-472, 506-506, 553-553, 560-560, 575-575, 641-641, 699-699, 708-708, 733-733


451-453: LGTM! Consistent usage of renamed function and extracted constant.

The updates to use xtr_faucet_component() and FUNDED_ACCOUNT_INITIAL_BALANCE align with the refactoring and improve code clarity.

Also applies to: 487-487


507-545: LGTM! The new helper consolidates faucet creation logic.

The create_test_faucet_component method consolidates previously duplicated faucet setup code. The assumptions about component structure (having at least one vault) are reasonable for test faucets, and the .expect() message provides clear debugging context if the assumption is violated.

crates/engine/tests/account.rs (2)

16-16: LGTM! Import updated to match the renamed function.

The import change from test_faucet_component to xtr_faucet_component is consistent with the refactoring.


25-25: LGTM! Test setup simplified using the new faucet helper.

Line 25 now uses the create_test_faucet_component helper, which simplifies the test setup and eliminates manual faucet component creation logic. Line 232 consistently uses the renamed xtr_faucet_component() function.

Also applies to: 232-232

crates/template_test_tooling/src/builtin_component_state.rs (3)

14-14: LGTM! Import updates align with XTR faucet refactoring.

The import changes bring in the necessary XTR faucet constants (XTR_FAUCET_TEMPLATE_ADDRESS, XTR_FAUCET_VAULT_ADDRESS) and updated function references, supporting the transition from generic test faucet to XTR-specific faucet implementation.

Also applies to: 22-22, 26-26, 29-29, 32-32


86-86: LGTM! Using Amount::MAX ensures the test faucet never runs out of tokens.

Changing the initial supply from 1_000_000 to Amount::MAX is appropriate for test infrastructure, ensuring that tests have sufficient tokens regardless of their consumption patterns.


93-93: LGTM! Consistent application of XTR faucet constants and naming.

The changes consistently update the builtin faucet state initialization to use XTR-specific constants (XTR_FAUCET_VAULT_ADDRESS, XTR_FAUCET_TEMPLATE_ADDRESS) and naming ("XtrFaucet" module name, xtr_faucet_component() function). Using predefined constants instead of derived IDs improves maintainability.

Also applies to: 98-98, 103-103, 105-106

crates/template_lib_types/src/amount/amount.rs (7)

152-157: LGTM: Signature change improves consistency.

Changing the parameter from other: &Self to other: Self aligns checked_mul with the other checked arithmetic methods (checked_add, checked_sub, checked_div), which all take Self by value. This improves API consistency.


311-319: LGTM: Previous test coverage concern has been addressed.

The pow and checked_pow implementations are straightforward. Test coverage for these methods has been added (lines 520-521, 535-536), addressing the concern raised in the previous review.


408-409: LGTM: Macro-based operator implementations.

The updated newtype_ops! invocations now include rem and neg, generating implementations for remainder and negation operations. This is cleaner than manual implementations and ensures consistency across all operator traits.


512-537: LGTM: Comprehensive test coverage.

The test coverage is thorough, including:

  • Basic arithmetic operations with the updated checked_mul signature
  • New checked_pow functionality (lines 520-521)
  • Overflow detection for all checked operations (lines 523-536)

This addresses the previous review concern about missing test coverage for power operations.


539-548: LGTM: Feature-gated tests for checked_sqrt.

The tests for the extra-arith feature are correctly gated and cover both the success case (integer square root) and the error case (negative input returns None).


7-7: Neg removal from imports is correct and verified.

The newtype_ops! macro includes neg in its operation list (lines 408-409) and correctly generates the Neg implementation for both Self and &Self forms. No manual Neg implementation remains, and negation is working correctly across all test cases (checked_sub returning negative values, unary minus operator usage, etc.).


279-291: No issues found - dependency configuration is correct.

The num-integer dependency is properly declared in Cargo.toml (line 23) with optional = true, and the extra-arith feature correctly enables it using the "dep:num-integer" syntax. The checked_sqrt() implementation properly gates the feature-dependent code, and tests confirm it works as intended (sqrt(27) returns 5; negative values return None).

crates/engine_types/src/events.rs (1)

115-117: LGTM! Simplified return value handling.

The removal of the .map(|s| s.as_str()) mapping suggests that Metadata::get now returns Option<&str> directly, making this cleaner and more direct.

integration_tests/Cargo.toml (1)

12-13: LGTM! Dependency sourcing aligned with workspace changes.

The switch from workspace-based to git-based dependencies for Minotari components and the addition of tari_comms dependencies align with the workspace-level dependency cleanup and ensure integration tests use the specified v5.1.0-rc.1 tag.

Also applies to: 26-26, 49-50

Cargo.toml (2)

3-3: Version bump reflects breaking changes.

The bump from 0.15.1 to 0.16.0 correctly signals the breaking changes documented in the PR objectives (data directory deletion required, template ABI changes).


213-213: New dependency for arithmetic operations.

The num-integer dependency supports the extra-arith feature used in the liquidity pool template (see crates/template_builtin/templates/liquidity_pool/Cargo.toml line 8).

crates/template_lib/src/models/bucket.rs (4)

172-182: LGTM! Proper cleanup mechanism for empty buckets.

The drop_empty() method provides an explicit way to dispose of empty buckets, preventing dangling bucket errors. The panic on non-empty buckets is appropriate defensive programming.


196-214: Documentation and implementation clarified.

The updated documentation clearly describes the behavior for different resource types and the distinction between unlocked amounts and locked/confidential funds. The use of BucketGetAmountArg::AmountOnly makes the intent explicit.


216-227: LGTM! New locked_amount accessor.

Provides access to locked amounts separately from unlocked amounts, supporting the broader API refactor to distinguish between locked and unlocked balances.


184-194: Review comment is incorrect.

The implementation correctly detects confidential commitments. The engine-side BucketGetAmountArg::Everything handler at crates/engine/src/runtime/impl.rs:1959-1974 explicitly adds bucket.number_of_confidential_commitments() to the total amount returned. Therefore, a bucket with zero revealed balance but non-zero confidential commitments will return a non-zero amount, and is_empty() will correctly return false.

The concern that drop_empty() could panic due to is_empty() incorrectly reporting an empty bucket is unfounded—the current implementation prevents this scenario.

Note: The engine implementation contains a bug where confidential commitment count is added twice (lines 1964 and 1967), but this does not affect the correctness of the is_empty() logic since any non-zero commitment count still results in a non-zero total.

Likely an incorrect or invalid review comment.

crates/template_lib_types/src/resource_type.rs (2)

64-96: LGTM! Clean feature-gated parsing with proper exports.

The parsing logic is now cleanly isolated behind the std feature gate, and the past review concern about ParseResourceTypeError visibility has been properly addressed with the re-export on line 96.


98-102: New ResourceInfo struct exposes divisibility.

The addition of ResourceInfo provides a structured way to return both resource type and divisibility information together, addressing the ABI gap mentioned in the PR objectives.

crates/template_builtin/templates/liquidity_pool/Cargo.toml (1)

1-19: LGTM! Well-configured liquidity pool template.

The template configuration properly enables the extra-arith feature for arithmetic operations (supporting the liquidity pool calculations) and includes appropriate release optimizations for WASM compilation.

crates/engine_types/src/bucket.rs (1)

51-57: LGTM! API refactor aligns with ResourceContainer changes.

The replacement of amount() with is_empty() and unlocked_amount() provides clearer semantics and properly delegates to the engine-side ResourceContainer implementation, which correctly checks all resource states including confidential commitments.

crates/engine/src/runtime/working_state.rs (1)

1328-1371: Fee collection/refund migration to unlocked_amount is consistent

Using unlocked_amount for fee accounting and refunds aligns with new semantics.

crates/engine/src/runtime/impl.rs (1)

1383-1393: Vault::Create initializes fungible vaults via public_fungible(…, 0) — LGTM

This aligns with the new constructor and avoids negative/locked states.

crates/template_builtin/templates/liquidity_pool/src/lib.rs (1)

331-344: Fungibility check is correct and matches engine variants

Accepting Fungible, Confidential, and Stealth as “fungible‑like” is consistent with ResourceType semantics.

Comment thread crates/engine/src/runtime/impl.rs
@sdbondi
sdbondi merged commit ddc8057 into tari-project:development Nov 6, 2025
11 checks passed
@sdbondi
sdbondi deleted the wip-liquidity-pool branch November 6, 2025 05:09
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