Smart Contract

DCATransactionHandlerEVMMinimal

A.ca7ee55e4fc3251a.DCATransactionHandlerEVMMinimal

Valid From

135,629,328

Deployed

5d ago
Feb 23, 2026, 12:44:35 AM UTC

Dependents

3 imports
1import FlowTransactionScheduler from 0xe467b9dd11fa00df
2import DCAControllerUnified from 0xca7ee55e4fc3251a
3import DCAPlanUnified from 0xca7ee55e4fc3251a
4import FungibleToken from 0xf233dcee88fe0abe
5import FlowToken from 0x1654653399040a61
6import EVM from 0xe467b9dd11fa00df
7import DeFiActions from 0xca7ee55e4fc3251a
8import UniswapV3SwapperConnector from 0xca7ee55e4fc3251a
9
10/// DCATransactionHandlerEVMMinimal: Incremental EVM swap handler
11///
12/// PURPOSE: Test EVM swap execution step by step
13/// Step 3: Add UniswapV3 swap logic
14///
15/// Storage: /storage/DCATransactionHandlerEVMMinimal
16access(all) contract DCATransactionHandlerEVMMinimal {
17
18    /// Simple transaction data
19    access(all) struct SimpleTransactionData {
20        access(all) let planId: UInt64
21        init(planId: UInt64) { self.planId = planId }
22    }
23
24    /// Events
25    access(all) event HandlerExecuted(
26        transactionId: UInt64,
27        planId: UInt64,
28        owner: Address,
29        requiresEVM: Bool,
30        timestamp: UFix64
31    )
32
33    access(all) event SwapCompleted(
34        transactionId: UInt64,
35        planId: UInt64,
36        amountIn: UFix64,
37        amountOut: UFix64
38    )
39
40    // Debug events for step-by-step tracing
41    access(all) event DebugStep(step: String, transactionId: UInt64)
42    access(all) event DebugQuote(expectedAmount: UFix64, transactionId: UInt64)
43
44    /// Handler resource
45    access(all) resource Handler: FlowTransactionScheduler.TransactionHandler {
46        access(self) let controllerCap: Capability<auth(DCAControllerUnified.Owner) &DCAControllerUnified.Controller>
47
48        init(controllerCap: Capability<auth(DCAControllerUnified.Owner) &DCAControllerUnified.Controller>) {
49            pre { controllerCap.check(): "Invalid controller capability" }
50            self.controllerCap = controllerCap
51        }
52
53        access(FlowTransactionScheduler.Execute) fun executeTransaction(id: UInt64, data: AnyStruct?) {
54            emit DebugStep(step: "1_start", transactionId: id)
55
56            let timestamp = getCurrentBlock().timestamp
57            let ownerAddress = self.controllerCap.address
58
59            // Parse data
60            let txData = data as? SimpleTransactionData
61            if txData == nil {
62                emit DebugStep(step: "FAIL_invalid_data", transactionId: id)
63                return
64            }
65            let planId = txData!.planId
66
67            emit DebugStep(step: "2_data_parsed", transactionId: id)
68
69            // Borrow controller
70            let controller = self.controllerCap.borrow()
71            if controller == nil {
72                emit DebugStep(step: "FAIL_no_controller", transactionId: id)
73                return
74            }
75
76            emit DebugStep(step: "3_controller_borrowed", transactionId: id)
77
78            // Borrow plan
79            let planRef = controller!.borrowPlan(id: planId)
80            if planRef == nil {
81                emit DebugStep(step: "FAIL_no_plan", transactionId: id)
82                return
83            }
84
85            emit DebugStep(step: "4_plan_borrowed", transactionId: id)
86
87            let requiresEVM = planRef!.requiresEVM()
88
89            if !requiresEVM {
90                emit DebugStep(step: "5_not_evm_plan", transactionId: id)
91                emit HandlerExecuted(transactionId: id, planId: planId, owner: ownerAddress, requiresEVM: false, timestamp: timestamp)
92                return
93            }
94
95            emit DebugStep(step: "5_evm_plan_confirmed", transactionId: id)
96
97            // Get COA capability
98            let coaCap = controller!.getCOACapability()
99            if coaCap == nil || !coaCap!.check() {
100                emit DebugStep(step: "FAIL_coa_issue", transactionId: id)
101                return
102            }
103
104            emit DebugStep(step: "6_coa_valid", transactionId: id)
105
106            // Get source vault
107            let sourceVaultCap = controller!.getSourceVaultCapability()
108            if sourceVaultCap == nil || !sourceVaultCap!.check() {
109                emit DebugStep(step: "FAIL_source_vault", transactionId: id)
110                return
111            }
112            let sourceVault = sourceVaultCap!.borrow()!
113
114            emit DebugStep(step: "7_source_vault_valid", transactionId: id)
115
116            // Get target vault
117            let targetVaultCap = controller!.getTargetVaultCapability()
118            if targetVaultCap == nil || !targetVaultCap!.check() {
119                emit DebugStep(step: "FAIL_target_vault", transactionId: id)
120                return
121            }
122
123            emit DebugStep(step: "8_target_vault_valid", transactionId: id)
124
125            let amountIn = planRef!.amountPerInterval
126            if sourceVault.balance < amountIn {
127                emit DebugStep(step: "FAIL_insufficient_balance", transactionId: id)
128                return
129            }
130
131            emit DebugStep(step: "9_balance_ok", transactionId: id)
132
133            // Withdraw FLOW for swap
134            let tokensToSwap <- sourceVault.withdraw(amount: amountIn) as! @FlowToken.Vault
135
136            emit DebugStep(step: "10_flow_withdrawn", transactionId: id)
137
138            // Setup EVM addresses
139            let usdfEVMAddress = EVM.addressFromString("0x2aabea2058b5ac2d339b163c6ab6f2b6d53aabed")
140            let wflowEVMAddress = EVM.addressFromString("0xd3bF53DAC106A0290B0483EcBC89d40FcC961f3e")
141            let tokenPath: [EVM.EVMAddress] = [wflowEVMAddress, usdfEVMAddress]
142
143            emit DebugStep(step: "11_addresses_setup", transactionId: id)
144
145            // Create swapper
146            let swapper <- UniswapV3SwapperConnector.createSwapperWithDefaults(
147                tokenPath: tokenPath,
148                feePath: [3000],  // 0.3% fee tier
149                inVaultType: planRef!.sourceTokenType,
150                outVaultType: planRef!.targetTokenType,
151                coaCapability: coaCap!
152            )
153
154            emit DebugStep(step: "12_swapper_created", transactionId: id)
155
156            // Get quote
157            let quote = swapper.getQuote(
158                fromTokenType: planRef!.sourceTokenType,
159                toTokenType: planRef!.targetTokenType,
160                amount: amountIn
161            )
162
163            emit DebugStep(step: "13_quote_received", transactionId: id)
164            emit DebugQuote(expectedAmount: quote.expectedAmount, transactionId: id)
165
166            // Apply slippage
167            let minAmount = quote.expectedAmount * (10000.0 - UFix64(planRef!.maxSlippageBps)) / 10000.0
168            let adjustedQuote = DeFiActions.Quote(
169                expectedAmount: quote.expectedAmount,
170                minAmount: minAmount,
171                slippageTolerance: UFix64(planRef!.maxSlippageBps) / 10000.0,
172                deadline: nil,
173                data: quote.data
174            )
175
176            emit DebugStep(step: "14_quote_adjusted", transactionId: id)
177
178            // Execute swap
179            let swapped <- swapper.swap(inVault: <-tokensToSwap, quote: adjustedQuote)
180            let amountOut = swapped.balance
181
182            emit DebugStep(step: "15_swap_executed", transactionId: id)
183
184            // Deposit to target
185            let targetVault = targetVaultCap!.borrow()!
186            targetVault.deposit(from: <-swapped)
187
188            emit DebugStep(step: "16_deposited", transactionId: id)
189
190            // Cleanup
191            destroy swapper
192
193            emit HandlerExecuted(transactionId: id, planId: planId, owner: ownerAddress, requiresEVM: true, timestamp: timestamp)
194            emit SwapCompleted(transactionId: id, planId: planId, amountIn: amountIn, amountOut: amountOut)
195
196            emit DebugStep(step: "17_complete", transactionId: id)
197        }
198
199        access(all) view fun getViews(): [Type] {
200            return [Type<StoragePath>()]
201        }
202
203        access(all) fun resolveView(_ view: Type): AnyStruct? {
204            switch view {
205                case Type<StoragePath>():
206                    return /storage/DCATransactionHandlerEVMMinimal
207                default:
208                    return nil
209            }
210        }
211    }
212
213    access(all) fun createHandler(
214        controllerCap: Capability<auth(DCAControllerUnified.Owner) &DCAControllerUnified.Controller>
215    ): @Handler {
216        return <- create Handler(controllerCap: controllerCap)
217    }
218
219    access(all) fun createTransactionData(planId: UInt64): SimpleTransactionData {
220        return SimpleTransactionData(planId: planId)
221    }
222}
223