From 38e09c02ebcc20e01541839b848113d49416cb2f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 19 Feb 2026 18:21:19 +0000 Subject: [PATCH 1/8] fix: remove EPosition entitlement from beta capability to prevent unauthorized withdrawals The beta capability was granting both EParticipant and EPosition entitlements, which gave any beta user direct access to pool-level withdraw, withdrawAndPull, depositAndPush, lockPosition, unlockPosition, and borrowPosition methods. This allowed withdrawals from any user's position by ID. Beta users only need EParticipant (createPosition, depositToPosition) for normal operations. This removes EPosition from both the publish and claim transactions and their test copies. https://claude.ai/code/session_011G8tWcco9U7kPvNbf5kLgc --- .../transactions/flow-alp/beta/claim_and_save_beta_cap.cdc | 6 +++--- .../tests/transactions/flow-alp/beta/publish_beta_cap.cdc | 4 ++-- .../transactions/flow-alp/beta/claim_and_save_beta_cap.cdc | 6 +++--- cadence/transactions/flow-alp/beta/publish_beta_cap.cdc | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cadence/tests/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc b/cadence/tests/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc index f5695230..282d8006 100644 --- a/cadence/tests/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc +++ b/cadence/tests/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc @@ -3,15 +3,15 @@ import "FlowALPv0" transaction(adminAddr: Address) { prepare(user: auth(SaveValue, LoadValue, ClaimInboxCapability) &Account) { - let claimed: Capability = + let claimed: Capability = user.inbox.claim< - auth(FlowALPv0.EParticipant, FlowALPv0.EPosition) &FlowALPv0.Pool + auth(FlowALPv0.EParticipant) &FlowALPv0.Pool >("FlowALPv0BetaCap", provider: adminAddr) ?? panic("No beta capability found in inbox") if user.storage.type(at: FlowALPv0.PoolCapStoragePath) != nil { let _ = user.storage.load< - Capability + Capability >(from: FlowALPv0.PoolCapStoragePath) } user.storage.save(claimed, to: FlowALPv0.PoolCapStoragePath) diff --git a/cadence/tests/transactions/flow-alp/beta/publish_beta_cap.cdc b/cadence/tests/transactions/flow-alp/beta/publish_beta_cap.cdc index d6776e47..4b7bce19 100644 --- a/cadence/tests/transactions/flow-alp/beta/publish_beta_cap.cdc +++ b/cadence/tests/transactions/flow-alp/beta/publish_beta_cap.cdc @@ -3,9 +3,9 @@ import "FlowALPv0" transaction(grantee: Address) { prepare(admin: auth(IssueStorageCapabilityController, PublishInboxCapability) &Account) { - let poolCap: Capability = + let poolCap: Capability = admin.capabilities.storage.issue< - auth(FlowALPv0.EParticipant, FlowALPv0.EPosition) &FlowALPv0.Pool + auth(FlowALPv0.EParticipant) &FlowALPv0.Pool >(FlowALPv0.PoolStoragePath) assert(poolCap.check(), message: "Failed to issue beta capability") diff --git a/cadence/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc b/cadence/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc index 020738c4..f1a4f00f 100644 --- a/cadence/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc +++ b/cadence/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc @@ -5,14 +5,14 @@ transaction(adminAddr: Address) { prepare(user: auth(SaveValue, LoadValue, ClaimInboxCapability) &Account) { // Save claimed cap at the protocol-defined storage path to satisfy consumers/tests expecting this path let capPath = FlowALPv0.PoolCapStoragePath - let claimed: Capability = + let claimed: Capability = user.inbox.claim< - auth(FlowALPv0.EParticipant, FlowALPv0.EPosition) &FlowALPv0.Pool + auth(FlowALPv0.EParticipant) &FlowALPv0.Pool >("FlowALPv0BetaCap", provider: adminAddr) ?? panic("No beta capability found in inbox") if user.storage.type(at: capPath) != nil { - let _ = user.storage.load>(from: capPath) + let _ = user.storage.load>(from: capPath) } user.storage.save(claimed, to: capPath) } diff --git a/cadence/transactions/flow-alp/beta/publish_beta_cap.cdc b/cadence/transactions/flow-alp/beta/publish_beta_cap.cdc index c07e0151..ac917e34 100644 --- a/cadence/transactions/flow-alp/beta/publish_beta_cap.cdc +++ b/cadence/transactions/flow-alp/beta/publish_beta_cap.cdc @@ -3,9 +3,9 @@ import "FlowALPv0" transaction(grantee: Address) { prepare(admin: auth(IssueStorageCapabilityController, PublishInboxCapability) &Account) { - let poolCap: Capability = + let poolCap: Capability = admin.capabilities.storage.issue< - auth(FlowALPv0.EParticipant, FlowALPv0.EPosition) &FlowALPv0.Pool + auth(FlowALPv0.EParticipant) &FlowALPv0.Pool >(FlowALPv0.PoolStoragePath) assert(poolCap.check(), message: "Failed to issue beta capability") From 31275f3c7d2c2d9610fbb2977a21c8d855991854 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 19 Feb 2026 11:07:14 -0800 Subject: [PATCH 2/8] fix: remove EPosition entitlement from all transaction files EPosition is an internal-only entitlement that should never be granted to external users. Replace with EParticipant (for position creation) or ERebalance (for rebalancing) as appropriate. Update adversarial reentrancy test connector to use EParticipant and depositToPosition instead of withdrawAndPull. Co-Authored-By: Claude Opus 4.6 --- .../AdversarialReentrancyConnectors.cdc | 24 ++++++------------- .../pool-management/03_grant_beta.cdc | 6 ++--- .../pool-management/04_create_position.cdc | 2 +- .../create_position_reentrancy.cdc | 4 ++-- .../create_position_spoofing_source.cdc | 4 ++-- .../pool-management/rebalance_position.cdc | 4 ++-- .../flow-alp/position/create_position.cdc | 4 ++-- .../position/create_position_not_managed.cdc | 4 ++-- 8 files changed, 21 insertions(+), 31 deletions(-) diff --git a/cadence/tests/contracts/AdversarialReentrancyConnectors.cdc b/cadence/tests/contracts/AdversarialReentrancyConnectors.cdc index 5d69260d..6ac60fa6 100644 --- a/cadence/tests/contracts/AdversarialReentrancyConnectors.cdc +++ b/cadence/tests/contracts/AdversarialReentrancyConnectors.cdc @@ -107,12 +107,12 @@ access(all) contract AdversarialReentrancyConnectors { access(all) resource LiveData { /// Optional: Pool capability for recursive withdrawAndPull call - access(all) var recursivePool: Capability? + access(all) var recursivePool: Capability? /// Optional: Position ID for recursive withdrawAndPull call access(all) var recursivePositionID: UInt64? init() { self.recursivePositionID = nil; self.recursivePool = nil } - access(all) fun setRecursivePool(_ pool: Capability) { + access(all) fun setRecursivePool(_ pool: Capability) { self.recursivePool = pool } access(all) fun setRecursivePositionID(_ positionID: UInt64) { @@ -205,24 +205,14 @@ access(all) contract AdversarialReentrancyConnectors { log("=====Recursive pool: \(self.liveDataCap.check())") let liveData = self.liveDataCap.borrow() ?? panic("cant borrow LiveData") let poolRef = liveData.recursivePool!.borrow() ?? panic("cant borrow Recursive pool is nil") - // Call withdrawAndPull on the position - let recursiveVault <- poolRef.withdrawAndPull( + // Attempt reentrant deposit to the same position (should fail due to position lock) + let emptyVault <- DeFiActionsUtils.getEmptyVault(Type<@FlowToken.Vault>()) + poolRef.depositToPosition( pid: liveData.recursivePositionID!, - // type: Type<@MOET.Vault>(), - type: Type<@FlowToken.Vault>(), - // type: tokenType, - amount: 900.0, - pullFromTopUpSource: false + from: <-emptyVault ) - log("Recursive withdrawAndPull returned vault with balance: \(recursiveVault.balance)") - // If we got funds from the recursive call, return them - if recursiveVault.balance > 0.0 { - return <-recursiveVault - } - // Otherwise, destroy the empty vault and continue with normal withdrawal - destroy recursiveVault + log("Recursive depositToPosition succeeded (should not reach here)") - // Normal vault withdrawal let available = self.minimumAvailable() if !self.withdrawVault.check() || available == 0.0 || maxAmount == 0.0 { diff --git a/cadence/tests/transactions/flow-alp/pool-management/03_grant_beta.cdc b/cadence/tests/transactions/flow-alp/pool-management/03_grant_beta.cdc index 2d6d6ad0..7538cb4e 100644 --- a/cadence/tests/transactions/flow-alp/pool-management/03_grant_beta.cdc +++ b/cadence/tests/transactions/flow-alp/pool-management/03_grant_beta.cdc @@ -6,14 +6,14 @@ transaction() { admin: auth(Capabilities, Storage) &Account, tester: auth(Storage) &Account ) { - let poolCap: Capability = + let poolCap: Capability = admin.capabilities.storage.issue< - auth(FlowALPv0.EParticipant, FlowALPv0.EPosition) &FlowALPv0.Pool + auth(FlowALPv0.EParticipant) &FlowALPv0.Pool >(FlowALPv0.PoolStoragePath) // assert(poolCap.check(), message: "Failed to issue Pool capability") if tester.storage.type(at: FlowALPv0.PoolCapStoragePath) != nil { - tester.storage.load>( + tester.storage.load>( from: FlowALPv0.PoolCapStoragePath ) } diff --git a/cadence/tests/transactions/flow-alp/pool-management/04_create_position.cdc b/cadence/tests/transactions/flow-alp/pool-management/04_create_position.cdc index d42aa857..5659f3a6 100644 --- a/cadence/tests/transactions/flow-alp/pool-management/04_create_position.cdc +++ b/cadence/tests/transactions/flow-alp/pool-management/04_create_position.cdc @@ -7,7 +7,7 @@ import "DummyConnectors" transaction { prepare(admin: auth(BorrowValue, Storage, Capabilities) &Account) { - let pool = admin.storage.borrow(from: FlowALPv0.PoolStoragePath) + let pool = admin.storage.borrow(from: FlowALPv0.PoolStoragePath) // Ensure PositionManager exists if admin.storage.borrow<&FlowALPv0.PositionManager>(from: FlowALPv0.PositionStoragePath) == nil { diff --git a/cadence/tests/transactions/position-manager/create_position_reentrancy.cdc b/cadence/tests/transactions/position-manager/create_position_reentrancy.cdc index 4500178c..0d9a6b98 100644 --- a/cadence/tests/transactions/position-manager/create_position_reentrancy.cdc +++ b/cadence/tests/transactions/position-manager/create_position_reentrancy.cdc @@ -27,7 +27,7 @@ transaction(amount: UFix64, vaultStoragePath: StoragePath, pushToDrawDownSink: B // the position manager in the signer's account where we should store the new position let positionManager: auth(FlowALPv0.EPositionAdmin) &FlowALPv0.PositionManager // the authorized Pool capability - let poolCap: Capability + let poolCap: Capability // reference to signer's account for saving capability back let signerAccount: auth(LoadValue, BorrowValue, SaveValue, IssueStorageCapabilityController, PublishCapability, UnpublishCapability) &Account @@ -81,7 +81,7 @@ transaction(amount: UFix64, vaultStoragePath: StoragePath, pushToDrawDownSink: B ?? panic("PositionManager not found") // Load the authorized Pool capability from storage - self.poolCap = signer.storage.load>( + self.poolCap = signer.storage.load>( from: FlowALPv0.PoolCapStoragePath ) ?? panic("Could not load Pool capability from storage - ensure the signer has been granted Pool access with EParticipant entitlement") } diff --git a/cadence/tests/transactions/position-manager/create_position_spoofing_source.cdc b/cadence/tests/transactions/position-manager/create_position_spoofing_source.cdc index 6c0e8bae..57551b92 100644 --- a/cadence/tests/transactions/position-manager/create_position_spoofing_source.cdc +++ b/cadence/tests/transactions/position-manager/create_position_spoofing_source.cdc @@ -28,7 +28,7 @@ transaction(amount: UFix64, vaultStoragePath: StoragePath, pushToDrawDownSink: B // the position manager in the signer's account where we should store the new position let positionManager: auth(FlowALPv0.EPositionAdmin) &FlowALPv0.PositionManager // the authorized Pool capability - let poolCap: Capability + let poolCap: Capability // reference to signer's account for saving capability back let signerAccount: auth(LoadValue,BorrowValue, SaveValue, IssueStorageCapabilityController, PublishCapability, UnpublishCapability) &Account @@ -81,7 +81,7 @@ transaction(amount: UFix64, vaultStoragePath: StoragePath, pushToDrawDownSink: B ?? panic("PositionManager not found") // Load the authorized Pool capability from storage - self.poolCap = signer.storage.load>( + self.poolCap = signer.storage.load>( from: FlowALPv0.PoolCapStoragePath ) ?? panic("Could not load Pool capability from storage - ensure the signer has been granted Pool access with EParticipant entitlement") } diff --git a/cadence/transactions/flow-alp/pool-management/rebalance_position.cdc b/cadence/transactions/flow-alp/pool-management/rebalance_position.cdc index 0c3fa3d9..ee1df5ab 100644 --- a/cadence/transactions/flow-alp/pool-management/rebalance_position.cdc +++ b/cadence/transactions/flow-alp/pool-management/rebalance_position.cdc @@ -7,10 +7,10 @@ import "FlowALPv0" /// the position is beyond its min/max health. If `true`, the rebalance executes regardless of its relative health. /// transaction(pid: UInt64, force: Bool) { - let pool: auth(FlowALPv0.EPosition) &FlowALPv0.Pool + let pool: auth(FlowALPv0.ERebalance) &FlowALPv0.Pool prepare(signer: auth(BorrowValue) &Account) { - self.pool = signer.storage.borrow(from: FlowALPv0.PoolStoragePath) + self.pool = signer.storage.borrow(from: FlowALPv0.PoolStoragePath) ?? panic("Could not borrow reference to Pool from \(FlowALPv0.PoolStoragePath) - ensure a Pool has been configured") } diff --git a/cadence/transactions/flow-alp/position/create_position.cdc b/cadence/transactions/flow-alp/position/create_position.cdc index 97b91675..0ee259da 100644 --- a/cadence/transactions/flow-alp/position/create_position.cdc +++ b/cadence/transactions/flow-alp/position/create_position.cdc @@ -20,7 +20,7 @@ transaction(amount: UFix64, vaultStoragePath: StoragePath, pushToDrawDownSink: B // the position manager in the signer's account where we should store the new position let positionManager: auth(FlowALPv0.EPositionAdmin) &FlowALPv0.PositionManager // the authorized Pool capability - let poolCap: Capability + let poolCap: Capability // reference to signer's account for saving capability back let signerAccount: auth(Storage) &Account @@ -76,7 +76,7 @@ transaction(amount: UFix64, vaultStoragePath: StoragePath, pushToDrawDownSink: B ?? panic("PositionManager not found") // Load the authorized Pool capability from storage - self.poolCap = signer.storage.load>( + self.poolCap = signer.storage.load>( from: FlowALPv0.PoolCapStoragePath ) ?? panic("Could not load Pool capability from storage - ensure the signer has been granted Pool access with EParticipant entitlement") } diff --git a/cadence/transactions/flow-alp/position/create_position_not_managed.cdc b/cadence/transactions/flow-alp/position/create_position_not_managed.cdc index cce01a99..27f70ee8 100644 --- a/cadence/transactions/flow-alp/position/create_position_not_managed.cdc +++ b/cadence/transactions/flow-alp/position/create_position_not_managed.cdc @@ -18,7 +18,7 @@ transaction(amount: UFix64, vaultStoragePath: StoragePath, pushToDrawDownSink: B // this DeFiActions Source that will allow for the repayment of a loan if the position becomes undercollateralized let source: {DeFiActions.Source} // the authorized Pool capability - let poolCap: Capability + let poolCap: Capability // reference to signer's account for saving capability back let signerAccount: auth(Storage) &Account @@ -59,7 +59,7 @@ transaction(amount: UFix64, vaultStoragePath: StoragePath, pushToDrawDownSink: B ) // Load the authorized Pool capability from storage - self.poolCap = signer.storage.load>( + self.poolCap = signer.storage.load>( from: FlowALPv0.PoolCapStoragePath ) ?? panic("Could not load Pool capability from storage - ensure the signer has been granted Pool access with EParticipant entitlement") } From 558baf84f7c49a2a875afc4a411cc97eeee2327b Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 19 Feb 2026 11:17:33 -0800 Subject: [PATCH 3/8] fix: update reentrancy test to withdraw via Position instead of Pool Update AdversarialReentrancyConnectors to hold a PositionManager capability instead of a Pool capability. The adversarial source now attempts a reentrant withdrawal through the Position resource, which is the realistic attack vector since external users access positions through PositionManager, not the Pool directly. Co-Authored-By: Claude Opus 4.6 --- .../AdversarialReentrancyConnectors.cdc | 36 ++++++++++--------- .../create_position_reentrancy.cdc | 8 +++-- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/cadence/tests/contracts/AdversarialReentrancyConnectors.cdc b/cadence/tests/contracts/AdversarialReentrancyConnectors.cdc index 6ac60fa6..b85dc891 100644 --- a/cadence/tests/contracts/AdversarialReentrancyConnectors.cdc +++ b/cadence/tests/contracts/AdversarialReentrancyConnectors.cdc @@ -106,17 +106,18 @@ access(all) contract AdversarialReentrancyConnectors { } access(all) resource LiveData { - /// Optional: Pool capability for recursive withdrawAndPull call - access(all) var recursivePool: Capability? - /// Optional: Position ID for recursive withdrawAndPull call + /// Capability to the attacker's PositionManager for recursive withdrawal + access(all) var positionManagerCap: Capability? + /// Position ID for recursive withdrawal access(all) var recursivePositionID: UInt64? - init() { self.recursivePositionID = nil; self.recursivePool = nil } - access(all) fun setRecursivePool(_ pool: Capability) { - self.recursivePool = pool - } - access(all) fun setRecursivePositionID(_ positionID: UInt64) { - self.recursivePositionID = positionID + init() { self.recursivePositionID = nil; self.positionManagerCap = nil } + access(all) fun setRecursivePosition( + managerCap: Capability, + pid: UInt64 + ) { + self.positionManagerCap = managerCap + self.recursivePositionID = pid } } access(all) fun createLiveData(): @LiveData { @@ -202,16 +203,17 @@ access(all) contract AdversarialReentrancyConnectors { access(FungibleToken.Withdraw) fun withdrawAvailable(maxAmount: UFix64): @{FungibleToken.Vault} { // If recursive withdrawAndPull is configured, call it first log("VaultSource.withdrawAvailable called with maxAmount: \(maxAmount)") - log("=====Recursive pool: \(self.liveDataCap.check())") + log("=====Recursive position manager: \(self.liveDataCap.check())") let liveData = self.liveDataCap.borrow() ?? panic("cant borrow LiveData") - let poolRef = liveData.recursivePool!.borrow() ?? panic("cant borrow Recursive pool is nil") - // Attempt reentrant deposit to the same position (should fail due to position lock) - let emptyVault <- DeFiActionsUtils.getEmptyVault(Type<@FlowToken.Vault>()) - poolRef.depositToPosition( - pid: liveData.recursivePositionID!, - from: <-emptyVault + let manager = liveData.positionManagerCap!.borrow() ?? panic("cant borrow PositionManager") + let position = manager.borrowAuthorizedPosition(pid: liveData.recursivePositionID!) + // Attempt reentrant withdrawal via Position (should fail due to position lock) + let recursiveVault <- position.withdraw( + type: Type<@FlowToken.Vault>(), + amount: 900.0 ) - log("Recursive depositToPosition succeeded (should not reach here)") + log("Recursive withdraw succeeded with balance: \(recursiveVault.balance) (should not reach here)") + destroy recursiveVault // Normal vault withdrawal let available = self.minimumAvailable() diff --git a/cadence/tests/transactions/position-manager/create_position_reentrancy.cdc b/cadence/tests/transactions/position-manager/create_position_reentrancy.cdc index 0d9a6b98..c7cb9ceb 100644 --- a/cadence/tests/transactions/position-manager/create_position_reentrancy.cdc +++ b/cadence/tests/transactions/position-manager/create_position_reentrancy.cdc @@ -102,10 +102,12 @@ transaction(amount: UFix64, vaultStoragePath: StoragePath, pushToDrawDownSink: B self.positionManager.addPosition(position: <-position) let sourceRef = self.source as! AdversarialReentrancyConnectors.VaultSourceHacked - + let liveData = sourceRef.liveDataCap.borrow() ?? panic("cant borrow LiveData") - liveData.setRecursivePool(self.poolCap) - liveData.setRecursivePositionID(pid) + let managerCap = self.signerAccount.capabilities.storage.issue< + auth(FungibleToken.Withdraw, FlowALPv0.EPositionAdmin) &FlowALPv0.PositionManager + >(FlowALPv0.PositionStoragePath) + liveData.setRecursivePosition(managerCap: managerCap, pid: pid) self.signerAccount.storage.save(self.poolCap, to: FlowALPv0.PoolCapStoragePath) } From 993ff5cef2ec09a0a1313daee5ceed76dd376be5 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 19 Feb 2026 11:18:07 -0800 Subject: [PATCH 4/8] document the EPosition is internal-only --- cadence/contracts/FlowALPv0.cdc | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/cadence/contracts/FlowALPv0.cdc b/cadence/contracts/FlowALPv0.cdc index 52c40a90..1ec06fcc 100644 --- a/cadence/contracts/FlowALPv0.cdc +++ b/cadence/contracts/FlowALPv0.cdc @@ -176,16 +176,6 @@ access(all) contract FlowALPv0 { /* --- CONSTRUCTS & INTERNAL METHODS ---- */ - /// EPosition - /// - /// Entitlement for managing positions within the pool. - /// This entitlement grants access to position-specific operations including deposits, withdrawals, - /// rebalancing, and health parameter management for any position in the pool. - /// - /// Note that this entitlement provides access to all positions in the pool, - /// not just individual position owners' positions. - access(all) entitlement EPosition - /// ERebalance /// /// Entitlement for rebalancing positions. @@ -211,6 +201,17 @@ access(all) contract FlowALPv0 { /// and process queued operations. It should not be granted to external users. access(all) entitlement EImplementation + /// EPosition + /// + /// Entitlement for managing positions within the pool. + /// This entitlement grants access to position-specific operations including deposits, withdrawals, + /// rebalancing, and health parameter management for any position in the pool. + /// + /// IMPORTANT: this entitlement provides access to ALL positions in the pool and is for internal + /// implementation use only! Users access their individual positions via the Position resource. + access(all) entitlement EPosition + + /// EParticipant /// /// Entitlement for general participant operations that allow users to interact with the pool From 12520c71da4d2c6deacfd36276a79d6b0cbabc68 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 19 Feb 2026 11:22:39 -0800 Subject: [PATCH 5/8] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Müller --- .../transactions/flow-alp/beta/claim_and_save_beta_cap.cdc | 2 +- cadence/tests/transactions/flow-alp/beta/publish_beta_cap.cdc | 2 +- .../transactions/flow-alp/pool-management/03_grant_beta.cdc | 2 +- cadence/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc | 2 +- cadence/transactions/flow-alp/beta/publish_beta_cap.cdc | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cadence/tests/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc b/cadence/tests/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc index 282d8006..a6df9b9e 100644 --- a/cadence/tests/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc +++ b/cadence/tests/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc @@ -3,7 +3,7 @@ import "FlowALPv0" transaction(adminAddr: Address) { prepare(user: auth(SaveValue, LoadValue, ClaimInboxCapability) &Account) { - let claimed: Capability = + let claimed = user.inbox.claim< auth(FlowALPv0.EParticipant) &FlowALPv0.Pool >("FlowALPv0BetaCap", provider: adminAddr) diff --git a/cadence/tests/transactions/flow-alp/beta/publish_beta_cap.cdc b/cadence/tests/transactions/flow-alp/beta/publish_beta_cap.cdc index 4b7bce19..5700212e 100644 --- a/cadence/tests/transactions/flow-alp/beta/publish_beta_cap.cdc +++ b/cadence/tests/transactions/flow-alp/beta/publish_beta_cap.cdc @@ -3,7 +3,7 @@ import "FlowALPv0" transaction(grantee: Address) { prepare(admin: auth(IssueStorageCapabilityController, PublishInboxCapability) &Account) { - let poolCap: Capability = + let poolCap = admin.capabilities.storage.issue< auth(FlowALPv0.EParticipant) &FlowALPv0.Pool >(FlowALPv0.PoolStoragePath) diff --git a/cadence/tests/transactions/flow-alp/pool-management/03_grant_beta.cdc b/cadence/tests/transactions/flow-alp/pool-management/03_grant_beta.cdc index 7538cb4e..68c8d8ac 100644 --- a/cadence/tests/transactions/flow-alp/pool-management/03_grant_beta.cdc +++ b/cadence/tests/transactions/flow-alp/pool-management/03_grant_beta.cdc @@ -6,7 +6,7 @@ transaction() { admin: auth(Capabilities, Storage) &Account, tester: auth(Storage) &Account ) { - let poolCap: Capability = + let poolCap = admin.capabilities.storage.issue< auth(FlowALPv0.EParticipant) &FlowALPv0.Pool >(FlowALPv0.PoolStoragePath) diff --git a/cadence/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc b/cadence/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc index f1a4f00f..e9c5c0e2 100644 --- a/cadence/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc +++ b/cadence/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc @@ -5,7 +5,7 @@ transaction(adminAddr: Address) { prepare(user: auth(SaveValue, LoadValue, ClaimInboxCapability) &Account) { // Save claimed cap at the protocol-defined storage path to satisfy consumers/tests expecting this path let capPath = FlowALPv0.PoolCapStoragePath - let claimed: Capability = + let claimed = user.inbox.claim< auth(FlowALPv0.EParticipant) &FlowALPv0.Pool >("FlowALPv0BetaCap", provider: adminAddr) diff --git a/cadence/transactions/flow-alp/beta/publish_beta_cap.cdc b/cadence/transactions/flow-alp/beta/publish_beta_cap.cdc index ac917e34..18f60fbe 100644 --- a/cadence/transactions/flow-alp/beta/publish_beta_cap.cdc +++ b/cadence/transactions/flow-alp/beta/publish_beta_cap.cdc @@ -3,7 +3,7 @@ import "FlowALPv0" transaction(grantee: Address) { prepare(admin: auth(IssueStorageCapabilityController, PublishInboxCapability) &Account) { - let poolCap: Capability = + let poolCap = admin.capabilities.storage.issue< auth(FlowALPv0.EParticipant) &FlowALPv0.Pool >(FlowALPv0.PoolStoragePath) From 449a14f3d6d5b4b34ec9bbd763813cdf3d09d1f4 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 25 Mar 2026 12:36:14 -0700 Subject: [PATCH 6/8] Resolve merge conflicts: migrate entitlements to FlowALPModels without exposing EPosition Resolves conflicts from merging main into the EPosition removal branch. Entitlements now reference FlowALPModels (main's refactor) while keeping EPosition out of all user-facing beta capabilities (this branch's fix). Co-Authored-By: Claude Opus 4.6 (1M context) --- cadence/contracts/FlowALPv0.cdc | 54 -------- .../AdversarialReentrancyConnectors.cdc | 19 +-- .../flow-alp/beta/claim_and_save_beta_cap.cdc | 14 +-- .../flow-alp/beta/publish_beta_cap.cdc | 8 +- .../pool-management/03_grant_beta.cdc | 14 +-- .../pool-management/04_create_position.cdc | 6 +- .../create_position_reentrancy.cdc | 14 +-- .../create_position_spoofing_source.cdc | 12 +- .../flow-alp/beta/claim_and_save_beta_cap.cdc | 14 +-- .../flow-alp/beta/publish_beta_cap.cdc | 8 +- .../pool-management/rebalance_position.cdc | 11 +- .../flow-alp/position/create_position.cdc | 12 +- .../position/create_position_not_managed.cdc | 12 +- flow.json | 116 +++++++++--------- 14 files changed, 81 insertions(+), 233 deletions(-) diff --git a/cadence/contracts/FlowALPv0.cdc b/cadence/contracts/FlowALPv0.cdc index e2bb351a..224bf8e1 100644 --- a/cadence/contracts/FlowALPv0.cdc +++ b/cadence/contracts/FlowALPv0.cdc @@ -39,60 +39,6 @@ access(all) contract FlowALPv0 { /* --- CONSTRUCTS & INTERNAL METHODS ---- */ -<<<<<<< HEAD - /// ERebalance - /// - /// Entitlement for rebalancing positions. - access(all) entitlement ERebalance - - /// EGovernance - /// - /// Entitlement for governance operations that control pool-wide parameters and configuration. - /// This entitlement grants access to administrative functions that affect the entire pool, - /// including liquidation settings, token support, interest rates, and protocol parameters. - /// - /// This entitlement should be granted only to trusted governance entities that manage - /// the protocol's risk parameters and operational settings. - access(all) entitlement EGovernance - - /// EImplementation - /// - /// Entitlement for internal implementation operations that maintain the pool's state - /// and process asynchronous updates. This entitlement grants access to low-level state - /// management functions used by the protocol's internal mechanisms. - /// - /// This entitlement is used internally by the protocol to maintain state consistency - /// and process queued operations. It should not be granted to external users. - access(all) entitlement EImplementation - - /// EPosition - /// - /// Entitlement for managing positions within the pool. - /// This entitlement grants access to position-specific operations including deposits, withdrawals, - /// rebalancing, and health parameter management for any position in the pool. - /// - /// IMPORTANT: this entitlement provides access to ALL positions in the pool and is for internal - /// implementation use only! Users access their individual positions via the Position resource. - access(all) entitlement EPosition - - - /// EParticipant - /// - /// Entitlement for general participant operations that allow users to interact with the pool - /// at a basic level. This entitlement grants access to position creation and basic deposit - /// operations without requiring full position ownership. - /// - /// This entitlement is more permissive than EPosition and allows anyone to create positions - /// and make deposits, enabling public participation in the protocol while maintaining - /// separation between position creation and position management. - access(all) entitlement EParticipant - - /// Grants access to configure drawdown sinks, top-up sources, and other position settings, for the Position resource. - /// Withdrawal access is provided using FungibleToken.Withdraw. - access(all) entitlement EPositionAdmin - -======= ->>>>>>> main /* --- NUMERIC TYPES POLICY --- - External/public APIs (Vault amounts, deposits/withdrawals, events) use UFix64. - Internal accounting and risk math use UFix128: scaled/true balances, interest indices/rates, diff --git a/cadence/tests/contracts/AdversarialReentrancyConnectors.cdc b/cadence/tests/contracts/AdversarialReentrancyConnectors.cdc index 7d11167f..054761b5 100644 --- a/cadence/tests/contracts/AdversarialReentrancyConnectors.cdc +++ b/cadence/tests/contracts/AdversarialReentrancyConnectors.cdc @@ -4,6 +4,7 @@ import "FungibleTokenMetadataViews" import "DeFiActionsUtils" import "DeFiActions" import "FlowALPv0" +import "FlowALPPositionResources" import "FlowALPModels" import "MOET" @@ -107,32 +108,18 @@ access(all) contract AdversarialReentrancyConnectors { } access(all) resource LiveData { -<<<<<<< HEAD /// Capability to the attacker's PositionManager for recursive withdrawal - access(all) var positionManagerCap: Capability? + access(all) var positionManagerCap: Capability? /// Position ID for recursive withdrawal access(all) var recursivePositionID: UInt64? init() { self.recursivePositionID = nil; self.positionManagerCap = nil } access(all) fun setRecursivePosition( - managerCap: Capability, + managerCap: Capability, pid: UInt64 ) { self.positionManagerCap = managerCap self.recursivePositionID = pid -======= - /// Optional: Pool capability for recursive withdrawAndPull call - access(all) var recursivePool: Capability? - /// Optional: Position ID for recursive withdrawAndPull call - access(all) var recursivePositionID: UInt64? - - init() { self.recursivePositionID = nil; self.recursivePool = nil } - access(all) fun setRecursivePool(_ pool: Capability) { - self.recursivePool = pool - } - access(all) fun setRecursivePositionID(_ positionID: UInt64) { - self.recursivePositionID = positionID ->>>>>>> main } } access(all) fun createLiveData(): @LiveData { diff --git a/cadence/tests/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc b/cadence/tests/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc index c047906e..d69d800a 100644 --- a/cadence/tests/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc +++ b/cadence/tests/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc @@ -4,25 +4,15 @@ import "FlowALPModels" transaction(adminAddr: Address) { prepare(user: auth(SaveValue, LoadValue, ClaimInboxCapability) &Account) { -<<<<<<< HEAD let claimed = user.inbox.claim< - auth(FlowALPv0.EParticipant) &FlowALPv0.Pool -======= - let claimed: Capability = - user.inbox.claim< - auth(FlowALPModels.EParticipant, FlowALPModels.EPosition) &FlowALPv0.Pool ->>>>>>> main + auth(FlowALPModels.EParticipant) &FlowALPv0.Pool >("FlowALPv0BetaCap", provider: adminAddr) ?? panic("No beta capability found in inbox") if user.storage.type(at: FlowALPv0.PoolCapStoragePath) != nil { let _ = user.storage.load< -<<<<<<< HEAD - Capability -======= - Capability ->>>>>>> main + Capability >(from: FlowALPv0.PoolCapStoragePath) } user.storage.save(claimed, to: FlowALPv0.PoolCapStoragePath) diff --git a/cadence/tests/transactions/flow-alp/beta/publish_beta_cap.cdc b/cadence/tests/transactions/flow-alp/beta/publish_beta_cap.cdc index 72a55f30..7be8936c 100644 --- a/cadence/tests/transactions/flow-alp/beta/publish_beta_cap.cdc +++ b/cadence/tests/transactions/flow-alp/beta/publish_beta_cap.cdc @@ -4,15 +4,9 @@ import "FlowALPModels" transaction(grantee: Address) { prepare(admin: auth(IssueStorageCapabilityController, PublishInboxCapability) &Account) { -<<<<<<< HEAD let poolCap = admin.capabilities.storage.issue< - auth(FlowALPv0.EParticipant) &FlowALPv0.Pool -======= - let poolCap: Capability = - admin.capabilities.storage.issue< - auth(FlowALPModels.EParticipant, FlowALPModels.EPosition) &FlowALPv0.Pool ->>>>>>> main + auth(FlowALPModels.EParticipant) &FlowALPv0.Pool >(FlowALPv0.PoolStoragePath) assert(poolCap.check(), message: "Failed to issue beta capability") diff --git a/cadence/tests/transactions/flow-alp/pool-management/03_grant_beta.cdc b/cadence/tests/transactions/flow-alp/pool-management/03_grant_beta.cdc index e722e7e9..362efcfc 100644 --- a/cadence/tests/transactions/flow-alp/pool-management/03_grant_beta.cdc +++ b/cadence/tests/transactions/flow-alp/pool-management/03_grant_beta.cdc @@ -7,24 +7,14 @@ transaction() { admin: auth(Capabilities, Storage) &Account, tester: auth(Storage) &Account ) { -<<<<<<< HEAD let poolCap = admin.capabilities.storage.issue< - auth(FlowALPv0.EParticipant) &FlowALPv0.Pool -======= - let poolCap: Capability = - admin.capabilities.storage.issue< - auth(FlowALPModels.EParticipant, FlowALPModels.EPosition) &FlowALPv0.Pool ->>>>>>> main + auth(FlowALPModels.EParticipant) &FlowALPv0.Pool >(FlowALPv0.PoolStoragePath) // assert(poolCap.check(), message: "Failed to issue Pool capability") if tester.storage.type(at: FlowALPv0.PoolCapStoragePath) != nil { -<<<<<<< HEAD - tester.storage.load>( -======= - tester.storage.load>( ->>>>>>> main + tester.storage.load>( from: FlowALPv0.PoolCapStoragePath ) } diff --git a/cadence/tests/transactions/flow-alp/pool-management/04_create_position.cdc b/cadence/tests/transactions/flow-alp/pool-management/04_create_position.cdc index 2a640430..8894db2a 100644 --- a/cadence/tests/transactions/flow-alp/pool-management/04_create_position.cdc +++ b/cadence/tests/transactions/flow-alp/pool-management/04_create_position.cdc @@ -9,11 +9,7 @@ import "DummyConnectors" transaction { prepare(admin: auth(BorrowValue, Storage, Capabilities) &Account) { -<<<<<<< HEAD - let pool = admin.storage.borrow(from: FlowALPv0.PoolStoragePath) -======= - let pool = admin.storage.borrow(from: FlowALPv0.PoolStoragePath) ->>>>>>> main + let pool = admin.storage.borrow(from: FlowALPv0.PoolStoragePath) // Ensure PositionManager exists if admin.storage.borrow<&FlowALPPositionResources.PositionManager>(from: FlowALPv0.PositionStoragePath) == nil { diff --git a/cadence/tests/transactions/position-manager/create_position_reentrancy.cdc b/cadence/tests/transactions/position-manager/create_position_reentrancy.cdc index d4da2115..d611d650 100644 --- a/cadence/tests/transactions/position-manager/create_position_reentrancy.cdc +++ b/cadence/tests/transactions/position-manager/create_position_reentrancy.cdc @@ -29,11 +29,7 @@ transaction(amount: UFix64, vaultStoragePath: StoragePath, pushToDrawDownSink: B // the position manager in the signer's account where we should store the new position let positionManager: auth(FlowALPModels.EPositionAdmin) &FlowALPPositionResources.PositionManager // the authorized Pool capability -<<<<<<< HEAD - let poolCap: Capability -======= - let poolCap: Capability ->>>>>>> main + let poolCap: Capability // reference to signer's account for saving capability back let signerAccount: auth(LoadValue, BorrowValue, SaveValue, IssueStorageCapabilityController, PublishCapability, UnpublishCapability) &Account @@ -87,11 +83,7 @@ transaction(amount: UFix64, vaultStoragePath: StoragePath, pushToDrawDownSink: B ?? panic("PositionManager not found") // Load the authorized Pool capability from storage -<<<<<<< HEAD - self.poolCap = signer.storage.load>( -======= - self.poolCap = signer.storage.load>( ->>>>>>> main + self.poolCap = signer.storage.load>( from: FlowALPv0.PoolCapStoragePath ) ?? panic("Could not load Pool capability from storage - ensure the signer has been granted Pool access with EParticipant entitlement") } @@ -115,7 +107,7 @@ transaction(amount: UFix64, vaultStoragePath: StoragePath, pushToDrawDownSink: B let liveData = sourceRef.liveDataCap.borrow() ?? panic("cant borrow LiveData") let managerCap = self.signerAccount.capabilities.storage.issue< - auth(FungibleToken.Withdraw, FlowALPv0.EPositionAdmin) &FlowALPv0.PositionManager + auth(FungibleToken.Withdraw, FlowALPModels.EPositionAdmin) &FlowALPPositionResources.PositionManager >(FlowALPv0.PositionStoragePath) liveData.setRecursivePosition(managerCap: managerCap, pid: pid) diff --git a/cadence/tests/transactions/position-manager/create_position_spoofing_source.cdc b/cadence/tests/transactions/position-manager/create_position_spoofing_source.cdc index 67c1a4c5..6add45fc 100644 --- a/cadence/tests/transactions/position-manager/create_position_spoofing_source.cdc +++ b/cadence/tests/transactions/position-manager/create_position_spoofing_source.cdc @@ -30,11 +30,7 @@ transaction(amount: UFix64, vaultStoragePath: StoragePath, pushToDrawDownSink: B // the position manager in the signer's account where we should store the new position let positionManager: auth(FlowALPModels.EPositionAdmin) &FlowALPPositionResources.PositionManager // the authorized Pool capability -<<<<<<< HEAD - let poolCap: Capability -======= - let poolCap: Capability ->>>>>>> main + let poolCap: Capability // reference to signer's account for saving capability back let signerAccount: auth(LoadValue,BorrowValue, SaveValue, IssueStorageCapabilityController, PublishCapability, UnpublishCapability) &Account @@ -87,11 +83,7 @@ transaction(amount: UFix64, vaultStoragePath: StoragePath, pushToDrawDownSink: B ?? panic("PositionManager not found") // Load the authorized Pool capability from storage -<<<<<<< HEAD - self.poolCap = signer.storage.load>( -======= - self.poolCap = signer.storage.load>( ->>>>>>> main + self.poolCap = signer.storage.load>( from: FlowALPv0.PoolCapStoragePath ) ?? panic("Could not load Pool capability from storage - ensure the signer has been granted Pool access with EParticipant entitlement") } diff --git a/cadence/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc b/cadence/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc index daba5f17..fef64e81 100644 --- a/cadence/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc +++ b/cadence/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc @@ -6,24 +6,14 @@ transaction(adminAddr: Address) { prepare(user: auth(SaveValue, LoadValue, ClaimInboxCapability) &Account) { // Save claimed cap at the protocol-defined storage path to satisfy consumers/tests expecting this path let capPath = FlowALPv0.PoolCapStoragePath -<<<<<<< HEAD let claimed = user.inbox.claim< - auth(FlowALPv0.EParticipant) &FlowALPv0.Pool -======= - let claimed: Capability = - user.inbox.claim< - auth(FlowALPModels.EParticipant, FlowALPModels.EPosition) &FlowALPv0.Pool ->>>>>>> main + auth(FlowALPModels.EParticipant) &FlowALPv0.Pool >("FlowALPv0BetaCap", provider: adminAddr) ?? panic("No beta capability found in inbox") if user.storage.type(at: capPath) != nil { -<<<<<<< HEAD - let _ = user.storage.load>(from: capPath) -======= - let _ = user.storage.load>(from: capPath) ->>>>>>> main + let _ = user.storage.load>(from: capPath) } user.storage.save(claimed, to: capPath) } diff --git a/cadence/transactions/flow-alp/beta/publish_beta_cap.cdc b/cadence/transactions/flow-alp/beta/publish_beta_cap.cdc index 792fe703..9dd57240 100644 --- a/cadence/transactions/flow-alp/beta/publish_beta_cap.cdc +++ b/cadence/transactions/flow-alp/beta/publish_beta_cap.cdc @@ -4,15 +4,9 @@ import "FlowALPModels" transaction(grantee: Address) { prepare(admin: auth(IssueStorageCapabilityController, PublishInboxCapability) &Account) { -<<<<<<< HEAD let poolCap = admin.capabilities.storage.issue< - auth(FlowALPv0.EParticipant) &FlowALPv0.Pool -======= - let poolCap: Capability = - admin.capabilities.storage.issue< - auth(FlowALPModels.EParticipant, FlowALPModels.EPosition) &FlowALPv0.Pool ->>>>>>> main + auth(FlowALPModels.EParticipant) &FlowALPv0.Pool >(FlowALPv0.PoolStoragePath) assert(poolCap.check(), message: "Failed to issue beta capability") diff --git a/cadence/transactions/flow-alp/pool-management/rebalance_position.cdc b/cadence/transactions/flow-alp/pool-management/rebalance_position.cdc index 2a4416bc..15bcb94d 100644 --- a/cadence/transactions/flow-alp/pool-management/rebalance_position.cdc +++ b/cadence/transactions/flow-alp/pool-management/rebalance_position.cdc @@ -8,17 +8,10 @@ import "FlowALPModels" /// the position is beyond its min/max health. If `true`, the rebalance executes regardless of its relative health. /// transaction(pid: UInt64, force: Bool) { -<<<<<<< HEAD - let pool: auth(FlowALPv0.ERebalance) &FlowALPv0.Pool + let pool: auth(FlowALPModels.ERebalance) &FlowALPv0.Pool prepare(signer: auth(BorrowValue) &Account) { - self.pool = signer.storage.borrow(from: FlowALPv0.PoolStoragePath) -======= - let pool: auth(FlowALPModels.EPosition) &FlowALPv0.Pool - - prepare(signer: auth(BorrowValue) &Account) { - self.pool = signer.storage.borrow(from: FlowALPv0.PoolStoragePath) ->>>>>>> main + self.pool = signer.storage.borrow(from: FlowALPv0.PoolStoragePath) ?? panic("Could not borrow reference to Pool from \(FlowALPv0.PoolStoragePath) - ensure a Pool has been configured") } diff --git a/cadence/transactions/flow-alp/position/create_position.cdc b/cadence/transactions/flow-alp/position/create_position.cdc index aad260bd..e50585f7 100644 --- a/cadence/transactions/flow-alp/position/create_position.cdc +++ b/cadence/transactions/flow-alp/position/create_position.cdc @@ -22,11 +22,7 @@ transaction(amount: UFix64, vaultStoragePath: StoragePath, pushToDrawDownSink: B // the position manager in the signer's account where we should store the new position let positionManager: auth(FlowALPModels.EPositionAdmin) &FlowALPPositionResources.PositionManager // the authorized Pool capability -<<<<<<< HEAD - let poolCap: Capability -======= - let poolCap: Capability ->>>>>>> main + let poolCap: Capability // reference to signer's account for saving capability back let signerAccount: auth(Storage) &Account @@ -82,11 +78,7 @@ transaction(amount: UFix64, vaultStoragePath: StoragePath, pushToDrawDownSink: B ?? panic("PositionManager not found") // Load the authorized Pool capability from storage -<<<<<<< HEAD - self.poolCap = signer.storage.load>( -======= - self.poolCap = signer.storage.load>( ->>>>>>> main + self.poolCap = signer.storage.load>( from: FlowALPv0.PoolCapStoragePath ) ?? panic("Could not load Pool capability from storage - ensure the signer has been granted Pool access with EParticipant entitlement") } diff --git a/cadence/transactions/flow-alp/position/create_position_not_managed.cdc b/cadence/transactions/flow-alp/position/create_position_not_managed.cdc index af2ccabe..626496a0 100644 --- a/cadence/transactions/flow-alp/position/create_position_not_managed.cdc +++ b/cadence/transactions/flow-alp/position/create_position_not_managed.cdc @@ -19,11 +19,7 @@ transaction(amount: UFix64, vaultStoragePath: StoragePath, pushToDrawDownSink: B // this DeFiActions Source that will allow for the repayment of a loan if the position becomes undercollateralized let source: {DeFiActions.Source} // the authorized Pool capability -<<<<<<< HEAD - let poolCap: Capability -======= - let poolCap: Capability ->>>>>>> main + let poolCap: Capability // reference to signer's account for saving capability back let signerAccount: auth(Storage) &Account @@ -64,11 +60,7 @@ transaction(amount: UFix64, vaultStoragePath: StoragePath, pushToDrawDownSink: B ) // Load the authorized Pool capability from storage -<<<<<<< HEAD - self.poolCap = signer.storage.load>( -======= - self.poolCap = signer.storage.load>( ->>>>>>> main + self.poolCap = signer.storage.load>( from: FlowALPv0.PoolCapStoragePath ) ?? panic("Could not load Pool capability from storage - ensure the signer has been granted Pool access with EParticipant entitlement") } diff --git a/flow.json b/flow.json index 154d3e71..3289b703 100644 --- a/flow.json +++ b/flow.json @@ -3,15 +3,15 @@ "AdversarialReentrancyConnectors": { "source": "./cadence/tests/contracts/AdversarialReentrancyConnectors.cdc", "aliases": { - "testing": "0000000000000008", - "mainnet-fork": "6b00ff876c299c61" + "mainnet-fork": "6b00ff876c299c61", + "testing": "0000000000000008" } }, "AdversarialTypeSpoofingConnectors": { "source": "./cadence/tests/contracts/AdversarialTypeSpoofingConnectors.cdc", "aliases": { - "testing": "0000000000000008", - "mainnet-fork": "6b00ff876c299c61" + "mainnet-fork": "6b00ff876c299c61", + "testing": "0000000000000008" } }, "BandOracleConnectors": { @@ -45,167 +45,167 @@ "DummyConnectors": { "source": "./cadence/contracts/mocks/DummyConnectors.cdc", "aliases": { - "testing": "0000000000000007", - "mainnet-fork": "6d888f175c158410" + "mainnet-fork": "6d888f175c158410", + "testing": "0000000000000007" } }, "ExampleToken1": { "source": "./cadence/tests/contracts/ExampleToken1.cdc", "aliases": { - "testing": "0000000000000008", + "mainnet": "6b00ff876c299c61", "mainnet-fork": "6b00ff876c299c61", - "mainnet": "6b00ff876c299c61" + "testing": "0000000000000008" } }, "ExampleToken2": { "source": "./cadence/tests/contracts/ExampleToken2.cdc", "aliases": { - "testing": "0000000000000008", + "mainnet": "6b00ff876c299c61", "mainnet-fork": "6b00ff876c299c61", - "mainnet": "6b00ff876c299c61" + "testing": "0000000000000008" } }, "FlowALPEvents": { "source": "./cadence/contracts/FlowALPEvents.cdc", "aliases": { - "testing": "0000000000000007", - "mainnet-fork": "6b00ff876c299c61" + "mainnet-fork": "6b00ff876c299c61", + "testing": "0000000000000007" + } + }, + "FlowALPHealth": { + "source": "./cadence/contracts/FlowALPHealth.cdc", + "aliases": { + "mainnet-fork": "6b00ff876c299c61", + "testing": "0000000000000007" } }, "FlowALPInterestRates": { "source": "./cadence/contracts/FlowALPInterestRates.cdc", "aliases": { - "testing": "0000000000000007", - "mainnet-fork": "6b00ff876c299c61" + "mainnet-fork": "6b00ff876c299c61", + "testing": "0000000000000007" } }, - "FlowALPHealth": { - "source": "./cadence/contracts/FlowALPHealth.cdc", + "FlowALPMath": { + "source": "./cadence/lib/FlowALPMath.cdc", "aliases": { - "testing": "0000000000000007", - "mainnet-fork": "6b00ff876c299c61" + "mainnet": "6d888f175c158410", + "testing": "0000000000000007" } }, "FlowALPModels": { "source": "./cadence/contracts/FlowALPModels.cdc", "aliases": { - "testing": "0000000000000007", - "mainnet-fork": "6b00ff876c299c61" + "mainnet-fork": "6b00ff876c299c61", + "testing": "0000000000000007" } }, "FlowALPPositionResources": { "source": "./cadence/contracts/FlowALPPositionResources.cdc", "aliases": { - "testing": "0000000000000007", - "mainnet-fork": "6b00ff876c299c61" - } - }, - "FlowALPMath": { - "source": "./cadence/lib/FlowALPMath.cdc", - "aliases": { - "testing": "0000000000000007", - "mainnet": "6d888f175c158410" + "mainnet-fork": "6b00ff876c299c61", + "testing": "0000000000000007" } }, "FlowALPRebalancerPaidv1": { "source": "./cadence/contracts/FlowALPRebalancerPaidv1.cdc", "aliases": { - "testing": "0000000000000007", - "mainnet-fork": "6b00ff876c299c61" + "mainnet-fork": "6b00ff876c299c61", + "testing": "0000000000000007" } }, "FlowALPRebalancerv1": { "source": "./cadence/contracts/FlowALPRebalancerv1.cdc", "aliases": { - "testing": "0000000000000007", - "mainnet-fork": "6b00ff876c299c61" + "mainnet-fork": "6b00ff876c299c61", + "testing": "0000000000000007" } }, "FlowALPSupervisorv1": { "source": "./cadence/contracts/FlowALPSupervisorv1.cdc", "aliases": { - "testing": "0000000000000007", - "mainnet-fork": "6b00ff876c299c61" + "mainnet-fork": "6b00ff876c299c61", + "testing": "0000000000000007" } }, "FlowALPv0": { "source": "./cadence/contracts/FlowALPv0.cdc", "aliases": { - "testing": "0000000000000007", "mainnet": "6b00ff876c299c61", - "mainnet-fork": "6b00ff876c299c61" + "mainnet-fork": "6b00ff876c299c61", + "testing": "0000000000000007" } }, "FungibleTokenConnectors": { "source": "./FlowActions/cadence/contracts/connectors/FungibleTokenConnectors.cdc", "aliases": { - "testing": "0000000000000006", + "mainnet": "6b00ff876c299c61", "mainnet-fork": "6b00ff876c299c61", - "mainnet": "6b00ff876c299c61" + "testing": "0000000000000006" } }, "MOET": { "source": "./cadence/contracts/MOET.cdc", "aliases": { - "testing": "0000000000000007", - "mainnet": "6b00ff876c299c61" + "mainnet": "6b00ff876c299c61", + "testing": "0000000000000007" } }, "MockDexSwapper": { "source": "./cadence/contracts/mocks/MockDexSwapper.cdc", "aliases": { - "testing": "0000000000000007", - "testnet": "d2580caf2ef07c2f", "mainnet": "6b00ff876c299c61", - "mainnet-fork": "6b00ff876c299c61" + "mainnet-fork": "6b00ff876c299c61", + "testing": "0000000000000007", + "testnet": "d2580caf2ef07c2f" } }, "MockOracle": { "source": "./cadence/contracts/mocks/MockOracle.cdc", "aliases": { - "testing": "0000000000000007", - "testnet": "d2580caf2ef07c2f", "mainnet": "6b00ff876c299c61", - "mainnet-fork": "6b00ff876c299c61" + "mainnet-fork": "6b00ff876c299c61", + "testing": "0000000000000007", + "testnet": "d2580caf2ef07c2f" } }, "MockYieldToken": { "source": "./cadence/contracts/mocks/MockYieldToken.cdc", "aliases": { - "testing": "0000000000000007", - "mainnet-fork": "6b00ff876c299c61" + "mainnet-fork": "6b00ff876c299c61", + "testing": "0000000000000007" } }, "MultiMockOracle": { "source": "./cadence/tests/contracts/MultiMockOracle.cdc", "aliases": { - "testing": "0000000000000007", + "mainnet": "6b00ff876c299c61", "mainnet-fork": "6b00ff876c299c61", - "mainnet": "6b00ff876c299c61" + "testing": "0000000000000007" } }, "OracleStorage": { "source": "./cadence/tests/contracts/OracleStorage.cdc", "aliases": { - "testing": "0000000000000008", + "mainnet": "6b00ff876c299c61", "mainnet-fork": "6b00ff876c299c61", - "mainnet": "6b00ff876c299c61" + "testing": "0000000000000008" } }, "PriceOracleAggregatorv1": { "source": "./cadence/contracts/PriceOracleAggregatorv1.cdc", "aliases": { - "testing": "0000000000000007", + "mainnet": "6b00ff876c299c61", "mainnet-fork": "6b00ff876c299c61", - "mainnet": "6b00ff876c299c61" + "testing": "0000000000000007" } }, "PriceOracleRouterv1": { "source": "./cadence/contracts/PriceOracleRouterv1.cdc", "aliases": { - "testing": "0000000000000007", + "mainnet": "6b00ff876c299c61", "mainnet-fork": "6b00ff876c299c61", - "mainnet": "6b00ff876c299c61" + "testing": "0000000000000007" } } }, @@ -515,4 +515,4 @@ ] } } -} +} \ No newline at end of file From d1488a56f6f874ce7b8e7213aa129c1414cd4621 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 25 Mar 2026 12:59:45 -0700 Subject: [PATCH 7/8] remove files deleted on main --- .../flow-alp/beta/claim_and_save_beta_cap.cdc | 22 ----------- .../flow-alp/beta/publish_beta_cap.cdc | 18 --------- .../pool-management/04_create_position.cdc | 39 ------------------- 3 files changed, 79 deletions(-) delete mode 100644 cadence/tests/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc delete mode 100644 cadence/tests/transactions/flow-alp/beta/publish_beta_cap.cdc delete mode 100644 cadence/tests/transactions/flow-alp/pool-management/04_create_position.cdc diff --git a/cadence/tests/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc b/cadence/tests/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc deleted file mode 100644 index d69d800a..00000000 --- a/cadence/tests/transactions/flow-alp/beta/claim_and_save_beta_cap.cdc +++ /dev/null @@ -1,22 +0,0 @@ -import "FlowALPv0" -import "FlowALPModels" - -transaction(adminAddr: Address) { - - prepare(user: auth(SaveValue, LoadValue, ClaimInboxCapability) &Account) { - let claimed = - user.inbox.claim< - auth(FlowALPModels.EParticipant) &FlowALPv0.Pool - >("FlowALPv0BetaCap", provider: adminAddr) - ?? panic("No beta capability found in inbox") - - if user.storage.type(at: FlowALPv0.PoolCapStoragePath) != nil { - let _ = user.storage.load< - Capability - >(from: FlowALPv0.PoolCapStoragePath) - } - user.storage.save(claimed, to: FlowALPv0.PoolCapStoragePath) - } -} - - diff --git a/cadence/tests/transactions/flow-alp/beta/publish_beta_cap.cdc b/cadence/tests/transactions/flow-alp/beta/publish_beta_cap.cdc deleted file mode 100644 index 7be8936c..00000000 --- a/cadence/tests/transactions/flow-alp/beta/publish_beta_cap.cdc +++ /dev/null @@ -1,18 +0,0 @@ -import "FlowALPv0" -import "FlowALPModels" - -transaction(grantee: Address) { - - prepare(admin: auth(IssueStorageCapabilityController, PublishInboxCapability) &Account) { - let poolCap = - admin.capabilities.storage.issue< - auth(FlowALPModels.EParticipant) &FlowALPv0.Pool - >(FlowALPv0.PoolStoragePath) - - assert(poolCap.check(), message: "Failed to issue beta capability") - - admin.inbox.publish(poolCap, name: "FlowALPv0BetaCap", recipient: grantee) - } -} - - diff --git a/cadence/tests/transactions/flow-alp/pool-management/04_create_position.cdc b/cadence/tests/transactions/flow-alp/pool-management/04_create_position.cdc deleted file mode 100644 index 8894db2a..00000000 --- a/cadence/tests/transactions/flow-alp/pool-management/04_create_position.cdc +++ /dev/null @@ -1,39 +0,0 @@ -import "FungibleToken" -import "DeFiActions" -import "DeFiActionsUtils" -import "FlowALPv0" -import "FlowALPPositionResources" -import "FlowALPModels" -import "MOET" -import "DummyConnectors" - -transaction { - prepare(admin: auth(BorrowValue, Storage, Capabilities) &Account) { - let pool = admin.storage.borrow(from: FlowALPv0.PoolStoragePath) - - // Ensure PositionManager exists - if admin.storage.borrow<&FlowALPPositionResources.PositionManager>(from: FlowALPv0.PositionStoragePath) == nil { - let manager <- FlowALPv0.createPositionManager() - admin.storage.save(<-manager, to: FlowALPv0.PositionStoragePath) - } - - // Call EParticipant-gated methods - let zero1 <- DeFiActionsUtils.getEmptyVault(Type<@MOET.Vault>()) - let position <- pool.createPosition( - funds: <- zero1, - issuanceSink: DummyConnectors.DummySink(), - repaymentSource: nil, - pushToDrawDownSink: false - ) - - let pid = position.id - - // Add position to manager - let manager = admin.storage.borrow<&FlowALPPositionResources.PositionManager>(from: FlowALPv0.PositionStoragePath)! - manager.addPosition(position: <-position) - - // Also allowed with EParticipant: - let zero2 <- DeFiActionsUtils.getEmptyVault(Type<@MOET.Vault>()) - pool.depositToPosition(pid: pid, from: <- zero2) - } -} From 50abec35f17a5359406f68fb51a8335a86e36e08 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 26 Mar 2026 11:10:51 -0700 Subject: [PATCH 8/8] Remove unused EPosition cap tests and fix flaky supervisor stale UUID test Remove EPosition and EParticipantPosition test sections from cap_test.cdc since EPosition-entitled pool capabilities are not used. Fix flaky test_supervisor_stale_uuid_does_not_panic by letting the initial cron tick fire before setting up stale state, avoiding a race with the scheduler. Co-Authored-By: Claude Opus 4.6 (1M context) --- cadence/tests/cap_test.cdc | 253 +----------------- cadence/tests/paid_auto_balance_test.cdc | 13 +- .../eposition/deposit_and_push_any.cdc | 28 -- .../flow-alp/eposition/lock_any.cdc | 22 -- .../eposition/rebalance_position_via_cap.cdc | 23 -- .../eposition/withdraw_and_pull_any.cdc | 33 --- .../flow-alp/eposition/withdraw_any.cdc | 28 -- .../flow-alp/setup/grant_eposition_cap.cdc | 25 -- 8 files changed, 13 insertions(+), 412 deletions(-) delete mode 100644 cadence/tests/transactions/flow-alp/eposition/deposit_and_push_any.cdc delete mode 100644 cadence/tests/transactions/flow-alp/eposition/lock_any.cdc delete mode 100644 cadence/tests/transactions/flow-alp/eposition/rebalance_position_via_cap.cdc delete mode 100644 cadence/tests/transactions/flow-alp/eposition/withdraw_and_pull_any.cdc delete mode 100644 cadence/tests/transactions/flow-alp/eposition/withdraw_any.cdc delete mode 100644 cadence/tests/transactions/flow-alp/setup/grant_eposition_cap.cdc diff --git a/cadence/tests/cap_test.cdc b/cadence/tests/cap_test.cdc index 0cf9491f..d58c2a54 100644 --- a/cadence/tests/cap_test.cdc +++ b/cadence/tests/cap_test.cdc @@ -22,15 +22,6 @@ import "test_helpers.cdc" // Published via the FIXED publish_beta_cap.cdc. // Cap stored at FlowALPv0.PoolCapStoragePath. // -// ePositionUser — Capability -// EPosition-only capability; can perform pool-level position -// ops on any position by ID. No EParticipant. -// Cap stored at FlowALPv0.PoolCapStoragePath. -// -// eParticipantPositionUser — Capability over-grant -// Current (unfixed) beta cap — grants EPosition unnecessarily. -// Cap stored at FlowALPv0.PoolCapStoragePath. -// // eRebalanceUser — Capability // Narrowly-scoped cap for rebalancer contracts. // Cap stored at FlowALPv0.PoolCapStoragePath. @@ -52,7 +43,7 @@ import "test_helpers.cdc" // ============================================================================= -// Position created for PROTOCOL_ACCOUNT in setup — used as target for EPosition tests. +// Position created for PROTOCOL_ACCOUNT in setup — used as target for ERebalance tests. access(all) var setupPid: UInt64 = 0 access(all) var ePositionAdminPid: UInt64 = 0 @@ -61,8 +52,6 @@ access(all) var snapshot: UInt64 = 0 // Role accounts access(all) var userWithoutCap = Test.createAccount() access(all) var eParticipantUser = Test.createAccount() -access(all) var ePositionUser = Test.createAccount() -access(all) var eParticipantPositionUser = Test.createAccount() access(all) var eRebalanceUser = Test.createAccount() access(all) var ePositionAdminUser = Test.createAccount() access(all) var eGovernanceUser = Test.createAccount() @@ -71,7 +60,7 @@ access(all) var eGovernanceUser = Test.createAccount() /// Used in negative tests to verify governance methods are inaccessible to them. access(all) fun getNonGovernanceUsers(): [Test.TestAccount] { - return [eParticipantUser, ePositionUser, eParticipantPositionUser, eRebalanceUser, ePositionAdminUser] + return [eParticipantUser, eRebalanceUser, ePositionAdminUser] } access(all) @@ -150,28 +139,6 @@ fun setup() { Test.beSucceeded() ) - // ───────────────────────────────────────────────────────────────────────── - // EPosition user — EPosition-ONLY capability (no EParticipant) - // ───────────────────────────────────────────────────────────────────────── - setupMoetVault(ePositionUser, beFailed: false) - mintMoet(signer: PROTOCOL_ACCOUNT, to: ePositionUser.address, amount: 100.0, beFailed: false) - Test.expect( - _execute2Signers( - "../tests/transactions/flow-alp/setup/grant_eposition_cap.cdc", - [], - PROTOCOL_ACCOUNT, - ePositionUser - ), - Test.beSucceeded() - ) - - // ───────────────────────────────────────────────────────────────────────── - // EParticipantPosition user — EParticipant+EPosition capability (current over-grant) - // ───────────────────────────────────────────────────────────────────────── - setupMoetVault(eParticipantPositionUser, beFailed: false) - mintMoet(signer: PROTOCOL_ACCOUNT, to: eParticipantPositionUser.address, amount: 100.0, beFailed: false) - grantBetaPoolParticipantAccess(PROTOCOL_ACCOUNT, eParticipantPositionUser) - // ───────────────────────────────────────────────────────────────────────── // ERebalance user — ERebalance-only capability (rebalancer simulation) // ───────────────────────────────────────────────────────────────────────── @@ -277,222 +244,6 @@ fun testEParticipant_CreateAndDeposit() { Test.assertEqual(6.0, creditBalance) } -// ============================================================================= -// EParticipant+EPosition — over-grant (current beta cap via publish_beta_cap.cdc) -// ============================================================================= -// -// Actor: eParticipantPositionUser — Capability -// Issued by publish_beta_cap.cdc and stored at FlowALPv0.PoolCapStoragePath. -// This is the CURRENT (unfixed) beta cap. EPosition is NOT needed for normal -// user actions; its presence lets this actor perform pool-level position ops -// on ANY position, including positions owned by other accounts. -// -// Matrix rows: createPosition (EParticipant), depositToPosition (EParticipant), -// withdraw [OVERGRANT], withdrawAndPull [OVERGRANT], depositAndPush [OVERGRANT], -// lockPosition [OVERGRANT], unlockPosition [OVERGRANT], rebalancePosition [OVERGRANT], -// rebalance (Position) [OVERGRANT — same entry point as rebalancePosition] -// -// The [OVERGRANT] rows confirm the security issue: a normal beta user can operate on -// positions they do not own (setupPid is owned by PROTOCOL_ACCOUNT). - -/// Over-granted beta cap still allows EParticipant operations (createPosition, depositToPosition). -access(all) -fun testEParticipantPosition_CreateAndDeposit() { - safeReset() - - let result = _executeTransaction( - "../tests/transactions/flow-alp/eparticipant/create_and_deposit_via_cap.cdc", - [], - eParticipantPositionUser - ) - Test.expect(result, Test.beSucceeded()) - - // Verify position was created and funded: create_and_deposit_via_cap.cdc deposits - // 5.0 MOET (createPosition) + 1.0 MOET (depositToPosition) = 6.0 MOET credit. - let newPid = getLastPositionId() - let creditBalance = getCreditBalanceForType( - details: getPositionDetails(pid: newPid, beFailed: false), - vaultType: Type<@MOET.Vault>() - ) - Test.assertEqual(6.0, creditBalance) -} - -/// Over-granted beta cap allows Pool.withdraw on ANY position — including -/// setupPid owned by PROTOCOL_ACCOUNT. -access(all) -fun testEParticipantPosition_WithdrawAnyPosition() { - safeReset() - - let balanceBefore = getBalance(address: eParticipantPositionUser.address, vaultPublicPath: MOET.VaultPublicPath)! - let result = _executeTransaction( - "../tests/transactions/flow-alp/eposition/withdraw_any.cdc", - [setupPid, 1.0], - eParticipantPositionUser - ) - Test.expect(result, Test.beSucceeded()) - let balanceAfter = getBalance(address: eParticipantPositionUser.address, vaultPublicPath: MOET.VaultPublicPath)! - Test.assertEqual(balanceAfter, balanceBefore + 1.0) -} - -/// Over-granted beta cap allows Pool.withdrawAndPull on ANY position — including -/// positions owned by other accounts. -access(all) -fun testEParticipantPosition_WithdrawAndPullAnyPosition() { - safeReset() - - let balanceBefore = getBalance(address: eParticipantPositionUser.address, vaultPublicPath: MOET.VaultPublicPath)! - let result = _executeTransaction( - "../tests/transactions/flow-alp/eposition/withdraw_and_pull_any.cdc", - [setupPid, 1.0], - eParticipantPositionUser - ) - Test.expect(result, Test.beSucceeded()) - let balanceAfter = getBalance(address: eParticipantPositionUser.address, vaultPublicPath: MOET.VaultPublicPath)! - Test.assertEqual(balanceAfter, balanceBefore + 1.0) -} - -/// Over-granted beta cap allows Pool.depositAndPush on ANY position — including -/// positions owned by other accounts. -access(all) -fun testEParticipantPosition_DepositAndPushAnyPosition() { - safeReset() - - let creditBefore = getCreditBalanceForType( - details: getPositionDetails(pid: setupPid, beFailed: false), - vaultType: Type<@MOET.Vault>() - ) - let result = _executeTransaction( - "../tests/transactions/flow-alp/eposition/deposit_and_push_any.cdc", - [setupPid, 1.0], - eParticipantPositionUser - ) - Test.expect(result, Test.beSucceeded()) - let creditAfter = getCreditBalanceForType( - details: getPositionDetails(pid: setupPid, beFailed: false), - vaultType: Type<@MOET.Vault>() - ) - Test.assertEqual(creditBefore + 1.0, creditAfter) -} - -/// Over-granted beta cap allows Pool.lockPosition and Pool.unlockPosition on ANY position — -/// including positions owned by other accounts. -access(all) -fun testEParticipantPosition_LockUnlockAnyPosition() { - safeReset() - - let result = _executeTransaction( - "../tests/transactions/flow-alp/eposition/lock_any.cdc", - [setupPid], - eParticipantPositionUser - ) - Test.expect(result, Test.beSucceeded()) -} - -/// Over-granted beta cap allows Pool.rebalancePosition on any position. -access(all) -fun testEParticipantPosition_RebalancePosition() { - safeReset() - - let result = _executeTransaction( - "../tests/transactions/flow-alp/eposition/rebalance_position_via_cap.cdc", - [setupPid, true], - eParticipantPositionUser - ) - Test.expect(result, Test.beSucceeded()) -} - -// ============================================================================= -// EPosition — narrowly-scoped EPosition-only Pool capability -// ============================================================================= -// -// Actor: ePositionUser — Capability -// Matrix rows: withdraw, withdrawAndPull, depositAndPush, lockPosition, unlockPosition, -// rebalancePosition - -/// EPosition cap allows Pool.withdraw on ANY position by ID — including -/// setupPid owned by PROTOCOL_ACCOUNT. -access(all) -fun testEPosition_WithdrawAnyPosition() { - safeReset() - - let balanceBefore = getBalance(address: ePositionUser.address, vaultPublicPath: MOET.VaultPublicPath)! - let result = _executeTransaction( - "../tests/transactions/flow-alp/eposition/withdraw_any.cdc", - [setupPid, 1.0], - ePositionUser - ) - Test.expect(result, Test.beSucceeded()) - let balanceAfter = getBalance(address: ePositionUser.address, vaultPublicPath: MOET.VaultPublicPath)! - Test.assertEqual(balanceAfter, balanceBefore + 1.0) -} - -/// EPosition cap allows Pool.withdrawAndPull on ANY position — including positions -/// owned by other accounts. -access(all) -fun testEPosition_WithdrawAndPullAnyPosition() { - safeReset() - - let balanceBefore = getBalance(address: ePositionUser.address, vaultPublicPath: MOET.VaultPublicPath)! - let result = _executeTransaction( - "../tests/transactions/flow-alp/eposition/withdraw_and_pull_any.cdc", - [setupPid, 1.0], - ePositionUser - ) - Test.expect(result, Test.beSucceeded()) - let balanceAfter = getBalance(address: ePositionUser.address, vaultPublicPath: MOET.VaultPublicPath)! - Test.assertEqual(balanceAfter, balanceBefore + 1.0) -} - -/// EPosition cap allows Pool.depositAndPush on ANY position — including positions -/// owned by other accounts. -access(all) -fun testEPosition_DepositAndPushAnyPosition() { - safeReset() - - let creditBefore = getCreditBalanceForType( - details: getPositionDetails(pid: setupPid, beFailed: false), - vaultType: Type<@MOET.Vault>() - ) - let result = _executeTransaction( - "../tests/transactions/flow-alp/eposition/deposit_and_push_any.cdc", - [setupPid, 1.0], - ePositionUser - ) - Test.expect(result, Test.beSucceeded()) - let creditAfter = getCreditBalanceForType( - details: getPositionDetails(pid: setupPid, beFailed: false), - vaultType: Type<@MOET.Vault>() - ) - Test.assertEqual(creditBefore + 1.0, creditAfter) -} - -/// EPosition cap allows Pool.lockPosition and Pool.unlockPosition on ANY position — -/// including positions owned by other accounts. -access(all) -fun testEPosition_LockUnlockAnyPosition() { - safeReset() - - let result = _executeTransaction( - "../tests/transactions/flow-alp/eposition/lock_any.cdc", - [setupPid], - ePositionUser - ) - Test.expect(result, Test.beSucceeded()) -} - -/// EPosition cap allows Pool.rebalancePosition. -access(all) -fun testEPosition_RebalancePosition() { - safeReset() - - let result = _executeTransaction( - "../tests/transactions/flow-alp/eposition/rebalance_position_via_cap.cdc", - [setupPid, true], - ePositionUser - ) - Test.expect(result, Test.beSucceeded()) -} - // ============================================================================= // ERebalance — narrowly-scoped rebalancer capability // ============================================================================= diff --git a/cadence/tests/paid_auto_balance_test.cdc b/cadence/tests/paid_auto_balance_test.cdc index 50a92369..7873f9c5 100644 --- a/cadence/tests/paid_auto_balance_test.cdc +++ b/cadence/tests/paid_auto_balance_test.cdc @@ -316,6 +316,15 @@ access(all) fun test_supervisor_executed() { /// fixReschedule(uuid:) force-unwrapped borrowRebalancer(uuid)! which panicked on a stale UUID, /// reverting the whole executeTransaction and blocking recovery for all other rebalancers. access(all) fun test_supervisor_stale_uuid_does_not_panic() { + // Let the initial cron tick fire first (supervisor set is empty, so it does nothing + // except emit Executed). This avoids a race where the cron fires during the add/delete + // transactions below before the stale state is set up. + Test.moveTime(by: 100.0) + Test.commitBlock() + + let initialExecutedEvts = Test.eventsOfType(Type()) + Test.assert(initialExecutedEvts.length >= 1, message: "Initial cron tick should have fired") + // Get the UUID of the paid rebalancer created during setup. let createdEvts = Test.eventsOfType(Type()) Test.assertEqual(1, createdEvts.length) @@ -328,14 +337,14 @@ access(all) fun test_supervisor_stale_uuid_does_not_panic() { // stale UUID in the Supervisor's paidRebalancers set, simulating the FLO-27 bug scenario. deletePaidRebalancer(signer: userAccount, paidRebalancerStoragePath: paidRebalancerStoragePath) - // Advance time to trigger the Supervisor's scheduled tick. + // Advance time to trigger the next Supervisor tick. Test.moveTime(by: 60.0 * 60.0) Test.commitBlock() // The Supervisor must have executed without panicking. If fixReschedule force-unwrapped // the missing rebalancer the entire transaction would revert and Executed would not be emitted. let executedEvts = Test.eventsOfType(Type()) - Test.assert(executedEvts.length >= 1, message: "Supervisor should have executed at least 1 time") + Test.assert(executedEvts.length >= 2, message: "Supervisor should have executed at least 2 times (initial + stale prune)") // The stale UUID must have been pruned from the Supervisor's set. let removedEvts = Test.eventsOfType(Type()) diff --git a/cadence/tests/transactions/flow-alp/eposition/deposit_and_push_any.cdc b/cadence/tests/transactions/flow-alp/eposition/deposit_and_push_any.cdc deleted file mode 100644 index 3855b7fc..00000000 --- a/cadence/tests/transactions/flow-alp/eposition/deposit_and_push_any.cdc +++ /dev/null @@ -1,28 +0,0 @@ -import "FungibleToken" -import "FlowALPv0" -import "FlowALPModels" -import "MOET" - -/// Deposits MOET into any position via an EPosition capability at PoolCapStoragePath. -/// EPosition allows operations on any position by ID, regardless of ownership. -/// -/// @param pid: Target position ID (owned by a different account) -/// @param amount: Amount of MOET to deposit -transaction(pid: UInt64, amount: UFix64) { - let pool: auth(FlowALPModels.EPosition) &FlowALPv0.Pool - let funds: @{FungibleToken.Vault} - - prepare(signer: auth(BorrowValue) &Account) { - let cap = signer.storage.borrow<&Capability>( - from: FlowALPv0.PoolCapStoragePath - ) ?? panic("EPosition capability not found") - self.pool = cap.borrow() ?? panic("Could not borrow Pool with EPosition") - let vault = signer.storage.borrow(from: MOET.VaultStoragePath) - ?? panic("Could not borrow MOET vault with Withdraw entitlement") - self.funds <- vault.withdraw(amount: amount) - } - - execute { - self.pool.depositAndPush(pid: pid, from: <-self.funds, pushToDrawDownSink: false) - } -} diff --git a/cadence/tests/transactions/flow-alp/eposition/lock_any.cdc b/cadence/tests/transactions/flow-alp/eposition/lock_any.cdc deleted file mode 100644 index a6884a2e..00000000 --- a/cadence/tests/transactions/flow-alp/eposition/lock_any.cdc +++ /dev/null @@ -1,22 +0,0 @@ -import "FlowALPv0" -import "FlowALPModels" - -/// Locks then unlocks any position via an EPosition capability at PoolCapStoragePath. -/// EPosition allows operations on any position by ID, regardless of ownership. -/// -/// @param pid: Target position ID (may belong to a different account) -transaction(pid: UInt64) { - let pool: auth(FlowALPModels.EPosition) &FlowALPv0.Pool - - prepare(signer: auth(BorrowValue) &Account) { - let cap = signer.storage.borrow<&Capability>( - from: FlowALPv0.PoolCapStoragePath - ) ?? panic("EPosition capability not found") - self.pool = cap.borrow() ?? panic("Could not borrow Pool with EPosition") - } - - execute { - self.pool.lockPosition(pid) - self.pool.unlockPosition(pid) - } -} diff --git a/cadence/tests/transactions/flow-alp/eposition/rebalance_position_via_cap.cdc b/cadence/tests/transactions/flow-alp/eposition/rebalance_position_via_cap.cdc deleted file mode 100644 index 337aec42..00000000 --- a/cadence/tests/transactions/flow-alp/eposition/rebalance_position_via_cap.cdc +++ /dev/null @@ -1,23 +0,0 @@ -import "FlowALPv0" -import "FlowALPModels" - -/// Rebalances a position via an EPosition capability at PoolCapStoragePath. -/// EPosition satisfies the EPosition | ERebalance requirement of Pool.rebalancePosition. -/// -/// @param pid: Position to rebalance -/// @param force: Whether to force rebalance regardless of health bounds -transaction(pid: UInt64, force: Bool) { - let pool: auth(FlowALPModels.EPosition) &FlowALPv0.Pool - - prepare(signer: auth(BorrowValue) &Account) { - let cap = signer.storage.borrow<&Capability>( - from: FlowALPv0.PoolCapStoragePath - ) ?? panic("EPosition capability not found at PoolCapStoragePath") - self.pool = cap.borrow() ?? panic("Could not borrow Pool with EPosition") - } - - execute { - // Pool.rebalancePosition — requires EPosition | ERebalance; EPosition alone is sufficient - self.pool.rebalancePosition(pid: pid, force: force) - } -} diff --git a/cadence/tests/transactions/flow-alp/eposition/withdraw_and_pull_any.cdc b/cadence/tests/transactions/flow-alp/eposition/withdraw_and_pull_any.cdc deleted file mode 100644 index 72748857..00000000 --- a/cadence/tests/transactions/flow-alp/eposition/withdraw_and_pull_any.cdc +++ /dev/null @@ -1,33 +0,0 @@ -import "FungibleToken" -import "FlowALPv0" -import "FlowALPModels" -import "MOET" - -/// Withdraws and pulls MOET from any position via an EPosition capability at PoolCapStoragePath. -/// EPosition allows operations on any position by ID, regardless of ownership. -/// -/// @param pid: Target position ID (owned by a different account) -/// @param amount: Amount to withdraw -transaction(pid: UInt64, amount: UFix64) { - let pool: auth(FlowALPModels.EPosition) &FlowALPv0.Pool - let receiver: &{FungibleToken.Receiver} - - prepare(signer: auth(BorrowValue) &Account) { - let cap = signer.storage.borrow<&Capability>( - from: FlowALPv0.PoolCapStoragePath - ) ?? panic("EPosition capability not found") - self.pool = cap.borrow() ?? panic("Could not borrow Pool with EPosition") - self.receiver = signer.storage.borrow<&{FungibleToken.Receiver}>(from: MOET.VaultStoragePath) - ?? panic("No MOET vault receiver") - } - - execute { - let vault <- self.pool.withdrawAndPull( - pid: pid, - type: Type<@MOET.Vault>(), - amount: amount, - pullFromTopUpSource: false - ) - self.receiver.deposit(from: <-vault) - } -} diff --git a/cadence/tests/transactions/flow-alp/eposition/withdraw_any.cdc b/cadence/tests/transactions/flow-alp/eposition/withdraw_any.cdc deleted file mode 100644 index 3b7edb14..00000000 --- a/cadence/tests/transactions/flow-alp/eposition/withdraw_any.cdc +++ /dev/null @@ -1,28 +0,0 @@ -import "FungibleToken" -import "FlowALPv0" -import "FlowALPModels" -import "MOET" - -/// Withdraws MOET from any position via an EPosition capability at PoolCapStoragePath. -/// EPosition allows operations on any position by ID, regardless of ownership. -/// -/// @param pid: Target position ID (owned by a different account) -/// @param amount: Amount to withdraw -transaction(pid: UInt64, amount: UFix64) { - let pool: auth(FlowALPModels.EPosition) &FlowALPv0.Pool - let receiver: &{FungibleToken.Receiver} - - prepare(signer: auth(BorrowValue) &Account) { - let cap = signer.storage.borrow<&Capability>( - from: FlowALPv0.PoolCapStoragePath - ) ?? panic("EPosition capability not found") - self.pool = cap.borrow() ?? panic("Could not borrow Pool with EPosition") - self.receiver = signer.storage.borrow<&{FungibleToken.Receiver}>(from: MOET.VaultStoragePath) - ?? panic("No MOET vault receiver") - } - - execute { - let vault <- self.pool.withdraw(pid: pid, amount: amount, type: Type<@MOET.Vault>()) - self.receiver.deposit(from: <-vault) - } -} diff --git a/cadence/tests/transactions/flow-alp/setup/grant_eposition_cap.cdc b/cadence/tests/transactions/flow-alp/setup/grant_eposition_cap.cdc deleted file mode 100644 index 5fee69a5..00000000 --- a/cadence/tests/transactions/flow-alp/setup/grant_eposition_cap.cdc +++ /dev/null @@ -1,25 +0,0 @@ -import "FlowALPv0" -import "FlowALPModels" - -/// TEST SETUP — grants an EPosition-ONLY Pool capability to a user account. -/// This is a narrowly-scoped capability — no EParticipant, so the holder cannot -/// createPosition. EPosition alone allows pool-level position operations on any -/// position by ID (withdraw, depositAndPush, lockPosition, rebalancePosition, etc.). -/// Stored at FlowALPv0.PoolCapStoragePath. -transaction { - prepare( - admin: auth(IssueStorageCapabilityController) &Account, - user: auth(Storage) &Account - ) { - let cap = admin.capabilities.storage.issue( - FlowALPv0.PoolStoragePath - ) - // Overwrite any existing cap at this path - if user.storage.type(at: FlowALPv0.PoolCapStoragePath) != nil { - user.storage.load>( - from: FlowALPv0.PoolCapStoragePath - ) - } - user.storage.save(cap, to: FlowALPv0.PoolCapStoragePath) - } -}