Smart Contract

DCAController

A.ca7ee55e4fc3251a.DCAController

Valid From

134,619,554

Deployed

5d ago
Feb 22, 2026, 02:49:55 AM UTC

Dependents

8 imports
1import DCAPlan from 0xca7ee55e4fc3251a
2import FungibleToken from 0xf233dcee88fe0abe
3import FlowToken from 0x1654653399040a61
4import TeleportedTetherToken from 0xcfdd90d4a00f7b5b
5
6/// DCAController: User's DCA management resource
7///
8/// Each user has one DCAController stored in their account that:
9/// - Holds all their DCA plans
10/// - Stores capabilities to their token vaults
11/// - Provides public interface for querying plans
12///
13/// Educational Notes:
14/// - One controller per user, stored at /storage/DCAController
15/// - Controller holds references (not vaults) to user's tokens
16/// - Scheduled handlers borrow capabilities from the controller
17/// - Owner entitlement grants privileged access to handler
18access(all) contract DCAController {
19
20    /// Owner entitlement for privileged controller access
21    /// This is required by DCATransactionHandler to update plans
22    access(all) entitlement Owner
23
24    /// Storage paths
25    access(all) let ControllerStoragePath: StoragePath
26    access(all) let ControllerPublicPath: PublicPath
27
28    /// Event emitted when a controller is created
29    access(all) event ControllerCreated(owner: Address)
30
31    /// Event emitted when a plan is added to controller
32    access(all) event PlanAddedToController(owner: Address, planId: UInt64)
33
34    /// Event emitted when a plan is removed from controller
35    access(all) event PlanRemovedFromController(owner: Address, planId: UInt64)
36
37    /// Public interface for reading controller state
38    access(all) resource interface ControllerPublic {
39        access(all) view fun getPlanIds(): [UInt64]
40        access(all) view fun getPlan(id: UInt64): &DCAPlan.Plan?
41        access(all) fun getAllPlans(): [DCAPlan.PlanDetails]
42        access(all) fun getActivePlans(): [DCAPlan.PlanDetails]
43    }
44
45    /// The main Controller resource
46    ///
47    /// Stores all DCA plans for a user and manages vault capabilities.
48    access(all) resource Controller: ControllerPublic {
49        /// Dictionary of all plans owned by this controller
50        access(self) let plans: @{UInt64: DCAPlan.Plan}
51
52        /// Capability to withdraw from source token vault (typically FLOW)
53        /// This is used by scheduled handlers to fund DCA executions
54        access(self) var sourceVaultCap: Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>?
55
56        /// Capability to deposit to target token vault (typically Beaver)
57        /// This is used by scheduled handlers to deposit acquired tokens
58        access(self) var targetVaultCap: Capability<&{FungibleToken.Receiver}>?
59
60        init() {
61            self.plans <- {}
62            self.sourceVaultCap = nil
63            self.targetVaultCap = nil
64        }
65
66        /// Set the source vault capability
67        ///
68        /// This should be called once during setup to give the controller
69        /// permission to withdraw from the user's source token vault.
70        ///
71        /// @param cap: Capability with withdraw auth to source vault
72        access(all) fun setSourceVaultCapability(
73            cap: Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>
74        ) {
75            pre {
76                cap.check(): "Invalid source vault capability"
77            }
78            self.sourceVaultCap = cap
79        }
80
81        /// Set the target vault capability
82        ///
83        /// This should be called once during setup to give the controller
84        /// permission to deposit to the user's target token vault.
85        ///
86        /// @param cap: Capability to deposit to target vault
87        access(all) fun setTargetVaultCapability(
88            cap: Capability<&{FungibleToken.Receiver}>
89        ) {
90            pre {
91                cap.check(): "Invalid target vault capability"
92            }
93            self.targetVaultCap = cap
94        }
95
96        /// Get source vault capability (for scheduled handler)
97        access(all) fun getSourceVaultCapability(): Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>? {
98            return self.sourceVaultCap
99        }
100
101        /// Get target vault capability (for scheduled handler)
102        access(all) fun getTargetVaultCapability(): Capability<&{FungibleToken.Receiver}>? {
103            return self.targetVaultCap
104        }
105
106        /// Add a new plan to this controller
107        ///
108        /// @param plan: The DCA plan resource to add
109        access(all) fun addPlan(plan: @DCAPlan.Plan) {
110            let planId = plan.id
111
112            // Ensure no duplicate plan IDs
113            assert(!self.plans.containsKey(planId), message: "Plan already exists")
114
115            self.plans[planId] <-! plan
116
117            // Emit event (note: owner address must be obtained from context)
118            emit PlanAddedToController(owner: self.owner!.address, planId: planId)
119        }
120
121        /// Remove and return a plan from this controller
122        ///
123        /// This allows users to cancel plans or transfer them.
124        ///
125        /// @param id: Plan ID to remove
126        /// @return The removed plan resource
127        access(all) fun removePlan(id: UInt64): @DCAPlan.Plan {
128            pre {
129                self.plans.containsKey(id): "Plan does not exist"
130            }
131
132            let plan <- self.plans.remove(key: id)!
133            emit PlanRemovedFromController(owner: self.owner!.address, planId: id)
134            return <- plan
135        }
136
137        /// Borrow a reference to a plan (mutable)
138        ///
139        /// Used by scheduled handlers to update plan state during execution.
140        /// Requires Owner entitlement for privileged access.
141        ///
142        /// @param id: Plan ID
143        /// @return Mutable reference to the plan
144        access(Owner) fun borrowPlan(id: UInt64): &DCAPlan.Plan? {
145            return &self.plans[id]
146        }
147
148        // ========================================
149        // Public Interface Implementation
150        // ========================================
151
152        /// Get all plan IDs in this controller
153        access(all) view fun getPlanIds(): [UInt64] {
154            return self.plans.keys
155        }
156
157        /// Get a read-only reference to a specific plan
158        access(all) view fun getPlan(id: UInt64): &DCAPlan.Plan? {
159            return &self.plans[id]
160        }
161
162        /// Get details of all plans
163        access(all) fun getAllPlans(): [DCAPlan.PlanDetails] {
164            let details: [DCAPlan.PlanDetails] = []
165            for id in self.plans.keys {
166                if let plan = &self.plans[id] as &DCAPlan.Plan? {
167                    details.append(plan.getDetails())
168                }
169            }
170            return details
171        }
172
173        /// Get details of only active plans
174        access(all) fun getActivePlans(): [DCAPlan.PlanDetails] {
175            let details: [DCAPlan.PlanDetails] = []
176            for id in self.plans.keys {
177                if let plan = &self.plans[id] as &DCAPlan.Plan? {
178                    if plan.status == DCAPlan.PlanStatus.Active {
179                        details.append(plan.getDetails())
180                    }
181                }
182            }
183            return details
184        }
185
186        /// Check if controller has required capabilities configured
187        access(all) view fun isFullyConfigured(): Bool {
188            if let sourceCap = self.sourceVaultCap {
189                if let targetCap = self.targetVaultCap {
190                    return sourceCap.check() && targetCap.check()
191                }
192            }
193            return false
194        }
195    }
196
197    /// Create a new DCA controller
198    ///
199    /// Users call this once to set up their DCA management resource.
200    access(all) fun createController(): @Controller {
201        return <- create Controller()
202    }
203
204    init() {
205        self.ControllerStoragePath = /storage/DCAController
206        self.ControllerPublicPath = /public/DCAController
207    }
208}
209