Smart Contract
LayerZeroConnectors
A.6daee039a7b9c2f0.LayerZeroConnectors
1import DeFiActions from 0x92195d814edf9cb0
2import FungibleToken from 0xf233dcee88fe0abe
3import FlareFDCTriggers from 0x6daee039a7b9c2f0
4
5/// LayerZeroConnectors: Simplified Flow Actions connectors for LayerZero cross-chain messaging
6/// Uses struct-based DeFiActions pattern for cross-chain operations
7access(all) contract LayerZeroConnectors {
8
9 /// Events
10 access(all) event CrossChainMessageSent(
11 messageId: String,
12 targetChain: UInt16,
13 payload: String,
14 gasLimit: UInt256
15 )
16
17 access(all) event CrossChainMessageReceived(
18 messageId: String,
19 sourceChain: UInt16,
20 payload: String
21 )
22
23 access(all) event ActionExecuted(
24 actionId: String,
25 actionType: String,
26 success: Bool
27 )
28
29 /// LayerZero chain IDs
30 access(all) let ChainIds: {String: UInt16}
31
32 /// Cross-chain action types
33 access(all) enum CrossChainActionType: UInt8 {
34 access(all) case TokenTransfer
35 access(all) case LiquidityProvision
36 access(all) case Swap
37 access(all) case Stake
38 access(all) case Unstake
39 access(all) case Harvest
40 access(all) case Compound
41 }
42
43 /// Cross-chain message structure
44 access(all) struct CrossChainMessage {
45 access(all) let messageId: String
46 access(all) let sourceChain: UInt16
47 access(all) let targetChain: UInt16
48 access(all) let actionType: CrossChainActionType
49 access(all) let payload: {String: String}
50 access(all) let gasLimit: UInt256
51 access(all) let timestamp: UFix64
52
53 init(
54 messageId: String,
55 sourceChain: UInt16,
56 targetChain: UInt16,
57 actionType: CrossChainActionType,
58 payload: {String: String},
59 gasLimit: UInt256
60 ) {
61 self.messageId = messageId
62 self.sourceChain = sourceChain
63 self.targetChain = targetChain
64 self.actionType = actionType
65 self.payload = payload
66 self.gasLimit = gasLimit
67 self.timestamp = getCurrentBlock().timestamp
68 }
69 }
70
71 /// LayerZero Message Sink: Processes cross-chain messages following DeFiActions pattern
72 access(all) struct LayerZeroMessageSink: DeFiActions.Sink {
73 access(contract) var uniqueID: DeFiActions.UniqueIdentifier?
74 access(contract) let targetChain: UInt16
75 access(contract) let actionType: CrossChainActionType
76
77 init(
78 targetChain: UInt16,
79 actionType: CrossChainActionType,
80 uniqueID: DeFiActions.UniqueIdentifier?
81 ) {
82 self.targetChain = targetChain
83 self.actionType = actionType
84 self.uniqueID = uniqueID
85 }
86
87 /// Required by Sink: advertise the supported vault type
88 access(all) view fun getSinkType(): Type {
89 // Accept any FungibleToken vault for cross-chain messaging
90 return Type<@{FungibleToken.Vault}>()
91 }
92
93 /// This sink can accept unlimited capacity for message creation
94 access(all) fun minimumCapacity(): UFix64 {
95 return UFix64.max
96 }
97
98 /// Deposit vault and create cross-chain message
99 access(all) fun depositCapacity(from: auth(FungibleToken.Withdraw) &{FungibleToken.Vault}) {
100 let amount = from.balance
101 if amount == 0.0 { return }
102
103 // Create cross-chain message based on deposit
104 let messageId = self.generateMessageId()
105 let message = CrossChainMessage(
106 messageId: messageId,
107 sourceChain: LayerZeroConnectors.ChainIds["Flow"]!,
108 targetChain: self.targetChain,
109 actionType: self.actionType,
110 payload: {
111 "amount": amount.toString(),
112 "token_type": from.getType().identifier,
113 "timestamp": getCurrentBlock().timestamp.toString()
114 },
115 gasLimit: 200000
116 )
117
118 // Consume the vault (in real implementation, this would be bridged)
119 let vault <- from.withdraw(amount: amount)
120 destroy vault
121
122 // Send cross-chain message
123 LayerZeroConnectors.sendCrossChainMessage(message)
124 }
125
126 /// Report metadata about this component
127 access(all) fun getComponentInfo(): DeFiActions.ComponentInfo {
128 return DeFiActions.ComponentInfo(
129 type: self.getType(),
130 id: self.id(),
131 innerComponents: []
132 )
133 }
134
135 /// UniqueIdentifier passthrough
136 access(contract) view fun copyID(): DeFiActions.UniqueIdentifier? {
137 return self.uniqueID
138 }
139
140 /// Allow framework to set UniqueIdentifier
141 access(contract) fun setID(_ id: DeFiActions.UniqueIdentifier?) {
142 self.uniqueID = id
143 }
144
145 access(self) fun generateMessageId(): String {
146 let timestamp = getCurrentBlock().timestamp
147 let id = self.id() ?? 0
148 return "lz-".concat(id.toString()).concat("-").concat(timestamp.toString())
149 }
150 }
151
152 /// FDC Trigger Handler that processes Flare triggers
153 access(all) resource FDCLayerZeroHandler: FlareFDCTriggers.TriggerHandler {
154 access(all) let supportedTypes: [FlareFDCTriggers.TriggerType]
155 access(self) var isHandlerActive: Bool
156 access(self) let chainMapping: {String: UInt16}
157
158 access(all) fun handleTrigger(trigger: FlareFDCTriggers.FDCTrigger): Bool {
159 if !self.isHandlerActive {
160 return false
161 }
162
163 // Convert FDC trigger to LayerZero action type
164 let actionType = self.mapTriggerToAction(trigger.triggerType)
165 let targetChainId = self.chainMapping[trigger.targetChain.rawValue.toString()] ?? 101
166
167 // Create cross-chain message
168 let messageId = self.generateMessageId(trigger)
169 let payload: {String: String} = {}
170
171 // Convert trigger payload to string format
172 for key in trigger.payload.keys {
173 if let value = trigger.payload[key] {
174 // Convert AnyStruct to string representation
175 payload[key] = value.getType().identifier.concat(":").concat(key)
176 }
177 }
178
179 let message = CrossChainMessage(
180 messageId: messageId,
181 sourceChain: LayerZeroConnectors.ChainIds["Flow"]!,
182 targetChain: targetChainId,
183 actionType: actionType,
184 payload: payload,
185 gasLimit: 300000
186 )
187
188 LayerZeroConnectors.sendCrossChainMessage(message)
189
190 // Emit local event (not importing FlareFDCTriggers event)
191 emit ActionExecuted(
192 actionId: messageId,
193 actionType: actionType.rawValue.toString(),
194 success: true
195 )
196
197 return true
198 }
199
200 access(all) fun getSupportedTriggerTypes(): [FlareFDCTriggers.TriggerType] {
201 return self.supportedTypes
202 }
203
204 access(all) fun isActive(): Bool {
205 return self.isHandlerActive
206 }
207
208 access(all) fun setActive(_ active: Bool) {
209 self.isHandlerActive = active
210 }
211
212 access(self) fun mapTriggerToAction(_ triggerType: FlareFDCTriggers.TriggerType): CrossChainActionType {
213 switch triggerType {
214 case FlareFDCTriggers.TriggerType.PriceThreshold:
215 return CrossChainActionType.Swap
216 case FlareFDCTriggers.TriggerType.LiquidityChange:
217 return CrossChainActionType.LiquidityProvision
218 case FlareFDCTriggers.TriggerType.VolumeSpike:
219 return CrossChainActionType.Swap
220 case FlareFDCTriggers.TriggerType.DefiProtocolEvent:
221 return CrossChainActionType.Compound
222 default:
223 return CrossChainActionType.TokenTransfer
224 }
225 }
226
227 access(self) fun generateMessageId(_ trigger: FlareFDCTriggers.FDCTrigger): String {
228 return "fdc-".concat(trigger.id).concat("-").concat(getCurrentBlock().timestamp.toString())
229 }
230
231 init() {
232 self.supportedTypes = [
233 FlareFDCTriggers.TriggerType.PriceThreshold,
234 FlareFDCTriggers.TriggerType.VolumeSpike,
235 FlareFDCTriggers.TriggerType.LiquidityChange,
236 FlareFDCTriggers.TriggerType.DefiProtocolEvent
237 ]
238 self.isHandlerActive = true
239 self.chainMapping = {
240 "0": 101, // Ethereum
241 "1": 102, // BSC
242 "2": 109, // Polygon
243 "3": 110, // Arbitrum
244 "4": 111, // Optimism
245 "5": 106 // Avalanche
246 }
247 }
248 }
249
250 /// Message storage for cross-chain communication
251 access(self) var pendingMessages: {String: CrossChainMessage}
252 access(self) var messageNonce: UInt256
253
254 /// Send cross-chain message via LayerZero
255 access(all) fun sendCrossChainMessage(_ message: CrossChainMessage) {
256 self.pendingMessages[message.messageId] = message
257
258 // In real implementation, this would call LayerZero endpoint
259 emit CrossChainMessageSent(
260 messageId: message.messageId,
261 targetChain: message.targetChain,
262 payload: self.encodeMessage(message),
263 gasLimit: message.gasLimit
264 )
265 }
266
267 /// Receive cross-chain message from LayerZero
268 access(all) fun receiveCrossChainMessage(
269 messageId: String,
270 sourceChain: UInt16,
271 payload: String
272 ) {
273 emit CrossChainMessageReceived(
274 messageId: messageId,
275 sourceChain: sourceChain,
276 payload: payload
277 )
278 }
279
280 /// Factory functions
281 access(all) fun createLayerZeroMessageSink(
282 targetChain: UInt16,
283 actionType: CrossChainActionType,
284 uniqueID: DeFiActions.UniqueIdentifier?
285 ): LayerZeroMessageSink {
286 return LayerZeroMessageSink(
287 targetChain: targetChain,
288 actionType: actionType,
289 uniqueID: uniqueID
290 )
291 }
292
293 access(all) fun createFDCHandler(): @FDCLayerZeroHandler {
294 return <- create FDCLayerZeroHandler()
295 }
296
297 access(self) fun encodeMessage(_ message: CrossChainMessage): String {
298 // Encode message for LayerZero transmission
299 return message.messageId.concat(":").concat(message.actionType.rawValue.toString())
300 }
301
302 init() {
303 self.pendingMessages = {}
304 self.messageNonce = 0
305
306 // Initialize LayerZero chain IDs
307 self.ChainIds = {
308 "Flow": 114, // Flow (hypothetical LZ chain ID)
309 "Ethereum": 101,
310 "BSC": 102,
311 "Polygon": 109,
312 "Arbitrum": 110,
313 "Optimism": 111,
314 "Avalanche": 106
315 }
316 }
317}