DeploySEALED

○&?^▪□▒&!^&╲?◆▓?◆^◆@^▪▓@░■▒●!◇^#◆╲■╲▓◆□╲*●?╳╱~**◇█#▪??%◆□╱%&?╲$*

Transaction ID

Timestamp

Jan 30, 2026, 05:05:15 PM UTC
4w ago

Block Height

140,592,723

Computation

0

Execution Fee

0.01206 FLOW

Transaction Summary

Deploy

Contract deployment

Contract deployment

Script Arguments

0nameString
FlowTransactionSchedulerFlat
1codeString
import FungibleToken from 0xf233dcee88fe0abe import FlowToken from 0x1654653399040a61 import FlowFees from 0xf919ee77447b7497 import FlowStorageFees from 0xe467b9dd11fa00df import ViewResolver from 0x1d7e57aa55817448 /// FlowTransactionSchedulerFlat - Flattened version without SharedScheduler resource /// All fields are directly on the contract, no borrow() calls needed access(all) contract FlowTransactionSchedulerFlat { /// storage path for any auxiliary storage access(all) let storagePath: StoragePath /// Enums access(all) enum Priority: UInt8 { access(all) case High access(all) case Medium access(all) case Low } access(all) enum Status: UInt8 { access(all) case Unknown access(all) case Scheduled access(all) case Executed access(all) case Canceled } /// Events access(all) event Scheduled( id: UInt64, priority: UInt8, timestamp: UFix64, executionEffort: UInt64, fees: UFix64, transactionHandlerOwner: Address, transactionHandlerTypeIdentifier: String, transactionHandlerUUID: UInt64, transactionHandlerPublicPath: PublicPath? ) access(all) event PendingExecution( id: UInt64, priority: UInt8, executionEffort: UInt64, fees: UFix64, transactionHandlerOwner: Address, transactionHandlerTypeIdentifier: String ) access(all) event Executed( id: UInt64, priority: UInt8, executionEffort: UInt64, transactionHandlerOwner: Address, transactionHandlerTypeIdentifier: String, transactionHandlerUUID: UInt64, transactionHandlerPublicPath: PublicPath? ) access(all) event Canceled( id: UInt64, priority: UInt8, feesReturned: UFix64, feesDeducted: UFix64, transactionHandlerOwner: Address, transactionHandlerTypeIdentifier: String ) access(all) event CollectionLimitReached( collectionEffortLimit: UInt64?, collectionTransactionsLimit: Int? ) access(all) event RemovalLimitReached() access(all) event ConfigUpdated() access(all) event CriticalIssue(message: String) /// Entitlements access(all) entitlement Execute access(all) entitlement Process access(all) entitlement Cancel access(all) entitlement UpdateConfig /// Interfaces access(all) resource interface TransactionHandler: ViewResolver.Resolver { access(all) view fun getViews(): [Type] { return [] } access(all) fun resolveView(_ view: Type): AnyStruct? { return nil } access(Execute) fun executeTransaction(id: UInt64, data: AnyStruct?) } /// Resources access(all) resource ScheduledTransaction { access(all) let id: UInt64 access(all) let timestamp: UFix64 access(all) let handlerTypeIdentifier: String access(all) view fun status(): Status? { return FlowTransactionSchedulerFlat.getStatus(id: self.id) } init( id: UInt64, timestamp: UFix64, handlerTypeIdentifier: String ) { self.id = id self.timestamp = timestamp self.handlerTypeIdentifier = handlerTypeIdentifier } access(all) event ResourceDestroyed(id: UInt64 = self.id, timestamp: UFix64 = self.timestamp, handlerTypeIdentifier: String = self.handlerTypeIdentifier) } /// Structs access(all) struct EstimatedScheduledTransaction { access(all) let flowFee: UFix64? access(all) let timestamp: UFix64? access(all) let error: String? access(contract) view init(flowFee: UFix64?, timestamp: UFix64?, error: String?) { self.flowFee = flowFee self.timestamp = timestamp self.error = error } } access(all) struct TransactionData { access(all) let id: UInt64 access(all) let priority: Priority access(all) let executionEffort: UInt64 access(all) var status: Status access(all) let fees: UFix64 access(all) var scheduledTimestamp: UFix64 access(contract) let handler: Capability<auth(Execute) &{TransactionHandler}> access(all) let handlerTypeIdentifier: String access(all) let handlerAddress: Address access(contract) let data: AnyStruct? access(contract) init( id: UInt64, handler: Capability<auth(Execute) &{TransactionHandler}>, scheduledTimestamp: UFix64, data: AnyStruct?, priority: Priority, executionEffort: UInt64, fees: UFix64, ) { self.id = id self.handler = handler self.data = data self.priority = priority self.executionEffort = executionEffort self.fees = fees self.status = Status.Scheduled let handlerRef = handler.borrow() ?? panic("Invalid transaction handler: Could not borrow a reference to the transaction handler") self.handlerAddress = handler.address self.handlerTypeIdentifier = handlerRef.getType().identifier self.scheduledTimestamp = scheduledTimestamp } access(contract) fun setStatus(newStatus: Status) { pre { newStatus != Status.Unknown: "Invalid status: New status cannot be Unknown" self.status != Status.Executed && self.status != Status.Canceled: "Invalid status: Transaction with id \(self.id) is already finalized" newStatus == Status.Executed ? self.status == Status.Scheduled : true: "Invalid status: Transaction with id \(self.id) can only be set as Executed if it is Scheduled" newStatus == Status.Canceled ? self.status == Status.Scheduled : true: "Invalid status: Transaction with id \(self.id) can only be set as Canceled if it is Scheduled" } self.status = newStatus } access(contract) fun setScheduledTimestamp(newTimestamp: UFix64) { pre { self.status != Status.Executed && self.status != Status.Canceled: "Invalid status: Transaction with id \(self.id) is already finalized" } self.scheduledTimestamp = newTimestamp } access(contract) fun payAndRefundFees(refundMultiplier: UFix64): @FlowToken.Vault { pre { refundMultiplier >= 0.0 && refundMultiplier <= 1.0: "Invalid refund multiplier: The multiplier must be between 0.0 and 1.0 but got \(refundMultiplier)" } if refundMultiplier == 0.0 { FlowFees.deposit(from: <-FlowTransactionSchedulerFlat.withdrawFees(amount: self.fees)) return <-FlowToken.createEmptyVault(vaultType: Type<@FlowToken.Vault>()) } else { let amountToReturn = self.fees * refundMultiplier let amountToKeep = self.fees - amountToReturn let feesToReturn <- FlowTransactionSchedulerFlat.withdrawFees(amount: amountToReturn) FlowFees.deposit(from: <-FlowTransactionSchedulerFlat.withdrawFees(amount: amountToKeep)) return <-feesToReturn } } access(all) view fun getData(): AnyStruct? { return self.data } access(all) view fun borrowHandler(): &{TransactionHandler} { return self.handler.borrow() as? &{TransactionHandler} ?? panic("Invalid transaction handler: Could not borrow a reference to the transaction handler") } } access(all) struct interface SchedulerConfig { access(all) var maximumIndividualEffort: UInt64 access(all) var minimumExecutionEffort: UInt64 access(all) var slotTotalEffortLimit: UInt64 access(all) var slotSharedEffortLimit: UInt64 access(all) var priorityEffortReserve: {Priority: UInt64} access(all) var priorityEffortLimit: {Priority: UInt64} access(all) var maxDataSizeMB: UFix64 access(all) var priorityFeeMultipliers: {Priority: UFix64} access(all) var refundMultiplier: UFix64 access(all) var canceledTransactionsLimit: UInt access(all) var collectionEffortLimit: UInt64 access(all) var collectionTransactionsLimit: Int access(all) init( maximumIndividualEffort: UInt64, minimumExecutionEffort: UInt64, slotSharedEffortLimit: UInt64, priorityEffortReserve: {Priority: UInt64}, lowPriorityEffortLimit: UInt64, maxDataSizeMB: UFix64, priorityFeeMultipliers: {Priority: UFix64}, refundMultiplier: UFix64, canceledTransactionsLimit: UInt, collectionEffortLimit: UInt64, collectionTransactionsLimit: Int, txRemovalLimit: UInt ) { post { self.refundMultiplier >= 0.0 && self.refundMultiplier <= 1.0: "Invalid refund multiplier: The multiplier must be between 0.0 and 1.0 but got \(refundMultiplier)" self.priorityFeeMultipliers[Priority.Low]! >= 1.0: "Invalid priority fee multiplier: Low priority multiplier must be greater than or equal to 1.0 but got \(self.priorityFeeMultipliers[Priority.Low]!)" self.priorityFeeMultipliers[Priority.Medium]! > self.priorityFeeMultipliers[Priority.Low]!: "Invalid priority fee multiplier: Medium priority multiplier must be greater than or equal to \(priorityFeeMultipliers[Priority.Low]!) but got \(priorityFeeMultipliers[Priority.Medium]!)" self.priorityFeeMultipliers[Priority.High]! > self.priorityFeeMultipliers[Priority.Medium]!: "Invalid priority fee multiplier: High priority multiplier must be greater than or equal to \(priorityFeeMultipliers[Priority.Medium]!) but got \(priorityFeeMultipliers[Priority.High]!)" self.priorityEffortLimit[Priority.High]! >= self.priorityEffortReserve[Priority.High]!: "Invalid priority effort limit: High priority effort limit must be greater than or equal to the priority effort reserve of \(priorityEffortReserve[Priority.High]!)" self.priorityEffortLimit[Priority.Medium]! >= self.priorityEffortReserve[Priority.Medium]!: "Invalid priority effort limit: Medium priority effort limit must be greater than or equal to the priority effort reserve of \(priorityEffortReserve[Priority.Medium]!)" self.priorityEffortLimit[Priority.Low]! >= self.priorityEffortReserve[Priority.Low]!: "Invalid priority effort limit: Low priority effort limit must be greater than or equal to the priority effort reserve of \(priorityEffortReserve[Priority.Low]!)" self.priorityEffortReserve[Priority.Low]! == 0: "Invalid priority effort reserve: Low priority effort reserve must be 0" self.collectionTransactionsLimit >= 0: "Invalid collection transactions limit: Collection transactions limit must be greater than or equal to 0 but got \(collectionTransactionsLimit)" self.canceledTransactionsLimit >= 1: "Invalid canceled transactions limit: Canceled transactions limit must be greater than or equal to 1 but got \(canceledTransactionsLimit)" self.collectionEffortLimit > self.slotTotalEffortLimit: "Invalid collection effort limit: Collection effort limit must be greater than \(self.slotTotalEffortLimit) but got \(self.collectionEffortLimit)" } } access(all) view fun getTxRemovalLimit(): UInt } access(all) struct Config: SchedulerConfig { access(all) var maximumIndividualEffort: UInt64 access(all) var minimumExecutionEffort: UInt64 access(all) var slotTotalEffortLimit: UInt64 access(all) var slotSharedEffortLimit: UInt64 access(all) var priorityEffortReserve: {Priority: UInt64} access(all) var priorityEffortLimit: {Priority: UInt64} access(all) var maxDataSizeMB: UFix64 access(all) var priorityFeeMultipliers: {Priority: UFix64} access(all) var refundMultiplier: UFix64 access(all) var canceledTransactionsLimit: UInt access(all) var collectionEffortLimit: UInt64 access(all) var collectionTransactionsLimit: Int access(all) init( maximumIndividualEffort: UInt64, minimumExecutionEffort: UInt64, slotSharedEffortLimit: UInt64, priorityEffortReserve: {Priority: UInt64}, lowPriorityEffortLimit: UInt64, maxDataSizeMB: UFix64, priorityFeeMultipliers: {Priority: UFix64}, refundMultiplier: UFix64, canceledTransactionsLimit: UInt, collectionEffortLimit: UInt64, collectionTransactionsLimit: Int, txRemovalLimit: UInt ) { self.maximumIndividualEffort = maximumIndividualEffort self.minimumExecutionEffort = minimumExecutionEffort self.slotTotalEffortLimit = slotSharedEffortLimit + priorityEffortReserve[Priority.High]! + priorityEffortReserve[Priority.Medium]! self.slotSharedEffortLimit = slotSharedEffortLimit self.priorityEffortReserve = priorityEffortReserve self.priorityEffortLimit = { Priority.High: priorityEffortReserve[Priority.High]! + slotSharedEffortLimit, Priority.Medium: priorityEffortReserve[Priority.Medium]! + slotSharedEffortLimit, Priority.Low: lowPriorityEffortLimit } self.maxDataSizeMB = maxDataSizeMB self.priorityFeeMultipliers = priorityFeeMultipliers self.refundMultiplier = refundMultiplier self.canceledTransactionsLimit = canceledTransactionsLimit self.collectionEffortLimit = collectionEffortLimit self.collectionTransactionsLimit = collectionTransactionsLimit } access(all) view fun getTxRemovalLimit(): UInt { return FlowTransactionSchedulerFlat.account.storage.copy<UInt>(from: /storage/txRemovalLimitFlat) ?? 200 } } access(all) struct SortedTimestamps { access(self) var timestamps: [UFix64] access(all) init() { self.timestamps = [] } access(all) fun add(timestamp: UFix64) { var insertIndex = 0 for i, ts in self.timestamps { if timestamp < ts { insertIndex = i break } else if timestamp == ts { return } insertIndex = i + 1 } self.timestamps.insert(at: insertIndex, timestamp) } access(all) fun remove(timestamp: UFix64) { let index = self.timestamps.firstIndex(of: timestamp) if index != nil { self.timestamps.remove(at: index!) } } access(all) fun getBefore(current: UFix64): [UFix64] { let pastTimestamps: [UFix64] = [] for timestamp in self.timestamps { if timestamp <= current { pastTimestamps.append(timestamp) } else { break } } return pastTimestamps } access(all) fun hasBefore(current: UFix64): Bool { return self.timestamps.length > 0 && self.timestamps[0] <= current } access(all) fun getAll(): [UFix64] { return self.timestamps } } // ============================================ // CONTRACT-LEVEL STATE (formerly in SharedScheduler) // ============================================ access(contract) var nextID: UInt64 access(contract) var transactions: {UInt64: TransactionData} access(contract) var slotQueue: {UFix64: {Priority: {UInt64: UInt64}}} access(contract) var slotUsedEffort: {UFix64: {Priority: UInt64}} access(contract) var sortedTimestamps: SortedTimestamps access(contract) var canceledTransactions: [UInt64] access(contract) var config: {SchedulerConfig} // ============================================ // HELPER FUNCTIONS // ============================================ access(contract) fun depositFees(from: @FlowToken.Vault) { let vaultRef = self.account.storage.borrow<&FlowToken.Vault>(from: /storage/flowTokenVault) ?? panic("Unable to borrow reference to the default token vault") vaultRef.deposit(from: <-from) } access(contract) fun withdrawFees(amount: UFix64): @FlowToken.Vault { let vaultRef = self.account.storage.borrow<auth(FungibleToken.Withdraw) &FlowToken.Vault>(from: /storage/flowTokenVault) ?? panic("Unable to borrow reference to the default token vault") return <-vaultRef.withdraw(amount: amount) as! @FlowToken.Vault } access(self) fun getNextIDAndIncrement(): UInt64 { let nextID = self.nextID self.nextID = self.nextID + 1 return nextID } // ============================================ // PUBLIC/CONTRACT FUNCTIONS (no borrow needed!) // ============================================ access(all) view fun getConfig(): {SchedulerConfig} { return self.config } access(all) view fun getStatus(id: UInt64): Status? { if id == 0 as UInt64 || id >= self.nextID { return nil } if let tx = &self.transactions[id] as &TransactionData? { return tx.status } if self.canceledTransactions.contains(id) { return Status.Canceled } let firstCanceledID = self.canceledTransactions[0] if id > firstCanceledID { return Status.Executed } return Status.Unknown } access(all) view fun getTransactionData(id: UInt64): TransactionData? { return self.transactions[id] } access(all) view fun borrowHandlerForID(_ id: UInt64): &{TransactionHandler}? { return self.getTransactionData(id: id)?.borrowHandler() } access(all) view fun getCanceledTransactions(): [UInt64] { return self.canceledTransactions } access(all) fun getTransactionsForTimeframe(startTimestamp: UFix64, endTimestamp: UFix64): {UFix64: {UInt8: [UInt64]}} { var transactionsInTimeframe: {UFix64: {UInt8: [UInt64]}} = {} if startTimestamp > endTimestamp { return transactionsInTimeframe } let allTimestampsBeforeEnd = self.sortedTimestamps.getBefore(current: endTimestamp) for timestamp in allTimestampsBeforeEnd { if timestamp < startTimestamp { continue } let transactionPriorities = self.slotQueue[timestamp] ?? {} var timestampTransactions: {UInt8: [UInt64]} = {} for priority in transactionPriorities.keys { let transactionIDs = transactionPriorities[priority] ?? {} var priorityTransactions: [UInt64] = [] for id in transactionIDs.keys { priorityTransactions.append(id) } if priorityTransactions.length > 0 { timestampTransactions[priority.rawValue] = priorityTransactions } } if timestampTransactions.keys.length > 0 { transactionsInTimeframe[timestamp] = timestampTransactions } } return transactionsInTimeframe } access(all) view fun getSlotAvailableEffort(timestamp: UFix64, priority: Priority): UInt64 { let sanitizedTimestamp = UFix64(UInt64(timestamp)) let priorityLimit = self.config.priorityEffortLimit[priority]! if !self.slotUsedEffort.containsKey(sanitizedTimestamp) { return priorityLimit } let slotPriorityEffortsUsed = self.slotUsedEffort[sanitizedTimestamp]! let highReserve = self.config.priorityEffortReserve[Priority.High]! let mediumReserve = self.config.priorityEffortReserve[Priority.Medium]! let highUsed = slotPriorityEffortsUsed[Priority.High] ?? 0 let mediumUsed = slotPriorityEffortsUsed[Priority.Medium] ?? 0 if priority == Priority.Low { let highPlusMediumUsed = highUsed + mediumUsed let totalEffortRemaining = self.config.slotTotalEffortLimit.saturatingSubtract(highPlusMediumUsed) let lowEffortRemaining = totalEffortRemaining < priorityLimit ? totalEffortRemaining : priorityLimit let lowUsed = slotPriorityEffortsUsed[Priority.Low] ?? 0 return lowEffortRemaining.saturatingSubtract(lowUsed) } let highSharedUsed: UInt64 = highUsed.saturatingSubtract(highReserve) let mediumSharedUsed: UInt64 = mediumUsed.saturatingSubtract(mediumReserve) let totalShared = (self.config.slotTotalEffortLimit.saturatingSubtract(highReserve)).saturatingSubtract(mediumReserve) let highPlusMediumSharedUsed = highSharedUsed + mediumSharedUsed let sharedAvailable = totalShared.saturatingSubtract(highPlusMediumSharedUsed) let reserve = self.config.priorityEffortReserve[priority]! let used = slotPriorityEffortsUsed[priority] ?? 0 let unusedReserve: UInt64 = reserve.saturatingSubtract(used) let available = sharedAvailable + unusedReserve return available } access(all) fun getSizeOfData(_ data: AnyStruct?): UFix64 { if data == nil { return 0.0 } else { let type = data!.getType() if type.isSubtype(of: Type<Number>()) || type.isSubtype(of: Type<Bool>()) || type.isSubtype(of: Type<Address>()) || type.isSubtype(of: Type<Character>()) || type.isSubtype(of: Type<Capability>()) { return 0.0 } } let storagePath = /storage/dataTempFlat let storageUsedBefore = self.account.storage.used self.account.storage.save(data!, to: storagePath) let storageUsedAfter = self.account.storage.used self.account.storage.load<AnyStruct>(from: storagePath) return FlowStorageFees.convertUInt64StorageBytesToUFix64Megabytes(storageUsedAfter.saturatingSubtract(storageUsedBefore)) } access(contract) fun calculateFee(executionEffort: UInt64, priority: Priority, dataSizeMB: UFix64): UFix64 { let baseFee = FlowFees.computeFees(inclusionEffort: 1.0, executionEffort: UFix64(executionEffort)/100000000.0) let scaledExecutionFee = baseFee * self.config.priorityFeeMultipliers[priority]! let storageFee = FlowStorageFees.storageCapacityToFlow(dataSizeMB) let inclusionFee = 0.00001 return scaledExecutionFee + storageFee + inclusionFee } access(contract) view fun calculateScheduledTimestamp( timestamp: UFix64, priority: Priority, executionEffort: UInt64 ): UFix64? { var timestampToSearch = timestamp while true { let used = self.slotUsedEffort[timestampToSearch] if used == nil { return timestampToSearch } let available = self.getSlotAvailableEffort(timestamp: timestampToSearch, priority: priority) if executionEffort <= available { return timestampToSearch } if priority == Priority.High { return nil } timestampToSearch = timestampToSearch + 1.0 } return nil } // ============================================ // MAIN FUNCTIONS // ============================================ access(all) fun estimate( data: AnyStruct?, timestamp: UFix64, priority: Priority, executionEffort: UInt64 ): EstimatedScheduledTransaction { let sanitizedTimestamp = UFix64(UInt64(timestamp)) if sanitizedTimestamp <= getCurrentBlock().timestamp { return EstimatedScheduledTransaction( flowFee: nil, timestamp: nil, error: "Invalid timestamp: \(sanitizedTimestamp) is in the past, current timestamp: \(getCurrentBlock().timestamp)" ) } if executionEffort > self.config.maximumIndividualEffort { return EstimatedScheduledTransaction( flowFee: nil, timestamp: nil, error: "Invalid execution effort: \(executionEffort) is greater than the maximum transaction effort of \(self.config.maximumIndividualEffort)" ) } if executionEffort > self.config.priorityEffortLimit[priority]! { return EstimatedScheduledTransaction( flowFee: nil, timestamp: nil, error: "Invalid execution effort: \(executionEffort) is greater than the priority's max effort of \(self.config.priorityEffortLimit[priority]!)" ) } if executionEffort < self.config.minimumExecutionEffort { return EstimatedScheduledTransaction( flowFee: nil, timestamp: nil, error: "Invalid execution effort: \(executionEffort) is less than the minimum execution effort of \(self.config.minimumExecutionEffort)" ) } let dataSizeMB = self.getSizeOfData(data) if dataSizeMB > self.config.maxDataSizeMB { return EstimatedScheduledTransaction( flowFee: nil, timestamp: nil, error: "Invalid data size: \(dataSizeMB) is greater than the maximum data size of \(self.config.maxDataSizeMB)MB" ) } let fee = self.calculateFee(executionEffort: executionEffort, priority: priority, dataSizeMB: dataSizeMB) let scheduledTimestamp = self.calculateScheduledTimestamp( timestamp: sanitizedTimestamp, priority: priority, executionEffort: executionEffort ) if scheduledTimestamp == nil { return EstimatedScheduledTransaction( flowFee: nil, timestamp: nil, error: "Invalid execution effort: \(executionEffort) is greater than the priority's available effort for the requested timestamp." ) } if priority == Priority.Low { return EstimatedScheduledTransaction( flowFee: fee, timestamp: scheduledTimestamp, error: "Invalid Priority: Cannot estimate for Low Priority transactions. They will be included in the first block with available space after their requested timestamp." ) } return EstimatedScheduledTransaction(flowFee: fee, timestamp: scheduledTimestamp, error: nil) } access(all) fun schedule( handlerCap: Capability<auth(Execute) &{TransactionHandler}>, data: AnyStruct?, timestamp: UFix64, priority: Priority, executionEffort: UInt64, fees: @FlowToken.Vault ): @ScheduledTransaction { let estimate = self.estimate( data: data, timestamp: timestamp, priority: priority, executionEffort: executionEffort ) if estimate.error != nil && estimate.timestamp == nil { panic(estimate.error!) } assert( fees.balance >= estimate.flowFee!, message: "Insufficient fees: The Fee balance of \(fees.balance) is not sufficient to pay the required amount of \(estimate.flowFee!) for execution of the transaction." ) let transactionID = self.getNextIDAndIncrement() let transactionData = TransactionData( id: transactionID, handler: handlerCap, scheduledTimestamp: estimate.timestamp!, data: data, priority: priority, executionEffort: executionEffort, fees: fees.balance, ) self.depositFees(from: <-fees) let handlerRef = handlerCap.borrow() ?? panic("Invalid transaction handler: Could not borrow a reference to the transaction handler") let handlerPublicPath = handlerRef.resolveView(Type<PublicPath>()) as? PublicPath emit Scheduled( id: transactionData.id, priority: transactionData.priority.rawValue, timestamp: transactionData.scheduledTimestamp, executionEffort: transactionData.executionEffort, fees: transactionData.fees, transactionHandlerOwner: transactionData.handler.address, transactionHandlerTypeIdentifier: transactionData.handlerTypeIdentifier, transactionHandlerUUID: handlerRef.uuid, transactionHandlerPublicPath: handlerPublicPath ) self.addTransaction(slot: estimate.timestamp!, txData: transactionData) return <-create ScheduledTransaction( id: transactionID, timestamp: estimate.timestamp!, handlerTypeIdentifier: transactionData.handlerTypeIdentifier ) } access(all) fun cancel(scheduledTx: @ScheduledTransaction): @FlowToken.Vault { let id = scheduledTx.id destroy scheduledTx let tx = &self.transactions[id] as &TransactionData? ?? panic("Invalid ID: \(id) transaction not found") assert( tx.status == Status.Scheduled, message: "Transaction must be in a scheduled state in order to be canceled" ) let slotEfforts = self.slotUsedEffort[tx.scheduledTimestamp]! slotEfforts[tx.priority] = slotEfforts[tx.priority]!.saturatingSubtract(tx.executionEffort) self.slotUsedEffort[tx.scheduledTimestamp] = slotEfforts let totalFees = tx.fees let refundedFees <- tx.payAndRefundFees(refundMultiplier: self.config.refundMultiplier) var insertIndex = 0 for i, canceledID in self.canceledTransactions { if id < canceledID { insertIndex = i break } insertIndex = i + 1 } self.canceledTransactions.insert(at: insertIndex, id) if UInt(self.canceledTransactions.length) > self.config.canceledTransactionsLimit { self.canceledTransactions.remove(at: 0) } emit Canceled( id: tx.id, priority: tx.priority.rawValue, feesReturned: refundedFees.balance, feesDeducted: totalFees - refundedFees.balance, transactionHandlerOwner: tx.handler.address, transactionHandlerTypeIdentifier: tx.handlerTypeIdentifier ) self.removeTransaction(txData: tx) return <-refundedFees } // ============================================ // INTERNAL FUNCTIONS // ============================================ access(self) fun addTransaction(slot: UFix64, txData: TransactionData) { if self.slotQueue[slot] == nil { self.slotQueue[slot] = {} self.slotUsedEffort[slot] = { Priority.High: 0, Priority.Medium: 0, Priority.Low: 0 } self.sortedTimestamps.add(timestamp: slot) } let slotQueue = self.slotQueue[slot]! if let priorityQueue = slotQueue[txData.priority] { priorityQueue[txData.id] = txData.executionEffort slotQueue[txData.priority] = priorityQueue } else { slotQueue[txData.priority] = { txData.id: txData.executionEffort } } self.slotQueue[slot] = slotQueue let slotEfforts = self.slotUsedEffort[slot]! var newPriorityEffort = slotEfforts[txData.priority]! + txData.executionEffort slotEfforts[txData.priority] = newPriorityEffort var newTotalEffort: UInt64 = 0 for priority in slotEfforts.keys { newTotalEffort = newTotalEffort.saturatingAdd(slotEfforts[priority]!) } self.slotUsedEffort[slot] = slotEfforts let lowTransactionsToReschedule: [UInt64] = [] if newTotalEffort > self.config.slotTotalEffortLimit { let lowPriorityTransactions = slotQueue[Priority.Low]! for id in lowPriorityTransactions.keys { if newTotalEffort <= self.config.slotTotalEffortLimit { break } lowTransactionsToReschedule.append(id) newTotalEffort = newTotalEffort.saturatingSubtract(lowPriorityTransactions[id]!) } } self.transactions[txData.id] = txData self.rescheduleLowPriorityTransactions(slot: slot, transactions: lowTransactionsToReschedule) } access(self) fun rescheduleLowPriorityTransactions(slot: UFix64, transactions: [UInt64]) { for id in transactions { let tx = &self.transactions[id] as &TransactionData? if tx == nil { emit CriticalIssue(message: "Invalid ID: \(id) transaction not found while rescheduling low priority transactions") continue } if tx!.priority != Priority.Low { emit CriticalIssue(message: "Invalid Priority: Cannot reschedule transaction with id \(id) because it is not low priority") continue } if tx!.scheduledTimestamp != slot { emit CriticalIssue(message: "Invalid Timestamp: Cannot reschedule transaction with id \(id) because it is not scheduled at the same slot as the new transaction") continue } let newTimestamp = self.calculateScheduledTimestamp( timestamp: slot + 1.0, priority: Priority.Low, executionEffort: tx!.executionEffort )! let effort = tx!.executionEffort let transactionData = self.removeTransaction(txData: tx!) let slotEfforts = self.slotUsedEffort[slot]! slotEfforts[Priority.Low] = slotEfforts[Priority.Low]!.saturatingSubtract(effort) self.slotUsedEffort[slot] = slotEfforts transactionData.setScheduledTimestamp(newTimestamp: newTimestamp) self.addTransaction(slot: newTimestamp, txData: transactionData) } } access(self) fun removeTransaction(txData: &TransactionData): TransactionData { let transactionID = txData.id let slot = txData.scheduledTimestamp let transactionPriority = txData.priority let transactionObject = self.transactions.remove(key: transactionID)! if let transactionQueue = self.slotQueue[slot] { if let priorityQueue = transactionQueue[transactionPriority] { priorityQueue[transactionID] = nil if priorityQueue.keys.length == 0 { transactionQueue.remove(key: transactionPriority) } else { transactionQueue[transactionPriority] = priorityQueue } self.slotQueue[slot] = transactionQueue } if transactionQueue.keys.length == 0 { self.slotQueue.remove(key: slot) self.slotUsedEffort.remove(key: slot) self.sortedTimestamps.remove(timestamp: slot) } } return transactionObject } // ============================================ // INIT // ============================================ access(all) init() { self.storagePath = /storage/schedulerFlatData self.nextID = 1 self.canceledTransactions = [0 as UInt64] self.transactions = {} self.slotUsedEffort = {} self.slotQueue = {} self.sortedTimestamps = SortedTimestamps() let sharedEffortLimit: UInt64 = 5_000 let highPriorityEffortReserve: UInt64 = 10_000 let mediumPriorityEffortReserve: UInt64 = 2_500 self.config = Config( maximumIndividualEffort: 9999, minimumExecutionEffort: 100, slotSharedEffortLimit: sharedEffortLimit, priorityEffortReserve: { Priority.High: highPriorityEffortReserve, Priority.Medium: mediumPriorityEffortReserve, Priority.Low: 0 }, lowPriorityEffortLimit: 2_500, maxDataSizeMB: 0.001, priorityFeeMultipliers: { Priority.High: 10.0, Priority.Medium: 5.0, Priority.Low: 2.0 }, refundMultiplier: 0.5, canceledTransactionsLimit: 1000, collectionEffortLimit: 500_000, collectionTransactionsLimit: 150, txRemovalLimit: 200 ) } }

Cadence Script

1transaction(name: String, code: String ) {
2		prepare(signer: auth(AddContract) &Account) {
3			signer.contracts.add(name: name, code: code.utf8 )
4		}
5	}