Skip to content

Feature: Persist Looping Position State And Close Positions #414

Description

@YaronZaki

Labels / Complexity: feature, soroban, protocol, rust · Extremely High — 500

Context

LoopingContract (quantara/soroban/contracts/looping/src/lib.rs) is the leverage-loop engine, but open_position only increments a counter and discards every input:

let key = symbol_short!("pos_cnt");
let count: u64 = env.storage().instance().get(&key).unwrap_or(0u64);
let position_id = count + 1;
env.storage().instance().set(&key, &position_id);
position_id  // collateral, leverage, and user are never stored

close_position is an empty stub whose body is the comment // Stub: full unwind logic will be implemented in a future PR. The off-chain backend already models positions (Position, Transaction, ExtraDeposit in quantara/web_app/db/models.py) and writes a PositionOpened outbox event (quantara/web_app/tasks/outbox_relay.py), but none of that state exists on-chain.

This blocks the protocol's core loop: without an on-chain position record, there is no way to unwind a position, enforce leverage limits at close, or reconcile the backend's position lifecycle with the contract. A naive hardcoded version (a single u64 balance) would create a migration trap the moment multi-asset positions or partial closes are added.

Goal

Give the looping contract a persistent per-user position model and a working close path. Opening a position must store enough state (owner, collateral, borrowed asset, leverage) to later identify and close it, and closing must be authenticated and idempotent.

Scope

1. Position storage

Define a Position contract type (owner, collateral asset/amount, debt asset/amount, leverage, status) stored under a monotonically increasing position id. Preserve the existing pos_cnt counter semantics or migrate it deliberately.

2. Open position

Persist the full position in open_position, validating the leverage range already asserted (100..=500) and recording the collateral/debt amounts passed by the caller.

3. Close position

Implement close_position to authenticate the owner, verify the position exists and is open, mark it closed, and return a result the caller can use. Keep it idempotent (closing a closed position is a defined error, not a panic).

4. Tests

Add deterministic unit tests for open/close round-trips, double-close rejection, and non-owner close rejection, following the existing common crate's proptest style (quantara/soroban/contracts/common/src/math.rs).

Downstream impact

The backend's Position lifecycle and outbox relay (quantara/web_app/tasks/outbox_relay.py) assume an opened/closed status exists off-chain; this change should define the on-chain status values that the relay will eventually mirror. There are no generated bindings to regenerate, but the frontend deployContract / invokeSorobanContract path (quantara/frontend/src/services/contract.js, soroban.js) will need the new entry-point signatures when the flow is wired end-to-end.

Acceptance criteria

Contract

  • open_position persists the position record keyed by a returned id.
  • close_position is authenticated, idempotent, and restricted to the position owner.
  • Position state is queryable (a getter returns owner/status/amounts).

Tests

  • Unit tests cover open/close round-trip, double-close, non-owner close, and leverage-range validation.
  • Tests run via cd quantara/soroban/contracts && cargo test -p looping.

Out of scope

Do not implement the actual borrow-swap-redeposit mechanics or multi-asset support in this issue; only the position record and its lifecycle.

Getting started

Files in scope: quantara/soroban/contracts/looping/src/lib.rs, quantara/soroban/contracts/looping/Cargo.toml. Verify with:

cd quantara/soroban/contracts
cargo test -p looping
cargo build --target wasm32-unknown-unknown --release

Good first files to read: quantara/soroban/contracts/looping/src/lib.rs, quantara/soroban/contracts/liquidation/src/lib.rs (contracttype/storage pattern), quantara/soroban/contracts/common/src/auth.rs.

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardThird CampaignCampaign: Third CampaignfeatureprotocolImported from PRODUCTION_ISSUES.mdrustImported from PRODUCTION_ISSUES.mdsorobanImported from PRODUCTION_ISSUES.md

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions