diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 2aed8d25d9..cfcbee35c7 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -3789,6 +3789,7 @@ dependencies = [ "borsh 0.9.3", "bs58 0.5.0", "clap 4.4.6", + "ethers", "hex 0.4.3", "hyperlane-core", "hyperlane-sealevel-connection-client", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index b1400bbf87..e445af2794 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -276,3 +276,27 @@ version = "=0.1.0" git = "https://github.com/hyperlane-xyz/solana-program-library.git" branch = "hyperlane" + +# Overflow checks for Sealevel programs. +# Profiles must be specified at the root of the workspace. + +[profile.dev.package.hyperlane-sealevel-mailbox] +overflow-checks = true + +[profile.dev.package.hyperlane-sealevel-igp] +overflow-checks = true + +[profile.dev.package.hyperlane-sealevel-validator-announce] +overflow-checks = true + +[profile.dev.package.hyperlane-sealevel-token] +overflow-checks = true + +[profile.dev.package.hyperlane-sealevel-token-collateral] +overflow-checks = true + +[profile.dev.package.hyperlane-sealevel-token-native] +overflow-checks = true + +[profile.dev.package.hyperlane-sealevel-multisig-ism-message-id] +overflow-checks = true diff --git a/rust/config/mainnet_config.json b/rust/config/mainnet_config.json index df31c64f33..79d6275208 100644 --- a/rust/config/mainnet_config.json +++ b/rust/config/mainnet_config.json @@ -148,7 +148,7 @@ "domain": 1399811149, "addresses": { "mailbox": "Ge9atjAc3Ltu91VTbNpJDCjZ9CFxFyck4h3YBcTF9XPq", - "interchainGasPaymaster": "FCNfmLSZLo5x7oNYmkYU8WdPUu7pj636P9CaMxkmaCp7", + "interchainGasPaymaster": "Ho7b7gidMacE4AckGZPJ7zn2oa45xVarcxLi2yzcbkNZ", "validatorAnnounce": "C88Lk5GR6cPxYoJxPbNDDEwsx5Kxn1wZEomvQ2So333g" }, "protocol": "sealevel", diff --git a/rust/config/testnet_config.json b/rust/config/testnet_config.json index baf249942c..e1773acd9a 100644 --- a/rust/config/testnet_config.json +++ b/rust/config/testnet_config.json @@ -131,7 +131,7 @@ "domain": 1399811151, "addresses": { "mailbox": "4v25Dz9RccqUrTzmfHzJMsjd1iVoNrWzeJ4o6GYuJrVn", - "interchainGasPaymaster": "7hMPEGdgBQFsjEz3aaNwZp8WMFHs615zAM3erXBDJuJR", + "interchainGasPaymaster": "Gh1B2JLZAY188CAYnWPup8TvGDxHNVWbVVKZi6wh75xZ", "validatorAnnounce": "CMHKvdq4CopDf7qXnDCaTybS15QekQeRt4oUB219yxsp" }, "protocol": "sealevel", diff --git a/rust/hyperlane-core/src/utils.rs b/rust/hyperlane-core/src/utils.rs index 81ff5fda32..f8e7759aa2 100644 --- a/rust/hyperlane-core/src/utils.rs +++ b/rust/hyperlane-core/src/utils.rs @@ -1,4 +1,4 @@ -use std::{str::FromStr, time::Duration}; +use std::str::FromStr; use eyre::Result; use sha3::{digest::Update, Digest, Keccak256}; diff --git a/rust/sealevel/client/Cargo.toml b/rust/sealevel/client/Cargo.toml index 976bf11632..8c14541fac 100644 --- a/rust/sealevel/client/Cargo.toml +++ b/rust/sealevel/client/Cargo.toml @@ -10,6 +10,7 @@ borsh.workspace = true bs58.workspace = true bincode.workspace = true clap = { workspace = true, features = ["derive"] } +ethers.workspace = true hex.workspace = true pretty_env_logger.workspace = true serde.workspace = true diff --git a/rust/sealevel/client/src/artifacts.rs b/rust/sealevel/client/src/artifacts.rs index 7caf4806f0..d6e39beeec 100644 --- a/rust/sealevel/client/src/artifacts.rs +++ b/rust/sealevel/client/src/artifacts.rs @@ -54,6 +54,13 @@ pub(crate) fn read_json(path: &Path) -> T where T: DeserializeOwned, { - let file = File::open(path).expect("Failed to open JSON file"); - serde_json::from_reader(file).expect("Failed to read JSON file") + try_read_json(path).expect("Failed to read JSON from file") +} + +pub(crate) fn try_read_json(path: &Path) -> std::io::Result +where + T: DeserializeOwned, +{ + let file = File::open(path)?; + Ok(serde_json::from_reader(file)?) } diff --git a/rust/sealevel/client/src/context.rs b/rust/sealevel/client/src/context.rs index 80b0ea9e2b..5cb9ba383b 100644 --- a/rust/sealevel/client/src/context.rs +++ b/rust/sealevel/client/src/context.rs @@ -239,14 +239,17 @@ impl<'ctx, 'rpc> TxnBuilder<'ctx, 'rpc> { fn wait_for_user_confirmation() { println!("Continue? [y/n] then press Enter"); let mut input = [0u8; 1]; - std::io::stdin().read_exact(&mut input).unwrap(); - match input[0] { - b'y' => { - println!("Continuing..."); + loop { + std::io::stdin().read_exact(&mut input).unwrap(); + match input[0] { + b'y' => { + println!("Continuing..."); + break; + } + b'n' => { + panic!("User requested exit"); + } + _ => {} } - b'n' => { - panic!("User requested exit"); - } - _ => {} } } diff --git a/rust/sealevel/client/src/core.rs b/rust/sealevel/client/src/core.rs index 8aaf51adb1..783fd3a5f4 100644 --- a/rust/sealevel/client/src/core.rs +++ b/rust/sealevel/client/src/core.rs @@ -18,7 +18,8 @@ use hyperlane_sealevel_igp::accounts::{SOL_DECIMALS, TOKEN_EXCHANGE_RATE_SCALE}; pub(crate) fn process_core_cmd(mut ctx: Context, cmd: CoreCmd) { match cmd.cmd { CoreSubCmd::Deploy(core) => { - let environments_dir = create_new_directory(&core.environments_dir, &core.environment); + let environments_dir = + create_new_directory(&core.env_args.environments_dir, &core.env_args.environment); let chain_dir = create_new_directory(&environments_dir, &core.chain); let core_dir = create_new_directory(&chain_dir, "core"); let key_dir = create_new_directory(&core_dir, "keys"); diff --git a/rust/sealevel/client/src/helloworld.rs b/rust/sealevel/client/src/helloworld.rs index 6226b48282..999e623f7d 100644 --- a/rust/sealevel/client/src/helloworld.rs +++ b/rust/sealevel/client/src/helloworld.rs @@ -10,6 +10,7 @@ use hyperlane_sealevel_hello_world::{ }, program_storage_pda_seeds, }; +use hyperlane_sealevel_igp::accounts::InterchainGasPaymasterType; use serde::{Deserialize, Serialize}; use solana_sdk::{instruction::Instruction, pubkey::Pubkey}; @@ -180,6 +181,26 @@ impl ConnectionClient for HelloWorldDeployer { set_interchain_security_module_instruction(*program_id, storage.owner.unwrap(), ism) .unwrap() } + + fn get_interchain_gas_paymaster( + &self, + client: &RpcClient, + program_id: &Pubkey, + ) -> Option<(Pubkey, InterchainGasPaymasterType)> { + let storage = self.get_storage(client, program_id); + + storage.igp + } + + fn set_interchain_gas_paymaster_instruction( + &self, + _client: &RpcClient, + _program_id: &Pubkey, + _igp_config: Option<(Pubkey, InterchainGasPaymasterType)>, + ) -> Option { + // There is no way to set the IGP on HelloWorld + None + } } fn deploy_helloworld(ctx: &mut Context, deploy: HelloWorldDeploy) { @@ -190,8 +211,8 @@ fn deploy_helloworld(ctx: &mut Context, deploy: HelloWorldDeploy) { &deploy.context, deploy.config_file, deploy.chain_config_file, - deploy.environments_dir, - &deploy.environment, + deploy.env_args.environments_dir, + &deploy.env_args.environment, deploy.built_so_dir, ) } diff --git a/rust/sealevel/client/src/igp.rs b/rust/sealevel/client/src/igp.rs new file mode 100644 index 0000000000..f41ddad2fb --- /dev/null +++ b/rust/sealevel/client/src/igp.rs @@ -0,0 +1,610 @@ +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +use crate::{ + artifacts::{read_json, try_read_json, write_json, SingularProgramIdArtifact}, + cmd_utils::{create_and_write_keypair, create_new_directory, deploy_program}, + read_core_program_ids, + router::ChainMetadata, + Context, GasOverheadSubCmd, GetSetCmd, IgpCmd, IgpSubCmd, +}; + +use std::{ + fs::File, + path::{Path, PathBuf}, + str::FromStr, +}; + +use solana_sdk::{ + pubkey::Pubkey, + signature::{Keypair, Signer as _}, +}; + +use hyperlane_core::H256; + +use hyperlane_sealevel_igp::{ + accounts::{ + GasOracle, GasPaymentAccount, IgpAccount, InterchainGasPaymasterType, OverheadIgpAccount, + ProgramDataAccount as IgpProgramDataAccount, RemoteGasData, + }, + igp_program_data_pda_seeds, + instruction::{GasOracleConfig, GasOverheadConfig}, +}; + +#[derive(Debug, Serialize, Deserialize, Default)] +struct IgpAccountsArtifacts { + salt: H256, + #[serde(default)] + #[serde(with = "crate::serde::serde_option_pubkey")] + igp_account: Option, + #[serde(default)] + #[serde(with = "crate::serde::serde_option_pubkey")] + overhead_igp_account: Option, +} + +fn get_context_salt(context: Option<&String>) -> H256 { + context + .map(|c| { + if c == "default" { + H256::zero() + } else { + ethers::utils::keccak256(c.as_bytes()).into() + } + }) + .unwrap_or_else(H256::zero) +} + +fn get_context_dir_name(context: Option<&String>) -> &str { + context.map(|c| c.as_str()).unwrap_or("default") +} + +pub(crate) fn process_igp_cmd(mut ctx: Context, cmd: IgpCmd) { + match cmd.cmd { + IgpSubCmd::DeployProgram(deploy) => { + let environments_dir = create_new_directory( + &deploy.env_args.environments_dir, + &deploy.env_args.environment, + ); + let ism_dir = create_new_directory(&environments_dir, "igp"); + let chain_dir = create_new_directory(&ism_dir, &deploy.chain); + let key_dir = create_new_directory(&chain_dir, "keys"); + + let program_id = deploy_igp_program(&mut ctx, &deploy.built_so_dir, true, &key_dir); + + write_json::( + &chain_dir.join("program-ids.json"), + program_id.into(), + ); + } + IgpSubCmd::InitIgpAccount(init) => { + let environments_dir = + create_new_directory(&init.env_args.environments_dir, &init.env_args.environment); + let ism_dir = create_new_directory(&environments_dir, "igp"); + let chain_dir = create_new_directory(&ism_dir, &init.chain); + let context_dir = + create_new_directory(&chain_dir, get_context_dir_name(init.context.as_ref())); + + let artifacts_path = context_dir.join("igp-accounts.json"); + + let existing_artifacts = try_read_json::(&artifacts_path).ok(); + + let salt = get_context_salt(init.context.as_ref()); + + let chain_configs = + read_json::>(&init.chain_config_file); + + let igp_account = init_and_configure_igp_account( + &mut ctx, + init.program_id, + chain_configs.get(&init.chain).unwrap().domain_id(), + salt, + init.gas_oracle_config_file, + ); + + let artifacts = IgpAccountsArtifacts { + salt, + igp_account: Some(igp_account), + overhead_igp_account: existing_artifacts.and_then(|a| a.overhead_igp_account), + }; + + write_json(&artifacts_path, artifacts); + } + IgpSubCmd::InitOverheadIgpAccount(init) => { + let environments_dir = + create_new_directory(&init.env_args.environments_dir, &init.env_args.environment); + let ism_dir = create_new_directory(&environments_dir, "igp"); + let chain_dir = create_new_directory(&ism_dir, &init.chain); + let context_dir = + create_new_directory(&chain_dir, get_context_dir_name(init.context.as_ref())); + + let artifacts_path = context_dir.join("igp-accounts.json"); + + let existing_artifacts = try_read_json::(&artifacts_path).ok(); + + let salt = get_context_salt(init.context.as_ref()); + + let chain_configs = + read_json::>(&init.chain_config_file); + + let overhead_igp_account = init_and_configure_overhead_igp_account( + &mut ctx, + init.program_id, + init.inner_igp_account, + chain_configs.get(&init.chain).unwrap().domain_id(), + salt, + init.overhead_config_file, + ); + + let artifacts = IgpAccountsArtifacts { + salt, + igp_account: existing_artifacts.and_then(|a| a.igp_account), + overhead_igp_account: Some(overhead_igp_account), + }; + + write_json(&artifacts_path, artifacts); + } + IgpSubCmd::Query(query) => { + let (program_data_account_pda, _program_data_account_bump) = + Pubkey::find_program_address(igp_program_data_pda_seeds!(), &query.program_id); + + let accounts = ctx + .client + .get_multiple_accounts_with_commitment( + &[program_data_account_pda, query.igp_account], + ctx.commitment, + ) + .unwrap() + .value; + + let igp_program_data = + IgpProgramDataAccount::fetch(&mut &accounts[0].as_ref().unwrap().data[..]) + .unwrap() + .into_inner(); + + println!("IGP program data: {:?}", igp_program_data); + + let igp = IgpAccount::fetch(&mut &accounts[1].as_ref().unwrap().data[..]) + .unwrap() + .into_inner(); + + println!("IGP account: {:?}", igp); + + if let Some(gas_payment_account_pubkey) = query.gas_payment_account { + let account = ctx + .client + .get_account_with_commitment(&gas_payment_account_pubkey, ctx.commitment) + .unwrap() + .value + .unwrap(); + let gas_payment_account = GasPaymentAccount::fetch(&mut &account.data[..]) + .unwrap() + .into_inner(); + println!("Gas payment account: {:?}", gas_payment_account); + } + } + IgpSubCmd::PayForGas(payment_details) => { + let unique_gas_payment_keypair = Keypair::new(); + let salt = H256::zero(); + let (igp_account, _igp_account_bump) = Pubkey::find_program_address( + hyperlane_sealevel_igp::igp_pda_seeds!(salt), + &payment_details.program_id, + ); + + let (overhead_igp_account, _) = Pubkey::find_program_address( + hyperlane_sealevel_igp::overhead_igp_pda_seeds!(salt), + &payment_details.program_id, + ); + let (ixn, gas_payment_data_account) = + hyperlane_sealevel_igp::instruction::pay_for_gas_instruction( + payment_details.program_id, + ctx.payer_pubkey, + igp_account, + Some(overhead_igp_account), + unique_gas_payment_keypair.pubkey(), + H256::from_str(&payment_details.message_id).unwrap(), + payment_details.destination_domain, + payment_details.gas, + ) + .unwrap(); + + ctx.new_txn() + .add(ixn) + .send(&[&*ctx.payer_signer(), &unique_gas_payment_keypair]); + + println!( + "Made a payment for message {} with gas payment data account {}", + payment_details.message_id, gas_payment_data_account + ); + } + IgpSubCmd::Claim(claim) => { + let igp_account = ctx + .client + .get_account_with_commitment(&claim.igp_account, ctx.commitment) + .unwrap() + .value + .unwrap(); + let igp_account = IgpAccount::fetch(&mut &igp_account.data[..]) + .unwrap() + .into_inner(); + + let ixn = hyperlane_sealevel_igp::instruction::claim_instruction( + claim.program_id, + claim.igp_account, + igp_account.beneficiary, + ) + .unwrap(); + + ctx.new_txn() + .add_with_description( + ixn, + format!( + "Claiming from IGP account {} to beneficiary {}", + claim.igp_account, igp_account.beneficiary + ), + ) + .send_with_payer(); + } + IgpSubCmd::SetIgpBeneficiary(set_beneficiary) => { + let igp_account = ctx + .client + .get_account_with_commitment(&set_beneficiary.igp_account, ctx.commitment) + .unwrap() + .value + .unwrap(); + let igp_account = IgpAccount::fetch(&mut &igp_account.data[..]) + .unwrap() + .into_inner(); + + let ixn = hyperlane_sealevel_igp::instruction::set_beneficiary_instruction( + set_beneficiary.program_id, + set_beneficiary.igp_account, + igp_account.owner.unwrap(), + set_beneficiary.new_beneficiary, + ) + .unwrap(); + + ctx.new_txn() + .add_with_description( + ixn, + format!( + "Change beneficiary of IGP account {} to beneficiary {}", + set_beneficiary.igp_account, set_beneficiary.new_beneficiary + ), + ) + .send_with_payer(); + } + IgpSubCmd::GasOracleConfig(args) => { + let core_program_ids = read_core_program_ids( + &args.env_args.environments_dir, + &args.env_args.environment, + &args.chain_name, + ); + match args.cmd { + GetSetCmd::Set(set_args) => { + let remote_gas_data = RemoteGasData { + token_exchange_rate: set_args.token_exchange_rate, + gas_price: set_args.gas_price, + token_decimals: set_args.token_decimals, + }; + let gas_oracle_config = GasOracleConfig { + domain: args.remote_domain, + gas_oracle: Some(GasOracle::RemoteGasData(remote_gas_data)), + }; + let instruction = + hyperlane_sealevel_igp::instruction::set_gas_oracle_configs_instruction( + core_program_ids.igp_program_id, + core_program_ids.igp_account, + ctx.payer_pubkey, + vec![gas_oracle_config], + ) + .unwrap(); + ctx.new_txn().add(instruction).send_with_payer(); + println!("Set gas oracle for remote domain {:?}", args.remote_domain); + } + GetSetCmd::Get(_) => { + let igp_account = ctx + .client + .get_account_with_commitment(&core_program_ids.igp_account, ctx.commitment) + .unwrap() + .value + .expect( + "IGP account not found. Make sure you are connected to the right RPC.", + ); + + let igp_account = IgpAccount::fetch(&mut &igp_account.data[..]) + .unwrap() + .into_inner(); + + println!( + "IGP account gas oracle: {:#?}", + igp_account.gas_oracles.get(&args.remote_domain) + ); + } + } + } + IgpSubCmd::DestinationGasOverhead(args) => { + let core_program_ids = read_core_program_ids( + &args.env_args.environments_dir, + &args.env_args.environment, + &args.chain_name, + ); + match args.cmd { + GasOverheadSubCmd::Get => { + // Read the gas overhead config + let overhead_igp_account = ctx + .client + .get_account_with_commitment( + &core_program_ids.overhead_igp_account, + ctx.commitment, + ) + .unwrap() + .value + .expect("Overhead IGP account not found. Make sure you are connected to the right RPC."); + let overhead_igp_account = + OverheadIgpAccount::fetch(&mut &overhead_igp_account.data[..]) + .unwrap() + .into_inner(); + println!( + "Overhead IGP account gas oracle: {:#?}", + overhead_igp_account.gas_overheads.get(&args.remote_domain) + ); + } + GasOverheadSubCmd::Set(set_args) => { + let overhead_config = GasOverheadConfig { + destination_domain: args.remote_domain, + gas_overhead: Some(set_args.gas_overhead), + }; + // Set the gas overhead config + let instruction = + hyperlane_sealevel_igp::instruction::set_destination_gas_overheads( + core_program_ids.igp_program_id, + core_program_ids.overhead_igp_account, + ctx.payer_pubkey, + vec![overhead_config], + ) + .unwrap(); + ctx.new_txn().add(instruction).send_with_payer(); + println!( + "Set gas overheads for remote domain {:?}", + args.remote_domain + ) + } + } + } + IgpSubCmd::TransferIgpOwnership(ref transfer_ownership) + | IgpSubCmd::TransferOverheadIgpOwnership(ref transfer_ownership) => { + let igp_account_type = match cmd.cmd { + IgpSubCmd::TransferIgpOwnership(_) => { + InterchainGasPaymasterType::Igp(transfer_ownership.igp_account) + } + IgpSubCmd::TransferOverheadIgpOwnership(_) => { + InterchainGasPaymasterType::OverheadIgp(transfer_ownership.igp_account) + } + _ => unreachable!(), + }; + let instruction = + hyperlane_sealevel_igp::instruction::transfer_igp_account_ownership_instruction( + transfer_ownership.program_id, + igp_account_type.clone(), + ctx.payer_pubkey, + Some(transfer_ownership.new_owner), + ) + .unwrap(); + ctx.new_txn() + .add_with_description( + instruction, + format!( + "Transfer ownership of {:?} to {}", + igp_account_type, transfer_ownership.new_owner + ), + ) + .send_with_payer(); + } + } +} + +#[allow(clippy::too_many_arguments)] +fn deploy_igp_program( + ctx: &mut Context, + built_so_dir: &Path, + use_existing_keys: bool, + key_dir: &Path, +) -> Pubkey { + let (keypair, keypair_path) = create_and_write_keypair( + key_dir, + "hyperlane_sealevel_igp-keypair.json", + use_existing_keys, + ); + let program_id = keypair.pubkey(); + + deploy_program( + ctx.payer_keypair_path(), + keypair_path.to_str().unwrap(), + built_so_dir + .join("hyperlane_sealevel_igp.so") + .to_str() + .unwrap(), + &ctx.client.url(), + ); + + println!("Deployed IGP at program ID {}", program_id); + + let (program_data_account, _program_data_bump) = Pubkey::find_program_address( + hyperlane_sealevel_igp::igp_program_data_pda_seeds!(), + &program_id, + ); + + // Initialize the program data + let instruction = + hyperlane_sealevel_igp::instruction::init_instruction(program_id, ctx.payer_pubkey) + .unwrap(); + + ctx.new_txn() + .add_with_description( + instruction, + format!("Initializing IGP program data {}", program_data_account), + ) + .send_with_payer(); + + program_id +} + +fn init_and_configure_igp_account( + ctx: &mut Context, + program_id: Pubkey, + local_domain: u32, + salt: H256, + gas_oracle_config_file: Option, +) -> Pubkey { + let gas_oracle_configs = gas_oracle_config_file + .as_deref() + .map(|p| { + let file = File::open(p).expect("Failed to open oracle config file"); + serde_json::from_reader::<_, Vec>(file) + .expect("Failed to parse oracle config file") + }) + .unwrap_or_default() + .into_iter() + .filter(|c| c.domain != local_domain) + .collect::>(); + + // Initialize IGP with the given salt + let (igp_account_pda, _igp_account_bump) = + Pubkey::find_program_address(hyperlane_sealevel_igp::igp_pda_seeds!(salt), &program_id); + + if ctx + .client + .get_account_with_commitment(&igp_account_pda, ctx.commitment) + .unwrap() + .value + .is_none() + { + let instruction = hyperlane_sealevel_igp::instruction::init_igp_instruction( + program_id, + ctx.payer_pubkey, + salt, + Some(ctx.payer_pubkey), + ctx.payer_pubkey, + ) + .unwrap(); + + ctx.new_txn() + .add_with_description( + instruction, + format!("Initializing IGP account {}", igp_account_pda), + ) + .send_with_payer(); + } else { + println!( + "IGP account {} already exists, not creating", + igp_account_pda + ); + } + + if !gas_oracle_configs.is_empty() { + // TODO: idempotency + + let domains = gas_oracle_configs + .iter() + .map(|c| c.domain) + .collect::>(); + let instruction = hyperlane_sealevel_igp::instruction::set_gas_oracle_configs_instruction( + program_id, + igp_account_pda, + ctx.payer_pubkey, + gas_oracle_configs, + ) + .unwrap(); + + ctx.new_txn().add(instruction).send_with_payer(); + + println!("Set gas oracle for remote domains {domains:?}",); + } else { + println!("Skipping settings gas oracle config"); + } + + igp_account_pda +} + +fn init_and_configure_overhead_igp_account( + ctx: &mut Context, + program_id: Pubkey, + inner_igp_account: Pubkey, + local_domain: u32, + salt: H256, + overhead_config_file: Option, +) -> Pubkey { + let overhead_configs = overhead_config_file + .as_deref() + .map(|p| { + let file = File::open(p).expect("Failed to open overhead config file"); + serde_json::from_reader::<_, Vec>(file) + .expect("Failed to parse overhead config file") + }) + .unwrap_or_default() + .into_iter() + .filter(|c| c.destination_domain != local_domain) + .map(|c| (c.destination_domain, c)) + .collect::>() // dedup + .into_values() + .collect::>(); + + let (overhead_igp_account, _) = Pubkey::find_program_address( + hyperlane_sealevel_igp::overhead_igp_pda_seeds!(salt), + &program_id, + ); + + if ctx + .client + .get_account_with_commitment(&overhead_igp_account, ctx.commitment) + .unwrap() + .value + .is_none() + { + let instruction = hyperlane_sealevel_igp::instruction::init_overhead_igp_instruction( + program_id, + ctx.payer_pubkey, + salt, + Some(ctx.payer_pubkey), + inner_igp_account, + ) + .unwrap(); + + ctx.new_txn() + .add_with_description( + instruction, + format!("Initializing overhead IGP account {}", overhead_igp_account), + ) + .send_with_payer(); + } else { + println!( + "Overhead IGP account {} already exists, not creating", + overhead_igp_account + ); + } + + if !overhead_configs.is_empty() { + // TODO: idempotency + + let domains = overhead_configs + .iter() + .map(|c| c.destination_domain) + .collect::>(); + + let instruction = hyperlane_sealevel_igp::instruction::set_destination_gas_overheads( + program_id, + overhead_igp_account, + ctx.payer_pubkey, + overhead_configs, + ) + .unwrap(); + + ctx.new_txn().add(instruction).send_with_payer(); + + println!("Set gas overheads for remote domains {domains:?}",) + } else { + println!("Skipping setting gas overheads"); + } + + overhead_igp_account +} diff --git a/rust/sealevel/client/src/main.rs b/rust/sealevel/client/src/main.rs index bef6cc2095..74e3de7203 100644 --- a/rust/sealevel/client/src/main.rs +++ b/rust/sealevel/client/src/main.rs @@ -23,12 +23,8 @@ use account_utils::DiscriminatorEncode; use hyperlane_core::{H160, H256}; use hyperlane_sealevel_connection_client::router::RemoteRouterConfig; use hyperlane_sealevel_igp::{ - accounts::{ - GasOracle, GasPaymentAccount, IgpAccount, InterchainGasPaymasterType, OverheadIgpAccount, - ProgramDataAccount as IgpProgramDataAccount, RemoteGasData, - }, + accounts::{InterchainGasPaymasterType, OverheadIgpAccount}, igp_gas_payment_pda_seeds, igp_program_data_pda_seeds, - instruction::{GasOracleConfig, GasOverheadConfig}, }; use hyperlane_sealevel_mailbox::{ accounts::{InboxAccount, OutboxAccount}, @@ -67,12 +63,14 @@ mod cmd_utils; mod context; mod r#core; mod helloworld; +mod igp; mod multisig_ism; mod router; mod serde; mod warp_route; use crate::helloworld::process_helloworld_cmd; +use crate::igp::process_igp_cmd; use crate::multisig_ism::process_multisig_ism_message_id_cmd; use crate::warp_route::process_warp_route_cmd; pub(crate) use crate::{context::*, core::*}; @@ -115,6 +113,14 @@ enum HyperlaneSealevelCmd { HelloWorld(HelloWorldCmd), } +#[derive(Args)] +struct EnvironmentArgs { + #[arg(long)] + environment: String, + #[arg(long)] + environments_dir: PathBuf, +} + #[derive(Args)] pub(crate) struct WarpRouteCmd { #[command(subcommand)] @@ -129,10 +135,8 @@ pub(crate) enum WarpRouteSubCmd { #[derive(Args)] pub(crate) struct WarpRouteDeploy { - #[arg(long)] - environment: String, - #[arg(long)] - environments_dir: PathBuf, + #[command(flatten)] + env_args: EnvironmentArgs, #[arg(long)] built_so_dir: PathBuf, #[arg(long)] @@ -168,8 +172,8 @@ enum CoreSubCmd { struct CoreDeploy { #[arg(long)] local_domain: u32, - #[arg(long)] - environment: String, + #[command(flatten)] + env_args: EnvironmentArgs, #[arg(long)] gas_oracle_config_file: Option, #[arg(long)] @@ -178,8 +182,6 @@ struct CoreDeploy { chain: String, #[arg(long)] use_existing_keys: bool, - #[arg(long)] - environments_dir: PathBuf, #[arg(long, num_args = 1.., value_delimiter = ',')] remote_domains: Vec, #[arg(long)] @@ -381,14 +383,63 @@ struct IgpCmd { #[derive(Subcommand)] enum IgpSubCmd { + DeployProgram(IgpDeployProgramArgs), + InitIgpAccount(InitIgpAccountArgs), + InitOverheadIgpAccount(InitOverheadIgpAccountArgs), Query(IgpQueryArgs), PayForGas(PayForGasArgs), + Claim(ClaimArgs), + SetIgpBeneficiary(SetIgpBeneficiaryArgs), GasOracleConfig(GasOracleConfigArgs), DestinationGasOverhead(DestinationGasOverheadArgs), TransferIgpOwnership(TransferIgpOwnership), TransferOverheadIgpOwnership(TransferIgpOwnership), } +#[derive(Args)] +struct IgpDeployProgramArgs { + #[command(flatten)] + env_args: EnvironmentArgs, + #[arg(long)] + chain: String, + #[arg(long)] + built_so_dir: PathBuf, +} + +#[derive(Args)] +struct InitIgpAccountArgs { + #[arg(long)] + program_id: Pubkey, + #[command(flatten)] + env_args: EnvironmentArgs, + #[arg(long)] + chain: String, + #[arg(long)] + chain_config_file: PathBuf, + #[arg(long)] + context: Option, + #[arg(long)] + gas_oracle_config_file: Option, +} + +#[derive(Args)] +struct InitOverheadIgpAccountArgs { + #[arg(long)] + program_id: Pubkey, + #[command(flatten)] + env_args: EnvironmentArgs, + #[arg(long)] + chain: String, + #[arg(long)] + chain_config_file: PathBuf, + #[arg(long)] + inner_igp_account: Pubkey, + #[arg(long)] + context: Option, + #[arg(long)] + overhead_config_file: Option, +} + #[derive(Args)] struct IgpQueryArgs { #[arg(long)] @@ -423,11 +474,26 @@ struct PayForGasArgs { } #[derive(Args)] -struct GasOracleConfigArgs { +struct ClaimArgs { #[arg(long)] - environment: String, + program_id: Pubkey, #[arg(long)] - environments_dir: PathBuf, + igp_account: Pubkey, +} + +#[derive(Args)] +struct SetIgpBeneficiaryArgs { + #[arg(long)] + program_id: Pubkey, + #[arg(long)] + igp_account: Pubkey, + new_beneficiary: Pubkey, +} + +#[derive(Args)] +struct GasOracleConfigArgs { + #[command(flatten)] + env_args: EnvironmentArgs, #[arg(long)] chain_name: String, #[arg(long)] @@ -451,10 +517,8 @@ struct GetGasOracleArgs; #[derive(Args)] struct DestinationGasOverheadArgs { - #[arg(long)] - environment: String, - #[arg(long)] - environments_dir: PathBuf, + #[command(flatten)] + env_args: EnvironmentArgs, #[arg(long)] chain_name: String, #[arg(long)] @@ -535,10 +599,8 @@ enum MultisigIsmMessageIdSubCmd { #[derive(Args)] struct MultisigIsmMessageIdDeploy { - #[arg(long)] - environment: String, - #[arg(long)] - environments_dir: PathBuf, + #[command(flatten)] + env_args: EnvironmentArgs, #[arg(long)] built_so_dir: PathBuf, #[arg(long)] @@ -597,10 +659,8 @@ pub(crate) enum HelloWorldSubCmd { #[derive(Args)] pub(crate) struct HelloWorldDeploy { - #[arg(long)] - environment: String, - #[arg(long)] - environments_dir: PathBuf, + #[command(flatten)] + env_args: EnvironmentArgs, #[arg(long)] built_so_dir: PathBuf, #[arg(long)] @@ -1313,202 +1373,3 @@ fn process_validator_announce_cmd(ctx: Context, cmd: ValidatorAnnounceCmd) { } } } - -fn process_igp_cmd(ctx: Context, cmd: IgpCmd) { - match cmd.cmd { - IgpSubCmd::Query(query) => { - let (program_data_account_pda, _program_data_account_bump) = - Pubkey::find_program_address(igp_program_data_pda_seeds!(), &query.program_id); - - let accounts = ctx - .client - .get_multiple_accounts_with_commitment( - &[program_data_account_pda, query.igp_account], - ctx.commitment, - ) - .unwrap() - .value; - - let igp_program_data = - IgpProgramDataAccount::fetch(&mut &accounts[0].as_ref().unwrap().data[..]) - .unwrap() - .into_inner(); - - println!("IGP program data: {:?}", igp_program_data); - - let igp = IgpAccount::fetch(&mut &accounts[1].as_ref().unwrap().data[..]) - .unwrap() - .into_inner(); - - println!("IGP account: {:?}", igp); - - if let Some(gas_payment_account_pubkey) = query.gas_payment_account { - let account = ctx - .client - .get_account_with_commitment(&gas_payment_account_pubkey, ctx.commitment) - .unwrap() - .value - .unwrap(); - let gas_payment_account = GasPaymentAccount::fetch(&mut &account.data[..]) - .unwrap() - .into_inner(); - println!("Gas payment account: {:?}", gas_payment_account); - } - } - IgpSubCmd::PayForGas(payment_details) => { - let unique_gas_payment_keypair = Keypair::new(); - let salt = H256::zero(); - let (igp_account, _igp_account_bump) = Pubkey::find_program_address( - hyperlane_sealevel_igp::igp_pda_seeds!(salt), - &payment_details.program_id, - ); - - let (overhead_igp_account, _) = Pubkey::find_program_address( - hyperlane_sealevel_igp::overhead_igp_pda_seeds!(salt), - &payment_details.program_id, - ); - let (ixn, gas_payment_data_account) = - hyperlane_sealevel_igp::instruction::pay_for_gas_instruction( - payment_details.program_id, - ctx.payer_pubkey, - igp_account, - Some(overhead_igp_account), - unique_gas_payment_keypair.pubkey(), - H256::from_str(&payment_details.message_id).unwrap(), - payment_details.destination_domain, - payment_details.gas, - ) - .unwrap(); - - ctx.new_txn() - .add(ixn) - .send(&[&*ctx.payer_signer(), &unique_gas_payment_keypair]); - - println!( - "Made a payment for message {} with gas payment data account {}", - payment_details.message_id, gas_payment_data_account - ); - } - IgpSubCmd::GasOracleConfig(args) => { - let core_program_ids = - read_core_program_ids(&args.environments_dir, &args.environment, &args.chain_name); - match args.cmd { - GetSetCmd::Set(set_args) => { - let remote_gas_data = RemoteGasData { - token_exchange_rate: set_args.token_exchange_rate, - gas_price: set_args.gas_price, - token_decimals: set_args.token_decimals, - }; - let gas_oracle_config = GasOracleConfig { - domain: args.remote_domain, - gas_oracle: Some(GasOracle::RemoteGasData(remote_gas_data)), - }; - let instruction = - hyperlane_sealevel_igp::instruction::set_gas_oracle_configs_instruction( - core_program_ids.igp_program_id, - core_program_ids.igp_account, - ctx.payer_pubkey, - vec![gas_oracle_config], - ) - .unwrap(); - ctx.new_txn().add(instruction).send_with_payer(); - println!("Set gas oracle for remote domain {:?}", args.remote_domain); - } - GetSetCmd::Get(_) => { - let igp_account = ctx - .client - .get_account_with_commitment(&core_program_ids.igp_account, ctx.commitment) - .unwrap() - .value - .expect( - "IGP account not found. Make sure you are connected to the right RPC.", - ); - - let igp_account = IgpAccount::fetch(&mut &igp_account.data[..]) - .unwrap() - .into_inner(); - - println!( - "IGP account gas oracle: {:#?}", - igp_account.gas_oracles.get(&args.remote_domain) - ); - } - } - } - IgpSubCmd::DestinationGasOverhead(args) => { - let core_program_ids = - read_core_program_ids(&args.environments_dir, &args.environment, &args.chain_name); - match args.cmd { - GasOverheadSubCmd::Get => { - // Read the gas overhead config - let overhead_igp_account = ctx - .client - .get_account_with_commitment( - &core_program_ids.overhead_igp_account, - ctx.commitment, - ) - .unwrap() - .value - .expect("Overhead IGP account not found. Make sure you are connected to the right RPC."); - let overhead_igp_account = - OverheadIgpAccount::fetch(&mut &overhead_igp_account.data[..]) - .unwrap() - .into_inner(); - println!( - "Overhead IGP account gas oracle: {:#?}", - overhead_igp_account.gas_overheads.get(&args.remote_domain) - ); - } - GasOverheadSubCmd::Set(set_args) => { - let overhead_config = GasOverheadConfig { - destination_domain: args.remote_domain, - gas_overhead: Some(set_args.gas_overhead), - }; - // Set the gas overhead config - let instruction = - hyperlane_sealevel_igp::instruction::set_destination_gas_overheads( - core_program_ids.igp_program_id, - core_program_ids.overhead_igp_account, - ctx.payer_pubkey, - vec![overhead_config], - ) - .unwrap(); - ctx.new_txn().add(instruction).send_with_payer(); - println!( - "Set gas overheads for remote domain {:?}", - args.remote_domain - ) - } - } - } - IgpSubCmd::TransferIgpOwnership(ref transfer_ownership) - | IgpSubCmd::TransferOverheadIgpOwnership(ref transfer_ownership) => { - let igp_account_type = match cmd.cmd { - IgpSubCmd::TransferIgpOwnership(_) => { - InterchainGasPaymasterType::Igp(transfer_ownership.igp_account) - } - IgpSubCmd::TransferOverheadIgpOwnership(_) => { - InterchainGasPaymasterType::OverheadIgp(transfer_ownership.igp_account) - } - _ => unreachable!(), - }; - let instruction = - hyperlane_sealevel_igp::instruction::transfer_igp_account_ownership_instruction( - transfer_ownership.program_id, - igp_account_type.clone(), - ctx.payer_pubkey, - Some(transfer_ownership.new_owner), - ) - .unwrap(); - ctx.new_txn() - .add_with_description( - instruction, - format!( - "Transfer ownership of {:?} to {}", - igp_account_type, transfer_ownership.new_owner - ), - ) - .send_with_payer(); - } - } -} diff --git a/rust/sealevel/client/src/multisig_ism.rs b/rust/sealevel/client/src/multisig_ism.rs index d5676b2815..e354f39bb8 100644 --- a/rust/sealevel/client/src/multisig_ism.rs +++ b/rust/sealevel/client/src/multisig_ism.rs @@ -45,8 +45,10 @@ impl From for ValidatorsAndThreshold { pub(crate) fn process_multisig_ism_message_id_cmd(mut ctx: Context, cmd: MultisigIsmMessageIdCmd) { match cmd.cmd { MultisigIsmMessageIdSubCmd::Deploy(deploy) => { - let environments_dir = - create_new_directory(&deploy.environments_dir, &deploy.environment); + let environments_dir = create_new_directory( + &deploy.env_args.environments_dir, + &deploy.env_args.environment, + ); let ism_dir = create_new_directory(&environments_dir, "multisig-ism-message-id"); let chain_dir = create_new_directory(&ism_dir, &deploy.chain); let context_dir = create_new_directory(&chain_dir, &deploy.context); diff --git a/rust/sealevel/client/src/router.rs b/rust/sealevel/client/src/router.rs index 4eaf4cae78..ae20abc5bd 100644 --- a/rust/sealevel/client/src/router.rs +++ b/rust/sealevel/client/src/router.rs @@ -268,6 +268,21 @@ pub(crate) trait ConnectionClient: Ownable { program_id: &Pubkey, ism: Option, ) -> Instruction; + + /// Gets the IGP configured on-chain. + fn get_interchain_gas_paymaster( + &self, + client: &RpcClient, + program_id: &Pubkey, + ) -> Option<(Pubkey, InterchainGasPaymasterType)>; + + /// Gets an instruction to set the IGP. + fn set_interchain_gas_paymaster_instruction( + &self, + client: &RpcClient, + program_id: &Pubkey, + igp_config: Option<(Pubkey, InterchainGasPaymasterType)>, + ) -> Option; } /// Idempotently deploys routers on multiple Sealevel chains and enrolls all routers (including @@ -426,8 +441,6 @@ fn configure_connection_client( router_config: &RouterConfig, chain_config: &ChainMetadata, ) { - // Just ISM for now - let client = chain_config.client(); let actual_ism = deployer.get_interchain_security_module(&client, program_id); @@ -451,6 +464,35 @@ fn configure_connection_client( .with_client(&client) .send_with_payer(); } + + let actual_igp = deployer.get_interchain_gas_paymaster(&client, program_id); + let expected_igp = router_config + .connection_client + .interchain_gas_paymaster_config(&client); + + if actual_igp != expected_igp { + let instruction = deployer.set_interchain_gas_paymaster_instruction( + &client, + program_id, + expected_igp.clone(), + ); + if let Some(instruction) = instruction { + ctx.new_txn() + .add_with_description( + instruction, + format!( + "Setting IGP for chain: {} ({}) to {:?}", + chain_config.name, + chain_config.domain_id(), + expected_igp + ), + ) + .with_client(&client) + .send_with_payer(); + } else { + println!("WARNING: Invalid configured IGP {:?}, expected {:?} for chain {} ({}), but cannot craft instruction to change it", actual_igp, expected_igp, chain_config.name, chain_config.domain_id()); + } + } } // Idempotent. @@ -541,17 +583,18 @@ fn enroll_all_remote_routers< .collect::>(); if !router_configs.is_empty() { - println!( - "Enrolling routers for chain: {}, program_id {}, routers: {:?}", - chain_name, program_id, router_configs, - ); - ctx.new_txn() - .add(deployer.enroll_remote_routers_instruction( - program_id, - ctx.payer_pubkey, - router_configs, - )) + .add_with_description( + deployer.enroll_remote_routers_instruction( + program_id, + ctx.payer_pubkey, + router_configs.clone(), + ), + format!( + "Enrolling routers for chain: {}, program_id {}, routers: {:?}", + chain_name, program_id, router_configs, + ), + ) .with_client(&chain_config.client()) .send_with_payer(); } else { diff --git a/rust/sealevel/client/src/warp_route.rs b/rust/sealevel/client/src/warp_route.rs index 5e67fcf379..4425363c25 100644 --- a/rust/sealevel/client/src/warp_route.rs +++ b/rust/sealevel/client/src/warp_route.rs @@ -20,7 +20,7 @@ use hyperlane_sealevel_token_lib::{ accounts::{HyperlaneToken, HyperlaneTokenAccount}, hyperlane_token_pda_seeds, instruction::{ - enroll_remote_routers_instruction, set_destination_gas_configs, + enroll_remote_routers_instruction, set_destination_gas_configs, set_igp_instruction, set_interchain_security_module_instruction, transfer_ownership_instruction, Init, }, }; @@ -125,8 +125,8 @@ pub(crate) fn process_warp_route_cmd(mut ctx: Context, cmd: WarpRouteCmd) { &deploy.warp_route_name, deploy.token_config_file, deploy.chain_config_file, - deploy.environments_dir, - &deploy.environment, + deploy.env_args.environments_dir, + &deploy.env_args.environment, deploy.built_so_dir, ); } @@ -434,6 +434,27 @@ impl ConnectionClient for WarpRouteDeployer { set_interchain_security_module_instruction(*program_id, token_data.owner.unwrap(), ism) .unwrap() } + + fn get_interchain_gas_paymaster( + &self, + client: &RpcClient, + program_id: &Pubkey, + ) -> Option<(Pubkey, InterchainGasPaymasterType)> { + let token_data = get_token_data::<()>(client, program_id); + + token_data.interchain_gas_paymaster + } + + fn set_interchain_gas_paymaster_instruction( + &self, + client: &RpcClient, + program_id: &Pubkey, + igp_config: Option<(Pubkey, InterchainGasPaymasterType)>, + ) -> Option { + let token_data = get_token_data::<()>(client, program_id); + + Some(set_igp_instruction(*program_id, token_data.owner.unwrap(), igp_config).unwrap()) + } } fn get_token_data(client: &RpcClient, program_id: &Pubkey) -> HyperlaneToken diff --git a/rust/sealevel/environments/mainnet2/gas-oracle-configs.json b/rust/sealevel/environments/mainnet2/gas-oracle-configs.json index 75bc47071a..3e0dc41986 100644 --- a/rust/sealevel/environments/mainnet2/gas-oracle-configs.json +++ b/rust/sealevel/environments/mainnet2/gas-oracle-configs.json @@ -12,8 +12,8 @@ "domain": 22222, "gasOracle": { "type": "remoteGasData", - "tokenExchangeRate": "4700000000000000", - "gasPrice": "1000000000", + "tokenExchangeRate": "6010517751000000", + "gasPrice": "30000000000", "tokenDecimals": 18 } } diff --git a/rust/sealevel/environments/mainnet2/igp/solana/default/igp-accounts.json b/rust/sealevel/environments/mainnet2/igp/solana/default/igp-accounts.json new file mode 100644 index 0000000000..44640e7c4a --- /dev/null +++ b/rust/sealevel/environments/mainnet2/igp/solana/default/igp-accounts.json @@ -0,0 +1,5 @@ +{ + "salt": "0x0000000000000000000000000000000000000000000000000000000000000000", + "igp_account": "Ho7b7gidMacE4AckGZPJ7zn2oa45xVarcxLi2yzcbkNZ", + "overhead_igp_account": "83JFTZNPK7eAxcmQN5VH77ytwgmkTaRvV9CSZbCrRYjt" +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet2/igp/solana/nautilus/igp-accounts.json b/rust/sealevel/environments/mainnet2/igp/solana/nautilus/igp-accounts.json new file mode 100644 index 0000000000..a768a762ea --- /dev/null +++ b/rust/sealevel/environments/mainnet2/igp/solana/nautilus/igp-accounts.json @@ -0,0 +1,5 @@ +{ + "salt": "0x8e392b0456451647822c921d1f33163a6d3a93b7dc378a504d4f75de2fd429ed", + "igp_account": "EJ55wbYpQX7MTAujjgo5hKMKXCUpEeacSmSnhXEj9DJs", + "overhead_igp_account": "2HnezHeMqrMweTjsmtoXu7YFU9y69yrkTEdZ2xyn2s3o" +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet2/igp/solana/program-ids.json b/rust/sealevel/environments/mainnet2/igp/solana/program-ids.json new file mode 100644 index 0000000000..18bbcf118a --- /dev/null +++ b/rust/sealevel/environments/mainnet2/igp/solana/program-ids.json @@ -0,0 +1,3 @@ +{ + "program_id": "2gRBpM5yAwpTTpy6K9e4QUMni3RHQffBXvvdoepfnH8n" +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet2/overhead-config.json b/rust/sealevel/environments/mainnet2/overhead-config.json new file mode 100644 index 0000000000..47099cf4e7 --- /dev/null +++ b/rust/sealevel/environments/mainnet2/overhead-config.json @@ -0,0 +1,10 @@ +[ + { + "destinationDomain": 56, + "gasOverhead": 159736 + }, + { + "destinationDomain": 22222, + "gasOverhead": 159736 + } +] \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet2/solana/core/program-ids.json b/rust/sealevel/environments/mainnet2/solana/core/program-ids.json index 53801da0ce..ca9b6b4540 100644 --- a/rust/sealevel/environments/mainnet2/solana/core/program-ids.json +++ b/rust/sealevel/environments/mainnet2/solana/core/program-ids.json @@ -2,7 +2,7 @@ "mailbox": "Ge9atjAc3Ltu91VTbNpJDCjZ9CFxFyck4h3YBcTF9XPq", "validator_announce": "C88Lk5GR6cPxYoJxPbNDDEwsx5Kxn1wZEomvQ2So333g", "multisig_ism_message_id": "6pHP4EeX2Xek24Be7PPTWCqcpmNEPENW1m9RnZSFSmA1", - "igp_program_id": "HksFWQM1EXJJ5mxo2uZoMfmksXHaNhCunh71NqcQQHZ8", - "overhead_igp_account": "GTj6WzNxLNFydq5zJrV9p13fyqotRoo1MQykNCWuVpbS", - "igp_account": "FCNfmLSZLo5x7oNYmkYU8WdPUu7pj636P9CaMxkmaCp7" + "igp_program_id": "2gRBpM5yAwpTTpy6K9e4QUMni3RHQffBXvvdoepfnH8n", + "overhead_igp_account": "83JFTZNPK7eAxcmQN5VH77ytwgmkTaRvV9CSZbCrRYjt", + "igp_account": "Ho7b7gidMacE4AckGZPJ7zn2oa45xVarcxLi2yzcbkNZ" } \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet2/warp-routes/zbc/program-ids.json b/rust/sealevel/environments/mainnet2/warp-routes/zbc/program-ids.json index 3df636e1f4..1ffd0c0a19 100644 --- a/rust/sealevel/environments/mainnet2/warp-routes/zbc/program-ids.json +++ b/rust/sealevel/environments/mainnet2/warp-routes/zbc/program-ids.json @@ -1,10 +1,14 @@ { - "solana": { - "hex": "0xc5ba229fa2822fe65ac2bd0a93d8371d75292c3415dd381923c1088a3308528b", - "base58": "EJqwFjvVJSAxH8Ur2PYuMfdvoJeutjmH6GkoEFQ4MdSa" + "bsc": { + "hex": "0x000000000000000000000000c27980812e2e66491fd457d488509b7e04144b98", + "base58": "1111111111113i9HKBuaFrAQeGvhv3fJnCCDkg7h" }, "nautilus": { "hex": "0x0000000000000000000000004501bbe6e731a4bc5c60c03a77435b2f6d5e9fe7", "base58": "111111111111xm5qkrK7gZ8Cmjr4ggPLRxy2T8a" + }, + "solana": { + "hex": "0xc5ba229fa2822fe65ac2bd0a93d8371d75292c3415dd381923c1088a3308528b", + "base58": "EJqwFjvVJSAxH8Ur2PYuMfdvoJeutjmH6GkoEFQ4MdSa" } } \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet2/warp-routes/zbc/token-config.json b/rust/sealevel/environments/mainnet2/warp-routes/zbc/token-config.json index 51e235a3e3..c4390211c8 100644 --- a/rust/sealevel/environments/mainnet2/warp-routes/zbc/token-config.json +++ b/rust/sealevel/environments/mainnet2/warp-routes/zbc/token-config.json @@ -6,7 +6,16 @@ "token": "wzbcJyhGhQDLTV1S99apZiiBdE4jmYfbw99saMMdP59", "splTokenProgram": "token", "interchainSecurityModule": "9k74DkJvS2x9QhG4XfnKsLkqaCDyVfaj8s6FyJyhAeEP", - "owner": "EzppBFV2taxWw8kEjxNYvby6q7W1biJEqwP3iC7YgRe3" + "owner": "EzppBFV2taxWw8kEjxNYvby6q7W1biJEqwP3iC7YgRe3", + "interchainGasPaymaster": "2HnezHeMqrMweTjsmtoXu7YFU9y69yrkTEdZ2xyn2s3o" + }, + "bsc": { + "type": "collateral", + "decimals": 9, + "token": "0x37a56cdcD83Dce2868f721De58cB3830C44C6303", + "name": "Zebec", + "symbol": "ZBC", + "foreignDeployment": "0xC27980812E2E66491FD457D488509b7E04144b98" }, "nautilus": { "type": "native", diff --git a/rust/sealevel/environments/testnet3/gas-oracle-configs.json b/rust/sealevel/environments/testnet3/gas-oracle-configs.json index 0933279114..f6e549c52c 100644 --- a/rust/sealevel/environments/testnet3/gas-oracle-configs.json +++ b/rust/sealevel/environments/testnet3/gas-oracle-configs.json @@ -3,8 +3,8 @@ "domain": 97, "gasOracle": { "type": "remoteGasData", - "tokenExchangeRate": "10000000000000000000", - "gasPrice": "15000000000", + "tokenExchangeRate": "100000000000000000000", + "gasPrice": "3000000000", "tokenDecimals": 18 } }, @@ -12,7 +12,7 @@ "domain": 88002, "gasOracle": { "type": "remoteGasData", - "tokenExchangeRate": "10000000000000000000", + "tokenExchangeRate": "6010517751000000", "gasPrice": "1000000000", "tokenDecimals": 18 } diff --git a/rust/sealevel/environments/testnet3/igp/solanadevnet/default/igp-accounts.json b/rust/sealevel/environments/testnet3/igp/solanadevnet/default/igp-accounts.json new file mode 100644 index 0000000000..2ff22ab559 --- /dev/null +++ b/rust/sealevel/environments/testnet3/igp/solanadevnet/default/igp-accounts.json @@ -0,0 +1,5 @@ +{ + "salt": "0x0000000000000000000000000000000000000000000000000000000000000000", + "igp_account": "Gh1B2JLZAY188CAYnWPup8TvGDxHNVWbVVKZi6wh75xZ", + "overhead_igp_account": "2wF3dmWZUZ4C1XvjJQApXcCRPAzkMq1jQ5zjiz6UBoDq" +} \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/igp/solanadevnet/keys/hyperlane_sealevel_igp-keypair.json b/rust/sealevel/environments/testnet3/igp/solanadevnet/keys/hyperlane_sealevel_igp-keypair.json new file mode 100644 index 0000000000..c587eed457 --- /dev/null +++ b/rust/sealevel/environments/testnet3/igp/solanadevnet/keys/hyperlane_sealevel_igp-keypair.json @@ -0,0 +1 @@ +[39,68,118,60,252,163,147,215,30,21,34,243,250,218,38,178,89,96,18,211,111,240,130,128,168,136,9,32,193,229,111,212,135,98,147,162,46,76,218,95,57,220,202,172,45,124,19,155,23,152,163,84,94,18,47,72,105,192,193,121,254,26,93,107] \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/igp/solanadevnet/nautilus/igp-accounts.json b/rust/sealevel/environments/testnet3/igp/solanadevnet/nautilus/igp-accounts.json new file mode 100644 index 0000000000..0aa645aa6b --- /dev/null +++ b/rust/sealevel/environments/testnet3/igp/solanadevnet/nautilus/igp-accounts.json @@ -0,0 +1,5 @@ +{ + "salt": "0x8e392b0456451647822c921d1f33163a6d3a93b7dc378a504d4f75de2fd429ed", + "igp_account": "2bRij36tZrKj7BvehBuHzWGZEyv7qnMAMY5Gj35yxhZf", + "overhead_igp_account": "AUkbgGYywVyttiFyyzGpj26pc2cQUtJMAfej2LnapfTM" +} \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/igp/solanadevnet/program-ids.json b/rust/sealevel/environments/testnet3/igp/solanadevnet/program-ids.json new file mode 100644 index 0000000000..8e51996f19 --- /dev/null +++ b/rust/sealevel/environments/testnet3/igp/solanadevnet/program-ids.json @@ -0,0 +1,3 @@ +{ + "program_id": "A7VDpC9jWKKwJDkQxTYHysQr14ESFRScJg3WqaZZ4XGN" +} \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/overhead-config.json b/rust/sealevel/environments/testnet3/overhead-config.json new file mode 100644 index 0000000000..9e272f15de --- /dev/null +++ b/rust/sealevel/environments/testnet3/overhead-config.json @@ -0,0 +1,10 @@ +[ + { + "destinationDomain": 97, + "gasOverhead": 159736 + }, + { + "destinationDomain": 88002, + "gasOverhead": 159736 + } +] \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/solanadevnet/core/program-ids.json b/rust/sealevel/environments/testnet3/solanadevnet/core/program-ids.json index ab3f1f5933..72393c321d 100644 --- a/rust/sealevel/environments/testnet3/solanadevnet/core/program-ids.json +++ b/rust/sealevel/environments/testnet3/solanadevnet/core/program-ids.json @@ -2,7 +2,7 @@ "mailbox": "4v25Dz9RccqUrTzmfHzJMsjd1iVoNrWzeJ4o6GYuJrVn", "validator_announce": "CMHKvdq4CopDf7qXnDCaTybS15QekQeRt4oUB219yxsp", "multisig_ism_message_id": "64xkGhsZbxgP5rBJfpPcpmzkzTGkpSVHiDLcMKS5gmQw", - "igp_program_id": "HyPQPLfGXDTAQTxzGp7r1uy18KxS89GKgreSHpjeuYDn", - "overhead_igp_account": "AR4hjWPqXEobLvzmv8MTh5k4Se49iTDzbvNX4DpdQGJZ", - "igp_account": "7hMPEGdgBQFsjEz3aaNwZp8WMFHs615zAM3erXBDJuJR" + "igp_program_id": "A7VDpC9jWKKwJDkQxTYHysQr14ESFRScJg3WqaZZ4XGN", + "overhead_igp_account": "2wF3dmWZUZ4C1XvjJQApXcCRPAzkMq1jQ5zjiz6UBoDq", + "igp_account": "Gh1B2JLZAY188CAYnWPup8TvGDxHNVWbVVKZi6wh75xZ" } \ No newline at end of file diff --git a/rust/sealevel/libraries/access-control/Cargo.toml b/rust/sealevel/libraries/access-control/Cargo.toml index 516ea54202..964624bf58 100644 --- a/rust/sealevel/libraries/access-control/Cargo.toml +++ b/rust/sealevel/libraries/access-control/Cargo.toml @@ -12,6 +12,3 @@ solana-program.workspace = true [lib] crate-type = ["cdylib", "lib"] - -[profile.release] -overflow-checks = true diff --git a/rust/sealevel/libraries/account-utils/Cargo.toml b/rust/sealevel/libraries/account-utils/Cargo.toml index 03446005f9..c74f43ad32 100644 --- a/rust/sealevel/libraries/account-utils/Cargo.toml +++ b/rust/sealevel/libraries/account-utils/Cargo.toml @@ -14,6 +14,3 @@ spl-type-length-value.workspace = true [lib] crate-type = ["cdylib", "lib"] - -[profile.release] -overflow-checks = true diff --git a/rust/sealevel/libraries/ecdsa-signature/Cargo.toml b/rust/sealevel/libraries/ecdsa-signature/Cargo.toml index ddb511b400..7a5f78d236 100644 --- a/rust/sealevel/libraries/ecdsa-signature/Cargo.toml +++ b/rust/sealevel/libraries/ecdsa-signature/Cargo.toml @@ -16,6 +16,3 @@ getrandom = { workspace = true, features = ["custom"] } [lib] crate-type = ["cdylib", "lib"] - -[profile.release] -overflow-checks = true diff --git a/rust/sealevel/libraries/hyperlane-sealevel-connection-client/Cargo.toml b/rust/sealevel/libraries/hyperlane-sealevel-connection-client/Cargo.toml index 8772c3f241..0a8cc148ae 100644 --- a/rust/sealevel/libraries/hyperlane-sealevel-connection-client/Cargo.toml +++ b/rust/sealevel/libraries/hyperlane-sealevel-connection-client/Cargo.toml @@ -18,6 +18,3 @@ hyperlane-sealevel-igp = { path = "../../programs/hyperlane-sealevel-igp", featu [lib] crate-type = ["cdylib", "lib"] - -[profile.release] -overflow-checks = true diff --git a/rust/sealevel/libraries/hyperlane-sealevel-token/Cargo.toml b/rust/sealevel/libraries/hyperlane-sealevel-token/Cargo.toml index 41db4d57a8..46273e87ff 100644 --- a/rust/sealevel/libraries/hyperlane-sealevel-token/Cargo.toml +++ b/rust/sealevel/libraries/hyperlane-sealevel-token/Cargo.toml @@ -29,6 +29,3 @@ serializable-account-meta = { path = "../serializable-account-meta" } [lib] crate-type = ["cdylib", "lib"] - -[profile.release] -overflow-checks = true diff --git a/rust/sealevel/libraries/interchain-security-module-interface/Cargo.toml b/rust/sealevel/libraries/interchain-security-module-interface/Cargo.toml index 56d5a9a600..350cf2e4c4 100644 --- a/rust/sealevel/libraries/interchain-security-module-interface/Cargo.toml +++ b/rust/sealevel/libraries/interchain-security-module-interface/Cargo.toml @@ -12,6 +12,3 @@ spl-type-length-value.workspace = true [lib] crate-type = ["cdylib", "lib"] - -[profile.release] -overflow-checks = true diff --git a/rust/sealevel/libraries/message-recipient-interface/Cargo.toml b/rust/sealevel/libraries/message-recipient-interface/Cargo.toml index 1cdec684d8..75bfd9c363 100644 --- a/rust/sealevel/libraries/message-recipient-interface/Cargo.toml +++ b/rust/sealevel/libraries/message-recipient-interface/Cargo.toml @@ -17,6 +17,3 @@ getrandom = { workspace = true, features = ["custom"] } [lib] crate-type = ["cdylib", "lib"] - -[profile.release] -overflow-checks = true diff --git a/rust/sealevel/libraries/multisig-ism/Cargo.toml b/rust/sealevel/libraries/multisig-ism/Cargo.toml index 0d60d6b893..15d149f358 100644 --- a/rust/sealevel/libraries/multisig-ism/Cargo.toml +++ b/rust/sealevel/libraries/multisig-ism/Cargo.toml @@ -23,6 +23,3 @@ hex.workspace = true [lib] crate-type = ["cdylib", "lib"] - -[profile.release] -overflow-checks = true diff --git a/rust/sealevel/libraries/serializable-account-meta/Cargo.toml b/rust/sealevel/libraries/serializable-account-meta/Cargo.toml index f1657b4071..cab84fe371 100644 --- a/rust/sealevel/libraries/serializable-account-meta/Cargo.toml +++ b/rust/sealevel/libraries/serializable-account-meta/Cargo.toml @@ -11,6 +11,3 @@ solana-program.workspace = true [lib] crate-type = ["cdylib", "lib"] - -[profile.release] -overflow-checks = true diff --git a/rust/sealevel/libraries/test-transaction-utils/Cargo.toml b/rust/sealevel/libraries/test-transaction-utils/Cargo.toml index ac819fda96..418c7c38fd 100644 --- a/rust/sealevel/libraries/test-transaction-utils/Cargo.toml +++ b/rust/sealevel/libraries/test-transaction-utils/Cargo.toml @@ -12,6 +12,3 @@ solana-sdk.workspace = true [lib] crate-type = ["cdylib", "lib"] - -[profile.release] -overflow-checks = true diff --git a/rust/sealevel/libraries/test-utils/Cargo.toml b/rust/sealevel/libraries/test-utils/Cargo.toml index bec4e6d943..ff87235806 100644 --- a/rust/sealevel/libraries/test-utils/Cargo.toml +++ b/rust/sealevel/libraries/test-utils/Cargo.toml @@ -24,6 +24,3 @@ serializable-account-meta = { path = "../serializable-account-meta" } [lib] crate-type = ["cdylib", "lib"] - -[profile.release] -overflow-checks = true diff --git a/rust/sealevel/programs/hyperlane-sealevel-igp-test/Cargo.toml b/rust/sealevel/programs/hyperlane-sealevel-igp-test/Cargo.toml index 80e49ec4dc..c73bf4a6d5 100644 --- a/rust/sealevel/programs/hyperlane-sealevel-igp-test/Cargo.toml +++ b/rust/sealevel/programs/hyperlane-sealevel-igp-test/Cargo.toml @@ -17,6 +17,3 @@ hyperlane-sealevel-igp = { path = "../hyperlane-sealevel-igp" } solana-program-test.workspace = true solana-sdk.workspace = true hyperlane-test-utils = { path = "../../libraries/test-utils" } - -[profile.release] -overflow-checks = true diff --git a/rust/sealevel/programs/hyperlane-sealevel-igp/Cargo.toml b/rust/sealevel/programs/hyperlane-sealevel-igp/Cargo.toml index 442ada46ea..3d60523999 100644 --- a/rust/sealevel/programs/hyperlane-sealevel-igp/Cargo.toml +++ b/rust/sealevel/programs/hyperlane-sealevel-igp/Cargo.toml @@ -27,6 +27,3 @@ serde = { workspace = true, optional = true } [lib] crate-type = ["cdylib", "lib"] - -[profile.release] -overflow-checks = true diff --git a/rust/sealevel/programs/hyperlane-sealevel-igp/src/instruction.rs b/rust/sealevel/programs/hyperlane-sealevel-igp/src/instruction.rs index 2617cdd9ad..829d3248f8 100644 --- a/rust/sealevel/programs/hyperlane-sealevel-igp/src/instruction.rs +++ b/rust/sealevel/programs/hyperlane-sealevel-igp/src/instruction.rs @@ -347,3 +347,56 @@ pub fn transfer_igp_account_ownership_instruction( }; Ok(instruction) } + +/// Gets an instruction to claim funds from an IGP to the beneficiary. +pub fn claim_instruction( + program_id: Pubkey, + igp: Pubkey, + beneficiary: Pubkey, +) -> Result { + let ixn = Instruction::Claim; + + // Accounts: + // 0. [executable] The system program. + // 1. [writeable] The IGP. + // 2. [writeable] The IGP beneficiary. + let accounts = vec![ + AccountMeta::new_readonly(solana_program::system_program::id(), false), + AccountMeta::new(igp, false), + AccountMeta::new(beneficiary, false), + ]; + + let instruction = SolanaInstruction { + program_id, + data: ixn.try_to_vec()?, + accounts, + }; + + Ok(instruction) +} + +/// Gets an instruction to claim funds from an IGP to the beneficiary. +pub fn set_beneficiary_instruction( + program_id: Pubkey, + igp: Pubkey, + igp_owner: Pubkey, + new_beneficiary: Pubkey, +) -> Result { + let ixn = Instruction::SetIgpBeneficiary(new_beneficiary); + + // Accounts: + // 0. [] The IGP. + // 1. [signer] The owner of the IGP account. + let accounts = vec![ + AccountMeta::new(igp, false), + AccountMeta::new(igp_owner, true), + ]; + + let instruction = SolanaInstruction { + program_id, + data: ixn.try_to_vec()?, + accounts, + }; + + Ok(instruction) +} diff --git a/rust/sealevel/programs/hyperlane-sealevel-token-collateral/Cargo.toml b/rust/sealevel/programs/hyperlane-sealevel-token-collateral/Cargo.toml index bfef40811f..2255326d5d 100644 --- a/rust/sealevel/programs/hyperlane-sealevel-token-collateral/Cargo.toml +++ b/rust/sealevel/programs/hyperlane-sealevel-token-collateral/Cargo.toml @@ -37,6 +37,3 @@ hyperlane-sealevel-test-ism = { path = "../ism/test-ism", features = ["no-entryp [lib] crate-type = ["cdylib", "lib"] - -[profile.release] -overflow-checks = true diff --git a/rust/sealevel/programs/hyperlane-sealevel-token-native/Cargo.toml b/rust/sealevel/programs/hyperlane-sealevel-token-native/Cargo.toml index 5db9317646..63f0f852c4 100644 --- a/rust/sealevel/programs/hyperlane-sealevel-token-native/Cargo.toml +++ b/rust/sealevel/programs/hyperlane-sealevel-token-native/Cargo.toml @@ -37,6 +37,3 @@ tarpc = "~0.29" [lib] crate-type = ["cdylib", "lib"] - -[profile.release] -overflow-checks = true diff --git a/rust/sealevel/programs/hyperlane-sealevel-token/Cargo.toml b/rust/sealevel/programs/hyperlane-sealevel-token/Cargo.toml index ab33980ed1..61fafa0c00 100644 --- a/rust/sealevel/programs/hyperlane-sealevel-token/Cargo.toml +++ b/rust/sealevel/programs/hyperlane-sealevel-token/Cargo.toml @@ -37,6 +37,3 @@ hyperlane-sealevel-test-ism = { path = "../ism/test-ism", features = ["no-entryp [lib] crate-type = ["cdylib", "lib"] - -[profile.release] -overflow-checks = true diff --git a/rust/sealevel/programs/ism/multisig-ism-message-id/Cargo.toml b/rust/sealevel/programs/ism/multisig-ism-message-id/Cargo.toml index 8a35c3b96f..d7580309d1 100644 --- a/rust/sealevel/programs/ism/multisig-ism-message-id/Cargo.toml +++ b/rust/sealevel/programs/ism/multisig-ism-message-id/Cargo.toml @@ -34,6 +34,3 @@ multisig-ism = { path = "../../../libraries/multisig-ism", features = ["test-dat [lib] crate-type = ["cdylib", "lib"] - -[profile.release] -overflow-checks = true diff --git a/rust/sealevel/programs/ism/test-ism/Cargo.toml b/rust/sealevel/programs/ism/test-ism/Cargo.toml index fe0c83f64a..d3ad8e19a4 100644 --- a/rust/sealevel/programs/ism/test-ism/Cargo.toml +++ b/rust/sealevel/programs/ism/test-ism/Cargo.toml @@ -24,6 +24,3 @@ hyperlane-test-transaction-utils = { path = "../../../libraries/test-transaction [lib] crate-type = ["cdylib", "lib"] - -[profile.release] -overflow-checks = true diff --git a/rust/sealevel/programs/mailbox-test/Cargo.toml b/rust/sealevel/programs/mailbox-test/Cargo.toml index f88118df6c..a6fabc330c 100644 --- a/rust/sealevel/programs/mailbox-test/Cargo.toml +++ b/rust/sealevel/programs/mailbox-test/Cargo.toml @@ -34,6 +34,3 @@ serializable-account-meta = { path = "../../libraries/serializable-account-meta" [lib] crate-type = ["cdylib", "lib"] - -[profile.release] -overflow-checks = true diff --git a/rust/sealevel/programs/mailbox/Cargo.toml b/rust/sealevel/programs/mailbox/Cargo.toml index 179891f422..580263875c 100644 --- a/rust/sealevel/programs/mailbox/Cargo.toml +++ b/rust/sealevel/programs/mailbox/Cargo.toml @@ -37,6 +37,3 @@ log.workspace = true [lib] crate-type = ["cdylib", "lib"] - -[profile.release] -overflow-checks = true diff --git a/rust/sealevel/programs/test-send-receiver/Cargo.toml b/rust/sealevel/programs/test-send-receiver/Cargo.toml index 61cd5fc229..3e0daf980e 100644 --- a/rust/sealevel/programs/test-send-receiver/Cargo.toml +++ b/rust/sealevel/programs/test-send-receiver/Cargo.toml @@ -24,6 +24,3 @@ serializable-account-meta = { path = "../../libraries/serializable-account-meta" [lib] crate-type = ["cdylib", "lib"] - -[profile.release] -overflow-checks = true diff --git a/rust/sealevel/programs/validator-announce/Cargo.toml b/rust/sealevel/programs/validator-announce/Cargo.toml index 9720a05d43..100df57b57 100644 --- a/rust/sealevel/programs/validator-announce/Cargo.toml +++ b/rust/sealevel/programs/validator-announce/Cargo.toml @@ -27,6 +27,3 @@ hyperlane-test-utils ={ path = "../../libraries/test-utils" } [lib] crate-type = ["cdylib", "lib"] - -[profile.release] -overflow-checks = true diff --git a/typescript/sdk/src/consts/environments/mainnet.json b/typescript/sdk/src/consts/environments/mainnet.json index b5c4f927ac..8346531fd7 100644 --- a/typescript/sdk/src/consts/environments/mainnet.json +++ b/typescript/sdk/src/consts/environments/mainnet.json @@ -159,8 +159,8 @@ "validatorAnnounce": "C88Lk5GR6cPxYoJxPbNDDEwsx5Kxn1wZEomvQ2So333g", "proxyAdmin": "11111111111111111111111111111111", "mailbox": "Ge9atjAc3Ltu91VTbNpJDCjZ9CFxFyck4h3YBcTF9XPq", - "interchainGasPaymaster": "FCNfmLSZLo5x7oNYmkYU8WdPUu7pj636P9CaMxkmaCp7", - "defaultIsmInterchainGasPaymaster": "GTj6WzNxLNFydq5zJrV9p13fyqotRoo1MQykNCWuVpbS", + "interchainGasPaymaster": "Ho7b7gidMacE4AckGZPJ7zn2oa45xVarcxLi2yzcbkNZ", + "defaultIsmInterchainGasPaymaster": "83JFTZNPK7eAxcmQN5VH77ytwgmkTaRvV9CSZbCrRYjt", "multisigIsm": "11111111111111111111111111111111", "testRecipient": "11111111111111111111111111111111", "interchainAccountIsm": "11111111111111111111111111111111", diff --git a/typescript/sdk/src/consts/environments/testnet.json b/typescript/sdk/src/consts/environments/testnet.json index bf09e67509..b3afc76b06 100644 --- a/typescript/sdk/src/consts/environments/testnet.json +++ b/typescript/sdk/src/consts/environments/testnet.json @@ -165,8 +165,8 @@ "validatorAnnounce": "0x0000000000000000000000000000000000000000", "proxyAdmin": "0x0000000000000000000000000000000000000000", "mailbox": "4v25Dz9RccqUrTzmfHzJMsjd1iVoNrWzeJ4o6GYuJrVn", - "interchainGasPaymaster": "7hMPEGdgBQFsjEz3aaNwZp8WMFHs615zAM3erXBDJuJR", - "defaultIsmInterchainGasPaymaster": "0x0000000000000000000000000000000000000000", + "interchainGasPaymaster": "Gh1B2JLZAY188CAYnWPup8TvGDxHNVWbVVKZi6wh75xZ", + "defaultIsmInterchainGasPaymaster": "2wF3dmWZUZ4C1XvjJQApXcCRPAzkMq1jQ5zjiz6UBoDq", "multisigIsm": "0x0000000000000000000000000000000000000000", "testRecipient": "0x0000000000000000000000000000000000000000", "interchainAccountIsm": "0x0000000000000000000000000000000000000000",