1use std::{
2 iter::Sum,
3 ops::{Add, AddAssign},
4};
5
6use penumbra_sdk_asset::{asset, Value, STAKING_TOKEN_ASSET_ID};
7use serde::{Deserialize, Serialize};
8
9use penumbra_sdk_num::Amount;
10use penumbra_sdk_proto::{core::component::fee::v1 as pb, DomainType};
11
12use crate::Fee;
13
14#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
18#[serde(try_from = "pb::Gas", into = "pb::Gas")]
19pub struct Gas {
20 pub block_space: u64,
21 pub compact_block_space: u64,
22 pub verification: u64,
23 pub execution: u64,
24}
25
26impl DomainType for Gas {
27 type Proto = pb::Gas;
28}
29
30impl From<Gas> for pb::Gas {
31 fn from(gas: Gas) -> Self {
32 pb::Gas {
33 block_space: gas.block_space,
34 compact_block_space: gas.compact_block_space,
35 verification: gas.verification,
36 execution: gas.execution,
37 }
38 }
39}
40
41impl TryFrom<pb::Gas> for Gas {
42 type Error = anyhow::Error;
43
44 fn try_from(proto: pb::Gas) -> Result<Self, Self::Error> {
45 Ok(Gas {
46 block_space: proto.block_space,
47 compact_block_space: proto.compact_block_space,
48 verification: proto.verification,
49 execution: proto.execution,
50 })
51 }
52}
53
54impl Gas {
55 pub fn zero() -> Self {
56 Self {
57 block_space: 0,
58 compact_block_space: 0,
59 verification: 0,
60 execution: 0,
61 }
62 }
63}
64
65impl Add for Gas {
66 type Output = Self;
67
68 fn add(self, rhs: Self) -> Self::Output {
69 Self {
70 block_space: self.block_space + rhs.block_space,
71 compact_block_space: self.compact_block_space + rhs.compact_block_space,
72 verification: self.verification + rhs.verification,
73 execution: self.execution + rhs.execution,
74 }
75 }
76}
77
78impl AddAssign for Gas {
79 fn add_assign(&mut self, rhs: Self) {
80 *self = *self + rhs;
81 }
82}
83
84impl Sum for Gas {
85 fn sum<I: Iterator<Item = Gas>>(iter: I) -> Gas {
86 iter.fold(Gas::zero(), |acc, x| acc + x)
87 }
88}
89
90#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
96#[serde(try_from = "pb::GasPrices", into = "pb::GasPrices")]
97pub struct GasPrices {
98 pub asset_id: asset::Id,
99 pub block_space_price: u64,
100 pub compact_block_space_price: u64,
101 pub verification_price: u64,
102 pub execution_price: u64,
103}
104
105impl Default for GasPrices {
106 fn default() -> Self {
107 Self {
108 asset_id: *STAKING_TOKEN_ASSET_ID,
109 block_space_price: 0,
110 compact_block_space_price: 0,
111 verification_price: 0,
112 execution_price: 0,
113 }
114 }
115}
116
117impl GasPrices {
118 pub fn zero() -> Self {
119 Self::default()
120 }
121
122 pub fn fee(&self, gas: &Gas) -> Fee {
124 let amount = Amount::from(
125 (self.block_space_price * gas.block_space) / 1_000
126 + (self.compact_block_space_price * gas.compact_block_space) / 1_000
127 + (self.verification_price * gas.verification) / 1_000
128 + (self.execution_price * gas.execution) / 1_000,
129 );
130
131 Fee(Value {
132 asset_id: self.asset_id,
133 amount,
134 })
135 }
136}
137
138impl DomainType for GasPrices {
139 type Proto = pb::GasPrices;
140}
141
142impl From<GasPrices> for pb::GasPrices {
143 fn from(prices: GasPrices) -> Self {
144 pb::GasPrices {
145 asset_id: if prices.asset_id == *STAKING_TOKEN_ASSET_ID {
147 None
148 } else {
149 Some(prices.asset_id.into())
150 },
151 block_space_price: prices.block_space_price,
152 compact_block_space_price: prices.compact_block_space_price,
153 verification_price: prices.verification_price,
154 execution_price: prices.execution_price,
155 }
156 }
157}
158
159impl TryFrom<pb::GasPrices> for GasPrices {
160 type Error = anyhow::Error;
161
162 fn try_from(proto: pb::GasPrices) -> Result<Self, Self::Error> {
163 Ok(GasPrices {
164 block_space_price: proto.block_space_price,
165 compact_block_space_price: proto.compact_block_space_price,
166 verification_price: proto.verification_price,
167 execution_price: proto.execution_price,
168 asset_id: proto
169 .asset_id
170 .map(TryInto::try_into)
171 .transpose()?
172 .unwrap_or_else(|| *STAKING_TOKEN_ASSET_ID),
173 })
174 }
175}