Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions cadence/contracts/FlowALPv0.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -1882,6 +1882,11 @@ access(all) contract FlowALPv0 {
)
}

/// Returns the IDs of all currently open positions in this pool
access(all) view fun getPositionIDs(): [UInt64] {
return self.positions.keys
}

/// Returns the details of a given position as a PositionDetails external struct
access(all) fun getPositionDetails(pid: UInt64): PositionDetails {
if self.debugLogging {
Expand Down
16 changes: 8 additions & 8 deletions cadence/scripts/flow-alp/get_position_by_id.cdc
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// Returns the details of a specific position by its ID.
import "FlowALPv0"

access(all) fun main(poolAddress: Address, positionID: UInt64): FlowALPv0.PositionDetails {
let account = getAccount(poolAddress)
access(all) fun main(positionID: UInt64): FlowALPv0.PositionDetails {
let protocolAddress = Type<@FlowALPv0.Pool>().address!
let account = getAccount(protocolAddress)
let pool = account.capabilities.borrow<&FlowALPv0.Pool>(FlowALPv0.PoolPublicPath)
?? panic("Could not find Pool at path \(FlowALPv0.PoolPublicPath)")

let poolRef = account.capabilities
.borrow<&FlowALPv0.Pool>(FlowALPv0.PoolPublicPath)
?? panic("Could not borrow Pool reference from \(poolAddress)")

return poolRef.getPositionDetails(pid: positionID)
}
return pool.getPositionDetails(pid: positionID)
}
11 changes: 11 additions & 0 deletions cadence/scripts/flow-alp/get_position_ids.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Returns the IDs of all currently open positions in the pool.
import "FlowALPv0"

access(all) fun main(): [UInt64] {
let protocolAddress = Type<@FlowALPv0.Pool>().address!
let account = getAccount(protocolAddress)
let pool = account.capabilities.borrow<&FlowALPv0.Pool>(FlowALPv0.PoolPublicPath)
?? panic("Could not find Pool at path \(FlowALPv0.PoolPublicPath)")

return pool.getPositionIDs()
}
15 changes: 15 additions & 0 deletions cadence/scripts/flow-alp/get_positions_by_ids.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Returns the details of multiple positions by their IDs.
import "FlowALPv0"

access(all) fun main(positionIDs: [UInt64]): [FlowALPv0.PositionDetails] {
let protocolAddress = Type<@FlowALPv0.Pool>().address!
let account = getAccount(protocolAddress)
let pool = account.capabilities.borrow<&FlowALPv0.Pool>(FlowALPv0.PoolPublicPath)
?? panic("Could not find Pool at path \(FlowALPv0.PoolPublicPath)")

let details: [FlowALPv0.PositionDetails] = []
for id in positionIDs {
details.append(pool.getPositionDetails(pid: id))
}
return details
}
99 changes: 99 additions & 0 deletions cadence/tests/get_position_ids_test.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import Test
import BlockchainHelpers

import "MOET"
import "FlowALPv0"
import "test_helpers.cdc"

// -----------------------------------------------------------------------------
// getPositionIDs Test
//
// Verifies that Pool.getPositionIDs() correctly reflects opened and closed
// positions via the get_position_ids.cdc script.
// -----------------------------------------------------------------------------

access(all)
fun setup() {
deployContracts()
}

// =============================================================================
// Test: getPositionIDs tracks opens and closes correctly
// =============================================================================
access(all)
fun test_getPositionIDs_lifecycle() {
// --- Setup ---
setMockOraclePrice(signer: PROTOCOL_ACCOUNT, forTokenIdentifier: FLOW_TOKEN_IDENTIFIER, price: 1.0)

createAndStorePool(signer: PROTOCOL_ACCOUNT, defaultTokenIdentifier: MOET_TOKEN_IDENTIFIER, beFailed: false)
addSupportedTokenZeroRateCurve(
signer: PROTOCOL_ACCOUNT,
tokenTypeIdentifier: FLOW_TOKEN_IDENTIFIER,
collateralFactor: 0.8,
borrowFactor: 1.0,
depositRate: 1_000_000.0,
depositCapacityCap: 1_000_000.0
)

let user = Test.createAccount()
setupMoetVault(user, beFailed: false)
mintFlow(to: user, amount: 10_000.0)

// --- No positions yet ---
var ids = getPositionIDs()
Test.assertEqual(0, ids.length)

// --- Open position 0 (with borrow) ---
createPosition(signer: user, amount: 100.0, vaultStoragePath: FLOW_VAULT_STORAGE_PATH, pushToDrawDownSink: true)

ids = getPositionIDs()
Test.assertEqual(1, ids.length)
Test.assert(ids.contains(UInt64(0)), message: "Expected position 0 in IDs")

// --- Open position 1 (with borrow) ---
createPosition(signer: user, amount: 100.0, vaultStoragePath: FLOW_VAULT_STORAGE_PATH, pushToDrawDownSink: true)

ids = getPositionIDs()
Test.assertEqual(2, ids.length)
Test.assert(ids.contains(UInt64(0)), message: "Expected position 0 in IDs")
Test.assert(ids.contains(UInt64(1)), message: "Expected position 1 in IDs")

// --- Open position 2 (no borrow, so closing won't need MOET repay) ---
createPosition(signer: user, amount: 100.0, vaultStoragePath: FLOW_VAULT_STORAGE_PATH, pushToDrawDownSink: false)

ids = getPositionIDs()
Test.assertEqual(3, ids.length)
Test.assert(ids.contains(UInt64(2)), message: "Expected position 2 in IDs")

// --- Close position 2 (no debt, straightforward) ---
closePosition(user: user, positionID: 2)

ids = getPositionIDs()
Test.assertEqual(2, ids.length)
Test.assert(!ids.contains(UInt64(2)), message: "Position 2 should be removed after close")
Test.assert(ids.contains(UInt64(0)), message: "Position 0 should still exist")
Test.assert(ids.contains(UInt64(1)), message: "Position 1 should still exist")

// --- Close position 0 (has debt, repay needed) ---
closePosition(user: user, positionID: 0)

ids = getPositionIDs()
Test.assertEqual(1, ids.length)
Test.assert(!ids.contains(UInt64(0)), message: "Position 0 should be removed after close")
Test.assert(ids.contains(UInt64(1)), message: "Position 1 should still exist")

// --- Open position 3 (new position after some closures) ---
createPosition(signer: user, amount: 100.0, vaultStoragePath: FLOW_VAULT_STORAGE_PATH, pushToDrawDownSink: true)

ids = getPositionIDs()
Test.assertEqual(2, ids.length)
Test.assert(ids.contains(UInt64(1)), message: "Position 1 should still exist")
Test.assert(ids.contains(UInt64(3)), message: "Expected position 3 in IDs")

// --- Close remaining positions ---
closePosition(user: user, positionID: 1)
closePosition(user: user, positionID: 3)

ids = getPositionIDs()
Test.assertEqual(0, ids.length)
}
68 changes: 68 additions & 0 deletions cadence/tests/get_positions_by_ids_test.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import Test
import BlockchainHelpers

import "MOET"
import "FlowALPv0"
import "test_helpers.cdc"

// -----------------------------------------------------------------------------
// getPositionsByIDs Test
//
// Verifies that the get_positions_by_ids.cdc script correctly returns position
// details for the requested IDs.
// -----------------------------------------------------------------------------

access(all)
fun setup() {
deployContracts()
}

// =============================================================================
// Test: getPositionsByIDs returns correct details for multiple positions
// =============================================================================
access(all)
fun test_getPositionsByIDs() {
// --- Setup ---
setMockOraclePrice(signer: PROTOCOL_ACCOUNT, forTokenIdentifier: FLOW_TOKEN_IDENTIFIER, price: 1.0)

createAndStorePool(signer: PROTOCOL_ACCOUNT, defaultTokenIdentifier: MOET_TOKEN_IDENTIFIER, beFailed: false)
addSupportedTokenZeroRateCurve(
signer: PROTOCOL_ACCOUNT,
tokenTypeIdentifier: FLOW_TOKEN_IDENTIFIER,
collateralFactor: 0.8,
borrowFactor: 1.0,
depositRate: 1_000_000.0,
depositCapacityCap: 1_000_000.0
)

let user = Test.createAccount()
setupMoetVault(user, beFailed: false)
mintFlow(to: user, amount: 10_000.0)

// --- Open two positions ---
createPosition(signer: user, amount: 100.0, vaultStoragePath: FLOW_VAULT_STORAGE_PATH, pushToDrawDownSink: true)
createPosition(signer: user, amount: 200.0, vaultStoragePath: FLOW_VAULT_STORAGE_PATH, pushToDrawDownSink: false)

// --- Fetch both positions by IDs ---
let details = getPositionsByIDs(positionIDs: [UInt64(0), UInt64(1)])
Test.assertEqual(2, details.length)

// Verify each result matches the single-position helper
let details0 = getPositionDetails(pid: 0, beFailed: false)
let details1 = getPositionDetails(pid: 1, beFailed: false)

Test.assertEqual(details0.health, details[0].health)
Test.assertEqual(details0.balances.length, details[0].balances.length)

Test.assertEqual(details1.health, details[1].health)
Test.assertEqual(details1.balances.length, details[1].balances.length)

// --- Empty input returns empty array ---
let emptyDetails = getPositionsByIDs(positionIDs: [])
Test.assertEqual(0, emptyDetails.length)

// --- Single ID works ---
let singleDetails = getPositionsByIDs(positionIDs: [UInt64(0)])
Test.assertEqual(1, singleDetails.length)
Test.assertEqual(details0.health, singleDetails[0].health)
}
30 changes: 30 additions & 0 deletions cadence/tests/test_helpers.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,36 @@ fun withdrawReserve(
Test.expect(txRes, beFailed ? Test.beFailed() : Test.beSucceeded())
}

access(all)
fun getPositionIDs(): [UInt64] {
let res = _executeScript(
"../scripts/flow-alp/get_position_ids.cdc",
[]
)
Test.expect(res, Test.beSucceeded())
return res.returnValue as! [UInt64]
}

access(all)
fun getPositionsByIDs(positionIDs: [UInt64]): [FlowALPv0.PositionDetails] {
let res = _executeScript(
"../scripts/flow-alp/get_positions_by_ids.cdc",
[positionIDs]
)
Test.expect(res, Test.beSucceeded())
return res.returnValue as! [FlowALPv0.PositionDetails]
}

access(all)
fun closePosition(user: Test.TestAccount, positionID: UInt64) {
let res = _executeTransaction(
"../transactions/flow-alp/position/repay_and_close_position.cdc",
[positionID],
user
)
Test.expect(res, Test.beSucceeded())
}

/* --- Assertion Helpers --- */

access(all) fun equalWithinVariance(_ expected: AnyStruct, _ actual: AnyStruct): Bool {
Expand Down
Loading