Skip to content

Commit e8e3fa2

Browse files
committed
Merge branch 'main' into holyfuchs/scheduled-rebalance
2 parents 31b6726 + f76929d commit e8e3fa2

2 files changed

Lines changed: 72 additions & 25 deletions

File tree

cadence/contracts/FlowCreditMarket.cdc

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,6 @@ access(all) contract FlowCreditMarket {
179179
/// Note that this entitlement provides access to all positions in the pool,
180180
/// not just individual position owners' positions.
181181
access(all) entitlement EPosition
182-
access(all) entitlement ERebalance
183182

184183
/// EGovernance
185184
///
@@ -3422,6 +3421,22 @@ access(all) contract FlowCreditMarket {
34223421
self._unlockPosition(pid)
34233422
}
34243423

3424+
/// Attempts to rebalance a position toward its configured `targetHealth` without acquiring
3425+
/// or releasing the position lock. This function performs *best-effort* rebalancing and may
3426+
/// partially rebalance or no-op depending on available sinks/sources and their capacity.
3427+
///
3428+
/// This helper is intentionally "no-lock" and "effects-only" with respect to orchestration.
3429+
/// Callers are responsible for acquiring and releasing the position lock and for enforcing
3430+
/// any higher-level invariants.
3431+
access(self) fun _rebalancePositionNoLock(pid: UInt64, force: Bool) {
3432+
post {
3433+
self.positionLock[pid] == nil: "Position is not unlocked"
3434+
}
3435+
self._lockPosition(pid)
3436+
self._rebalancePositionNoLock(pid: pid, force: force)
3437+
self._unlockPosition(pid)
3438+
}
3439+
34253440
/// Attempts to rebalance a position toward its configured `targetHealth` without acquiring
34263441
/// or releasing the position lock. This function performs *best-effort* rebalancing and may
34273442
/// partially rebalance or no-op depending on available sinks/sources and their capacity.
@@ -3853,7 +3868,7 @@ access(all) contract FlowCreditMarket {
38533868
/// The unique ID of the Position used to track deposits and withdrawals to the Pool
38543869
access(all) let id: UInt64
38553870

3856-
/// An authorized Capability to which the Position was opened
3871+
/// An authorized Capability to the Pool for which this Position was opened.
38573872
access(self) let pool: Capability<auth(EPosition) &Pool>
38583873

38593874
init(
@@ -4132,7 +4147,62 @@ access(all) contract FlowCreditMarket {
41324147
access(all) fun getPositionIDs(): [UInt64] {
41334148
return self.positions.keys
41344149
}
4150+
}
4151+
4152+
/// Creates and returns a new PositionManager resource
4153+
access(all) fun createPositionManager(): @PositionManager {
4154+
return <- create PositionManager()
4155+
}
4156+
4157+
/// PositionManager
4158+
///
4159+
/// A collection resource that manages multiple Position resources for an account.
4160+
/// This allows users to have multiple positions while using a single, constant storage path.
4161+
///
4162+
access(all) resource PositionManager {
4163+
4164+
/// Dictionary storing all positions owned by this manager, keyed by position ID
4165+
access(self) let positions: @{UInt64: Position}
4166+
4167+
init() {
4168+
self.positions <- {}
4169+
}
41354170

4171+
/// Adds a new position to the manager.
4172+
access(EPositionAdmin) fun addPosition(position: @Position) {
4173+
let pid = position.id
4174+
let old <- self.positions[pid] <- position
4175+
if old != nil {
4176+
panic("Cannot add position with same pid (\(pid)) as existing position: must explicitly remove existing position first")
4177+
}
4178+
destroy old
4179+
}
4180+
4181+
/// Removes and returns a position from the manager.
4182+
access(EPositionAdmin) fun removePosition(pid: UInt64): @Position {
4183+
if let position <- self.positions.remove(key: pid) {
4184+
return <-position
4185+
}
4186+
panic("Position with pid=\(pid) not found in PositionManager")
4187+
}
4188+
4189+
/// Internal method that returns a reference to a position authorized with all entitlements.
4190+
/// Callers who wish to provide a partially authorized reference can downcast the result as needed.
4191+
access(EPositionAdmin) fun borrowAuthorizedPosition(pid: UInt64): auth(FungibleToken.Withdraw, EPositionAdmin) &Position {
4192+
return (&self.positions[pid] as auth(FungibleToken.Withdraw, EPositionAdmin) &Position?)
4193+
?? panic("Position with pid=\(pid) not found in PositionManager")
4194+
}
4195+
4196+
/// Returns a public reference to a position with no entitlements.
4197+
access(all) fun borrowPosition(pid: UInt64): &Position {
4198+
return (&self.positions[pid] as &Position?)
4199+
?? panic("Position with pid=\(pid) not found in PositionManager")
4200+
}
4201+
4202+
/// Returns the IDs of all positions in this manager
4203+
access(all) fun getPositionIDs(): [UInt64] {
4204+
return self.positions.keys
4205+
}
41364206
}
41374207

41384208
/// Creates and returns a new PositionManager resource

cadence/tests/test_helpers.cdc

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -468,19 +468,6 @@ fun createPosition(signer: Test.TestAccount, amount: UFix64, vaultStoragePath: S
468468
Test.expect(openRes, Test.beSucceeded())
469469
}
470470

471-
access(all)
472-
fun createPositionNotManaged(signer: Test.TestAccount, amount: UFix64, vaultStoragePath: StoragePath, pushToDrawDownSink: Bool, positionStoragePath: StoragePath) {
473-
// Grant beta access to the signer if they don't have it yet
474-
grantBetaPoolParticipantAccess(PROTOCOL_ACCOUNT, signer)
475-
476-
let openRes = _executeTransaction(
477-
"../transactions/flow-credit-market/position/create_position_not_managed.cdc",
478-
[amount, vaultStoragePath, pushToDrawDownSink, positionStoragePath],
479-
signer
480-
)
481-
Test.expect(openRes, Test.beSucceeded())
482-
}
483-
484471
access(all)
485472
fun depositToPosition(signer: Test.TestAccount, positionID: UInt64, amount: UFix64, vaultStoragePath: StoragePath, pushToDrawDownSink: Bool) {
486473
let depositRes = _executeTransaction(
@@ -491,16 +478,6 @@ fun depositToPosition(signer: Test.TestAccount, positionID: UInt64, amount: UFix
491478
Test.expect(depositRes, Test.beSucceeded())
492479
}
493480

494-
access(all)
495-
fun depositToPositionNotManaged(signer: Test.TestAccount, positionStoragePath: StoragePath, amount: UFix64, vaultStoragePath: StoragePath, pushToDrawDownSink: Bool) {
496-
let depositRes = _executeTransaction(
497-
"./transactions/position/deposit_to_position.cdc",
498-
[positionStoragePath, amount, vaultStoragePath, pushToDrawDownSink],
499-
signer
500-
)
501-
Test.expect(depositRes, Test.beSucceeded())
502-
}
503-
504481
access(all)
505482
fun borrowFromPosition(signer: Test.TestAccount, positionId: UInt64, tokenTypeIdentifier: String, amount: UFix64, beFailed: Bool) {
506483
let borrowRes = _executeTransaction(

0 commit comments

Comments
 (0)