Skip to content
This repository was archived by the owner on Dec 2, 2025. It is now read-only.

provide investment information#20

Open
bernardev254 wants to merge 1 commit intotrustbridgecr:mainfrom
bernardev254:fix-13-implement-lender-investment-tracking-in-contract
Open

provide investment information#20
bernardev254 wants to merge 1 commit intotrustbridgecr:mainfrom
bernardev254:fix-13-implement-lender-investment-tracking-in-contract

Conversation

@bernardev254
Copy link

@bernardev254 bernardev254 commented Sep 28, 2025

Pull Request for TrustBridge - Close Issue

Pull Request Information

Implement lender investment tracking in the Soroban Rust Pool contract to support real-time lender dashboards.

Add here some information

🌀 Summary of Changes

  1. Added Investment struct in storage.rs to represent individual lender investments (amount, timestamp, returns, status).

  2. Storage helpers to create, update, and query lender investments, total balances, and rewards.

  3. Event emitters (InvestmentMade, RewardsDistributed, WithdrawalMade) in events.rs for front-end/indexer tracking.

  4. Contract view functions in contract.rs:

  • get_lender_investments

  • get_lender_total_balance

  • get_lender_rewards

  • get_lender_investment_history

  1. Write helpers in contract.rs to:
  • Record new investments when lenders supply funds.

  • Update rewards distribution.

  • Mark investments as withdrawn and adjust aggregates.

  • Add here the changes:

  • More changes:

🛠 Testing

Evidence Before Solution

Evidence After Solution


✅ ESLint Compliance (Mandatory)

To ensure that the code follows project standards, please run the following command and attach a screenshot of the output:

npm run lint

You should see:

✔ No ESLint warnings or errors

📸 Attach a screenshot showing the result of the lint check:

⚠️ Pull requests without this screenshot will be rejected.


📂 Related Issue

This pull request will **close #13 ** upon merging.


🎉 Thank you for reviewing this PR! 🎉

Summary by CodeRabbit

  • New Features

    • Introduces a full lender investment flow: make investments, accrue rewards, and withdraw specific investments.
    • Adds portfolio views: current investments, total balance, total rewards, and complete investment history.
    • Emits events for investments, rewards distribution, and withdrawals to enable real-time tracking and analytics.
    • Balances and rewards update automatically as actions occur.
  • Chores

    • Upgrades core SDK dependencies to a newer major version and integrates enhanced testing utilities.

@coderabbitai
Copy link

coderabbitai bot commented Sep 28, 2025

Walkthrough

Introduces lender investment tracking in the pool contract: new Investment model and storage keys; APIs for recording investments, distributing rewards, withdrawing, and querying investments/balances/rewards/history; corresponding events; and dependency bumps to soroban-sdk 22.0.0 plus a new dev-dependency blend-contract-sdk 1.22.0.

Changes

Cohort / File(s) Summary
Dependency updates
contracts/pool/Cargo.toml
Bump soroban-sdk to 22.0.0 in deps and dev-deps; add dev-dep blend-contract-sdk = 1.22.0 with testutils.
Lender investment tracking feature
contracts/pool/src/contract.rs, contracts/pool/src/events.rs, contracts/pool/src/storage.rs
Add Investment type and per-lender storage (investments, total balance, total rewards); implement APIs: record investment, add rewards, withdraw, and views for investments, totals, rewards, history; emit InvestmentMade, RewardsDistributed, WithdrawalMade events.

Sequence Diagram(s)

sequenceDiagram
  actor Lender
  participant PoolContract
  participant Storage
  participant Events

  rect rgba(200,230,255,0.3)
  note right of Lender: Record investment
  Lender->>PoolContract: record_investment(lender, amount)
  PoolContract->>Storage: add_lender_investment(lender, Investment{amount,timestamp,...})
  PoolContract->>Storage: inc_lender_total_balance(lender, +amount)
  PoolContract->>Events: investment_made(lender, amount, timestamp)
  PoolContract-->>Lender: ok
  end

  rect rgba(220,255,220,0.3)
  note right of Lender: Distribute rewards
  Lender->>PoolContract: add_rewards(lender, index, rewards)
  PoolContract->>Storage: inc_lender_total_rewards(lender, +rewards)
  PoolContract->>Storage: update Investment[index].returns += rewards
  PoolContract->>Events: rewards_distributed(lender, rewards)
  PoolContract-->>Lender: ok
  end

  rect rgba(255,235,200,0.3)
  note right of Lender: Withdraw investment
  Lender->>PoolContract: withdraw_investment(lender, index)
  PoolContract->>Storage: read Investment[index], totals
  PoolContract->>Storage: inc_lender_total_balance(lender, -amount)
  PoolContract->>Storage: mark Investment[index].status = withdrawn
  PoolContract->>Events: withdrawal_made(lender, amount, timestamp)
  PoolContract-->>Lender: ok
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

I nibble on blocks and bytes so bright,
Ledger carrots stacked just right.
Investments hop, rewards accrue,
Events go “thump!” as balances grew.
History trails where footprints weave—
A bunny banker, on-chain, I believe. 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The PR includes dependency version bumps in contracts/pool/Cargo.toml (upgrading soroban-sdk to 22.0.0 and adding blend-contract-sdk) that are not related to the lender investment tracking objectives defined in issue #13. Remove or justify the dependency updates in Cargo.toml or document their necessity in the linked issue to ensure that only relevant changes are included.
Description Check ⚠️ Warning The pull request description uses the provided template but leaves several placeholders unfilled, including generic “Add here some information” lines, placeholder change entries, and testing evidence links without actual content or screenshots, which makes the description incomplete and inconsistent with the template. Please complete all sections of the template by replacing placeholders with actual descriptions, list all changes in the summary, provide real testing evidence URLs and screenshots for ESLint compliance, and remove any unused template instructions.
Title Check ❓ Inconclusive The title “provide investment information” is related to the new investment features but is overly broad and does not clearly describe the implementation of lender investment tracking and APIs in the Pool contract. Rename the title to explicitly reference the core change, such as “Implement lender investment tracking in Pool contract,” to clearly convey the purpose and scope.
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues Check ✅ Passed The changes implement all primary objectives from issue #13 by adding the Investment struct, storage helpers for tracking per-lender investments and rewards, query functions for investment history and balances, and emitting the required events (InvestmentMade, RewardsDistributed, WithdrawalMade).
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bfaba2d and 514c16e.

📒 Files selected for processing (4)
  • contracts/pool/Cargo.toml (1 hunks)
  • contracts/pool/src/contract.rs (3 hunks)
  • contracts/pool/src/events.rs (2 hunks)
  • contracts/pool/src/storage.rs (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
contracts/pool/src/storage.rs (1)
contracts/pool/src/contract.rs (4)
  • get_lender_investments (334-334)
  • get_lender_investments (701-703)
  • get_lender_total_balance (337-337)
  • get_lender_total_balance (705-707)
contracts/pool/src/contract.rs (2)
contracts/pool/src/storage.rs (24)
  • e (169-169)
  • e (203-205)
  • e (229-231)
  • e (249-251)
  • e (266-268)
  • e (289-291)
  • e (309-311)
  • e (332-334)
  • e (351-353)
  • e (364-366)
  • e (390-392)
  • e (412-414)
  • e (446-448)
  • e (459-461)
  • e (496-498)
  • e (531-533)
  • get_lender_investments (679-685)
  • get_lender_total_balance (701-708)
  • extend_instance (155-159)
  • add_lender_investment (694-698)
  • inc_lender_total_balance (717-720)
  • set_lender_investments (688-691)
  • inc_lender_total_rewards (739-742)
  • get_lender_total_rewards (723-730)
contracts/pool/src/events.rs (3)
  • investment_made (378-384)
  • rewards_distributed (386-391)
  • withdrawal_made (393-398)

Comment on lines +620 to +699
fn record_investment(e: Env, lender: Address, amount: i128) {
storage::extend_instance(&e);

// basic checks - adjust as needed
if amount <= 0 {
panic_with_error!(&e, PoolError::BadRequest);
}

let ts: u64 = e.ledger().timestamp() as u64;

let inv = Investment {
amount,
timestamp: ts,
returns: 0,
status: 0u32, // active
};

// append investment
storage::add_lender_investment(&e, &lender, &inv);

// increment aggregate balance
storage::inc_lender_total_balance(&e, &lender, amount);

// emit event
PoolEvents::investment_made(&e, lender.clone(), amount, ts);
}

fn add_rewards(e: Env, lender: Address, index: u32, rewards: i128) {
storage::extend_instance(&e);

if rewards <= 0 {
panic_with_error!(&e, PoolError::BadRequest);
}

// load investments vec, update index
let mut invs = storage::get_lender_investments(&e, &lender);
let len = invs.len();
if (index as usize) >= len {
panic_with_error!(&e, PoolError::BadRequest);
}

let mut inv = invs.get(index as usize).unwrap();
inv.returns = inv.returns + rewards;
// update vec slot
invs.set(index as usize, inv.clone());
storage::set_lender_investments(&e, &lender, &invs);

// update aggregate rewards
storage::inc_lender_total_rewards(&e, &lender, rewards);

// emit event
PoolEvents::rewards_distributed(&e, lender.clone(), rewards);
}

fn withdraw_investment(e: Env, lender: Address, index: u32) {
storage::extend_instance(&e);

let mut invs = storage::get_lender_investments(&e, &lender);
let len = invs.len();
if (index as usize) >= len {
panic_with_error!(&e, PoolError::BadRequest);
}

let mut inv = invs.get(index as usize).unwrap();
if inv.status != 0u32 {
// already withdrawn
panic_with_error!(&e, PoolError::BadRequest);
}

inv.status = 1u32; // withdrawn
invs.set(index as usize, inv.clone());
storage::set_lender_investments(&e, &lender, &invs);

// decrement aggregate balance
storage::inc_lender_total_balance(&e, &lender, -inv.amount);

// emit event
let ts: u64 = e.ledger().timestamp() as u64;
PoolEvents::withdrawal_made(&e, lender.clone(), inv.amount, ts);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Investment mutators need authentication or non-public exposure

record_investment, add_rewards, and withdraw_investment are now in the public Pool trait (Lines 620-699), so anyone can invoke them directly. None of them call require_auth, which lets an attacker (1) credit themselves arbitrary balances via record_investment, (2) mint rewards with add_rewards, or (3) zero out another lender’s position using withdraw_investment. This is a critical authorization hole. Either keep these helpers internal (remove from the trait) or gate them with the appropriate require_auth checks aligned with the supply/reward flows.

Comment on lines +378 to +398
pub fn investment_made(e: &Env, lender: &Address, amount: i128, timestamp: u64) {
// Topic: ("InvestmentMade", lender)
e.events().publish(
(Symbol::short("InvestmentMade"), lender.clone()),
(amount, timestamp),
);
}

pub fn rewards_distributed(e: &Env, lender: &Address, amount: i128) {
e.events().publish(
(Symbol::short("RewardsDistributed"), lender.clone()),
amount,
);
}

pub fn withdrawal_made(e: &Env, lender: &Address, amount: i128, timestamp: u64) {
e.events().publish(
(Symbol::short("WithdrawalMade"), lender.clone()),
(amount, timestamp),
);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Replace Symbol::short with length-compliant symbols for new events

Symbol::short only accepts ASCII strings up to 9 characters. The literals "InvestmentMade", "RewardsDistributed", and "WithdrawalMade" (Lines 381, 388, 395) all exceed that limit, so these calls will panic every time the events fire. Please switch to Symbol::new(&e, "...") (or shorten the identifiers to ≤9 chars) before shipping.

🤖 Prompt for AI Agents
In contracts/pool/src/events.rs around lines 378 to 398, the calls to
Symbol::short use string literals longer than 9 chars which will panic; replace
each Symbol::short("InvestmentMade"), Symbol::short("RewardsDistributed"), and
Symbol::short("WithdrawalMade") with Symbol::new(&e, "<same-name>") (or
alternatively shorten each identifier to ≤9 ASCII chars) so the symbols are
length-compliant; update the three event publish calls accordingly to use
Symbol::new(&e, "...") with the same readable names.

Comment on lines +674 to +742
const KEY_LENDER_INVESTMENTS: Symbol = Symbol::short("lender_invs"); // (lender -> Vec<Investment>)
const KEY_LENDER_TOTAL_BAL: Symbol = Symbol::short("lender_bal"); // (lender -> i128)
const KEY_LENDER_TOTAL_REW: Symbol = Symbol::short("lender_rew"); // (lender -> i128)

/// Return the investments vec for a lender (or an empty Vec if not set).
pub fn get_lender_investments(e: &Env, lender: &Address) -> Vec<Investment> {
let key = (KEY_LENDER_INVESTMENTS.clone(), lender.clone());
e.storage()
.instance()
.get(&key)
.unwrap_or_else(|_| Vec::new(e))
}

/// Persist the investments vec for a lender.
pub fn set_lender_investments(e: &Env, lender: &Address, invs: &Vec<Investment>) {
let key = (KEY_LENDER_INVESTMENTS.clone(), lender.clone());
e.storage().instance().set(&key, invs);
}

/// Append a new investment to a lender's investment vector and persist.
pub fn add_lender_investment(e: &Env, lender: &Address, inv: &Investment) {
let mut vec = get_lender_investments(e, lender);
vec.push_back(inv.clone());
set_lender_investments(e, lender, &vec);
}

/// Get lender total balance (aggregated active amounts). Returns 0 if none.
pub fn get_lender_total_balance(e: &Env, lender: &Address) -> i128 {
let key = (KEY_LENDER_TOTAL_BAL.clone(), lender.clone());
e.storage()
.instance()
.get(&key)
.unwrap_or(Ok(0i128))
.unwrap()
}

/// Set lender total balance.
pub fn set_lender_total_balance(e: &Env, lender: &Address, bal: &i128) {
let key = (KEY_LENDER_TOTAL_BAL.clone(), lender.clone());
e.storage().instance().set(&key, bal);
}

/// Increase lender total balance by delta (can be negative to decrease).
pub fn inc_lender_total_balance(e: &Env, lender: &Address, delta: i128) {
let cur = get_lender_total_balance(e, lender);
set_lender_total_balance(e, lender, &(cur + delta));
}

/// Get lender total rewards (aggregated). Returns 0 if none.
pub fn get_lender_total_rewards(e: &Env, lender: &Address) -> i128 {
let key = (KEY_LENDER_TOTAL_REW.clone(), lender.clone());
e.storage()
.instance()
.get(&key)
.unwrap_or(Ok(0i128))
.unwrap()
}

/// Set lender total rewards.
pub fn set_lender_total_rewards(e: &Env, lender: &Address, rew: &i128) {
let key = (KEY_LENDER_TOTAL_REW.clone(), lender.clone());
e.storage().instance().set(&key, rew);
}

/// Increase lender total rewards by delta.
pub fn inc_lender_total_rewards(e: &Env, lender: &Address, delta: i128) {
let cur = get_lender_total_rewards(e, lender);
set_lender_total_rewards(e, lender, &(cur + delta));
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Symbol::short constants exceed the permitted 9-character length

Symbol::short("lender_invs"), "lender_bal", and "lender_rew" (Lines 675-677) are all ≥10 characters. Symbol::short will panic when the contract loads these keys, breaking every storage call beneath. Please shorten the identifiers to ≤9 characters (e.g., "lend_invs") or create the symbols at runtime with Symbol::new(&e, ...).

🤖 Prompt for AI Agents
In contracts/pool/src/storage.rs around lines 674 to 742, the three
Symbol::short calls use identifiers >=10 chars which will panic at contract
load; replace them with either shorter identifiers (<=9 chars) like "lend_invs",
"lend_bal", "lend_rew" or change the constants to be created at runtime using
Symbol::new(&e, "<full_name>") and update all uses accordingly so storage keys
no longer trigger Symbol::short length panics.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement lender investment tracking in contract

1 participant

Comments