1use alloy::{
2 genesis::{ChainConfig, Genesis, GenesisAccount},
3 primitives::{Address, U256, address},
4 signers::{local::MnemonicBuilder, utils::secret_key_to_address},
5};
6use alloy_primitives::{B256, Bytes};
7use commonware_codec::Encode as _;
8use commonware_consensus::types::Epoch;
9use commonware_cryptography::{
10 Signer as _,
11 bls12381::{
12 dkg::{self, Output},
13 primitives::{sharing::Mode, variant::MinSig},
14 },
15 ed25519::PublicKey,
16};
17use commonware_math::algebra::Random as _;
18use commonware_utils::{N3f1, TryFromIterator as _, ordered};
19use eyre::{WrapErr as _, eyre};
20use indicatif::{ParallelProgressIterator, ProgressIterator};
21use itertools::Itertools;
22use rand_08::SeedableRng as _;
23use rayon::prelude::*;
24use reth_evm::{
25 Evm as _, EvmEnv, EvmFactory,
26 revm::{
27 DatabaseCommit,
28 context_interface::JournalTr as _,
29 database::{CacheDB, EmptyDB},
30 state::{AccountInfo, Bytecode},
31 },
32};
33use std::{
34 collections::BTreeMap,
35 iter::repeat_with,
36 net::SocketAddr,
37 path::{Path, PathBuf},
38};
39use tempo_chainspec::spec::{TEMPO_T0_BASE_FEE, TEMPO_T1_BASE_FEE};
40use tempo_consensus_config::{SigningKey, SigningShare};
41use tempo_contracts::{
42 ARACHNID_CREATE2_FACTORY_ADDRESS, CREATEX_ADDRESS, MULTICALL3_ADDRESS, PERMIT2_ADDRESS,
43 PERMIT2_SALT, SAFE_DEPLOYER_ADDRESS,
44 contracts::{ARACHNID_CREATE2_FACTORY_BYTECODE, CreateX, Multicall3, SafeDeployer},
45 precompiles::{IValidatorConfigV2, createTokenCall},
46};
47use tempo_dkg_onchain_artifacts::OnchainDkgOutcome;
48use tempo_evm::evm::{TempoEvm, TempoEvmFactory};
49use tempo_precompiles::{
50 PATH_USD_ADDRESS,
51 account_keychain::AccountKeychain,
52 address_registry::AddressRegistry,
53 nonce::NonceManager,
54 receive_policy_guard::ReceivePolicyGuard,
55 signature_verifier::SignatureVerifier,
56 stablecoin_dex::StablecoinDEX,
57 storage::{ContractStorage, StorageActions, StorageCtx},
58 tip_fee_manager::{IFeeManager, TipFeeManager},
59 tip20::{ISSUER_ROLE, ITIP20, TIP20Token},
60 tip20_factory::TIP20Factory,
61 tip403_registry::TIP403Registry,
62 validator_config_v2::ValidatorConfigV2,
63};
64
65#[derive(Debug, clap::Args)]
67pub(crate) struct GenesisArgs {
68 #[arg(short, long, default_value = "50000")]
70 accounts: u32,
71
72 #[arg(
74 short,
75 long,
76 default_value = "test test test test test test test test test test test junk"
77 )]
78 mnemonic: String,
79
80 #[arg(long, default_value = "0x0000000000000000000000000000000000000000")]
82 coinbase: Address,
83
84 #[arg(long, short, default_value = "1337")]
86 chain_id: u64,
87
88 #[arg(long, default_value_t = 500_000_000)]
90 gas_limit: u64,
91
92 #[arg(long, default_value_t = 302_400)]
94 epoch_length: u64,
95
96 #[arg(
98 long,
99 value_name = "<ip>:<port>",
100 value_delimiter = ',',
101 required_unless_present_all(["no_dkg_in_genesis"]),
102 )]
103 validators: Vec<SocketAddr>,
104
105 #[arg(long)]
108 no_dkg_in_genesis: bool,
109
110 #[arg(long)]
113 pub(crate) seed: Option<u64>,
114
115 #[arg(long)]
118 pathusd_admin: Option<Address>,
119
120 #[arg(long, default_value_t = u64::MAX)]
121 pathusd_amount: u64,
122
123 #[arg(long)]
126 validator_admin: Option<Address>,
127
128 #[arg(long, value_delimiter = ',')]
131 validator_addresses: Vec<Address>,
132
133 #[arg(long)]
135 no_extra_tokens: bool,
136
137 #[arg(long)]
139 deployment_gas_token: bool,
140
141 #[arg(long)]
143 deployment_gas_token_admin: Option<Address>,
144
145 #[arg(long)]
147 no_pairwise_liquidity: bool,
148
149 #[arg(long, default_value = "0")]
151 t0_time: u64,
152
153 #[arg(long, default_value = "0")]
155 t1_time: u64,
156
157 #[arg(long, default_value = "0")]
159 t1a_time: u64,
160
161 #[arg(long, default_value = "0")]
163 t1b_time: u64,
164
165 #[arg(long, default_value = "0")]
167 t1c_time: u64,
168
169 #[arg(long, default_value = "0")]
171 t2_time: u64,
172
173 #[arg(long, default_value = "0")]
175 t3_time: u64,
176
177 #[arg(long, default_value = "0")]
179 t4_time: u64,
180
181 #[arg(long, default_value = "0")]
183 t5_time: u64,
184
185 #[arg(long, default_value = "0")]
187 t6_time: u64,
188
189 #[arg(long, default_value = "0")]
191 t7_time: u64,
192
193 #[arg(long, default_value = "0")]
195 t8_time: u64,
196}
197
198#[derive(Clone, Debug)]
199pub(crate) struct ConsensusConfig {
200 pub(crate) output: Output<MinSig, PublicKey>,
201 pub(crate) validators: Vec<Validator>,
202}
203impl ConsensusConfig {
204 pub(crate) fn to_genesis_dkg_outcome(&self) -> OnchainDkgOutcome {
205 OnchainDkgOutcome {
206 epoch: Epoch::zero(),
207 output: self.output.clone(),
208 next_players: ordered::Set::try_from_iter(
209 self.validators.iter().map(Validator::public_key),
210 )
211 .unwrap(),
212 is_next_full_dkg: false,
213 }
214 }
215}
216
217#[derive(Clone, Debug)]
218pub(crate) struct Validator {
219 pub(crate) addr: SocketAddr,
220 pub(crate) signing_key: SigningKey,
221 pub(crate) signing_share: SigningShare,
222}
223
224impl Validator {
225 pub(crate) fn public_key(&self) -> PublicKey {
226 self.signing_key.public_key()
227 }
228
229 pub(crate) fn dst_dir(&self, path: impl AsRef<Path>) -> PathBuf {
230 path.as_ref().join(self.addr.to_string())
231 }
232 pub(crate) fn dst_signing_key(&self, path: impl AsRef<Path>) -> PathBuf {
233 self.dst_dir(path).join("signing.key")
234 }
235
236 pub(crate) fn dst_signing_share(&self, path: impl AsRef<Path>) -> PathBuf {
237 self.dst_dir(path).join("signing.share")
238 }
239}
240
241impl GenesisArgs {
242 pub(crate) fn chain_id(&self) -> u64 {
243 self.chain_id
244 }
245
246 pub(crate) fn set_chain_id(&mut self, chain_id: u64) {
247 self.chain_id = chain_id;
248 }
249
250 pub(crate) fn validator_onchain_addresses(&self) -> eyre::Result<Vec<Address>> {
251 if self.validator_addresses.is_empty() {
252 let validator_count = u32::try_from(self.validators.len())
253 .map_err(|_| eyre!("too many validators to derive account addresses"))?;
254 if self.accounts < validator_count.saturating_add(1) {
255 return Err(eyre!("not enough accounts created for validators"));
256 }
257
258 (1..=validator_count)
259 .map(|worker_id| {
260 let signer = MnemonicBuilder::from_phrase_nth(&self.mnemonic, worker_id);
261 Ok(secret_key_to_address(signer.credential()))
262 })
263 .collect()
264 } else {
265 if self.validator_addresses.len() < self.validators.len() {
266 return Err(eyre!("not enough addresses provided for validators"));
267 }
268
269 Ok(self.validator_addresses[0..self.validators.len()].to_vec())
270 }
271 }
272
273 pub(crate) async fn generate_genesis(self) -> eyre::Result<(Genesis, Option<ConsensusConfig>)> {
278 println!("Generating {:?} accounts", self.accounts);
279
280 let addresses: Vec<Address> = (0..self.accounts)
281 .into_par_iter()
282 .progress()
283 .map(|worker_id| -> eyre::Result<Address> {
284 let signer = MnemonicBuilder::from_phrase_nth(&self.mnemonic, worker_id);
285 let address = secret_key_to_address(signer.credential());
286 Ok(address)
287 })
288 .collect::<eyre::Result<Vec<Address>>>()?;
289
290 let pathusd_admin = self.pathusd_admin.unwrap_or_else(|| addresses[0]);
293 let validator_admin = self.validator_admin.unwrap_or_else(|| addresses[0]);
294 let mut evm = setup_tempo_evm(self.chain_id);
295
296 deploy_arachnid_create2_factory(&mut evm);
297 deploy_permit2(&mut evm)?;
298
299 println!("Initializing registry");
300 initialize_registry(&mut evm)?;
301
302 println!("Initializing TIP20Factory");
304 initialize_tip20_factory(&mut evm)?;
305
306 println!("Creating pathUSD through factory");
307 create_path_usd_token(pathusd_admin, &addresses, self.pathusd_amount, &mut evm)?;
308
309 let (alpha_token_address, beta_token_address, theta_token_address) =
310 if !self.no_extra_tokens {
311 println!("Initializing TIP20 tokens");
312 let alpha = create_and_mint_token(
313 "AlphaUSD",
314 "AlphaUSD",
315 "USD",
316 PATH_USD_ADDRESS,
317 pathusd_admin,
318 &addresses,
319 U256::from(u64::MAX),
320 SaltOrAddress::Address(address!("20C0000000000000000000000000000000000001")),
321 &mut evm,
322 )?;
323
324 let beta = create_and_mint_token(
325 "BetaUSD",
326 "BetaUSD",
327 "USD",
328 PATH_USD_ADDRESS,
329 pathusd_admin,
330 &addresses,
331 U256::from(u64::MAX),
332 SaltOrAddress::Address(address!("20C0000000000000000000000000000000000002")),
333 &mut evm,
334 )?;
335
336 let theta = create_and_mint_token(
337 "ThetaUSD",
338 "ThetaUSD",
339 "USD",
340 PATH_USD_ADDRESS,
341 pathusd_admin,
342 &addresses,
343 U256::from(u64::MAX),
344 SaltOrAddress::Address(address!("20C0000000000000000000000000000000000003")),
345 &mut evm,
346 )?;
347
348 (Some(alpha), Some(beta), Some(theta))
349 } else {
350 println!("Skipping extra token creation (--no-extra-tokens)");
351 (None, None, None)
352 };
353
354 if self.deployment_gas_token && self.deployment_gas_token_admin.is_none() {
355 eyre::bail!(
356 "--deployment-gas-token-admin is required when --deployment-gas-token is set"
357 );
358 }
359
360 let deployment_gas_token = {
361 if self.deployment_gas_token {
362 let mut rng = rand_08::rngs::StdRng::seed_from_u64(
363 self.seed.unwrap_or_else(rand_08::random::<u64>),
364 );
365
366 let mut salt_bytes = [0u8; 32];
367 rand_08::Rng::fill(&mut rng, &mut salt_bytes);
368
369 let address = create_and_mint_token(
370 "DONOTUSE",
371 "DONOTUSE",
372 "USD",
373 PATH_USD_ADDRESS,
374 self.deployment_gas_token_admin.expect(
375 "Deployment gas token admin is required if you want to deploy the token",
376 ),
377 &addresses,
378 U256::from(u64::MAX),
379 SaltOrAddress::Salt(B256::from(salt_bytes)),
380 &mut evm,
381 )?;
382
383 println!("Deployment gas token address: {address}");
384 Some(address)
385 } else {
386 None
387 }
388 };
389
390 println!(
391 "generating consensus config for validators: {:?}",
392 self.validators
393 );
394 let consensus_config =
395 generate_consensus_config(&self.validators, self.seed, self.no_dkg_in_genesis);
396
397 let validator_onchain_addresses = if self.validator_addresses.is_empty() {
398 if addresses.len() < self.validators.len() + 1 {
399 return Err(eyre!("not enough accounts created for validators"));
400 }
401
402 &addresses[1..self.validators.len() + 1]
403 } else {
404 if self.validator_addresses.len() < self.validators.len() {
405 return Err(eyre!("not enough addresses provided for validators"));
406 }
407
408 &self.validator_addresses[0..self.validators.len()]
409 };
410
411 println!("Initializing validator config v2");
412 initialize_validator_config_v2(
413 validator_admin,
414 &mut evm,
415 &consensus_config,
416 validator_onchain_addresses,
417 self.no_dkg_in_genesis,
418 self.chain_id,
419 )?;
420
421 println!("Initializing fee manager");
422 let default_user_fee_token = if let Some(address) = deployment_gas_token {
423 address
424 } else {
425 alpha_token_address.unwrap_or(PATH_USD_ADDRESS)
426 };
427
428 let default_validator_fee_token = if let Some(address) = deployment_gas_token {
429 address
430 } else {
431 PATH_USD_ADDRESS
432 };
433
434 initialize_fee_manager(
435 default_validator_fee_token,
436 default_user_fee_token,
437 addresses.clone(),
438 vec![self.coinbase],
440 &mut evm,
441 );
442
443 println!("Initializing stablecoin exchange");
444 initialize_stablecoin_dex(&mut evm)?;
445
446 println!("Initializing nonce manager");
447 initialize_nonce_manager(&mut evm)?;
448
449 println!("Initializing account keychain");
450 initialize_account_keychain(&mut evm)?;
451
452 println!("Initializing TIP20 registry");
453 initialize_address_registry(&mut evm)?;
454
455 if self.t3_time == 0 {
456 println!("Initializing signature verifier (T3 active at genesis)");
457 initialize_signature_verifier(&mut evm)?;
458 }
459
460 if self.t6_time == 0 {
461 println!("Initializing TIP-1028 ReceivePolicyGuard (T6 active at genesis)");
462 initialize_receive_policy_guard(&mut evm)?;
463 }
464
465 if !self.no_pairwise_liquidity {
466 if let (Some(alpha), Some(beta), Some(theta)) =
467 (alpha_token_address, beta_token_address, theta_token_address)
468 {
469 println!("Minting pairwise FeeAMM liquidity");
470 mint_pairwise_liquidity(
471 alpha,
472 vec![PATH_USD_ADDRESS, beta, theta],
473 U256::from(10u64.pow(10)),
474 pathusd_admin,
475 &mut evm,
476 );
477 } else {
478 println!("Skipping pairwise liquidity (extra tokens not created)");
479 }
480 } else {
481 println!("Skipping pairwise liquidity (--no-pairwise-liquidity)");
482 }
483
484 evm.ctx_mut()
485 .journaled_state
486 .load_account(ARACHNID_CREATE2_FACTORY_ADDRESS)?;
487 evm.ctx_mut()
488 .journaled_state
489 .load_account(PERMIT2_ADDRESS)?;
490
491 println!("Saving EVM state to allocation");
493 let evm_state = evm.ctx_mut().journaled_state.evm_state();
494 let mut genesis_alloc: BTreeMap<Address, GenesisAccount> = evm_state
495 .iter()
496 .progress()
497 .map(|(address, account)| {
498 let storage = if !account.storage.is_empty() {
499 Some(
500 account
501 .storage
502 .iter()
503 .map(|(key, val)| ((*key).into(), val.present_value.into()))
504 .collect(),
505 )
506 } else {
507 None
508 };
509 let genesis_account = GenesisAccount {
510 nonce: Some(account.info.nonce),
511 code: account.info.code.as_ref().map(|c| c.original_bytes()),
512 storage,
513 ..Default::default()
514 };
515 (*address, genesis_account)
516 })
517 .collect();
518
519 genesis_alloc.insert(
520 MULTICALL3_ADDRESS,
521 GenesisAccount {
522 code: Some(Bytes::from_static(&Multicall3::DEPLOYED_BYTECODE)),
523 nonce: Some(1),
524 ..Default::default()
525 },
526 );
527
528 genesis_alloc.insert(
529 CREATEX_ADDRESS,
530 GenesisAccount {
531 code: Some(Bytes::from_static(&CreateX::DEPLOYED_BYTECODE)),
532 nonce: Some(1),
533 ..Default::default()
534 },
535 );
536
537 genesis_alloc.insert(
538 SAFE_DEPLOYER_ADDRESS,
539 GenesisAccount {
540 code: Some(Bytes::from_static(&SafeDeployer::DEPLOYED_BYTECODE)),
541 nonce: Some(1),
542 ..Default::default()
543 },
544 );
545
546 let mut chain_config = ChainConfig {
547 chain_id: self.chain_id,
548 homestead_block: Some(0),
549 eip150_block: Some(0),
550 eip155_block: Some(0),
551 eip158_block: Some(0),
552 byzantium_block: Some(0),
553 constantinople_block: Some(0),
554 petersburg_block: Some(0),
555 istanbul_block: Some(0),
556 berlin_block: Some(0),
557 london_block: Some(0),
558 merge_netsplit_block: Some(0),
559 shanghai_time: Some(0),
560 cancun_time: Some(0),
561 prague_time: Some(0),
562 osaka_time: Some(0),
563 terminal_total_difficulty: Some(U256::from(0)),
564 terminal_total_difficulty_passed: true,
565 deposit_contract_address: Some(Address::ZERO),
566 ..Default::default()
567 };
568
569 chain_config
570 .extra_fields
571 .insert_value("epochLength".to_string(), self.epoch_length)?;
572 chain_config
573 .extra_fields
574 .insert_value("t0Time".to_string(), self.t0_time)?;
575 chain_config
576 .extra_fields
577 .insert_value("t1Time".to_string(), self.t1_time)?;
578 chain_config
579 .extra_fields
580 .insert_value("t1aTime".to_string(), self.t1a_time)?;
581 chain_config
582 .extra_fields
583 .insert_value("t1bTime".to_string(), self.t1b_time)?;
584 chain_config
585 .extra_fields
586 .insert_value("t1cTime".to_string(), self.t1c_time)?;
587 chain_config
588 .extra_fields
589 .insert_value("t2Time".to_string(), self.t2_time)?;
590 chain_config
591 .extra_fields
592 .insert_value("t3Time".to_string(), self.t3_time)?;
593 chain_config
594 .extra_fields
595 .insert_value("t4Time".to_string(), self.t4_time)?;
596 chain_config
597 .extra_fields
598 .insert_value("t5Time".to_string(), self.t5_time)?;
599 chain_config
600 .extra_fields
601 .insert_value("t6Time".to_string(), self.t6_time)?;
602 chain_config
603 .extra_fields
604 .insert_value("t7Time".to_string(), self.t7_time)?;
605 chain_config
606 .extra_fields
607 .insert_value("t8Time".to_string(), self.t8_time)?;
608 let mut extra_data = Bytes::from_static(b"tempo-genesis");
609
610 if let Some(consensus_config) = &consensus_config {
611 if self.no_dkg_in_genesis {
612 println!("no-initial-dkg-in-genesis passed; not writing to header extra_data");
613 } else {
614 extra_data = consensus_config
615 .to_genesis_dkg_outcome()
616 .encode()
617 .to_vec()
618 .into();
619 }
620 }
621
622 let base_fee: u128 = if self.t1_time == 0 {
624 u128::from(TEMPO_T1_BASE_FEE)
625 } else {
626 u128::from(TEMPO_T0_BASE_FEE)
627 };
628
629 let mut genesis = Genesis::default()
630 .with_gas_limit(self.gas_limit)
631 .with_base_fee(Some(base_fee))
632 .with_nonce(0x42)
633 .with_extra_data(extra_data)
634 .with_coinbase(self.coinbase);
635
636 genesis.alloc = genesis_alloc;
637 genesis.config = chain_config;
638
639 Ok((genesis, consensus_config))
640 }
641}
642
643fn setup_tempo_evm(chain_id: u64) -> TempoEvm<CacheDB<EmptyDB>> {
644 let db = CacheDB::default();
645 let mut env = EvmEnv::default().with_timestamp(U256::ZERO);
647 env.cfg_env.chain_id = chain_id;
648
649 let factory = TempoEvmFactory::default();
650 factory.create_evm(db, env)
651}
652
653fn deploy_arachnid_create2_factory(evm: &mut TempoEvm<CacheDB<EmptyDB>>) {
655 println!("Deploying Arachnid CREATE2 factory at {ARACHNID_CREATE2_FACTORY_ADDRESS}");
656
657 evm.db_mut().insert_account_info(
658 ARACHNID_CREATE2_FACTORY_ADDRESS,
659 AccountInfo {
660 code: Some(Bytecode::new_raw(ARACHNID_CREATE2_FACTORY_BYTECODE)),
661 nonce: 0,
662 ..Default::default()
663 },
664 );
665}
666
667fn deploy_permit2(evm: &mut TempoEvm<CacheDB<EmptyDB>>) -> eyre::Result<()> {
669 let bytecode = &tempo_contracts::Permit2::BYTECODE;
671 let calldata: Bytes = PERMIT2_SALT
672 .as_slice()
673 .iter()
674 .chain(bytecode.iter())
675 .copied()
676 .collect();
677
678 println!("Deploying Permit2 via CREATE2 to {PERMIT2_ADDRESS}");
679
680 let result =
681 evm.transact_system_call(Address::ZERO, ARACHNID_CREATE2_FACTORY_ADDRESS, calldata)?;
682
683 if !result.result.is_success() {
684 return Err(eyre!("Permit2 deployment failed: {:?}", result));
685 }
686
687 evm.db_mut().commit(result.state);
688
689 println!("Permit2 deployed successfully at {PERMIT2_ADDRESS}");
690 Ok(())
691}
692
693fn initialize_tip20_factory(evm: &mut TempoEvm<CacheDB<EmptyDB>>) -> eyre::Result<()> {
695 let ctx = evm.ctx_mut();
696 StorageCtx::enter_evm(
697 &mut ctx.journaled_state,
698 &ctx.block,
699 &ctx.cfg,
700 &ctx.tx,
701 StorageActions::disabled(),
702 || TIP20Factory::new().initialize(),
703 )?;
704 Ok(())
705}
706
707fn create_path_usd_token(
710 admin: Address,
711 recipients: &[Address],
712 amount_per_recipient: u64,
713 evm: &mut TempoEvm<CacheDB<EmptyDB>>,
714) -> eyre::Result<()> {
715 let ctx = evm.ctx_mut();
716 StorageCtx::enter_evm(
717 &mut ctx.journaled_state,
718 &ctx.block,
719 &ctx.cfg,
720 &ctx.tx,
721 StorageActions::disabled(),
722 || {
723 TIP20Factory::new().create_token_reserved_address(
724 PATH_USD_ADDRESS,
725 "pathUSD",
726 "pathUSD",
727 "USD",
728 Address::ZERO,
729 admin,
730 )?;
731
732 let mut token = TIP20Token::from_address(PATH_USD_ADDRESS)
734 .expect("Could not create pathUSD token instance");
735 token.grant_role_internal(admin, *ISSUER_ROLE)?;
736
737 for recipient in recipients.iter().progress() {
739 token
740 .mint(
741 admin,
742 ITIP20::mintCall {
743 to: *recipient,
744 amount: U256::from(amount_per_recipient),
745 },
746 )
747 .expect("Could not mint pathUSD");
748 }
749
750 Ok(())
751 },
752 )
753}
754
755enum SaltOrAddress {
756 Salt(B256),
757 Address(Address),
758}
759
760#[expect(clippy::too_many_arguments)]
762fn create_and_mint_token(
763 symbol: &str,
764 name: &str,
765 currency: &str,
766 quote_token: Address,
767 admin: Address,
768 recipients: &[Address],
769 mint_amount: U256,
770 salt_or_address: SaltOrAddress,
771 evm: &mut TempoEvm<CacheDB<EmptyDB>>,
772) -> eyre::Result<Address> {
773 let ctx = evm.ctx_mut();
774 StorageCtx::enter_evm(
775 &mut ctx.journaled_state,
776 &ctx.block,
777 &ctx.cfg,
778 &ctx.tx,
779 StorageActions::disabled(),
780 || {
781 let mut factory = TIP20Factory::new();
782 assert!(
783 factory
784 .is_initialized()
785 .expect("Could not check factory initialization"),
786 "TIP20Factory must be initialized before creating tokens"
787 );
788
789 let token_address = match salt_or_address {
790 SaltOrAddress::Salt(salt) => factory
791 .create_token(
792 admin,
793 createTokenCall {
794 name: name.into(),
795 symbol: symbol.into(),
796 currency: currency.into(),
797 quoteToken: quote_token,
798 salt,
799 admin,
800 },
801 )
802 .expect("Could not create token"),
803 SaltOrAddress::Address(address) => factory
804 .create_token_reserved_address(
805 address,
806 name,
807 symbol,
808 currency,
809 quote_token,
810 admin,
811 )
812 .expect("Could not create token"),
813 };
814
815 let mut token =
816 TIP20Token::from_address(token_address).expect("Could not create token instance");
817 token.grant_role_internal(admin, *ISSUER_ROLE)?;
818
819 let result = token.set_supply_cap(
820 admin,
821 ITIP20::setSupplyCapCall {
822 newSupplyCap: U256::from(u128::MAX),
823 },
824 );
825 assert!(result.is_ok());
826
827 token
828 .mint(
829 admin,
830 ITIP20::mintCall {
831 to: admin,
832 amount: mint_amount,
833 },
834 )
835 .expect("Token minting failed");
836
837 for address in recipients.iter().progress() {
838 token
839 .mint(
840 admin,
841 ITIP20::mintCall {
842 to: *address,
843 amount: U256::from(u64::MAX),
844 },
845 )
846 .expect("Could not mint fee token");
847 }
848
849 Ok(token.address())
850 },
851 )
852}
853
854fn initialize_fee_manager(
855 validator_fee_token_address: Address,
856 user_fee_token_address: Address,
857 initial_accounts: Vec<Address>,
858 validators: Vec<Address>,
859 evm: &mut TempoEvm<CacheDB<EmptyDB>>,
860) {
861 let ctx = evm.ctx_mut();
863 StorageCtx::enter_evm(
864 &mut ctx.journaled_state,
865 &ctx.block,
866 &ctx.cfg,
867 &ctx.tx,
868 StorageActions::disabled(),
869 || {
870 let mut fee_manager = TipFeeManager::new();
871 fee_manager
872 .initialize()
873 .expect("Could not init fee manager");
874 println!(
875 "Setting user fee token {user_fee_token_address} for {} accounts",
876 initial_accounts.len()
877 );
878 for address in initial_accounts.iter().progress() {
879 fee_manager
880 .set_user_token(
881 *address,
882 IFeeManager::setUserTokenCall {
883 token: user_fee_token_address,
884 },
885 )
886 .expect("Could not set fee token");
887 }
888
889 for validator in validators {
891 println!("Setting user token for {validator} {validator_fee_token_address}");
892 fee_manager
893 .set_validator_token(
894 validator,
895 IFeeManager::setValidatorTokenCall {
896 token: validator_fee_token_address,
897 },
898 Address::random(),
900 )
901 .expect("Could not set validator fee token");
902 }
903 },
904 );
905}
906
907fn initialize_registry(evm: &mut TempoEvm<CacheDB<EmptyDB>>) -> eyre::Result<()> {
909 let ctx = evm.ctx_mut();
910 StorageCtx::enter_evm(
911 &mut ctx.journaled_state,
912 &ctx.block,
913 &ctx.cfg,
914 &ctx.tx,
915 StorageActions::disabled(),
916 || TIP403Registry::new().initialize(),
917 )?;
918
919 Ok(())
920}
921
922fn initialize_stablecoin_dex(evm: &mut TempoEvm<CacheDB<EmptyDB>>) -> eyre::Result<()> {
923 let ctx = evm.ctx_mut();
924 StorageCtx::enter_evm(
925 &mut ctx.journaled_state,
926 &ctx.block,
927 &ctx.cfg,
928 &ctx.tx,
929 StorageActions::disabled(),
930 || StablecoinDEX::new().initialize(),
931 )?;
932
933 Ok(())
934}
935
936fn initialize_nonce_manager(evm: &mut TempoEvm<CacheDB<EmptyDB>>) -> eyre::Result<()> {
937 let ctx = evm.ctx_mut();
938 StorageCtx::enter_evm(
939 &mut ctx.journaled_state,
940 &ctx.block,
941 &ctx.cfg,
942 &ctx.tx,
943 StorageActions::disabled(),
944 || NonceManager::new().initialize(),
945 )?;
946
947 Ok(())
948}
949
950fn initialize_account_keychain(evm: &mut TempoEvm<CacheDB<EmptyDB>>) -> eyre::Result<()> {
952 let ctx = evm.ctx_mut();
953 StorageCtx::enter_evm(
954 &mut ctx.journaled_state,
955 &ctx.block,
956 &ctx.cfg,
957 &ctx.tx,
958 StorageActions::disabled(),
959 || AccountKeychain::new().initialize(),
960 )?;
961
962 Ok(())
963}
964
965fn initialize_address_registry(evm: &mut TempoEvm<CacheDB<EmptyDB>>) -> eyre::Result<()> {
966 let ctx = evm.ctx_mut();
967 StorageCtx::enter_evm(
968 &mut ctx.journaled_state,
969 &ctx.block,
970 &ctx.cfg,
971 &ctx.tx,
972 StorageActions::disabled(),
973 || AddressRegistry::new().initialize(),
974 )?;
975
976 Ok(())
977}
978
979fn initialize_signature_verifier(evm: &mut TempoEvm<CacheDB<EmptyDB>>) -> eyre::Result<()> {
980 let ctx = evm.ctx_mut();
981 StorageCtx::enter_evm(
982 &mut ctx.journaled_state,
983 &ctx.block,
984 &ctx.cfg,
985 &ctx.tx,
986 StorageActions::disabled(),
987 || SignatureVerifier::new().initialize(),
988 )?;
989
990 Ok(())
991}
992
993fn initialize_receive_policy_guard(evm: &mut TempoEvm<CacheDB<EmptyDB>>) -> eyre::Result<()> {
994 let ctx = evm.ctx_mut();
995 StorageCtx::enter_evm(
996 &mut ctx.journaled_state,
997 &ctx.block,
998 &ctx.cfg,
999 &ctx.tx,
1000 StorageActions::disabled(),
1001 || ReceivePolicyGuard::new().initialize(),
1002 )?;
1003
1004 Ok(())
1005}
1006
1007fn initialize_validator_config_v2(
1012 admin: Address,
1013 evm: &mut TempoEvm<CacheDB<EmptyDB>>,
1014 consensus_config: &Option<ConsensusConfig>,
1015 onchain_validator_addresses: &[Address],
1016 no_dkg_in_genesis: bool,
1017 chain_id: u64,
1018) -> eyre::Result<()> {
1019 let ctx = evm.ctx_mut();
1020 StorageCtx::enter_evm(
1021 &mut ctx.journaled_state,
1022 &ctx.block,
1023 &ctx.cfg,
1024 &ctx.tx,
1025 StorageActions::disabled(),
1026 || {
1027 let mut v2 = ValidatorConfigV2::new();
1028 v2.initialize(admin)
1029 .wrap_err("failed to initialize validator config v2")?;
1030
1031 if no_dkg_in_genesis {
1032 println!("no-dkg-in-genesis passed; not writing validators to genesis block");
1033 return Ok(());
1034 }
1035
1036 let Some(consensus_config) = consensus_config.clone() else {
1037 println!("no consensus config passed; no validators to write to contract");
1038 return Ok(());
1039 };
1040
1041 let num_validators = consensus_config.validators.len();
1042 if onchain_validator_addresses.len() < num_validators {
1043 return Err(eyre!(
1044 "need {} addresses for all validators, but only {} were provided",
1045 num_validators,
1046 onchain_validator_addresses.len()
1047 ));
1048 }
1049
1050 println!("writing {num_validators} validators into v2 contract");
1051 for (i, validator) in consensus_config.validators.iter().enumerate() {
1052 let validator_address = onchain_validator_addresses[i];
1053 let public_key = validator.public_key();
1054 let pubkey: B256 = public_key.encode().as_ref().try_into().unwrap();
1055 let addr = validator.addr;
1056
1057 let config = tempo_validator_config::ValidatorConfig {
1058 chain_id,
1059 validator_address,
1060 public_key: pubkey,
1061 ingress: addr,
1062 egress: addr.ip(),
1063 };
1064
1065 let message = config.add_validator_message_hash(validator_address);
1066 let private_key = validator.signing_key.clone().into_inner();
1067 let signature = private_key.sign(
1068 tempo_precompiles::validator_config_v2::VALIDATOR_NS_ADD,
1069 message.as_slice(),
1070 );
1071
1072 v2.add_validator(
1073 admin,
1074 IValidatorConfigV2::addValidatorCall {
1075 validatorAddress: validator_address,
1076 publicKey: pubkey,
1077 ingress: config.ingress.to_string(),
1078 egress: config.egress.to_string(),
1079 feeRecipient: validator_address,
1080 signature: signature.encode().to_vec().into(),
1081 },
1082 )
1083 .wrap_err("failed to add validator to V2")?;
1084
1085 println!(
1086 "added validator (v2)\
1087 \n\tpublic key: {public_key}\
1088 \n\tonchain address: {validator_address}\
1089 \n\tnet address: {addr}"
1090 );
1091 }
1092 Ok(())
1093 },
1094 )
1095}
1096
1097fn generate_consensus_config(
1099 validators: &[SocketAddr],
1100 seed: Option<u64>,
1101 no_dkg_in_genesis: bool,
1102) -> Option<ConsensusConfig> {
1103 use commonware_cryptography::ed25519::PrivateKey;
1104
1105 match (validators.is_empty(), no_dkg_in_genesis) {
1106 (_, true) => {
1107 println!(
1108 "no-dkg-in-genesis passed; not generating any consensus config because I can't write it to the genesis block"
1109 );
1110 return None;
1111 }
1112 (true, false) => {
1113 panic!("no validators provided and no-dkg-in-genesis not set");
1114 }
1115 _ => {}
1116 }
1117
1118 let mut rng = rand_08::rngs::StdRng::seed_from_u64(seed.unwrap_or_else(rand_08::random::<u64>));
1119
1120 let mut signer_keys = repeat_with(|| PrivateKey::random(&mut rng))
1121 .take(validators.len())
1122 .collect::<Vec<_>>();
1123 signer_keys.sort_by_key(|key| key.public_key());
1124
1125 let (output, shares) = dkg::deal::<_, _, N3f1>(
1126 &mut rng,
1127 Mode::NonZeroCounter,
1128 ordered::Set::try_from_iter(signer_keys.iter().map(|key| key.public_key())).unwrap(),
1129 )
1130 .unwrap();
1131
1132 let validators = validators
1133 .iter()
1134 .copied()
1135 .zip_eq(signer_keys)
1136 .zip_eq(shares)
1137 .map(|((addr, signing_key), (verifying_key, signing_share))| {
1138 assert_eq!(signing_key.public_key(), verifying_key);
1139 Validator {
1140 addr,
1141 signing_key: SigningKey::from(signing_key),
1142 signing_share: SigningShare::from(signing_share),
1143 }
1144 })
1145 .collect();
1146
1147 Some(ConsensusConfig { output, validators })
1148}
1149
1150fn mint_pairwise_liquidity(
1151 a_token: Address,
1152 b_tokens: Vec<Address>,
1153 amount: U256,
1154 admin: Address,
1155 evm: &mut TempoEvm<CacheDB<EmptyDB>>,
1156) {
1157 let ctx = evm.ctx_mut();
1158 StorageCtx::enter_evm(
1159 &mut ctx.journaled_state,
1160 &ctx.block,
1161 &ctx.cfg,
1162 &ctx.tx,
1163 StorageActions::disabled(),
1164 || {
1165 let mut fee_manager = TipFeeManager::new();
1166
1167 for b_token_address in b_tokens {
1168 fee_manager
1169 .mint(admin, a_token, b_token_address, amount, admin)
1170 .expect("Could not mint A -> B Liquidity pool");
1171 }
1172 },
1173 );
1174}