DeploySEALED
~■*~█#@◇▓▪$□■○╳╱!◆▫~░#▫■@█▫?%◇╳▓●▪%▪■░$!▒●░^!▪%!◇▫○*█▓&╱█#$□●#$○
Transaction ID
Execution Fee
0.00001034 FLOWTransaction Summary
DeployContract deployment
Contract deployment
Script Arguments
0nameString
ActionRouter
1codeString
import FlowToken from 0x1654653399040a61
import stFlowToken from 0xd6f80565193ad727
import LiquidStaking from 0xd6f80565193ad727
import FungibleToken from 0xf233dcee88fe0abe
access(all) contract ActionRouter {
access(all) let AdminStoragePath: StoragePath
access(all) let FlowVaultStoragePath: StoragePath
access(all) let StFlowVaultStoragePath: StoragePath
access(self) var authorizedEVMCallers: {Address: Bool}
access(self) var isActive: Bool
access(self) var minStakeAmount: UFix64
access(self) var maxStakeAmount: UFix64
access(self) var maxOperationsPerBlock: UInt64
access(self) var currentBlockOperations: UInt64
access(self) var lastBlockHeight: UInt64
access(all) var totalStakeOperations: UInt64
access(all) var totalUnstakeOperations: UInt64
access(all) var totalFlowStaked: UFix64
access(all) var totalStFlowMinted: UFix64
access(all) event StakeExecuted(amount: UFix64, recipient: Address, stFlowReceived: UFix64, exchangeRate: UFix64, requestId: String)
access(all) event UnstakeExecuted(amount: UFix64, recipient: Address, flowReceived: UFix64, requestId: String)
access(all) event EVMCallerAuthorized(caller: Address)
access(all) event EVMCallerRevoked(caller: Address)
access(all) event RouterConfigUpdated(minStake: UFix64, maxStake: UFix64)
// Struct definitions moved inside the contract
access(all) struct StakeResult {
access(all) let flowAmount: UFix64
access(all) let stFlowReceived: UFix64
access(all) let exchangeRate: UFix64
access(all) let requestId: String
access(all) let success: Bool
init(flowAmount: UFix64, stFlowReceived: UFix64, exchangeRate: UFix64, requestId: String, success: Bool) {
self.flowAmount = flowAmount
self.stFlowReceived = stFlowReceived
self.exchangeRate = exchangeRate
self.requestId = requestId
self.success = success
}
}
access(all) struct UnstakeResult {
access(all) let stFlowAmount: UFix64
access(all) let flowReceived: UFix64
access(all) let requestId: String
access(all) let success: Bool
init(stFlowAmount: UFix64, flowReceived: UFix64, requestId: String, success: Bool) {
self.stFlowAmount = stFlowAmount
self.flowReceived = flowReceived
self.requestId = requestId
self.success = success
}
}
access(all) struct RouterStats {
access(all) let totalStakeOps: UInt64
access(all) let totalUnstakeOps: UInt64
access(all) let totalFlowStaked: UFix64
access(all) let totalStFlowMinted: UFix64
access(all) let currentFlowBalance: UFix64
access(all) let currentStFlowBalance: UFix64
access(all) let isActive: Bool
init(
totalStakeOps: UInt64,
totalUnstakeOps: UInt64,
totalFlowStaked: UFix64,
totalStFlowMinted: UFix64,
currentFlowBalance: UFix64,
currentStFlowBalance: UFix64,
isActive: Bool
) {
self.totalStakeOps = totalStakeOps
self.totalUnstakeOps = totalUnstakeOps
self.totalFlowStaked = totalFlowStaked
self.totalStFlowMinted = totalStFlowMinted
self.currentFlowBalance = currentFlowBalance
self.currentStFlowBalance = currentStFlowBalance
self.isActive = isActive
}
}
init() {
self.AdminStoragePath = /storage/ActionRouterAdmin
self.FlowVaultStoragePath = /storage/ActionRouterFlowVault
self.StFlowVaultStoragePath = /storage/ActionRouterStFlowVault
self.authorizedEVMCallers = {}
self.isActive = true
self.minStakeAmount = 1.0
self.maxStakeAmount = 10000.0
self.maxOperationsPerBlock = 10
self.currentBlockOperations = 0
self.lastBlockHeight = getCurrentBlock().height
self.totalStakeOperations = 0
self.totalUnstakeOperations = 0
self.totalFlowStaked = 0.0
self.totalStFlowMinted = 0.0
let admin <- create Admin()
self.account.storage.save(<-admin, to: self.AdminStoragePath)
let flowVault <- FlowToken.createEmptyVault(vaultType: Type<@FlowToken.Vault>())
self.account.storage.save(<-flowVault, to: self.FlowVaultStoragePath)
// Only create the capability if it doesn't already exist
if self.account.capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver).check() == false {
let flowReceiverCap = self.account.capabilities.storage.issue<&{FungibleToken.Receiver}>(self.FlowVaultStoragePath)
self.account.capabilities.publish(flowReceiverCap, at: /public/flowTokenReceiver)
}
}
access(all) resource Admin {
access(all) fun authorizeEVMCaller(caller: Address) {
ActionRouter.authorizedEVMCallers[caller] = true
emit EVMCallerAuthorized(caller: caller)
}
access(all) fun revokeEVMCaller(caller: Address) {
ActionRouter.authorizedEVMCallers.remove(key: caller)
emit EVMCallerRevoked(caller: caller)
}
access(all) fun setActive(active: Bool) {
ActionRouter.isActive = active
}
access(all) fun updateLimits(minStake: UFix64, maxStake: UFix64, maxOpsPerBlock: UInt64) {
pre {
minStake > 0.0: "Minimum stake must be positive"
maxStake >= minStake: "Maximum stake must be >= minimum"
maxOpsPerBlock > 0: "Max operations per block must be positive"
}
ActionRouter.minStakeAmount = minStake
ActionRouter.maxStakeAmount = maxStake
ActionRouter.maxOperationsPerBlock = maxOpsPerBlock
emit RouterConfigUpdated(minStake: minStake, maxStake: maxStake)
}
access(all) fun emergencyWithdraw(amount: UFix64, recipient: Address) {
pre {
amount > 0.0: "Amount must be positive"
}
// Fixed: Request authorized reference with Withdraw entitlement
let flowVaultRef = ActionRouter.account.storage.borrow<auth(FungibleToken.Withdraw) &FlowToken.Vault>(from: ActionRouter.FlowVaultStoragePath)
?? panic("Could not borrow FlowToken vault")
let withdrawnFlow <- flowVaultRef.withdraw(amount: amount)
let recipientAccount = getAccount(recipient)
// Fixed: Use FungibleToken.Receiver instead of FlowToken.Receiver
let recipientFlowVault = recipientAccount.capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver).borrow()
?? panic("Could not borrow recipient FlowToken receiver")
recipientFlowVault.deposit(from: <-withdrawnFlow)
}
}
access(self) fun checkRateLimit() {
let currentBlock = getCurrentBlock().height
if currentBlock > self.lastBlockHeight {
self.currentBlockOperations = 0
self.lastBlockHeight = currentBlock
}
assert(
self.currentBlockOperations < self.maxOperationsPerBlock,
message: "Rate limit exceeded for this block"
)
self.currentBlockOperations = self.currentBlockOperations + 1
}
access(all) fun stakeFlow(amount: UFix64, recipient: Address, requestId: String): StakeResult {
pre {
self.isActive: "Router is not active"
self.authorizedEVMCallers[recipient] == true: "Caller not authorized"
amount >= self.minStakeAmount: "Amount below minimum"
amount <= self.maxStakeAmount: "Amount above maximum"
}
self.checkRateLimit()
// Fixed: Request authorized reference with Withdraw entitlement
let flowVaultRef = self.account.storage.borrow<auth(FungibleToken.Withdraw) &FlowToken.Vault>(from: self.FlowVaultStoragePath)
?? panic("Could not borrow FlowToken vault")
assert(flowVaultRef.balance >= amount, message: "Insufficient FLOW balance in router")
let flowToStake <- flowVaultRef.withdraw(amount: amount) as! @FlowToken.Vault
// Note: You'll need to check the actual LiquidStaking contract for the correct function signature
let exchangeRateBefore = 1.0 // Placeholder - replace with actual call when LiquidStaking contract is available
let stFlowVault <- LiquidStaking.stake(flowVault: <-flowToStake)
let stFlowAmount = stFlowVault.balance
let exchangeRateAfter = 1.0 // Placeholder - replace with actual call when LiquidStaking contract is available
let existingStFlowVaultRef = self.account.storage.borrow<&stFlowToken.Vault>(from: self.StFlowVaultStoragePath)
if existingStFlowVaultRef != nil {
existingStFlowVaultRef!.deposit(from: <-stFlowVault)
} else {
self.account.storage.save(<-stFlowVault, to: self.StFlowVaultStoragePath)
}
self.totalStakeOperations = self.totalStakeOperations + 1
self.totalFlowStaked = self.totalFlowStaked + amount
self.totalStFlowMinted = self.totalStFlowMinted + stFlowAmount
emit StakeExecuted(
amount: amount,
recipient: recipient,
stFlowReceived: stFlowAmount,
exchangeRate: exchangeRateAfter,
requestId: requestId
)
return StakeResult(
flowAmount: amount,
stFlowReceived: stFlowAmount,
exchangeRate: exchangeRateAfter,
requestId: requestId,
success: true
)
}
access(all) fun unstakeFlow(stFlowAmount: UFix64, recipient: Address, requestId: String): UnstakeResult {
pre {
self.isActive: "Router is not active"
self.authorizedEVMCallers[recipient] == true: "Caller not authorized"
stFlowAmount > 0.0: "Amount must be positive"
}
self.checkRateLimit()
// Fixed: Request authorized reference with Withdraw entitlement
let stFlowVaultRef = self.account.storage.borrow<auth(FungibleToken.Withdraw) &stFlowToken.Vault>(from: self.StFlowVaultStoragePath)
?? panic("Could not borrow stFlowToken vault")
assert(stFlowVaultRef.balance >= stFlowAmount, message: "Insufficient stFLOW balance")
let stFlowToUnstake <- stFlowVaultRef.withdraw(amount: stFlowAmount) as! @stFlowToken.Vault
// Note: You'll need to check what LiquidStaking.unstake actually returns
let withdrawVoucher <- LiquidStaking.unstake(stFlowVault: <-stFlowToUnstake)
// This depends on what LiquidStaking.unstake returns - you may need to redeem the voucher
// For now, assuming it returns a flow vault directly
let flowAmount = 0.0 // You'll need to get this from the voucher or returned vault
// Handle the withdraw voucher appropriately - this is a placeholder
destroy withdrawVoucher
self.totalUnstakeOperations = self.totalUnstakeOperations + 1
emit UnstakeExecuted(
amount: stFlowAmount,
recipient: recipient,
flowReceived: flowAmount,
requestId: requestId
)
return UnstakeResult(
stFlowAmount: stFlowAmount,
flowReceived: flowAmount,
requestId: requestId,
success: true
)
}
access(all) view fun getExchangeRate(): UFix64 {
// Note: You'll need to check the actual LiquidStaking contract for the correct function
return 1.0 // Placeholder - replace with actual call when LiquidStaking contract is available
}
access(all) view fun getStFlowBalance(): UFix64 {
let stFlowVaultRef = self.account.storage.borrow<&stFlowToken.Vault>(from: self.StFlowVaultStoragePath)
if stFlowVaultRef != nil {
return stFlowVaultRef!.balance
}
return 0.0
}
access(all) view fun getFlowBalance(): UFix64 {
let flowVaultRef = self.account.storage.borrow<&FlowToken.Vault>(from: self.FlowVaultStoragePath)
if flowVaultRef != nil {
return flowVaultRef!.balance
}
return 0.0
}
// Fixed: Made this function non-view to avoid "impure operation" error
access(all) fun getStats(): RouterStats {
return RouterStats(
totalStakeOps: self.totalStakeOperations,
totalUnstakeOps: self.totalUnstakeOperations,
totalFlowStaked: self.totalFlowStaked,
totalStFlowMinted: self.totalStFlowMinted,
currentFlowBalance: self.getFlowBalance(),
currentStFlowBalance: self.getStFlowBalance(),
isActive: self.isActive
)
}
access(all) view fun isAuthorized(caller: Address): Bool {
return self.authorizedEVMCallers[caller] == true
}
}
Cadence Script
1transaction(name: String, code: String ) {
2 prepare(signer: auth(AddContract) &Account) {
3 signer.contracts.add(name: name, code: code.utf8 )
4 }
5 }