DeploySEALED

○~▒▓╲~?&!■&▫#╱■▪▫◆■□▓◆▫????~$*█╳◇#▪◆!■●$▫#&▒▪^╲╲●●◇!%&#□▓╳▒%▫■╳%

Transaction ID

Timestamp

Dec 01, 2025, 09:29:58 PM UTC
2mo ago

Block Height

134,619,625

Computation

0

Execution Fee

0.00002574 FLOW

Transaction Summary

Deploy

Contract deployment

Contract deployment

Script Arguments

0nameString
DCATransactionHandler
1codeString
import FlowTransactionScheduler from 0xe467b9dd11fa00df import DCAController from 0xca7ee55e4fc3251a import DCAPlan from 0xca7ee55e4fc3251a import DeFiMath from 0xca7ee55e4fc3251a import FungibleToken from 0xf233dcee88fe0abe import FlowToken from 0x1654653399040a61 import SwapRouter from 0xa6850776a94e6551 import TeleportedTetherToken from 0xcfdd90d4a00f7b5b /// DCATransactionHandler: Scheduled transaction handler for DCA execution /// /// This contract implements the FlowTransactionScheduler.TransactionHandler interface /// to enable autonomous DCA plan execution via Forte Scheduled Transactions. /// /// Architecture: /// 1. User creates DCA plan with DCAController /// 2. Plan schedules execution via FlowTransactionScheduler /// 3. At scheduled time, scheduler calls this handler's executeTransaction() /// 4. Handler: /// - Validates plan is ready /// - Builds DeFi Actions stack (Source → Swapper → Sink) /// - Executes swap /// - Updates plan accounting /// - Reschedules next execution if plan is still active /// /// Educational Notes: /// - Implements FlowTransactionScheduler.TransactionHandler interface /// - Access control: executeTransaction is access(FlowTransactionScheduler.Execute) /// - Uses DeFi Actions for composable swap execution /// - Stores metadata via getViews/resolveView for discoverability access(all) contract DCATransactionHandler { /// Event emitted when handler starts execution access(all) event HandlerExecutionStarted( transactionId: UInt64, planId: UInt64, owner: Address, timestamp: UFix64 ) /// Event emitted when handler completes successfully access(all) event HandlerExecutionCompleted( transactionId: UInt64, planId: UInt64, owner: Address, amountIn: UFix64, amountOut: UFix64, nextExecutionScheduled: Bool, timestamp: UFix64 ) /// Event emitted when handler execution fails access(all) event HandlerExecutionFailed( transactionId: UInt64, planId: UInt64?, owner: Address?, reason: String, timestamp: UFix64 ) /// Handler resource that implements the Scheduled Transaction interface /// /// Each user has one instance of this stored in their account. /// The scheduler calls executeTransaction() when a DCA plan is due. access(all) resource Handler: FlowTransactionScheduler.TransactionHandler { /// Reference to the user's DCA controller /// This capability allows the handler to access and update plans access(self) let controllerCap: Capability<auth(DCAController.Owner) &DCAController.Controller> init(controllerCap: Capability<auth(DCAController.Owner) &DCAController.Controller>) { pre { controllerCap.check(): "Invalid controller capability" } self.controllerCap = controllerCap } /// Main execution entrypoint called by FlowTransactionScheduler /// /// @param id: Transaction ID from the scheduler /// @param data: Encoded plan data (contains planId) /// /// This function has restricted access - only FlowTransactionScheduler can call it access(FlowTransactionScheduler.Execute) fun executeTransaction(id: UInt64, data: AnyStruct?) { let timestamp = getCurrentBlock().timestamp // Parse plan ID from transaction data let planData = data as! {String: UInt64}? ?? panic("Invalid transaction data format") let planId = planData["planId"] ?? panic("Plan ID not found in transaction data") let ownerAddress = self.controllerCap.address emit HandlerExecutionStarted( transactionId: id, planId: planId, owner: ownerAddress, timestamp: timestamp ) // Borrow controller let controller = self.controllerCap.borrow() ?? panic("Could not borrow DCA controller") // Borrow plan let planRef = controller.borrowPlan(id: planId) ?? panic("Could not borrow plan with ID: ".concat(planId.toString())) // Validate plan is ready if !planRef.isReadyForExecution() { emit HandlerExecutionFailed( transactionId: id, planId: planId, owner: ownerAddress, reason: "Plan not ready for execution", timestamp: timestamp ) return } // Check max executions if planRef.hasReachedMaxExecutions() { emit HandlerExecutionFailed( transactionId: id, planId: planId, owner: ownerAddress, reason: "Plan has reached maximum executions", timestamp: timestamp ) return } // Get vault capabilities let sourceVaultCap = controller.getSourceVaultCapability() ?? panic("Source vault capability not configured") let targetVaultCap = controller.getTargetVaultCapability() ?? panic("Target vault capability not configured") // Validate capabilities if !sourceVaultCap.check() { emit HandlerExecutionFailed( transactionId: id, planId: planId, owner: ownerAddress, reason: "Invalid source vault capability", timestamp: timestamp ) return } if !targetVaultCap.check() { emit HandlerExecutionFailed( transactionId: id, planId: planId, owner: ownerAddress, reason: "Invalid target vault capability", timestamp: timestamp ) return } // Execute the swap let result = self.executeSwap( planRef: planRef, sourceVaultCap: sourceVaultCap, targetVaultCap: targetVaultCap ) if result.success { // Record successful execution planRef.recordExecution( amountIn: result.amountIn!, amountOut: result.amountOut! ) // Schedule next execution if plan is still active var nextScheduled = false if planRef.status == DCAPlan.PlanStatus.Active && !planRef.hasReachedMaxExecutions() { planRef.scheduleNextExecution() nextScheduled = true // TODO: In production, call FlowTransactionScheduler.schedule() here // to schedule the next execution. See CLAUDE.md for integration notes. } emit HandlerExecutionCompleted( transactionId: id, planId: planId, owner: ownerAddress, amountIn: result.amountIn!, amountOut: result.amountOut!, nextExecutionScheduled: nextScheduled, timestamp: timestamp ) } else { emit HandlerExecutionFailed( transactionId: id, planId: planId, owner: ownerAddress, reason: result.errorMessage ?? "Unknown error", timestamp: timestamp ) } } /// Execute swap using IncrementFi SwapRouter /// /// This uses IncrementFi's production swap infrastructure to swap tokens. /// Supports USDT ↔ FLOW swaps with slippage protection. /// /// @param planRef: Reference to the DCA plan /// @param sourceVaultCap: Capability to withdraw from source vault /// @param targetVaultCap: Capability to deposit to target vault /// @return ExecutionResult with success status and amounts access(self) fun executeSwap( planRef: &DCAPlan.Plan, sourceVaultCap: Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>, targetVaultCap: Capability<&{FungibleToken.Receiver}> ): ExecutionResult { // Get amount to invest let amountIn = planRef.amountPerInterval // Borrow source vault and check balance let sourceVault = sourceVaultCap.borrow() ?? panic("Could not borrow source vault") if sourceVault.balance < amountIn { return ExecutionResult( success: false, amountIn: nil, amountOut: nil, errorMessage: "Insufficient balance in source vault. Required: ".concat(amountIn.toString()).concat(", Available: ").concat(sourceVault.balance.toString()) ) } // Withdraw tokens to swap let tokensToSwap <- sourceVault.withdraw(amount: amountIn) // Determine swap path based on token types let sourceTypeId = planRef.sourceTokenType.identifier let targetTypeId = planRef.targetTokenType.identifier let tokenPath: [String] = [] if sourceTypeId.contains("TeleportedTetherToken") && targetTypeId.contains("FlowToken") { // USDT → FLOW tokenPath.append("A.cfdd90d4a00f7b5b.TeleportedTetherToken") tokenPath.append("A.1654653399040a61.FlowToken") } else if sourceTypeId.contains("FlowToken") && targetTypeId.contains("TeleportedTetherToken") { // FLOW → USDT tokenPath.append("A.1654653399040a61.FlowToken") tokenPath.append("A.cfdd90d4a00f7b5b.TeleportedTetherToken") } else { // Unsupported swap path destroy tokensToSwap return ExecutionResult( success: false, amountIn: nil, amountOut: nil, errorMessage: "Unsupported token pair. Only USDT ↔ FLOW swaps are currently supported." ) } // Get expected output amount from IncrementFi let expectedAmountsOut = SwapRouter.getAmountsOut( amountIn: amountIn, tokenKeyPath: tokenPath ) let expectedAmountOut = expectedAmountsOut[expectedAmountsOut.length - 1] // Calculate minimum output with slippage protection // maxSlippageBps is in basis points (100 = 1%) let slippageMultiplier = UInt64(10000) - planRef.maxSlippageBps let minAmountOut = expectedAmountOut * UFix64(slippageMultiplier) / 10000.0 // Set deadline (5 minutes from now) let deadline = getCurrentBlock().timestamp + 300.0 // Execute swap via IncrementFi SwapRouter let swappedTokens <- SwapRouter.swapExactTokensForTokens( exactVaultIn: <-tokensToSwap, amountOutMin: minAmountOut, tokenKeyPath: tokenPath, deadline: deadline ) // Get actual output amount let actualAmountOut = swappedTokens.balance // Deposit to target vault let targetVault = targetVaultCap.borrow() ?? panic("Could not borrow target vault") targetVault.deposit(from: <-swappedTokens) return ExecutionResult( success: true, amountIn: amountIn, amountOut: actualAmountOut, errorMessage: nil ) } /// Get supported view types (for resource metadata) access(all) view fun getViews(): [Type] { return [Type<StoragePath>(), Type<PublicPath>()] } /// Resolve a specific view type access(all) fun resolveView(_ view: Type): AnyStruct? { switch view { case Type<StoragePath>(): return /storage/DCATransactionHandler case Type<PublicPath>(): return /public/DCATransactionHandler default: return nil } } } /// Result struct for swap execution access(all) struct ExecutionResult { access(all) let success: Bool access(all) let amountIn: UFix64? access(all) let amountOut: UFix64? access(all) let errorMessage: String? init(success: Bool, amountIn: UFix64?, amountOut: UFix64?, errorMessage: String?) { self.success = success self.amountIn = amountIn self.amountOut = amountOut self.errorMessage = errorMessage } } /// Factory function to create a new handler /// /// @param controllerCap: Capability to the user's DCA controller /// @return New handler resource access(all) fun createHandler( controllerCap: Capability<auth(DCAController.Owner) &DCAController.Controller> ): @Handler { return <- create Handler(controllerCap: controllerCap) } }

Cadence Script

1transaction(name: String, code: String ) {
2		prepare(signer: auth(AddContract) &Account) {
3			signer.contracts.add(name: name, code: code.utf8 )
4		}
5	}