Smart Contract

FlowMateScheduledActionsHandler

A.136a10c590912ef8.FlowMateScheduledActionsHandler

Valid From

130,946,564

Deployed

1w ago
Feb 21, 2026, 03:23:12 PM UTC

Dependents

3 imports
1import FlowTransactionScheduler from 0xe467b9dd11fa00df
2import FungibleToken from 0xf233dcee88fe0abe
3import FlowToken from 0x1654653399040a61
4import USDCFlow from 0xf1ab99c82dee3526
5import SwapConfig from 0xb78ef7afa52ff906
6import IncrementFiSwapConnectors from 0xefa9bd7d1b17f1ed
7import DeFiActions from 0x92195d814edf9cb0
8import MetadataViews from 0x1d7e57aa55817448
9
10access(all) contract FlowMateScheduledActionsHandler {
11
12    access(all) let HandlerStoragePath: StoragePath
13    access(all) let HandlerPublicPath: PublicPath
14
15    access(all) event ActionExecuted(id: UInt64, actionType: String, success: Bool, message: String)
16    access(all) event SendTokenExecuted(id: UInt64, recipient: Address, amount: UFix64, tokenType: String)
17    access(all) event SwapTokenExecuted(id: UInt64, fromToken: String, toToken: String, amountIn: UFix64, amountOut: UFix64)
18    access(all) event RecurringPaymentExecuted(id: UInt64, recipient: Address, amount: UFix64, tokenType: String)
19    access(all) event BatchSendExecuted(id: UInt64, recipientCount: Int, totalAmount: UFix64)
20
21    access(all) struct ActionData {
22        access(all) let type: String
23        access(all) let parameters: {String: AnyStruct}
24
25        init(type: String, parameters: {String: AnyStruct}) {
26            self.type = type
27            self.parameters = parameters
28        }
29    }
30
31    access(all) resource Handler: FlowTransactionScheduler.TransactionHandler {
32        
33        access(all) let flowVaultCap: Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>
34        access(all) let usdcVaultCap: Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>
35
36        init(
37            flowVaultCap: Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>,
38            usdcVaultCap: Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>
39        ) {
40            self.flowVaultCap = flowVaultCap
41            self.usdcVaultCap = usdcVaultCap
42        }
43
44        access(FlowTransactionScheduler.Execute) 
45        fun executeTransaction(id: UInt64, data: AnyStruct?) {
46            let actionData = data as? ActionData
47                ?? panic("Invalid action data provided")
48
49            switch actionData.type {
50                case "send_token":
51                    self.executeSendToken(id: id, params: actionData.parameters)
52                case "swap_token":
53                    self.executeSwapToken(id: id, params: actionData.parameters)
54                case "recurring_payment":
55                    self.executeRecurringPayment(id: id, params: actionData.parameters)
56                case "batch_send":
57                    self.executeBatchSend(id: id, params: actionData.parameters)
58                default:
59                    emit ActionExecuted(id: id, actionType: actionData.type, success: false, message: "Unknown action type")
60                    panic("Unknown action type: ".concat(actionData.type))
61            }
62        }
63
64        access(self) fun executeSendToken(id: UInt64, params: {String: AnyStruct}) {
65            let recipient = params["recipient"] as? Address
66                ?? panic("Missing or invalid recipient address")
67            let amount = params["amount"] as? UFix64
68                ?? panic("Missing or invalid amount")
69            let tokenType = params["tokenType"] as? String ?? "FlowToken"
70
71            let vaultCap = tokenType == "FlowToken" ? self.flowVaultCap : self.usdcVaultCap
72            let vault = vaultCap.borrow()
73                ?? panic("Could not borrow vault capability")
74
75            if vault.balance < amount {
76                emit ActionExecuted(id: id, actionType: "send_token", success: false, message: "Insufficient balance")
77                panic("Insufficient balance")
78            }
79
80            let paymentVault <- vault.withdraw(amount: amount)
81
82            let receiverPath = tokenType == "FlowToken" 
83                ? /public/flowTokenReceiver 
84                : USDCFlow.ReceiverPublicPath
85
86            let receiverCap = getAccount(recipient)
87                .capabilities.borrow<&{FungibleToken.Receiver}>(receiverPath)
88                ?? panic("Recipient does not have a receiver capability")
89
90            receiverCap.deposit(from: <-paymentVault)
91
92            emit SendTokenExecuted(id: id, recipient: recipient, amount: amount, tokenType: tokenType)
93            emit ActionExecuted(id: id, actionType: "send_token", success: true, message: "Token sent successfully")
94        }
95
96        access(self) fun executeSwapToken(id: UInt64, params: {String: AnyStruct}) {
97            let fromToken = params["fromToken"] as? String ?? "FlowToken"
98            let toToken = params["toToken"] as? String ?? "USDCFlow"
99            let amount = params["amount"] as? UFix64
100                ?? panic("Missing or invalid amount")
101
102            let fromVaultCap = fromToken == "FlowToken" ? self.flowVaultCap : self.usdcVaultCap
103            let toVaultCap = toToken == "FlowToken" ? self.flowVaultCap : self.usdcVaultCap
104
105            let fromVault = fromVaultCap.borrow()
106                ?? panic("Could not borrow source vault")
107
108            if fromVault.balance < amount {
109                emit ActionExecuted(id: id, actionType: "swap_token", success: false, message: "Insufficient balance for swap")
110                panic("Insufficient balance for swap")
111            }
112
113            let fromKey = SwapConfig.SliceTokenTypeIdentifierFromVaultType(
114                vaultTypeIdentifier: fromToken == "FlowToken" 
115                    ? Type<@FlowToken.Vault>().identifier 
116                    : Type<@USDCFlow.Vault>().identifier
117            )
118            let toKey = SwapConfig.SliceTokenTypeIdentifierFromVaultType(
119                vaultTypeIdentifier: toToken == "FlowToken" 
120                    ? Type<@FlowToken.Vault>().identifier 
121                    : Type<@USDCFlow.Vault>().identifier
122            )
123
124            let swapper = IncrementFiSwapConnectors.Swapper(
125                path: [fromKey, toKey],
126                inVault: fromToken == "FlowToken" ? Type<@FlowToken.Vault>() : Type<@USDCFlow.Vault>(),
127                outVault: toToken == "FlowToken" ? Type<@FlowToken.Vault>() : Type<@USDCFlow.Vault>(),
128                uniqueID: nil
129            )
130
131            let quote = swapper.quoteOut(forProvided: amount, reverse: false)
132
133            if quote.outAmount <= 0.0 {
134                emit ActionExecuted(id: id, actionType: "swap_token", success: false, message: "Invalid swap quote")
135                panic("Invalid swap quote")
136            }
137
138            let inputVault <- fromVault.withdraw(amount: amount)
139            let outputVault <- swapper.swap(quote: quote, inVault: <-inputVault)
140
141            let amountOut = outputVault.balance
142
143            let toVault = toVaultCap.borrow()
144                ?? panic("Could not borrow destination vault")
145            toVault.deposit(from: <-outputVault)
146
147            emit SwapTokenExecuted(id: id, fromToken: fromToken, toToken: toToken, amountIn: amount, amountOut: amountOut)
148            emit ActionExecuted(id: id, actionType: "swap_token", success: true, message: "Swap executed successfully")
149        }
150
151        access(self) fun executeRecurringPayment(id: UInt64, params: {String: AnyStruct}) {
152            self.executeSendToken(id: id, params: params)
153            emit RecurringPaymentExecuted(
154                id: id, 
155                recipient: params["recipient"] as! Address, 
156                amount: params["amount"] as! UFix64, 
157                tokenType: params["tokenType"] as? String ?? "FlowToken"
158            )
159        }
160
161        access(self) fun executeBatchSend(id: UInt64, params: {String: AnyStruct}) {
162            let recipients = params["recipients"] as? {Address: UFix64}
163                ?? panic("Missing or invalid recipients")
164            let tokenType = params["tokenType"] as? String ?? "FlowToken"
165
166            let vaultCap = tokenType == "FlowToken" ? self.flowVaultCap : self.usdcVaultCap
167            let vault = vaultCap.borrow()
168                ?? panic("Could not borrow vault capability")
169
170            var totalAmount: UFix64 = 0.0
171            for amount in recipients.values {
172                totalAmount = totalAmount + amount
173            }
174
175            if vault.balance < totalAmount {
176                emit ActionExecuted(id: id, actionType: "batch_send", success: false, message: "Insufficient balance for batch send")
177                panic("Insufficient balance for batch send")
178            }
179
180            let receiverPath = tokenType == "FlowToken" 
181                ? /public/flowTokenReceiver 
182                : USDCFlow.ReceiverPublicPath
183
184            for recipient in recipients.keys {
185                let amount = recipients[recipient]!
186                let paymentVault <- vault.withdraw(amount: amount)
187
188                let receiverCap = getAccount(recipient)
189                    .capabilities.borrow<&{FungibleToken.Receiver}>(receiverPath)
190                    ?? panic("Recipient ".concat(recipient.toString()).concat(" does not have a receiver capability"))
191
192                receiverCap.deposit(from: <-paymentVault)
193            }
194
195            emit BatchSendExecuted(id: id, recipientCount: recipients.length, totalAmount: totalAmount)
196            emit ActionExecuted(id: id, actionType: "batch_send", success: true, message: "Batch send completed successfully")
197        }
198
199        access(all) view fun getViews(): [Type] {
200            return [
201                Type<StoragePath>(), 
202                Type<PublicPath>(), 
203                Type<MetadataViews.Display>()
204            ]
205        }
206
207        access(all) fun resolveView(_ view: Type): AnyStruct? {
208            switch view {
209                case Type<StoragePath>():
210                    return FlowMateScheduledActionsHandler.HandlerStoragePath
211                case Type<PublicPath>():
212                    return FlowMateScheduledActionsHandler.HandlerPublicPath
213                case Type<MetadataViews.Display>():
214                    return MetadataViews.Display(
215                        name: "FlowMate Universal Action Handler",
216                        description: "Handles all scheduled actions for FlowMate including send, swap, recurring payments, and batch operations",
217                        thumbnail: MetadataViews.HTTPFile(url: "")
218                    )
219                default:
220                    return nil
221            }
222        }
223    }
224
225    access(all) fun createHandler(
226        flowVaultCap: Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>,
227        usdcVaultCap: Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>
228    ): @Handler {
229        return <- create Handler(flowVaultCap: flowVaultCap, usdcVaultCap: usdcVaultCap)
230    }
231
232    init() {
233        self.HandlerStoragePath = /storage/flowMateActionHandler
234        self.HandlerPublicPath = /public/flowMateActionHandler
235    }
236}
237