DeploySEALED
■▪■╲○╳╲$#$░█●■%▓●╲●▒░▫?╲█$◆◆■░□▪~*▒^▫#$*▓@░╲#●╳&╳╳$░◇&$&○╳$$&▫~█
Transaction ID
Execution Fee
0.00000924 FLOWTransaction Summary
DeployContract deployment
Contract deployment
Script Arguments
0nameString
StrategyExecutor
1codeString
import FungibleToken from 0xf233dcee88fe0abe
import FlowToken from 0x1654653399040a61
import stFlowToken from 0xd6f80565193ad727
import VaultCore from 0x79f5b5b0f95a160b
import LiquidStaking from 0xd6f80565193ad727
import LendingInterfaces from 0x2df970b6cdee5735
import LendingComptroller from 0xf80cb737bfe7c792
import LendingConfig from 0x2df970b6cdee5735
import SwapRouter from 0xa6850776a94e6551
import SwapInterfaces from 0xb78ef7afa52ff906
/// Non-custodial strategy executor
/// Executes strategies directly on vault funds without withdrawal
access(all) contract StrategyExecutor {
access(all) event StrategyExecuted(strategy: String, inputAmount: UFix64, outputAmount: UFix64)
access(all) event YieldHarvested(strategy: String, amount: UFix64)
// Execute staking strategy on vault funds
access(all) fun executeStaking(vaultRef: &VaultCore.Vault, amount: UFix64) {
// Vault withdraws for strategy (internal operation)
let flowVault <- vaultRef.withdrawForStrategy(
assetType: VaultCore.AssetType.flow,
amount: amount
) as! @FlowToken.Vault
// Stake FLOW -> stFLOW
let stFlowResult <- LiquidStaking.stake(flowVault: <-flowVault)
let resultAmount = stFlowResult.balance
// Return stFLOW directly to vault
vaultRef.depositFromStrategy(
assetType: VaultCore.AssetType.stflow,
from: <-stFlowResult
)
// Record yield
vaultRef.recordYieldHarvest(
assetType: VaultCore.AssetType.stflow,
amount: 0.0
)
emit StrategyExecuted(strategy: "Staking", inputAmount: amount, outputAmount: resultAmount)
}
// Execute looping strategy on vault funds
access(all) fun executeLooping(vaultRef: &VaultCore.Vault, amount: UFix64, loops: UInt8) {
pre {
loops >= 1 && loops <= 3: "Loops must be 1-3"
}
// Get user certificate for lending
let certificate <- LendingComptroller.IssueUserCertificate()
// Withdraw initial FLOW from vault
var flowVault <- vaultRef.withdrawForStrategy(
assetType: VaultCore.AssetType.flow,
amount: amount
) as! @FlowToken.Vault
// Get pool references
let stFlowPool = getAccount(0x44fe3d9157770b2d).capabilities
.borrow<&{LendingInterfaces.PoolPublic}>(LendingConfig.PoolPublicPublicPath)
?? panic("Cannot access stFlow pool")
let flowPool = getAccount(0x7492e2f9b4acea9a).capabilities
.borrow<&{LendingInterfaces.PoolPublic}>(LendingConfig.PoolPublicPublicPath)
?? panic("Cannot access FLOW pool")
var totalStFlow: UFix64 = 0.0
var currentLoop: UInt8 = 0
// Execute loops
while currentLoop < loops {
let currentBalance = flowVault.balance
if currentBalance == 0.0 {
break
}
// Withdraw all FLOW for this loop iteration
let flowForThisLoop <- flowVault.withdraw(amount: currentBalance) as! @FlowToken.Vault
let flowAmount = flowForThisLoop.balance
// Stake FLOW -> stFLOW
let stFlowVault <- LiquidStaking.stake(flowVault: <-flowForThisLoop)
totalStFlow = totalStFlow + stFlowVault.balance
// Supply stFLOW as collateral
stFlowPool.supply(
supplierAddr: certificate.owner!.address,
inUnderlyingVault: <-stFlowVault
)
currentLoop = currentLoop + 1
// If not the last loop, borrow more FLOW
if currentLoop < loops {
let borrowAmount = flowAmount * 0.7
if borrowAmount > 0.0 {
let borrowedFlow <- flowPool.borrow(
userCertificate: &certificate as &{LendingInterfaces.IdentityCertificate},
borrowAmount: borrowAmount
) as! @FlowToken.Vault
// Deposit borrowed FLOW back into our working vault
flowVault.deposit(from: <-borrowedFlow)
}
}
}
// Clean up
destroy flowVault
destroy certificate
// Record the leveraged position in vault
vaultRef.recordYieldHarvest(
assetType: VaultCore.AssetType.stflow,
amount: totalStFlow
)
emit StrategyExecuted(strategy: "Looping", inputAmount: amount, outputAmount: totalStFlow)
}
// Execute optimal swap strategy on vault funds
access(all) fun executeSwap(vaultRef: &VaultCore.Vault, amount: UFix64, fromAsset: VaultCore.AssetType, toAsset: VaultCore.AssetType) {
// Withdraw from vault for swap
let inputVault <- vaultRef.withdrawForStrategy(
assetType: fromAsset,
amount: amount
)
var outputVault: @{FungibleToken.Vault}? <- nil
if fromAsset == VaultCore.AssetType.flow && toAsset == VaultCore.AssetType.stflow {
// FLOW -> stFLOW optimal routing
let flowVault <- inputVault as! @FlowToken.Vault
let result <- self.optimalFlowToStFlow(flowVault: <-flowVault)
outputVault <-! result
} else if fromAsset == VaultCore.AssetType.stflow && toAsset == VaultCore.AssetType.flow {
// stFLOW -> FLOW optimal routing
let stFlowVault <- inputVault as! @stFlowToken.Vault
let result <- self.optimalStFlowToFlow(stFlowVault: <-stFlowVault)
outputVault <-! result
} else {
destroy inputVault
panic("Unsupported swap pair")
}
let outputAmount = outputVault?.balance ?? 0.0
// Return swapped funds to vault
vaultRef.depositFromStrategy(
assetType: toAsset,
from: <-outputVault!
)
emit StrategyExecuted(strategy: "Swap", inputAmount: amount, outputAmount: outputAmount)
}
// Optimal FLOW -> stFLOW routing
access(self) fun optimalFlowToStFlow(flowVault: @FlowToken.Vault): @stFlowToken.Vault {
let amount = flowVault.balance
// Check staking rate
let stakingRate = LiquidStaking.calcStFlowFromFlow(flowAmount: amount)
// Check DEX rates
var bestDexRate: UFix64 = 0.0
var bestDex: String = ""
let pairV1 = getAccount(0x396c0cda3302d8c5).capabilities
.borrow<&{SwapInterfaces.PairPublic}>(/public/increment_swap_pair)
if pairV1 != nil {
let v1Rate = pairV1!.getAmountOut(
amountIn: amount,
tokenInKey: "A.1654653399040a61.FlowToken"
)
if v1Rate > bestDexRate {
bestDexRate = v1Rate
bestDex = "V1"
}
}
let pairStable = getAccount(0xc353b9d685ec427d).capabilities
.borrow<&{SwapInterfaces.PairPublic}>(/public/increment_swap_pair)
if pairStable != nil {
let stableRate = pairStable!.getAmountOut(
amountIn: amount,
tokenInKey: "A.1654653399040a61.FlowToken"
)
if stableRate > bestDexRate {
bestDexRate = stableRate
bestDex = "Stable"
}
}
// Execute via best route
if stakingRate >= bestDexRate {
return <- LiquidStaking.stake(flowVault: <-flowVault)
} else if bestDex == "V1" {
return <- pairV1!.swap(vaultIn: <-flowVault, exactAmountOut: nil) as! @stFlowToken.Vault
} else {
return <- pairStable!.swap(vaultIn: <-flowVault, exactAmountOut: nil) as! @stFlowToken.Vault
}
}
// Optimal stFLOW -> FLOW routing
access(self) fun optimalStFlowToFlow(stFlowVault: @stFlowToken.Vault): @FlowToken.Vault {
let amount = stFlowVault.balance
// Check DEX rates (unstaking has delay, so DEX is preferred)
var bestRate: UFix64 = 0.0
var bestPair: &{SwapInterfaces.PairPublic}? = nil
let pairV1 = getAccount(0x396c0cda3302d8c5).capabilities
.borrow<&{SwapInterfaces.PairPublic}>(/public/increment_swap_pair)
if pairV1 != nil {
let v1Rate = pairV1!.getAmountOut(
amountIn: amount,
tokenInKey: "A.d6f80565193ad727.stFlowToken"
)
if v1Rate > bestRate {
bestRate = v1Rate
bestPair = pairV1
}
}
let pairStable = getAccount(0xc353b9d685ec427d).capabilities
.borrow<&{SwapInterfaces.PairPublic}>(/public/increment_swap_pair)
if pairStable != nil {
let stableRate = pairStable!.getAmountOut(
amountIn: amount,
tokenInKey: "A.d6f80565193ad727.stFlowToken"
)
if stableRate > bestRate {
bestRate = stableRate
bestPair = pairStable
}
}
if bestPair != nil {
return <- bestPair!.swap(vaultIn: <-stFlowVault, exactAmountOut: nil) as! @FlowToken.Vault
} else {
// Fallback to SwapRouter
let deadline = getCurrentBlock().timestamp + 300.0
return <- SwapRouter.swapExactTokensForTokens(
exactVaultIn: <-stFlowVault,
amountOutMin: 0.0,
tokenKeyPath: [
"A.d6f80565193ad727.stFlowToken",
"A.1654653399040a61.FlowToken"
],
deadline: deadline
) as! @FlowToken.Vault
}
}
// Harvest yields from strategies back to vault
access(all) fun harvestYields(vaultRef: &VaultCore.Vault, strategyType: String) {
var harvestedAmount: UFix64 = 0.0
switch strategyType {
case "Lending":
harvestedAmount = 0.0
case "Farming":
harvestedAmount = 0.0
default:
log("Unknown strategy type")
}
if harvestedAmount > 0.0 {
vaultRef.recordYieldHarvest(
assetType: VaultCore.AssetType.flow,
amount: harvestedAmount
)
emit YieldHarvested(strategy: strategyType, amount: harvestedAmount)
}
}
}
Cadence Script
1transaction(name: String, code: String ) {
2 prepare(signer: auth(AddContract) &Account) {
3 signer.contracts.add(name: name, code: code.utf8 )
4 }
5 }