Smart Contract
FlowMateScheduledActionsHandler
A.136a10c590912ef8.FlowMateScheduledActionsHandler
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