{-# OPTIONS --safe #-} open import Data.Integer using () renaming (+_ to pos) open import Data.Rational using (ℚ; floor; _*_; _÷_; _/_; _-_; >-nonZero; _⊓_) renaming (_⊔_ to _⊔ℚ_; NonZero to NonZeroℚ) open import Data.Rational.Literals using (number; fromℤ) open import Data.Rational.Properties using (pos⇒nonZero; positive⁻¹; +-mono-<-≤; normalize-pos; p≤p⊔q) open import Ledger.Conway.Abstract open import Ledger.Conway.Transaction open import Ledger.Conway.Types.Numeric.UnitInterval open import Agda.Builtin.FromNat open Number number renaming (fromNat to fromℕ) module Ledger.Conway.Rewards (txs : _) (open TransactionStructure txs) (abs : AbstractFunctions txs) where open import Ledger.Conway.Certs govStructure open import Ledger.Conway.Ledger txs abs open import Ledger.Prelude hiding (_/_; _*_; _-_; >-nonZero; _⊓_) open import Ledger.Conway.Utxo txs abs nonZero-max-1 : ∀ (n : ℕ) → NonZero (1 ⊔ n) nonZero-max-1 zero = nonZero nonZero-max-1 (suc n) = nonZero nonZero-1/n : ∀ (n : ℕ) → .{{_ : NonZero n}} → NonZeroℚ (1 / n) nonZero-1/n n {{prf}} = pos⇒nonZero (1 / n) {{normalize-pos 1 n {{prf}} {{_}} }} nonZero-1+max0-x : ∀ (x : ℚ) → NonZeroℚ (1 + (0 ⊔ℚ x)) nonZero-1+max0-x x = >-nonZero (+-mono-<-≤ (positive⁻¹ 1 {{_}}) (p≤p⊔q 0 x)) maxPool : PParams → Coin → UnitInterval → UnitInterval → Coin maxPool pparams rewardPot stake pledge = rewardℕ where a0 = 0 ⊔ℚ pparams .PParams.a0 1+a0 = 1 + a0 nopt = 1 ⊔ pparams .PParams.nopt instance nonZero-nopt : NonZero nopt nonZero-nopt = nonZero-max-1 (pparams .PParams.nopt) z0 = 1 / nopt stake' = fromUnitInterval stake ⊓ z0 pledge' = fromUnitInterval pledge ⊓ z0 instance nonZeroz0 : NonZeroℚ z0 nonZeroz0 = nonZero-1/n nopt nonZero-1+a0 : NonZeroℚ (1+a0) nonZero-1+a0 = nonZero-1+max0-x (pparams .PParams.a0) rewardℚ = fromℕ rewardPot ÷ 1+a0 * (stake' + pledge' * a0 * (stake' - pledge' * (z0 - stake') ÷ z0) ÷ z0) rewardℕ = posPart (floor rewardℚ) mkApparentPerformance : UnitInterval → ℕ → ℕ → ℚ mkApparentPerformance stake poolBlocks totalBlocks = ratioBlocks ÷₀ stake' where stake' = fromUnitInterval stake instance nonZero-totalBlocks : NonZero (1 ⊔ totalBlocks) nonZero-totalBlocks = nonZero-max-1 totalBlocks ratioBlocks = (pos poolBlocks) / (1 ⊔ totalBlocks) rewardOwners : Coin → PoolParams → UnitInterval → UnitInterval → Coin rewardOwners rewards pool ownerStake stake = if rewards ≤ cost then rewards else cost + posPart (floor ( (fromℕ rewards - fromℕ cost) * (margin + (1 - margin) * ratioStake))) where ratioStake = fromUnitInterval ownerStake ÷₀ fromUnitInterval stake cost = pool .PoolParams.cost margin = fromUnitInterval (pool .PoolParams.margin) rewardMember : Coin → PoolParams → UnitInterval → UnitInterval → Coin rewardMember rewards pool memberStake stake = if rewards ≤ cost then 0 else posPart (floor ( (fromℕ rewards - fromℕ cost) * ((1 - margin) * ratioStake))) where ratioStake = fromUnitInterval memberStake ÷₀ fromUnitInterval stake cost = pool .PoolParams.cost margin = fromUnitInterval (pool .PoolParams.margin) Stake = Credential ⇀ Coin rewardOnePool : PParams → Coin → ℕ → ℕ → PoolParams → Stake → UnitInterval → UnitInterval → Coin → (Credential ⇀ Coin) rewardOnePool pparams rewardPot n N pool stakeDistr σ σa tot = rewards where mkRelativeStake = λ coin → clamp (coin /₀ tot) owners = mapˢ KeyHashObj (pool .PoolParams.owners) ownerStake = ∑[ c ← stakeDistr ∣ owners ] c pledge = pool .PoolParams.pledge maxP = if pledge ≤ ownerStake then maxPool pparams rewardPot σ (mkRelativeStake pledge) else 0 apparentPerformance = mkApparentPerformance σa n N poolReward = posPart (floor (apparentPerformance * fromℕ maxP)) memberRewards = mapValues (λ coin → rewardMember poolReward pool (mkRelativeStake coin) σ) (stakeDistr ∣ owners ᶜ) ownersRewards = ❴ pool .PoolParams.rewardAccount , rewardOwners poolReward pool (mkRelativeStake ownerStake) σ ❵ᵐ rewards = memberRewards ∪⁺ ownersRewards Delegations = Credential ⇀ KeyHash poolStake : KeyHash → Delegations → Stake → Stake poolStake hk delegs stake = stake ∣ dom (delegs ∣^ ❴ hk ❵) BlocksMade = KeyHash ⇀ ℕ uncurryᵐ : ∀ {A B C : Type} ⦃ _ : DecEq A ⦄ ⦃ _ : DecEq B ⦄ → A ⇀ (B ⇀ C) → (A × B) ⇀ C uncurryᵐ {A} {B} {C} abc = mapFromPartialFun lookup' domain' where lookup' : (A × B) → Maybe C lookup' (a , b) = lookupᵐ? abc a >>= (λ bc → lookupᵐ? bc b) joinˢ : ∀ {X} → ℙ (ℙ X) → ℙ X joinˢ = concatMapˢ id domain' : ℙ (A × B) domain' = joinˢ (range (mapWithKey (λ a bc → range (mapWithKey (λ b _ → (a , b)) bc)) abc)) reward : PParams → BlocksMade → Coin → (KeyHash ⇀ PoolParams) → Stake → Delegations → Coin → (Credential ⇀ Coin) reward pp blocks rewardPot poolParams stake delegs total = rewards where active = ∑[ c ← stake ] c Σ_/total = λ st → clamp ((∑[ c ← st ] c) /₀ total) Σ_/active = λ st → clamp ((∑[ c ← st ] c) /₀ active) N = ∑[ m ← blocks ] m mkPoolData = λ hk p → map (λ n → (n , p , poolStake hk delegs stake)) (lookupᵐ? blocks hk) pdata = mapMaybeWithKeyᵐ mkPoolData poolParams results : (KeyHash × Credential) ⇀ Coin results = uncurryᵐ (mapValues (λ (n , p , s) → rewardOnePool pp rewardPot n N p s (Σ s /total) (Σ s /active) total) pdata) rewards = aggregateBy (mapˢ (λ (kh , cred) → (kh , cred) , cred) (dom results)) results record RewardUpdate : Set where field Δt Δr Δf : ℤ rs : Credential ⇀ Coin flowConservation : Δt + Δr + Δf + pos (∑[ c ← rs ] c) ≡ 0 Δt-nonnegative : 0 ≤ Δt Δf-nonpositive : Δf ≤ 0 record Snapshot : Set where field stake : Credential ⇀ Coin delegations : Credential ⇀ KeyHash poolParameters : KeyHash ⇀ PoolParams instance unquoteDecl HasCast-Snapshot = derive-HasCast [ (quote Snapshot , HasCast-Snapshot) ] private getStakeCred : TxOut → Maybe Credential getStakeCred (a , _ , _ , _) = stakeCred a stakeDistr : UTxO → DState → PState → Snapshot stakeDistr utxo stᵈ pState = $\begin{pmatrix} \,\href{Axiom.Set.Map.Dec.html#1932}{\htmlId{6365}{\htmlClass{Function}{\text{aggregate₊}}}}\, (\,\href{Ledger.Conway.Rewards.html#6614}{\htmlId{6377}{\htmlClass{Function}{\text{stakeRelation}}}}\, \,\href{abstract-set-theory.FiniteSetTheory.html#2767}{\htmlId{6391}{\htmlClass{Function Operator}{\text{ᶠˢ}}}}\,) \\ \,\href{Ledger.Conway.Certs.html#3297}{\htmlId{6397}{\htmlClass{Function}{\text{stakeDelegs}}}}\, \\ \,\href{Ledger.Conway.Rewards.html#6436}{\htmlId{6411}{\htmlClass{Function}{\text{poolParams}}}}\, \end{pmatrix}$ where poolParams = pState .PState.pools open DState stᵈ using (stakeDelegs; rewards) m = mapˢ (λ a → (a , cbalance (utxo ∣^' λ i → getStakeCred i ≡ just a))) (dom rewards) stakeRelation = m ∪ ∣ rewards ∣ record Snapshots : Set where field mark set go : Snapshot feeSS : Coin instance unquoteDecl HasCast-Snapshots = derive-HasCast [ (quote Snapshots , HasCast-Snapshots) ] private variable lstate : LState mark set go : Snapshot feeSS : Coin data _⊢_⇀⦇_,SNAP⦈_ : LState → Snapshots → ⊤ → Snapshots → Type where SNAP : let open LState lstate; open UTxOState utxoSt; open CertState certState stake = stakeDistr utxo dState pState in lstate ⊢ $\begin{pmatrix} \,\href{Ledger.Conway.Rewards.html#6883}{\htmlId{7146}{\htmlClass{Generalizable}{\text{mark}}}}\, \\ \,\href{Ledger.Conway.Rewards.html#6888}{\htmlId{7153}{\htmlClass{Generalizable}{\text{set}}}}\, \\ \,\href{Ledger.Conway.Rewards.html#6892}{\htmlId{7159}{\htmlClass{Generalizable}{\text{go}}}}\, \\ \,\href{Ledger.Conway.Rewards.html#6908}{\htmlId{7164}{\htmlClass{Generalizable}{\text{feeSS}}}}\, \end{pmatrix}$ ⇀⦇ tt ,SNAP⦈ $\begin{pmatrix} \,\href{Ledger.Conway.Rewards.html#7086}{\htmlId{7187}{\htmlClass{Bound}{\text{stake}}}}\, \\ \,\href{Ledger.Conway.Rewards.html#6883}{\htmlId{7195}{\htmlClass{Generalizable}{\text{mark}}}}\, \\ \,\href{Ledger.Conway.Rewards.html#6888}{\htmlId{7202}{\htmlClass{Generalizable}{\text{set}}}}\, \\ \,\href{Ledger.Conway.Utxo.html#2833}{\htmlId{7208}{\htmlClass{Function}{\text{fees}}}}\, \end{pmatrix}$