Skip to content

Conversation

@nialexsan
Copy link
Collaborator

@nialexsan nialexsan commented Dec 10, 2025

Closes: #???

on pause due to non-upgradable changes in FlowCreditMarket

Description

testnet txns for vaults with the collaterals:

FlowYieldVaultsStrategies diff:
main...2d4e129#diff-3ca82892e3f3687d53c4df502a69e2b07e6f5a0038466d9792121979b51d2f32


For contributor use:

  • Targeted PR against master branch
  • Linked to Github issue with discussion and accepted design OR link to spec that describes this work.
  • Code follows the standards mentioned here.
  • Updated relevant documentation
  • Re-reviewed Files changed in the Github PR explorer
  • Added appropriate labels

@codecov
Copy link

codecov bot commented Dec 22, 2025

Codecov Report

❌ Patch coverage is 7.74648% with 131 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...adence/contracts/FlowYieldVaultsStrategiesV1_1.cdc 7.74% 131 Missing ⚠️

📢 Thoughts on this report? Let us know!

@nialexsan nialexsan marked this pull request as ready for review December 22, 2025 19:23
@nialexsan nialexsan requested a review from a team as a code owner December 22, 2025 19:23
@nialexsan nialexsan requested a review from bthaile December 22, 2025 19:31
strategy: Type,
collateral: Type
): Bool {
let composerConfig = self.configs[composer]!
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested fix: The hasConfig function uses force-unwrap (!) on optional values which can panic. Should be changed to use optional binding:

access(all) view fun hasConfig(
    composer: Type,
    strategy: Type,
    collateral: Type
): Bool {
    if let composerConfig = self.configs[composer] {
        if let strategyConfig = composerConfig[strategy] {
            return strategyConfig[collateral] != nil
        }
    }
    return false
}

@liobrasil
Copy link
Contributor

Additional changes needed:

  1. New transaction file needed: cadence/transactions/flow-yield-vaults/admin/upsert_musdf_config.cdc - This transaction allows admins to upsert mUSDFStrategy config for different collateral types. It should be added to this PR.

  2. Fix relative paths in local/setup_mainnet.sh: The paths to upsert_musdf_config.cdc should be ./cadence/transactions/... instead of ../cadence/transactions/...

These changes are staged locally and can be reviewed.

self.config = {}

let moetType = Type<@MOET.Vault>()
let moetEVMAddress = FlowEVMBridgeConfig.getEVMAddressAssociated(with: Type<@MOET.Vault>())
Copy link
Contributor

Choose a reason for hiding this comment

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

unused moetEVMAddress

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this is a safety check to be sure that moet evm address exists,
it will panic on the next line if it doesn't

Copy link
Member

Choose a reason for hiding this comment

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

Maybe make that more obvious by not introducing an unused variable:

        if FlowEVMBridgeConfig.getEVMAddressAssociated(with: Type<@MOET.Vault>()) == nil {
            panic("Could not find EVM address for \(moetType.identifier) - ensure the asset is onboarded to the VM Bridge")
        }

Copy link
Member

@turbolent turbolent left a comment

Choose a reason for hiding this comment

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

Nice, just a few minor suggestions. Only reviewed the Cadence code from a code quality perspective, IDK about the logic details

}
/// Executed when a Strategy is burned, cleaning up the Strategy's stored AutoBalancer
access(contract) fun burnCallback() {
FlowYieldVaultsAutoBalancers._cleanupAutoBalancer(id: self.id()!)
Copy link
Member

Choose a reason for hiding this comment

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

This will fail if there is no ID set. Is that intended?

strategy: Type,
collateral: Type
): Bool {
let composerConfig = self.configs[composer] ?? nil
Copy link
Member

Choose a reason for hiding this comment

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

Here and for the other ?? nil occurrences: The lookup already returns nil if there is no element, so nil ?? nil is just nil:

Suggested change
let composerConfig = self.configs[composer] ?? nil
let composerConfig = self.configs[composer]

Comment on lines 568 to 576
let composerConfig = self.configs[composer] ?? nil
if composerConfig == nil {
return false
}
let strategyConfig = composerConfig![strategy] ?? nil
if strategyConfig == nil {
return false
}
return strategyConfig![collateral] != nil
Copy link
Member

Choose a reason for hiding this comment

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

Avoid force-unwrapping and use if-let instead:

Suggested change
let composerConfig = self.configs[composer] ?? nil
if composerConfig == nil {
return false
}
let strategyConfig = composerConfig![strategy] ?? nil
if strategyConfig == nil {
return false
}
return strategyConfig![collateral] != nil
if let composerConfig = self.configs[composer] {
if let strategyConfig = composerConfig[strategy] {
return strategyConfig[collateral] != nil
}
}
return false

If there will be many elements in self.configs[composer] and composerConfig[strategy], you might want to consider taking references to the entries, so that the large dictionaries aren't copied.

return strategyConfig![collateral] != nil
}

access(all) view fun getSupportedComposers(): {Type: Bool} {
Copy link
Member

Choose a reason for hiding this comment

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

Don't know if this is used elsewhere outside of this contract, but inside of this contract the result is always just used for a lookup, so the construction of the dictionary is unnecessary.

Maybe consider replacing this, or at least adding isSupportedComposer(_ type: Type): Bool function which and calling it instead of self.getSupportedComposers()[type] == true:

fun isSupportedComposer(_ type: Type): Bool {
    return type == Type<@mUSDFStrategyComposer>()
}

pre {
self.getSupportedComposers()[type] == true:
"Unsupported StrategyComposer \(type.identifier) requested"
(&self.configs[type] as &{Type: {Type: FlowYieldVaultsStrategiesV1_1.CollateralConfig}}?) != nil:
Copy link
Member

Choose a reason for hiding this comment

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

AFAICS there should be no need to take a reference on the LHS:

Suggested change
(&self.configs[type] as &{Type: {Type: FlowYieldVaultsStrategiesV1_1.CollateralConfig}}?) != nil:
self.configs[type] != nil:

for stratType in config.keys {
let newPerCollateral = config[stratType]!
let existingPerCollateral = mergedComposerConfig[stratType] ?? {}
var mergedPerCollateral: {Type: FlowYieldVaultsStrategiesV1_1.CollateralConfig} = existingPerCollateral
Copy link
Member

Choose a reason for hiding this comment

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

Type annotation is unnecessary, it can be inferred:

Suggested change
var mergedPerCollateral: {Type: FlowYieldVaultsStrategiesV1_1.CollateralConfig} = existingPerCollateral
var mergedPerCollateral = existingPerCollateral

)

// Wrap into the nested config expected by upsertConfigFor
let singleCollateralConfig: {Type: {Type: FlowYieldVaultsStrategiesV1_1.CollateralConfig}} = {
Copy link
Member

Choose a reason for hiding this comment

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

Type annotation is probably unnecessary and can be inferred:

Suggested change
let singleCollateralConfig: {Type: {Type: FlowYieldVaultsStrategiesV1_1.CollateralConfig}} = {
let singleCollateralConfig = {

Comment on lines 671 to 675
self.configs = {
Type<@mUSDFStrategyComposer>(): {
Type<@mUSDFStrategy>(): {}
}
} as {Type: {Type: {Type: FlowYieldVaultsStrategiesV1_1.CollateralConfig}}}
Copy link
Member

Choose a reason for hiding this comment

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

Maybe only annotate the type of what cannot be inferred, i.e. the inner empty dictionary:

Suggested change
self.configs = {
Type<@mUSDFStrategyComposer>(): {
Type<@mUSDFStrategy>(): {}
}
} as {Type: {Type: {Type: FlowYieldVaultsStrategiesV1_1.CollateralConfig}}}
self.configs = {
Type<@mUSDFStrategyComposer>(): {
Type<@mUSDFStrategy>(): {} as {Type: FlowYieldVaultsStrategiesV1_1.CollateralConfig}
}
}

self.config = {}

let moetType = Type<@MOET.Vault>()
let moetEVMAddress = FlowEVMBridgeConfig.getEVMAddressAssociated(with: Type<@MOET.Vault>())
Copy link
Member

Choose a reason for hiding this comment

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

Maybe make that more obvious by not introducing an unused variable:

        if FlowEVMBridgeConfig.getEVMAddressAssociated(with: Type<@MOET.Vault>()) == nil {
            panic("Could not find EVM address for \(moetType.identifier) - ensure the asset is onboarded to the VM Bridge")
        }

@nialexsan nialexsan linked an issue Jan 9, 2026 that may be closed by this pull request
Comment on lines +43 to +50
0.8 \
1.0 \
0.8 \
0.0 \
0.04 \
0.4 \
1_000_000.0 \
1_000_000.0 \
Copy link
Collaborator Author

@nialexsan nialexsan Jan 13, 2026

Choose a reason for hiding this comment

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

@liobrasil just a sanity check
do these params look correct for this config:

- collateral factor: 0.8
- borrow factor: 1.0
- Kink interest curve
  - optimal utilization 80%
  - base rate 0%
  - slope1: 4
  - slope2: 40
- deposit rate: 1_000_000
- deposit capacity: 1_000_000

Copy link
Contributor

Choose a reason for hiding this comment

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

Aave Rate Strategy (Volatile One)

Collateral / Borrow Factors (WBTC)

Deposit Capacity (WBTC)

  • Current defaults feel too large for initial mainnet testing
  • Proposal: depositRate = 0.006 WBTC/hr
  • Proposal: depositCapacityCap = 0.1 WBTC
  • USD equivalents depend on BTC spot (e.g., at $90k/BTC this is ≈ $540/hr and ≈ $9000 cap)

@nialexsan nialexsan marked this pull request as draft January 14, 2026 15:17
@nialexsan
Copy link
Collaborator Author

on pause due to non-upgradable changes in FlowCreditMarket

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support additional collateral types: wBTC and wETH

7 participants