1use crate::sys::{MintNativeSysLog, SysAction, SysBase};
2use alloy::{
3 consensus::{ReceiptEnvelope, TxEip1559, TxReceipt},
4 primitives::{utils::format_ether, Address, Log, U256},
5};
6use signet_extract::ExtractedEvent;
7use signet_types::{
8 constants::MINTER_ADDRESS,
9 primitives::{Transaction, TransactionSigned},
10 MagicSig,
11};
12use signet_zenith::Passage;
13use trevm::{
14 helpers::Ctx,
15 revm::{context::result::EVMError, Database, DatabaseCommit, Inspector},
16 Trevm, MIN_TRANSACTION_GAS,
17};
18
19const ETH_DECIMALS: u8 = 18;
20
21#[derive(Debug, Clone, Copy)]
23pub struct MintNative {
24 recipient: Address,
26
27 decimals: u8,
29
30 host_amount: U256,
32
33 magic_sig: MagicSig,
35
36 nonce: Option<u64>,
38 rollup_chain_id: u64,
40}
41
42impl MintNative {
43 pub fn new<R: TxReceipt<Log = Log>>(
46 event: &ExtractedEvent<'_, R, Passage::EnterToken>,
47 decimals: u8,
48 ) -> Self {
49 Self {
50 recipient: event.event.recipient(),
51 decimals,
52 host_amount: event.event.amount(),
53 magic_sig: event.magic_sig(),
54 nonce: None,
55 rollup_chain_id: event.rollup_chain_id(),
56 }
57 }
58
59 pub fn new_with_nonce<R: TxReceipt<Log = Log>>(
61 event: &ExtractedEvent<'_, R, Passage::EnterToken>,
62 decimals: u8,
63 nonce: u64,
64 ) -> Self {
65 let mut mint = Self::new(event, decimals);
66 mint.populate_nonce(nonce);
67 mint
68 }
69
70 fn make_sys_log(&self) -> MintNativeSysLog {
72 MintNativeSysLog {
73 txHash: self.magic_sig.txid,
74 logIndex: self.magic_sig.event_idx as u64,
75 recipient: self.recipient,
76 amount: self.mint_amount(),
77 }
78 }
79
80 fn make_transaction(&self) -> TransactionSigned {
82 TransactionSigned::new_unhashed(
83 Transaction::Eip1559(TxEip1559 {
84 chain_id: self.rollup_chain_id,
85 nonce: self.nonce.expect("must be set"),
86 gas_limit: MIN_TRANSACTION_GAS,
87 max_fee_per_gas: 0,
88 max_priority_fee_per_gas: 0,
89 to: self.recipient.into(),
90 value: self.mint_amount(),
91 access_list: Default::default(),
92 input: Default::default(),
93 }),
94 self.magic_sig.into(),
95 )
96 }
97
98 pub fn mint_amount(&self) -> U256 {
101 let decimals = self.decimals;
102 adjust_decimals(self.host_amount, decimals, ETH_DECIMALS)
103 }
104}
105
106impl SysBase for MintNative {
107 fn name() -> &'static str {
108 "MintNative"
109 }
110
111 fn description(&self) -> String {
112 format!("Mint {} native tokens to {}", format_ether(self.mint_amount()), self.recipient)
113 }
114
115 fn has_nonce(&self) -> bool {
116 self.nonce.is_some()
117 }
118
119 fn populate_nonce(&mut self, nonce: u64) {
120 self.nonce = Some(nonce)
121 }
122
123 fn produce_transaction(&self) -> TransactionSigned {
124 self.make_transaction()
125 }
126
127 fn produce_log(&self) -> Log {
128 self.make_sys_log().into()
129 }
130
131 fn evm_sender(&self) -> Address {
132 MINTER_ADDRESS
133 }
134}
135
136impl SysAction for MintNative {
137 fn apply<Db, Insp, State>(
138 &self,
139 evm: &mut Trevm<Db, Insp, State>,
140 ) -> Result<(), EVMError<Db::Error>>
141 where
142 Db: Database + DatabaseCommit,
143 Insp: Inspector<Ctx<Db>>,
144 {
145 evm.try_increase_balance_unchecked(self.recipient, self.mint_amount())
147 .map(drop)
148 .map_err(EVMError::Database)
149 }
150
151 fn produce_receipt(&self, cumulative_gas_used: u64) -> ReceiptEnvelope {
152 ReceiptEnvelope::Eip1559(
153 alloy::consensus::Receipt {
154 status: true.into(),
155 cumulative_gas_used: cumulative_gas_used.saturating_add(MIN_TRANSACTION_GAS),
156 logs: vec![self.make_sys_log().into()],
157 }
158 .with_bloom(),
159 )
160 }
161}
162
163fn adjust_decimals(amount: U256, decimals: u8, target_decimals: u8) -> U256 {
170 if target_decimals == 0 || decimals == 0 {
171 return amount;
173 }
174
175 if decimals > target_decimals {
176 let divisor_exp = decimals - target_decimals;
177 let divisor = U256::from(10u64).pow(U256::from(divisor_exp));
178 amount / divisor
179 } else {
180 let multiplier_exp = target_decimals.checked_sub(decimals).unwrap_or_default();
181 let multiplier = U256::from(10u64).pow(U256::from(multiplier_exp));
182 amount * multiplier
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189 use alloy::uint;
190
191 #[test]
192 fn mint_amount_math() {
193 uint! {
194 assert_eq!(adjust_decimals(10_U256.pow(6_U256), 6, 18), 10_U256.pow(18_U256));
195 assert_eq!(adjust_decimals(10_U256.pow(18_U256), 18, 6), 10_U256.pow(6_U256));
196
197 assert_eq!(adjust_decimals(10_U256.pow(6_U256), 6, 12), 10_U256.pow(12_U256));
198 assert_eq!(adjust_decimals(10_U256.pow(12_U256), 12, 6), 10_U256.pow(6_U256));
199
200 assert_eq!(adjust_decimals(10_U256.pow(18_U256), 18, 12), 10_U256.pow(12_U256));
201 assert_eq!(adjust_decimals(10_U256.pow(12_U256), 12, 18), 10_U256.pow(18_U256));
202
203 assert_eq!(adjust_decimals(10_U256.pow(6_U256), 6, 0), 10_U256.pow(6_U256));
204
205 assert_eq!(adjust_decimals(10_U256.pow(18_U256), 3, 6), 10_U256.pow(21_U256));
206 assert_eq!(adjust_decimals(10_U256.pow(21_U256), 6, 3), 10_U256.pow(18_U256));
207 assert_eq!(adjust_decimals(10_U256.pow(18_U256), 6, 3), 10_U256.pow(15_U256));
208 }
209 }
210}