Skip to content

feat(tokenomics): transfer LP position NFT to liquidity owner#48

Open
loothero wants to merge 1 commit intonextfrom
feat/transfer-lp-nft-to-liquidity-owner
Open

feat(tokenomics): transfer LP position NFT to liquidity owner#48
loothero wants to merge 1 commit intonextfrom
feat/transfer-lp-nft-to-liquidity-owner

Conversation

@loothero
Copy link
Member

Summary

  • Adds liquidity_owner field to LiquidityConfig so the LP position NFT is transferred to a designated address after initial liquidity provision, instead of being permanently locked in the stream token contract
  • After provide_initial_liquidity() mints the Ekubo LP position, the NFT is transferred via IERC721::transfer_from on Ekubo's OwnedNFT contract to the liquidity_owner
  • Emits LiquidityPositionTransferred event with position ID and owner address

Changes

Interface (packages/interfaces/src/tokenomics/stream.cairo)

  • Added liquidity_owner: ContractAddress to LiquidityConfig struct
  • Added get_liquidity_owner() to IStreamToken trait

StreamComponent (packages/economy/src/tokenomics/stream/stream.cairo)

  • Added IERC721Dispatcher import for Ekubo NFT contract
  • Added Stream_liquidity_owner storage field
  • Added LiquidityPositionTransferred event
  • initializer() validates liquidity_owner != zero and stores it
  • provide_initial_liquidity() transfers LP NFT to owner after minting
  • Added get_liquidity_owner() getter

Safety: The LP position ID (Stream_liquidity_position_id) is only used by the read-only getter get_liquidity_position_id(). The TWAMM distribution positions (Stream_position_ids) are separate and remain in the contract for claim_distribution_proceeds() to work. Transferring the LP NFT has no impact on distribution functionality.

Tests (16 construction sites across 10 files)

  • All LiquidityConfig structs updated with liquidity_owner: OWNER()
  • All 7 lifecycle tests updated with mock_nft_transfer/stop_mock_nft_transfer helpers
  • LiquidityPositionTransferred event assertion added to lifecycle events test

Scripts & Docs

  • scripts/create_stream_token.sh: Extract, validate, display, and serialize liquidity_owner
  • Summit example configs: Added liquidity_owner field
  • AGENTS.md: Updated LiquidityConfig struct documentation

Test plan

  • scarb build compiles cleanly
  • snforge test --workspace — 2060 passed, 0 failed, 9 ignored
  • LiquidityPositionTransferred event verified in lifecycle tests
  • CI workflows pass

🤖 Generated with Claude Code

The StreamComponent creates an Ekubo LP position during
provide_initial_liquidity() but permanently locks the position NFT
inside the stream token contract. This adds a liquidity_owner field to
LiquidityConfig and transfers the LP position NFT to that address
immediately after minting, allowing the owner to manage or withdraw
liquidity.

- Add liquidity_owner to LiquidityConfig struct and IStreamToken trait
- Transfer LP NFT via IERC721 after position creation
- Emit LiquidityPositionTransferred event
- Validate liquidity_owner is non-zero in initializer
- Update deploy script, example configs, and all test sites

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 19, 2026 13:34
@coderabbitai
Copy link

coderabbitai bot commented Feb 19, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/transfer-lp-nft-to-liquidity-owner

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.

@gemini-code-assist
Copy link

Summary of Changes

Hello @loothero, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the tokenomics system by introducing explicit ownership for Liquidity Provider (LP) position NFTs. Previously, these NFTs remained within the stream token contract, limiting their utility. The changes now enable these NFTs to be transferred to a specified liquidity_owner upon initial liquidity provision, providing greater flexibility and control over LP positions. This ensures that the designated owner can manage or withdraw the liquidity associated with the NFT, while maintaining the integrity of the TWAMM distribution mechanisms.

Highlights

  • LP Position NFT Ownership: Introduced a liquidity_owner field to the LiquidityConfig struct, allowing the Ekubo LP position NFT to be transferred to a designated address after initial liquidity provision, rather than remaining locked in the stream token contract.
  • NFT Transfer Mechanism: Implemented the transfer of the Ekubo LP position NFT via IERC721::transfer_from to the specified liquidity_owner immediately after minting during the provide_initial_liquidity() call.
  • Event Emission: Added a new LiquidityPositionTransferred event, which is emitted upon successful transfer of the LP position NFT, including the position ID and the owner's address.
  • Contract Interface and Storage Updates: Updated the IStreamToken trait with a get_liquidity_owner() getter and added a Stream_liquidity_owner storage field to the StreamComponent to store the designated owner.
  • Validation and Testing: Added validation to ensure the liquidity_owner is not a zero address during initialization and comprehensively updated lifecycle tests with mocking helpers (mock_nft_transfer, stop_mock_nft_transfer) and event assertions to cover the new NFT transfer functionality.
  • Script and Documentation Updates: Modified the create_stream_token.sh script to extract, validate, display, and serialize the liquidity_owner field, and updated example configurations and documentation (AGENTS.md) to reflect the new LiquidityConfig structure.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • packages/economy/src/tokenomics/AGENTS.md
    • Added liquidity_owner field to the LiquidityConfig struct documentation.
  • packages/economy/src/tokenomics/constants.cairo
    • Added STREAM_INVALID_LIQUIDITY_OWNER error constant.
  • packages/economy/src/tokenomics/stream/stream.cairo
    • Imported IERC721Dispatcher for Ekubo NFT contract interaction.
    • Added Stream_liquidity_owner storage field to track the LP NFT owner.
    • Introduced LiquidityPositionTransferred event for NFT transfers.
    • Implemented get_liquidity_owner() getter function.
    • Added validation for liquidity_owner in the initializer() function.
    • Integrated IERC721::transfer_from call to transfer LP NFT to liquidity_owner in provide_initial_liquidity().
  • packages/economy/src/tokenomics/tests/helpers/deployment.cairo
    • Updated LiquidityConfig structs in test deployment helpers to include liquidity_owner.
  • packages/economy/src/tokenomics/tests/test_factory.cairo
    • Updated LiquidityConfig structs in factory tests to include liquidity_owner.
  • packages/economy/src/tokenomics/tests/test_factory_fork.cairo
    • Updated LiquidityConfig structs in fork factory tests to include liquidity_owner.
  • packages/economy/src/tokenomics/tests/test_premint.cairo
    • Updated LiquidityConfig structs in premint tests to include liquidity_owner.
  • packages/economy/src/tokenomics/tests/test_stream.cairo
    • Updated LiquidityConfig structs in stream tests to include liquidity_owner.
  • packages/economy/src/tokenomics/tests/test_stream_fork.cairo
    • Updated LiquidityConfig structs in fork stream tests to include liquidity_owner.
  • packages/economy/src/tokenomics/tests/test_stream_lifecycle.cairo
    • Updated LiquidityConfig structs in lifecycle tests to include liquidity_owner.
    • Added mock_nft_transfer and stop_mock_nft_transfer helper functions for testing NFT transfers.
    • Asserted LiquidityPositionTransferred event emission in lifecycle tests.
  • packages/interfaces/src/tokenomics/stream.cairo
    • Added liquidity_owner: ContractAddress to the LiquidityConfig struct.
    • Added get_liquidity_owner() to the IStreamToken trait.
  • packages/presets/src/tests/test_stream_token.cairo
    • Updated LiquidityConfig structs in preset stream token tests to include liquidity_owner.
  • scripts/create_stream_token.sh
    • Extracted liquidity_owner from liquidity_config in JSON input.
    • Added validation for the liquidity_owner address.
    • Included liquidity_owner in the displayed configuration summary.
    • Added liquidity_owner to the CREATE_CALLDATA for contract deployment.
    • Serialized liquidity_owner into the deployment file output.
  • scripts/examples/summit_attack_potion.json
    • Added liquidity_owner field to the example configuration.
  • scripts/examples/summit_poison_potion.json
    • Added liquidity_owner field to the example configuration.
  • scripts/examples/summit_revive_potion.json
    • Added liquidity_owner field to the example configuration.
  • scripts/examples/summit_xlife_potion.json
    • Added liquidity_owner field to the example configuration.
Activity
  • The pull request author, loothero, has thoroughly documented the changes in the PR description, including a detailed summary, specific file changes, and safety considerations.
  • Extensive testing has been performed, with 16 construction sites across 10 files updated to accommodate the new liquidity_owner field.
  • Lifecycle tests have been specifically updated with mock_nft_transfer and stop_mock_nft_transfer helpers to simulate and verify the NFT transfer logic.
  • The LiquidityPositionTransferred event has been explicitly asserted in lifecycle tests, confirming its correct emission.
  • The project successfully compiles (scarb build) and all workspace tests pass (snforge test --workspace), indicating a stable implementation.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@claude
Copy link

claude bot commented Feb 19, 2026

Claude finished @loothero's task —— View job


Claude Review - General Engineering Review

run=22183928383 attempt=1 sha=1c4ce167ce9dab818a8bb4294fee4214e8570a25 scope=general

Todo List

  • Get targeted diff for files outside packages/
  • Review script changes in create_stream_token.sh
  • Review new JSON example files
  • Check for any CI/workflow changes
  • Provide structured findings with severity levels

Findings

No issues found.

Analysis

The changes outside packages/ are well-implemented:

Script Changes (scripts/create_stream_token.sh)

  • ✅ Properly extracts liquidity_owner from JSON config using jq
  • ✅ Validates that liquidity_owner is not null/empty
  • ✅ Uses existing validate_address function to ensure proper hex format
  • ✅ Correctly includes liquidity_owner in the calldata serialization
  • ✅ Updates deployment JSON output to include the new field
  • ✅ Follows established patterns for field extraction and validation

JSON Example Files

  • ✅ Four new Summit example configurations with consistent structure
  • ✅ All include the required liquidity_owner field with valid hex addresses
  • ✅ JSON syntax appears well-formed
  • ✅ Follow existing naming conventions and structure

Security Considerations

  • ✅ Address validation prevents injection attacks
  • ✅ No hardcoded secrets or sensitive data
  • ✅ Follows existing security patterns in the script

Maintainability

  • ✅ Changes are consistent with existing codebase patterns
  • ✅ Clear variable naming and proper error messages
  • ✅ Documentation comment in script updated to reflect new field order

CI/CD

  • ✅ No workflow changes detected that would require matrix updates

Summary

0 CRITICAL, 0 HIGH, 0 MEDIUM, 0 LOW, 0 INFO

@claude
Copy link

claude bot commented Feb 19, 2026

Claude finished @loothero's task —— View job


Claude Review - Cairo/Starknet Contract Review

Todo List

  • Examine the PR diff and understand all changes
  • Review core stream.cairo implementation for security issues
  • Validate testing approach and coverage
  • Check Cairo idioms and best practices
  • Provide comprehensive review feedback

run=22183928383 attempt=1 sha=1c4ce167ce9dab818a8bb4294fee4214e8570a25 scope=packages

Summary

[HIGH] packages/economy/src/tokenomics/stream/stream.cairo:313 - Missing return value check on ERC721 transfer
Impact: The transfer_from call on line 313 could silently fail without reverting the transaction. If the NFT contract doesn't revert on failed transfers (non-standard behavior), the contract would emit LiquidityPositionTransferred event but the NFT would remain in the stream contract.
Fix: Add explicit return value check:

let transfer_success = nft.transfer_from(get_contract_address(), liquidity_owner, position_id.into());
assert(transfer_success, 'NFT transfer failed');

[HIGH] packages/economy/src/tokenomics/stream/stream.cairo:310-313 - Missing interface validation for external NFT contract
Impact: The code blindly trusts the NFT address returned by positions_dispatcher.get_nft_address() without validating it supports ERC721. A malicious or buggy positions contract could return an invalid address, causing the transfer to fail silently or behave unexpectedly.
Fix: Add SRC5 interface validation:

let nft_address = positions_dispatcher.get_nft_address();
let nft = IERC721Dispatcher { contract_address: nft_address };
// Validate ERC721 interface support via SRC5
assert(nft.supports_interface(IERC721_ID), 'Invalid NFT contract');

[MEDIUM] packages/economy/src/tokenomics/stream/stream.cairo:309-314 - Potential reentrancy risk via external NFT call
Impact: The external transfer_from call to the NFT contract happens before emitting the event. If the NFT contract or liquidity_owner has malicious callback logic, they could potentially reenter the stream contract during the transfer.
Fix: Follow checks-effects-interactions pattern - emit event before external call:

// Emit event first
self.emit(LiquidityPositionTransferred { position_id, owner: liquidity_owner });
// Then perform external call
nft.transfer_from(get_contract_address(), liquidity_owner, position_id.into());

[LOW] packages/economy/src/tokenomics/tests/test_stream_lifecycle.cairo:115-116 - Redundant mock calls for same interface
Impact: The test mocks both transferFrom and transfer_from selectors for the same NFT contract, suggesting uncertainty about the correct method name. This could mask issues if the actual contract uses a different method signature.
Fix: Verify the correct ERC721 method name from Ekubo's NFT contract and mock only the correct one. Remove the redundant mock:

// Keep only the correct method name based on actual contract
start_mock_call(MOCK_NFT(), selector!("transfer_from"), ());

[INFO] Multiple files - Consistent test coverage updates
Impact: All test files correctly updated LiquidityConfig structs with liquidity_owner: OWNER() field. The lifecycle tests properly mock NFT transfer operations and verify the LiquidityPositionTransferred event emission.
Fix: No action needed - testing coverage is comprehensive and follows best practices.

Positive Observations

  1. Proper validation: The initializer correctly validates liquidity_owner != zero_address with a specific error message
  2. Event emission: LiquidityPositionTransferred event includes both position_id and indexed owner for efficient filtering
  3. Test discipline: All lifecycle tests updated with proper mocking helpers (mock_nft_transfer/stop_mock_nft_transfer)
  4. Storage isolation: New Stream_liquidity_owner field follows existing storage patterns
  5. Interface consistency: get_liquidity_owner() getter added to IStreamToken trait

Recommendations

  1. Critical: Address the missing return value check and interface validation for the NFT transfer
  2. Important: Reorder the external call to follow checks-effects-interactions pattern
  3. Minor: Clean up test mocks to use only the correct ERC721 method signature

The core functionality is sound and well-tested, but the missing external call safety checks present security risks that should be addressed before merging.

Summary: 0 CRITICAL, 2 HIGH, 1 MEDIUM, 1 LOW, 1 INFO

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a liquidity_owner field, allowing the LP position NFT to be transferred to a designated address. This is a valuable feature that enhances the flexibility of managing liquidity positions. The implementation is thorough, with changes correctly propagated across contracts, interfaces, tests, and scripts. The documentation has also been updated to reflect this new functionality. I've identified a minor area for improvement in the test mocks to enhance clarity and remove redundant code.

Comment on lines +112 to +124
fn mock_nft_transfer(mock_positions: starknet::ContractAddress) {
start_mock_call(mock_positions, selector!("get_nft_address"), MOCK_NFT());
start_mock_call(mock_positions, selector!("transfer_from"), ());
start_mock_call(MOCK_NFT(), selector!("transferFrom"), ());
start_mock_call(MOCK_NFT(), selector!("transfer_from"), ());
}

fn stop_mock_nft_transfer(mock_positions: starknet::ContractAddress) {
stop_mock_call(mock_positions, selector!("get_nft_address"));
stop_mock_call(mock_positions, selector!("transfer_from"));
stop_mock_call(MOCK_NFT(), selector!("transferFrom"));
stop_mock_call(MOCK_NFT(), selector!("transfer_from"));
}

Choose a reason for hiding this comment

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

medium

The mock_nft_transfer and stop_mock_nft_transfer helper functions include mock calls that are not necessary for the test to pass and can be removed for clarity. The code under test only calls get_nft_address on the positions contract and transfer_from on the NFT contract. The mocks for transfer_from on the positions contract and transferFrom (with a capital F) on the NFT contract are not needed.

fn mock_nft_transfer(mock_positions: starknet::ContractAddress) {
    start_mock_call(mock_positions, selector!("get_nft_address"), MOCK_NFT());
    start_mock_call(MOCK_NFT(), selector!("transfer_from"), ());
}

fn stop_mock_nft_transfer(mock_positions: starknet::ContractAddress) {
    stop_mock_call(mock_positions, selector!("get_nft_address"));
    stop_mock_call(MOCK_NFT(), selector!("transfer_from"));
}

@github-actions
Copy link

Codex Review - General Engineering Review

[MEDIUM] scripts/examples/summit_attack_potion.json:2 - The new Summit example configs state total_supply is auto-calculated, but create_stream_token.sh still hard-requires total_supply, and all four new files omit it (summit_attack_potion.json, summit_poison_potion.json, summit_revive_potion.json, summit_xlife_potion.json).
Impact: Developers using these examples hit an immediate runtime failure (Missing required field: total_supply), so the examples are not executable as provided.
Fix: Add explicit "total_supply" to all four example JSON files and update the comment text, or implement real auto-calculation in scripts/create_stream_token.sh and make the field optional.

[LOW] scripts/create_stream_token.sh:541 - liquidity_owner is only format-validated; 0x0 is accepted and not rejected preflight.
Impact: The script can continue to on-chain steps (including approval) before failing later at token creation, causing avoidable failed runs and potentially leaving unnecessary allowance set.
Fix: Add a non-zero guard after address validation (for example, reject values matching ^0x0+$) before any transaction calls.

Summary: 0 CRITICAL, 0 HIGH, 1 MEDIUM, 1 LOW, 0 INFO

@github-actions
Copy link

Codex Review - Cairo/Starknet Contract Review

[MEDIUM] packages/economy/src/tokenomics/tests/test_stream_lifecycle.cairo:114 - mock_nft_transfer mocks transfer_from on mock_positions and both transferFrom + transfer_from on MOCK_NFT, which makes the test too permissive and can mask wrong call target/selector bugs.
Impact: Lifecycle tests can pass even if production code calls the wrong contract or wrong selector, increasing risk of runtime revert against real Ekubo interfaces.
Fix: Mock only expected calls: get_nft_address on positions and exactly one transfer selector on the NFT contract that matches the dispatcher ABI; remove extra selector mocks and the positions transfer_from mock.

[LOW] packages/economy/src/tokenomics/tests/test_stream_lifecycle.cairo:126 - New external NFT transfer path in provide_initial_liquidity() has success-path coverage only; no revert-path test was added.
Impact: Regressions in external call failure handling (selector mismatch, unexpected NFT behavior) may ship undetected.
Fix: Add a negative test (with #[should_panic(expected: ...)]) for provide_initial_liquidity() when NFT transfer fails, and assert expected failure behavior for this integration path.

Summary: 0 CRITICAL, 0 HIGH, 1 MEDIUM, 1 LOW, 0 INFO

@codecov
Copy link

codecov bot commented Feb 19, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds the ability to transfer the Ekubo LP position NFT to a designated owner address after initial liquidity provision, instead of permanently locking it in the stream token contract. This enables the liquidity owner to manage or withdraw liquidity as needed.

Changes:

  • Added liquidity_owner field to LiquidityConfig struct with validation and storage
  • Modified provide_initial_liquidity() to transfer the LP position NFT to the designated owner after minting
  • Added LiquidityPositionTransferred event and get_liquidity_owner() getter function

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
packages/interfaces/src/tokenomics/stream.cairo Added liquidity_owner field to LiquidityConfig and get_liquidity_owner() getter to interface
packages/economy/src/tokenomics/stream/stream.cairo Core implementation: storage, validation, NFT transfer logic, event, and getter
packages/economy/src/tokenomics/constants.cairo Added STREAM_INVALID_LIQUIDITY_OWNER error constant
packages/economy/src/tokenomics/tests/*.cairo Updated test fixtures to include liquidity_owner: OWNER() in all LiquidityConfig structs
packages/economy/src/tokenomics/tests/test_stream_lifecycle.cairo Added NFT transfer mocking helpers and event assertion for 7 lifecycle tests
packages/presets/src/tests/test_stream_token.cairo Updated test fixtures with liquidity_owner field
scripts/create_stream_token.sh Added extraction, validation, display, and serialization of liquidity_owner
scripts/examples/*.json Added liquidity_owner field to all 4 Summit example configurations
packages/economy/src/tokenomics/AGENTS.md Updated documentation for LiquidityConfig struct
Comments suppressed due to low confidence (1)

scripts/create_stream_token.sh:723

  • The CREATE_CALLDATA string is built from unvalidated numeric config fields (e.g., total_supply, stream_token_amount, paired_token_amount) and then interpolated unquoted into the sncast invocation via --calldata $CREATE_CALLDATA, which allows shell metacharacters in the JSON config to break out of the argument list and execute arbitrary commands. An attacker who can supply or modify the JSON config (for example, in a CI context or when sharing example configs) can inject characters like ; or command substitutions into these numeric fields, resulting in the script running unintended shell commands with the caller’s privileges. To fix this, ensure all numeric fields are strictly validated as digits-only before use and pass calldata as a properly quoted array of arguments (or otherwise avoid unquoted expansion into the shell command).
CREATE_CALLDATA="$NAME_CALLDATA $SYMBOL_CALLDATA $TOTAL_SUPPLY_WEI $PAIRED_TOKEN $POOL_FEE $STREAM_TOKEN_AMOUNT_WEI $PAIRED_TOKEN_AMOUNT_WEI $MIN_LIQUIDITY $LIQUIDITY_OWNER $ORDERS_CALLDATA $PREMINTS_CALLDATA"

print_verbose "Create calldata: $CREATE_CALLDATA"

CREATE_OUTPUT=$(sncast --account "$SNCAST_ACCOUNT" \
    --wait --wait-timeout 120 \
    invoke \
    --url "$STARKNET_RPC" \
    --contract-address "$STREAM_TOKEN_FACTORY" \
    --function "create_token" \
    --calldata $CREATE_CALLDATA 2>&1) || {

pub const STREAM_DISTRIBUTIONS_NOT_STARTED: felt252 = 'Distributions not started';
pub const STREAM_INVALID_ORDER_INDEX: felt252 = 'Invalid order index';
pub const STREAM_POSITION_NOT_FOUND: felt252 = 'Position not found';
pub const STREAM_INVALID_LIQUIDITY_OWNER: felt252 = 'Invalid liquidity owner';
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

The new error constant is added correctly, but there should be a corresponding test in the test_constants.cairo file to verify the error message. While that file isn't modified in this PR, this is a gap in test coverage that should be addressed to maintain consistency with other error constants.

Copilot uses AI. Check for mistakes.
Comment on lines +250 to +252
fn get_liquidity_owner(self: @ComponentState<TContractState>) -> ContractAddress {
self.Stream_liquidity_owner.read()
}
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

Missing test coverage for the get_liquidity_owner() getter function. Consider adding a test that verifies this function correctly returns the liquidity owner address that was set during initialization.

Copilot uses AI. Check for mistakes.
Comment on lines +309 to +314
// Transfer LP position NFT to liquidity owner
let nft_address = positions_dispatcher.get_nft_address();
let nft = IERC721Dispatcher { contract_address: nft_address };
let liquidity_owner = self.Stream_liquidity_owner.read();
nft.transfer_from(get_contract_address(), liquidity_owner, position_id.into());
self.emit(LiquidityPositionTransferred { position_id, owner: liquidity_owner });
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

The NFT transfer happens after the state write and event emission (lines 296-307). This creates a potential inconsistency if the NFT transfer fails - the contract state would show liquidity was provided (state=1), but the owner wouldn't have received the NFT. Consider restructuring so the state write happens after the NFT transfer completes successfully to ensure atomic state consistency. This would mean moving the state write and LiquidityProvided event emission to after line 314.

Copilot uses AI. Check for mistakes.
Comment on lines +115 to +122
start_mock_call(MOCK_NFT(), selector!("transferFrom"), ());
start_mock_call(MOCK_NFT(), selector!("transfer_from"), ());
}

fn stop_mock_nft_transfer(mock_positions: starknet::ContractAddress) {
stop_mock_call(mock_positions, selector!("get_nft_address"));
stop_mock_call(mock_positions, selector!("transfer_from"));
stop_mock_call(MOCK_NFT(), selector!("transferFrom"));
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

The mock setup includes both transferFrom (camelCase) and transfer_from (snake_case) variants on lines 115-116. However, based on the actual code in stream.cairo line 313 and consistent patterns throughout the codebase (e.g., in ticket_booth.cairo, prize.cairo), only transfer_from (snake_case) is the correct Cairo convention. The camelCase variant appears to be unnecessary. Consider removing line 115 to avoid confusion and reduce test maintenance.

Suggested change
start_mock_call(MOCK_NFT(), selector!("transferFrom"), ());
start_mock_call(MOCK_NFT(), selector!("transfer_from"), ());
}
fn stop_mock_nft_transfer(mock_positions: starknet::ContractAddress) {
stop_mock_call(mock_positions, selector!("get_nft_address"));
stop_mock_call(mock_positions, selector!("transfer_from"));
stop_mock_call(MOCK_NFT(), selector!("transferFrom"));
start_mock_call(MOCK_NFT(), selector!("transfer_from"), ());
}
fn stop_mock_nft_transfer(mock_positions: starknet::ContractAddress) {
stop_mock_call(mock_positions, selector!("get_nft_address"));
stop_mock_call(mock_positions, selector!("transfer_from"));

Copilot uses AI. Check for mistakes.
fn stop_mock_nft_transfer(mock_positions: starknet::ContractAddress) {
stop_mock_call(mock_positions, selector!("get_nft_address"));
stop_mock_call(mock_positions, selector!("transfer_from"));
stop_mock_call(MOCK_NFT(), selector!("transferFrom"));
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

Similar issue as above - line 122 stops mock for transferFrom (camelCase) but this is unnecessary. Only the snake_case variant transfer_from is used in the actual implementation. Consider removing line 122 to match the actual Cairo naming conventions.

Suggested change
stop_mock_call(MOCK_NFT(), selector!("transferFrom"));

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants