DeploySEALED

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

Transaction ID

Timestamp

Aug 26, 2024, 10:07:53 PM UTC
1y ago

Block Height

85,389,711

Computation

0

Execution Fee

0.00000219 FLOW

Transaction Summary

Deploy

Contract deployment

Contract deployment

Script Arguments

0nameString
FlowRewardsModels
1codeString
import FlowRewards from 0xa45ead1cf1ca9eda import FlowRewardsRegistry from 0xa45ead1cf1ca9eda import Clock from 0xa45ead1cf1ca9eda /// This contract defines reward boost and distribution models used in the FlowRewards contract /// access(all) contract FlowRewardsModels { /// Creates a new MultilinearBoostModel with the provided stages /// /// @param stages: The stages defining the boost and time boundaries for boost. These should be contiguous and /// ordered by start time - a condition enforced in resource init /// /// @return The newly created MultilinearBoostModel /// access(all) fun createBoostModel(stages: [Stage]): @{FlowRewards.BoostModel} { return <- create MultilinearBoostModel(stages: stages) } access(all) fun createDistributionModel(start: UFix64, end: UFix64): @{FlowRewards.DistributionModel} { return <- create LinearDistributionModel(start: start, end: end) } /// Each stage defines a start and end time and a boost applicable for that period /// access(all) struct Stage { /// The reward boost factor for this stage access(all) let boost: UFix64 /// The start timestamp boundary for this stage access(all) let start: UFix64 /// The end timestamp boundary for this stage access(all) let end: UFix64 init(boost: UFix64, start: UFix64, end: UFix64) { pre { start < end: "Start must be before end" } self.boost = boost self.start = start self.end = end } /// Calculates the boost amount for a given lock amount amount over this stage /// /// @param lockAmount: The lock amount amount to calculate boost amount against /// @param start: The start time which the lock amount should begin boosting. If this exceeds the stage end, no /// boost amount will be calculated /// @param upTo: The end time up to which the lock amount should boost. If this precedes the stage start, no /// boost amount will be calculated. If this exceeds the stage end, boost will be calculated up to the stage // end /// /// @return The boost amount for the lock amount over this stage /// access(all) fun calculateBoostAmount(lockAmount: UFix64, start: UFix64, upTo: UFix64): UFix64 { // Return early if defined bounds do not overlap with this stage if upTo <= self.start || self.end <= start { return 0.0 } let boostStart = start < self.start ? self.start : start let boostEnd = upTo < self.end ? upTo : self.end if boostEnd <= boostStart { return 0.0 } let time = boostEnd - boostStart let boostAmount = lockAmount * self.boost let proportionalTime = time / 31_536_000.0 return boostAmount * proportionalTime } } /// The MultilinearBoostModel defines a series of stages, each with fixed boosts and calculates rewards over those /// stages /// access(all) resource MultilinearBoostModel : FlowRewards.BoostModel { /// The stages defining the long-term boost and time boundaries for boost calculation access(all) let stages: [Stage] init(stages: [Stage]) { pre { stages.length > 0: "Must have at least one stage" } for i, stage in stages { if i < stages.length - 1 { assert(stage.end == stages[i + 1].start, message: "Stages must be contiguous") } } self.stages = stages } /// Returns the start time of the first stage /// /// @return The start timestamp of the first stage /// access(all) fun getBoostStart(): UFix64 { return self.stages[0].start } /// Returns the end time of the last stage /// /// @return The end timestamp of the last stage /// access(all) fun getBoostEnd(): UFix64 { return self.stages[self.stages.length - 1].end } /// Returns the boost factor for the model at the start of the boost period /// /// @return The starting boost factor for the model /// access(all) fun getStartingBoostFactor(): UFix64 { return self.getBoostFactor(atTime: self.getBoostStart()) } /// Calculates the boost factor for a lockup executed at the specified time /// /// @param start: The time at which the lockup was executed /// /// @return The boost factor for the lockup executed at the specified time /// access(all) fun getBoostFactor(atTime: UFix64?): UFix64 { let atTime = atTime ?? Clock.time() let modelEnd = self.getBoostEnd() // Cannot boost after model end, return 0.0 if modelEnd <= atTime { return 0.0 } let modelStart = self.getBoostStart() // If the lockup occurred before the model start, boost starts at the model start let boostStart = atTime <= modelStart ? modelStart : atTime let amount = 1_000.0 var boostAmount = 0.0 boostAmount = self._calculateBoostAmount(amount: amount, start: boostStart, upTo: modelEnd) // Determine the boost factor relative to the lock amount of 1_000.0 return boostAmount / amount } /// Calculates the boost amount for a given summary up to a specified time /// /// @param summary: The reward summary to calculate boost amount for /// @param upTo: The end time up to which to calculate boost. If nil, the current block timestamp is used. If /// the specified time is before the model start, no boost amount will be calculated. If the specified time /// is beyond the model end, the boost will be calculated up to the model end /// /// @return The total boost amount for the summary up to the specified time /// access(all) fun calculateBoostAmount( summary: &{FlowRewardsRegistry.Summary}, upTo: UFix64? ): UFix64 { let modelStart = self.getBoostStart() let modelEnd = self.getBoostEnd() var boostEnd = upTo ?? Clock.time() // Return early if boost period has not started as specified if boostEnd <= modelStart { return 0.0 } // Bound the boost end to the model end if requested threshold is beyond the model end boostEnd = modelEnd < boostEnd ? modelEnd : boostEnd var total = 0.0 for lockup in summary.lockups { let whenLocked = lockup.timestamp // If the lockup occurred after the model end, skip it if modelEnd <= whenLocked { continue } // If the lockup occurred before the model start, boost starts at the model start let boostStart = modelStart >= whenLocked ? modelStart : whenLocked total = total + self._calculateBoostAmount(amount: lockup.amount,start: boostStart, upTo: boostEnd) } return total } /// Calculates the boost for a given lock amount amount up to a specified time iterating over all stages /// access(self) fun _calculateBoostAmount(amount: UFix64, start: UFix64, upTo: UFix64): UFix64 { var total = 0.0 // Iterate over stages to calculate boost amount over each stage's boost for i, stage in self.stages { // Add the boost amount for this stage to the total total = total + stage.calculateBoostAmount( lockAmount: amount, start: start, upTo: upTo ) if upTo <= stage.end { break } } return total } } /// This resource defines a linear distribution model of locked + rewarded FLOW over a specified time period /// access(all) resource LinearDistributionModel : FlowRewards.DistributionModel { /// The start time of the distribution period access(all) let start: UFix64 /// The end time of the distribution period access(all) let end: UFix64 init(start: UFix64, end: UFix64) { pre { start < end: "Start must be before end" } self.start = start self.end = end } /// Returns the time at which the distribtuion model starts distributing funds /// /// @return The start time of the distribution period /// access(all) fun getDistributionStart(): UFix64 { return self.start } /// Returns the time at which the distribtuion model stops distributing funds /// /// @return The end time of the distribution period /// access(all) fun getDistributionEnd(): UFix64 { return self.end } /// Returns the amount that can be distributed at a given time according to the distribution period defined in /// this model at a linear rate /// NOTE: Any implementing contracts should be defined in this contract account as the summary is passed by /// reference /// /// @param maxDistribution: The maximum amount of the same denomination that can be distributed /// @param atTime: The time at which to calculate the distribution. If nil, the current block timestamp is used /// If the specified time is before the model start, no distribution will be calculated. If the specified /// time is beyond the model end, the distribution will be calculated up to the model end /// /// @return The total amount of locked and/or rewarded FLOW that can be distributed at the specified time /// access(all) fun calculateDistribution( maxDistribution: UFix64, atTime: UFix64? ): UFix64 { let distributionStart = self.getDistributionStart() let distributionEnd = self.getDistributionEnd() var distributionTime = atTime ?? Clock.time() // Return early if distribution period has not started as specified if distributionTime < distributionStart { return 0.0 } else if distributionTime >= distributionEnd { // If the distribution period has ended, return the max distribution return maxDistribution } // Calculate how far into the distribution period we are as a percentage at distributionTime let distributionPeriod = distributionEnd - distributionStart let timeElapsed = distributionTime - distributionStart let distributionPercentage = timeElapsed / distributionPeriod // Return the proportion of maxDistribution that can be distributed at this time return maxDistribution * distributionPercentage } } }

Cadence Script

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