DeploySEALED

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

Transaction ID

Timestamp

Dec 01, 2025, 09:28:50 PM UTC
2mo ago

Block Height

134,619,540

Computation

0

Execution Fee

0.00002674 FLOW

Transaction Summary

Deploy

Contract deployment

Contract deployment

Script Arguments

0nameString
DCAPlan
1codeString
import DeFiMath from 0xca7ee55e4fc3251a import FungibleToken from 0xf233dcee88fe0abe /// DCAPlan: Resource representing a single Dollar-Cost Averaging investment plan /// /// Each plan represents a recurring investment strategy: /// - Source token (e.g., FLOW) → Target token (e.g., Beaver) /// - Executes at regular intervals using Scheduled Transactions /// - Tracks performance metrics (total invested, acquired, average price) /// - Uses IncrementFi connectors for swaps /// /// Educational Notes: /// - Plans are resources owned by users, stored in DCAController /// - Each execution is atomic: withdraw → swap → deposit /// - Slippage protection prevents unfavorable trades access(all) contract DCAPlan { /// Event emitted when a new DCA plan is created access(all) event PlanCreated( planId: UInt64, owner: Address, sourceTokenType: String, targetTokenType: String, amountPerInterval: UFix64, intervalSeconds: UInt64, maxSlippageBps: UInt64 ) /// Event emitted when a plan is executed access(all) event PlanExecuted( planId: UInt64, executionCount: UInt64, amountIn: UFix64, amountOut: UFix64, executionPriceFP128: UInt128, newAvgPriceFP128: UInt128, timestamp: UFix64 ) /// Event emitted when a plan is updated access(all) event PlanUpdated( planId: UInt64, active: Bool, nextExecutionTime: UFix64? ) /// Event emitted when a plan is paused access(all) event PlanPaused(planId: UInt64, timestamp: UFix64) /// Event emitted when a plan is resumed access(all) event PlanResumed(planId: UInt64, nextExecutionTime: UFix64) /// Storage and public paths access(all) let PlanStoragePath: StoragePath access(all) let PlanPublicPath: PublicPath /// Global plan ID counter access(all) var nextPlanId: UInt64 /// Status enum for plan state access(all) enum PlanStatus: UInt8 { access(all) case Active access(all) case Paused access(all) case Completed access(all) case Cancelled } /// The core DCA Plan resource /// /// This resource encapsulates all state and logic for a single DCA plan. /// It is owned by the user and stored in their DCAController. access(all) resource Plan { /// Unique identifier for this plan access(all) let id: UInt64 /// Source token type identifier (e.g., A.1654653399040a61.FlowToken.Vault) access(all) let sourceTokenType: Type /// Target token type identifier (e.g., A.687e1a7aef17b78b.Beaver.Vault) access(all) let targetTokenType: Type /// Amount of source token to invest per interval access(all) let amountPerInterval: UFix64 /// Interval between executions in seconds access(all) let intervalSeconds: UInt64 /// Maximum acceptable slippage in basis points (e.g., 100 = 1%) access(all) let maxSlippageBps: UInt64 /// Optional maximum number of executions (nil = unlimited) access(all) let maxExecutions: UInt64? /// Current status of the plan access(all) var status: PlanStatus /// Timestamp of next scheduled execution access(all) var nextExecutionTime: UFix64? /// Number of times this plan has been executed access(all) var executionCount: UInt64 /// Total amount of source token invested (sum of all executions) access(all) var totalSourceInvested: UFix64 /// Total amount of target token acquired (sum of all executions) access(all) var totalTargetAcquired: UFix64 /// Weighted average execution price in FP128 format /// Represents target tokens received per source token access(all) var avgExecutionPriceFP128: UInt128 /// Timestamp when plan was created access(all) let createdAt: UFix64 /// Timestamp when plan was last executed access(all) var lastExecutedAt: UFix64? init( sourceTokenType: Type, targetTokenType: Type, amountPerInterval: UFix64, intervalSeconds: UInt64, maxSlippageBps: UInt64, maxExecutions: UInt64?, firstExecutionTime: UFix64 ) { pre { amountPerInterval > 0.0: "Amount per interval must be positive" intervalSeconds > 0: "Interval must be positive" DeFiMath.isValidSlippage(slippageBps: maxSlippageBps): "Invalid slippage value" firstExecutionTime > getCurrentBlock().timestamp: "First execution must be in the future" } self.id = DCAPlan.nextPlanId DCAPlan.nextPlanId = DCAPlan.nextPlanId + 1 self.sourceTokenType = sourceTokenType self.targetTokenType = targetTokenType self.amountPerInterval = amountPerInterval self.intervalSeconds = intervalSeconds self.maxSlippageBps = maxSlippageBps self.maxExecutions = maxExecutions self.status = PlanStatus.Active self.nextExecutionTime = firstExecutionTime self.executionCount = 0 self.totalSourceInvested = 0.0 self.totalTargetAcquired = 0.0 self.avgExecutionPriceFP128 = 0 self.createdAt = getCurrentBlock().timestamp self.lastExecutedAt = nil } /// Check if plan is ready for execution /// /// @return true if plan should execute now access(all) view fun isReadyForExecution(): Bool { if self.status != PlanStatus.Active { return false } if let nextTime = self.nextExecutionTime { return getCurrentBlock().timestamp >= nextTime } return false } /// Check if plan has reached max executions access(all) view fun hasReachedMaxExecutions(): Bool { if let max = self.maxExecutions { return self.executionCount >= max } return false } /// Record a successful execution /// /// Updates all accounting fields and calculates new average price. /// This should be called by the scheduled handler after swap execution. access(all) fun recordExecution(amountIn: UFix64, amountOut: UFix64) { pre { self.status == PlanStatus.Active: "Plan must be active" amountIn > 0.0: "Amount in must be positive" amountOut > 0.0: "Amount out must be positive" } // Calculate execution price let executionPrice = DeFiMath.calculatePriceFP128( amountIn: amountIn, amountOut: amountOut ) // Update weighted average price let newAvgPrice = DeFiMath.updateWeightedAveragePriceFP128( previousAvgPriceFP128: self.avgExecutionPriceFP128, totalPreviousIn: self.totalSourceInvested, newAmountIn: amountIn, newAmountOut: amountOut ) // Update accounting self.totalSourceInvested = self.totalSourceInvested + amountIn self.totalTargetAcquired = self.totalTargetAcquired + amountOut self.avgExecutionPriceFP128 = newAvgPrice self.executionCount = self.executionCount + 1 self.lastExecutedAt = getCurrentBlock().timestamp // Emit execution event emit PlanExecuted( planId: self.id, executionCount: self.executionCount, amountIn: amountIn, amountOut: amountOut, executionPriceFP128: executionPrice, newAvgPriceFP128: newAvgPrice, timestamp: getCurrentBlock().timestamp ) // Check if plan should complete if self.hasReachedMaxExecutions() { self.status = PlanStatus.Completed self.nextExecutionTime = nil emit PlanUpdated(planId: self.id, active: false, nextExecutionTime: nil) } } /// Schedule next execution /// /// Calculates and sets the next execution time based on interval. /// Should be called after recordExecution. access(all) fun scheduleNextExecution() { pre { self.status == PlanStatus.Active: "Plan must be active to schedule" !self.hasReachedMaxExecutions(): "Plan has reached max executions" } let currentTime = getCurrentBlock().timestamp let nextTime = currentTime + UFix64(self.intervalSeconds) self.nextExecutionTime = nextTime emit PlanUpdated( planId: self.id, active: true, nextExecutionTime: nextTime ) } /// Pause the plan /// /// Prevents further executions until resumed. access(all) fun pause() { pre { self.status == PlanStatus.Active: "Plan must be active to pause" } self.status = PlanStatus.Paused self.nextExecutionTime = nil emit PlanPaused(planId: self.id, timestamp: getCurrentBlock().timestamp) } /// Resume the plan /// /// Re-activates plan and schedules next execution. access(all) fun resume(nextExecutionTime: UFix64?) { pre { self.status == PlanStatus.Paused: "Plan must be paused to resume" !self.hasReachedMaxExecutions(): "Cannot resume completed plan" } self.status = PlanStatus.Active // If next execution time provided, use it; otherwise use interval from now let nextTime = nextExecutionTime ?? (getCurrentBlock().timestamp + UFix64(self.intervalSeconds)) self.nextExecutionTime = nextTime emit PlanResumed(planId: self.id, nextExecutionTime: nextTime) } /// Get plan details as a struct for easy querying access(all) fun getDetails(): PlanDetails { return PlanDetails( id: self.id, sourceTokenType: self.sourceTokenType.identifier, targetTokenType: self.targetTokenType.identifier, amountPerInterval: self.amountPerInterval, intervalSeconds: self.intervalSeconds, maxSlippageBps: self.maxSlippageBps, maxExecutions: self.maxExecutions, status: self.status.rawValue, nextExecutionTime: self.nextExecutionTime, executionCount: self.executionCount, totalSourceInvested: self.totalSourceInvested, totalTargetAcquired: self.totalTargetAcquired, avgExecutionPriceFP128: self.avgExecutionPriceFP128, avgExecutionPriceDisplay: DeFiMath.fp128ToUFix64(priceFP128: self.avgExecutionPriceFP128), createdAt: self.createdAt, lastExecutedAt: self.lastExecutedAt ) } } /// Public struct for plan details (used in scripts) access(all) struct PlanDetails { access(all) let id: UInt64 access(all) let sourceTokenType: String access(all) let targetTokenType: String access(all) let amountPerInterval: UFix64 access(all) let intervalSeconds: UInt64 access(all) let maxSlippageBps: UInt64 access(all) let maxExecutions: UInt64? access(all) let status: UInt8 access(all) let nextExecutionTime: UFix64? access(all) let executionCount: UInt64 access(all) let totalSourceInvested: UFix64 access(all) let totalTargetAcquired: UFix64 access(all) let avgExecutionPriceFP128: UInt128 access(all) let avgExecutionPriceDisplay: UFix64 access(all) let createdAt: UFix64 access(all) let lastExecutedAt: UFix64? init( id: UInt64, sourceTokenType: String, targetTokenType: String, amountPerInterval: UFix64, intervalSeconds: UInt64, maxSlippageBps: UInt64, maxExecutions: UInt64?, status: UInt8, nextExecutionTime: UFix64?, executionCount: UInt64, totalSourceInvested: UFix64, totalTargetAcquired: UFix64, avgExecutionPriceFP128: UInt128, avgExecutionPriceDisplay: UFix64, createdAt: UFix64, lastExecutedAt: UFix64? ) { self.id = id self.sourceTokenType = sourceTokenType self.targetTokenType = targetTokenType self.amountPerInterval = amountPerInterval self.intervalSeconds = intervalSeconds self.maxSlippageBps = maxSlippageBps self.maxExecutions = maxExecutions self.status = status self.nextExecutionTime = nextExecutionTime self.executionCount = executionCount self.totalSourceInvested = totalSourceInvested self.totalTargetAcquired = totalTargetAcquired self.avgExecutionPriceFP128 = avgExecutionPriceFP128 self.avgExecutionPriceDisplay = avgExecutionPriceDisplay self.createdAt = createdAt self.lastExecutedAt = lastExecutedAt } } /// Create a new DCA plan access(all) fun createPlan( sourceTokenType: Type, targetTokenType: Type, amountPerInterval: UFix64, intervalSeconds: UInt64, maxSlippageBps: UInt64, maxExecutions: UInt64?, firstExecutionTime: UFix64 ): @Plan { return <- create Plan( sourceTokenType: sourceTokenType, targetTokenType: targetTokenType, amountPerInterval: amountPerInterval, intervalSeconds: intervalSeconds, maxSlippageBps: maxSlippageBps, maxExecutions: maxExecutions, firstExecutionTime: firstExecutionTime ) } init() { self.nextPlanId = 1 self.PlanStoragePath = /storage/DCAPlan self.PlanPublicPath = /public/DCAPlan } }

Cadence Script

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