feat: SOL (solana <> eclipse) warp route (#4268)

### Description

<!--
What's included in this PR?
-->

### Drive-by changes

<!--
Are there any minor or drive-by changes also included?
-->

### Related issues

<!--
- Fixes #[issue number here]
-->

### Backward compatibility

<!--
Are these changes backward compatible? Are there any infrastructure
implications, e.g. changes that would prohibit deploying older commits
using this infra tooling?

Yes/No
-->

### Testing

<!--
What kind of testing have these changes undergone?

None/Manual/Unit Tests
-->

---------

Co-authored-by: Trevor Porter <tkporter4@gmail.com>
pull/4381/head
Daniel Savu 3 months ago committed by GitHub
parent 853f3e976b
commit dac2fb5210
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 5
      .github/workflows/test.yml
  2. 52
      rust/sealevel/client/src/cmd_utils.rs
  3. 1
      rust/sealevel/client/src/context.rs
  4. 57
      rust/sealevel/client/src/core.rs
  5. 12
      rust/sealevel/client/src/igp.rs
  6. 3
      rust/sealevel/client/src/main.rs
  7. 22
      rust/sealevel/client/src/multisig_ism.rs
  8. 10
      rust/sealevel/client/src/router.rs
  9. 150
      rust/sealevel/client/src/warp_route.rs
  10. 3
      rust/sealevel/environments/mainnet3/multisig-ism-message-id/solana/hyperlane/program-ids.json
  11. 2
      rust/sealevel/environments/mainnet3/solana/core/program-ids.json
  12. 10
      rust/sealevel/environments/mainnet3/warp-routes/eclipsesol/program-ids.json
  13. 15
      rust/sealevel/environments/mainnet3/warp-routes/eclipsesol/token-config.json
  14. 10
      rust/sealevel/environments/mainnet3/warp-routes/pzeth/program-ids.json
  15. 18
      rust/sealevel/environments/mainnet3/warp-routes/pzeth/token-config.json
  16. 25
      rust/sealevel/libraries/multisig-ism/src/test_data.rs
  17. 10
      rust/sealevel/programs/hyperlane-sealevel-token/src/plugin.rs
  18. 2
      rust/sealevel/programs/ism/multisig-ism-message-id/src/processor.rs
  19. 2
      rust/utils/run-locally/src/solana.rs
  20. 4
      typescript/infra/config/environments/mainnet3/gasPrices.json
  21. 11
      typescript/infra/config/environments/mainnet3/warp/addresses.json
  22. 34
      typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumSolanaPzETHWarpConfig.ts
  23. 19
      typescript/infra/config/environments/mainnet3/warp/verification.json
  24. 3
      typescript/infra/config/warp.ts
  25. 2
      typescript/sdk/src/providers/transactionFeeEstimators.ts
  26. 2
      typescript/sdk/src/router/GasRouterDeployer.ts

@ -246,6 +246,11 @@ jobs:
!./rust
key: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Install system dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -qq -y libudev-dev pkg-config protobuf-compiler
- name: Checkout registry
uses: ./.github/actions/checkout-registry

@ -13,6 +13,8 @@ use solana_sdk::{
signature::{Keypair, Signer},
};
const SOLANA_DOMAIN: u32 = 1399811149;
pub(crate) fn account_exists(client: &RpcClient, account: &Pubkey) -> Result<bool, ClientError> {
// Using `get_account_with_commitment` instead of `get_account` so we get Ok(None) when the account
// doesn't exist, rather than an error
@ -29,10 +31,17 @@ pub(crate) fn deploy_program_idempotent(
program_keypair_path: &str,
program_path: &str,
url: &str,
local_domain: u32,
) -> Result<(), ClientError> {
let client = RpcClient::new(url.to_string());
if !account_exists(&client, &program_keypair.pubkey())? {
deploy_program(payer_keypair_path, program_keypair_path, program_path, url);
deploy_program(
payer_keypair_path,
program_keypair_path,
program_path,
url,
local_domain,
);
} else {
println!("Program {} already deployed", program_keypair.pubkey());
}
@ -45,25 +54,29 @@ pub(crate) fn deploy_program(
program_keypair_path: &str,
program_path: &str,
url: &str,
local_domain: u32,
) {
build_cmd(
&[
"solana",
"--url",
url,
"-k",
payer_keypair_path,
"program",
"deploy",
program_path,
"--upgrade-authority",
payer_keypair_path,
"--program-id",
program_keypair_path,
],
None,
None,
);
let mut command = vec![
"solana",
"--url",
url,
"-k",
payer_keypair_path,
"program",
"deploy",
program_path,
"--upgrade-authority",
payer_keypair_path,
"--program-id",
program_keypair_path,
];
if local_domain.eq(&SOLANA_DOMAIN) {
// May need tweaking depending on gas prices / available balance
command.append(&mut vec!["--with-compute-unit-price", "550000"]);
}
build_cmd(command.as_slice(), None, None);
}
pub(crate) fn create_new_directory(parent_dir: &Path, name: &str) -> PathBuf {
@ -112,6 +125,7 @@ fn build_cmd(cmd: &[&str], wd: Option<&str>, env: Option<&HashMap<&str, &str>>)
if let Some(env) = env {
c.envs(env);
}
println!("Running command: {:?}", c);
let status = c.status().expect("Failed to run command");
assert!(
status.success(),

@ -29,6 +29,7 @@ pub(crate) struct Context {
pub require_tx_approval: bool,
}
#[derive(Debug)]
pub(crate) struct InstructionWithDescription {
pub instruction: Instruction,
pub description: Option<String>,

@ -1,22 +1,67 @@
use borsh::BorshDeserialize;
use hyperlane_sealevel_mailbox::protocol_fee::ProtocolFee;
use serde::{Deserialize, Serialize};
use solana_program::pubkey::Pubkey;
use solana_sdk::signature::Signer;
use solana_sdk::{compute_budget, compute_budget::ComputeBudgetInstruction};
use std::collections::HashMap;
use std::{fs::File, path::Path};
use crate::ONE_SOL_IN_LAMPORTS;
use crate::{
artifacts::{read_json, write_json},
cmd_utils::{create_and_write_keypair, create_new_directory, deploy_program},
multisig_ism::deploy_multisig_ism_message_id,
Context, CoreCmd, CoreDeploy, CoreSubCmd,
};
use crate::{DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT, ONE_SOL_IN_LAMPORTS};
use hyperlane_core::H256;
use hyperlane_sealevel_igp::accounts::{SOL_DECIMALS, TOKEN_EXCHANGE_RATE_SCALE};
pub(crate) fn adjust_gas_price_if_needed(chain_name: &str, ctx: &mut Context) {
if chain_name.eq("solana") {
let mut initial_instructions = ctx.initial_instructions.borrow_mut();
const PROCESS_DESIRED_PRIORITIZATION_FEE_LAMPORTS_PER_TX: u64 = 50_000_000;
const MICRO_LAMPORT_FEE_PER_LIMIT: u64 =
// Convert to micro-lamports
(PROCESS_DESIRED_PRIORITIZATION_FEE_LAMPORTS_PER_TX * 1_000_000)
// Divide by the max compute units
/ DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64;
for i in initial_instructions.iter_mut() {
if i.instruction.program_id != compute_budget::id() {
continue;
}
if let Ok(compute_budget_instruction) =
ComputeBudgetInstruction::try_from_slice(&i.instruction.data)
{
if matches!(
compute_budget_instruction,
ComputeBudgetInstruction::SetComputeUnitPrice { .. }
) {
// The compute unit price has already been set, so we override it and return early
i.instruction = ComputeBudgetInstruction::set_compute_unit_price(
MICRO_LAMPORT_FEE_PER_LIMIT,
);
return;
}
}
}
initial_instructions.push(
(
ComputeBudgetInstruction::set_compute_unit_price(MICRO_LAMPORT_FEE_PER_LIMIT),
Some(format!(
"Set compute unit price to {}",
MICRO_LAMPORT_FEE_PER_LIMIT
)),
)
.into(),
);
}
}
#[derive(serde::Serialize, serde::Deserialize, PartialEq, Debug)]
#[serde(rename_all = "camelCase")]
struct ProtocolFeeConfig {
@ -39,6 +84,8 @@ impl Default for ProtocolFeeConfig {
pub(crate) fn process_core_cmd(mut ctx: Context, cmd: CoreCmd) {
match cmd.cmd {
CoreSubCmd::Deploy(core) => {
adjust_gas_price_if_needed(core.chain.as_str(), &mut ctx);
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);
@ -50,9 +97,11 @@ pub(crate) fn process_core_cmd(mut ctx: Context, cmd: CoreCmd) {
&core.built_so_dir,
core.use_existing_keys,
&key_dir,
core.local_domain,
);
let mailbox_program_id = deploy_mailbox(&mut ctx, &core, &key_dir, ism_program_id);
let mailbox_program_id =
deploy_mailbox(&mut ctx, &core, &key_dir, ism_program_id, core.local_domain);
let validator_announce_program_id =
deploy_validator_announce(&mut ctx, &core, &key_dir, mailbox_program_id);
@ -78,6 +127,7 @@ fn deploy_mailbox(
core: &CoreDeploy,
key_dir: &Path,
default_ism: Pubkey,
local_domain: u32,
) -> Pubkey {
let (keypair, keypair_path) = create_and_write_keypair(
key_dir,
@ -94,6 +144,7 @@ fn deploy_mailbox(
.to_str()
.unwrap(),
&ctx.client.url(),
local_domain,
);
println!("Deployed Mailbox at program ID {}", program_id);
@ -152,6 +203,7 @@ fn deploy_validator_announce(
.to_str()
.unwrap(),
&ctx.client.url(),
core.local_domain,
);
println!("Deployed ValidatorAnnounce at program ID {}", program_id);
@ -237,6 +289,7 @@ fn deploy_igp(ctx: &mut Context, core: &CoreDeploy, key_dir: &Path) -> (Pubkey,
.to_str()
.unwrap(),
&ctx.client.url(),
core.local_domain,
);
println!("Deployed IGP at program ID {}", program_id);

@ -20,7 +20,7 @@ use solana_sdk::{
signature::{Keypair, Signer as _},
};
use hyperlane_core::H256;
use hyperlane_core::{KnownHyperlaneDomain, H256};
use hyperlane_sealevel_igp::{
accounts::{
@ -68,8 +68,14 @@ pub(crate) fn process_igp_cmd(mut ctx: Context, cmd: IgpCmd) {
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 local_domain = deploy
.chain
.parse::<KnownHyperlaneDomain>()
.map(|v| v as u32)
.expect("Invalid chain name");
let program_id = deploy_igp_program(&mut ctx, &deploy.built_so_dir, true, &key_dir);
let program_id =
deploy_igp_program(&mut ctx, &deploy.built_so_dir, true, &key_dir, local_domain);
write_json::<SingularProgramIdArtifact>(
&chain_dir.join("program-ids.json"),
@ -409,6 +415,7 @@ fn deploy_igp_program(
built_so_dir: &Path,
use_existing_keys: bool,
key_dir: &Path,
local_domain: u32,
) -> Pubkey {
let (keypair, keypair_path) = create_and_write_keypair(
key_dir,
@ -425,6 +432,7 @@ fn deploy_igp_program(
.to_str()
.unwrap(),
&ctx.client.url(),
local_domain,
);
println!("Deployed IGP at program ID {}", program_id);

@ -710,7 +710,7 @@ fn main() {
payer_keypair.pubkey(),
Some(PayerKeypair {
keypair: payer_keypair,
keypair_path,
keypair_path: keypair_path.clone(),
}),
)
} else {
@ -724,6 +724,7 @@ fn main() {
let commitment = CommitmentConfig::confirmed();
let mut instructions = vec![];
if cli.compute_budget != DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT {
assert!(cli.compute_budget <= MAX_COMPUTE_UNIT_LIMIT);
instructions.push(

@ -12,7 +12,7 @@ use crate::{
Context, MultisigIsmMessageIdCmd, MultisigIsmMessageIdSubCmd,
};
use hyperlane_core::H160;
use hyperlane_core::{KnownHyperlaneDomain, H160};
use hyperlane_sealevel_multisig_ism_message_id::{
access_control_pda_seeds,
@ -53,9 +53,19 @@ pub(crate) fn process_multisig_ism_message_id_cmd(mut ctx: Context, cmd: Multisi
let chain_dir = create_new_directory(&ism_dir, &deploy.chain);
let context_dir = create_new_directory(&chain_dir, &deploy.context);
let key_dir = create_new_directory(&context_dir, "keys");
let local_domain = deploy
.chain
.parse::<KnownHyperlaneDomain>()
.map(|v| v as u32)
.expect("Invalid chain name");
let ism_program_id =
deploy_multisig_ism_message_id(&mut ctx, &deploy.built_so_dir, true, &key_dir);
let ism_program_id = deploy_multisig_ism_message_id(
&mut ctx,
&deploy.built_so_dir,
true,
&key_dir,
local_domain,
);
write_json::<SingularProgramIdArtifact>(
&context_dir.join("program-ids.json"),
@ -158,6 +168,7 @@ pub(crate) fn deploy_multisig_ism_message_id(
built_so_dir: &Path,
use_existing_keys: bool,
key_dir: &Path,
local_domain: u32,
) -> Pubkey {
let (keypair, keypair_path) = create_and_write_keypair(
key_dir,
@ -174,6 +185,7 @@ pub(crate) fn deploy_multisig_ism_message_id(
.to_str()
.unwrap(),
&ctx.client.url(),
local_domain,
);
println!(
@ -197,6 +209,10 @@ pub(crate) fn deploy_multisig_ism_message_id(
),
)
.send_with_payer();
println!(
"initialized Multisig ISM Message ID at program ID {}",
program_id
);
program_id
}

@ -15,9 +15,10 @@ use hyperlane_sealevel_connection_client::router::RemoteRouterConfig;
use hyperlane_sealevel_igp::accounts::{Igp, InterchainGasPaymasterType, OverheadIgp};
use crate::{
adjust_gas_price_if_needed,
artifacts::{write_json, HexAndBase58ProgramIdArtifact},
cmd_utils::{create_and_write_keypair, create_new_directory, deploy_program_idempotent},
read_core_program_ids, Context, CoreProgramIds,
read_core_program_ids, warp_route, Context, CoreProgramIds,
};
/// Optional connection client configuration.
@ -185,6 +186,7 @@ pub(crate) trait RouterDeployer<Config: RouterConfigGetter + std::fmt::Debug>:
.to_str()
.unwrap(),
&chain_config.rpc_urls[0].http,
chain_config.domain_id(),
)
.unwrap();
@ -348,6 +350,8 @@ pub(crate) fn deploy_routers<
.filter(|(_, app_config)| app_config.router_config().foreign_deployment.is_none())
.collect::<HashMap<_, _>>();
warp_route::install_spl_token_cli();
// Now we deploy to chains that don't have a foreign deployment
for (chain_name, app_config) in app_configs_to_deploy.iter() {
let chain_config = chain_configs
@ -360,6 +364,8 @@ pub(crate) fn deploy_routers<
}
}
adjust_gas_price_if_needed(chain_name.as_str(), ctx);
// Deploy - this is idempotent.
let program_id = deployer.deploy(
ctx,
@ -537,6 +543,8 @@ fn enroll_all_remote_routers<
routers: &HashMap<u32, H256>,
) {
for (chain_name, _) in app_configs_to_deploy.iter() {
adjust_gas_price_if_needed(chain_name.as_str(), ctx);
let chain_config = chain_configs
.get(*chain_name)
.unwrap_or_else(|| panic!("Chain config not found for chain: {}", chain_name));

@ -3,7 +3,11 @@ use hyperlane_core::H256;
use hyperlane_sealevel_token_collateral::plugin::CollateralPlugin;
use hyperlane_sealevel_token_native::plugin::NativePlugin;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fmt::Debug};
use std::{
collections::HashMap,
fmt::Debug,
process::{Command, Stdio},
};
use solana_client::{client_error::ClientError, rpc_client::RpcClient};
@ -78,6 +82,7 @@ struct TokenMetadata {
name: String,
symbol: String,
total_supply: Option<String>,
uri: Option<String>,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
@ -243,6 +248,9 @@ impl RouterDeployer<TokenConfig> for WarpRouteDeployer {
remote_decimals: app_config.decimal_metadata.remote_decimals(),
};
let home_path = std::env::var("HOME").unwrap();
let spl_token_binary_path = format!("{home_path}/.cargo/bin/spl-token");
match &app_config.token_type {
TokenType::Native => ctx.new_txn().add(
hyperlane_sealevel_token_native::instruction::init_instruction(
@ -255,23 +263,51 @@ impl RouterDeployer<TokenConfig> for WarpRouteDeployer {
TokenType::Synthetic(_token_metadata) => {
let decimals = init.decimals;
let init_txn = ctx.new_txn().add(
hyperlane_sealevel_token::instruction::init_instruction(
program_id,
ctx.payer_pubkey,
init,
ctx.new_txn()
.add(
hyperlane_sealevel_token::instruction::init_instruction(
program_id,
ctx.payer_pubkey,
init,
)
.unwrap(),
)
.unwrap(),
);
.with_client(client)
.send_with_payer();
let (mint_account, _mint_bump) =
Pubkey::find_program_address(hyperlane_token_mint_pda_seeds!(), &program_id);
// TODO: Also set Metaplex metadata?
init_txn.add(
let mut cmd = Command::new(spl_token_binary_path.clone());
cmd.args([
"create-token",
mint_account.to_string().as_str(),
"--enable-metadata",
"-p",
spl_token_2022::id().to_string().as_str(),
"--url",
client.url().as_str(),
"--with-compute-unit-limit",
"500000",
"--mint-authority",
&ctx.payer_pubkey.to_string(),
"--fee-payer",
ctx.payer_keypair_path(),
]);
println!("running command: {:?}", cmd);
let status = cmd
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.status()
.expect("Failed to run command");
println!("initialized metadata pointer. Status: {status}");
ctx.new_txn().add(
spl_token_2022::instruction::initialize_mint2(
&spl_token_2022::id(),
&mint_account,
&mint_account,
&ctx.payer_pubkey,
None,
decimals,
)
@ -295,6 +331,71 @@ impl RouterDeployer<TokenConfig> for WarpRouteDeployer {
}
.with_client(client)
.send_with_payer();
if let TokenType::Synthetic(token_metadata) = &app_config.token_type {
let (mint_account, _mint_bump) =
Pubkey::find_program_address(hyperlane_token_mint_pda_seeds!(), &program_id);
let mut cmd = Command::new(spl_token_binary_path.clone());
cmd.args([
"initialize-metadata",
mint_account.to_string().as_str(),
token_metadata.name.as_str(),
token_metadata.symbol.as_str(),
token_metadata.uri.as_deref().unwrap_or(""),
"-p",
spl_token_2022::id().to_string().as_str(),
"--url",
client.url().as_str(),
"--with-compute-unit-limit",
"500000",
"--mint-authority",
ctx.payer_keypair_path(),
"--fee-payer",
ctx.payer_keypair_path(),
"--update-authority",
&ctx.payer_pubkey.to_string(),
]);
println!("running command: {:?}", cmd);
let status = cmd
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.status()
.expect("Failed to run command");
println!("initialized metadata. Status: {status}");
// Burn the metadata pointer, metadata, and mint authorities by moving them to the mint
let authorities_to_transfer = &["metadata-pointer", "metadata", "mint"];
for authority in authorities_to_transfer {
println!("Transferring authority: {authority} to the mint account {mint_account}");
let mut cmd = Command::new(spl_token_binary_path.clone());
cmd.args([
"authorize",
mint_account.to_string().as_str(),
authority,
mint_account.to_string().as_str(),
"-p",
spl_token_2022::id().to_string().as_str(),
"--url",
client.url().as_str(),
"--with-compute-unit-limit",
"500000",
"--fee-payer",
ctx.payer_keypair_path(),
"--authority",
ctx.payer_keypair_path(),
]);
println!("Running command: {:?}", cmd);
let status = cmd
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.status()
.expect("Failed to run command");
println!("Set the {authority} authority to the mint account. Status: {status}");
}
}
}
/// Sets gas router configs on all deployable chains.
@ -539,3 +640,30 @@ pub fn parse_token_account_data(token_type: FlatTokenType, data: &mut &[u8]) {
}
}
}
pub fn install_spl_token_cli() {
println!("Installing cargo 1.76.0 (required by spl-token-cli)");
Command::new("rustup")
.args(["toolchain", "install", "1.76.0"])
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.status()
.expect("Failed to run command");
println!("Installing the spl token cli");
Command::new("cargo")
.args([
"+1.76.0",
"install",
"spl-token-cli",
"--git",
"https://github.com/hyperlane-xyz/solana-program-library",
"--branch",
"dan/create-token-for-mint",
"--rev",
"c1278a3f1",
])
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.status()
.expect("Failed to run command");
}

@ -0,0 +1,3 @@
{
"program_id": "HDXcKGggF4aLRE1Q6ApF2rLwriSQjpjrnE811cwH21mr"
}

@ -1,7 +1,7 @@
{
"mailbox": "4rRZgaC1DaCqtWYLzg14ftuXKPuHe1nGEM6ZtNpim3Wz",
"validator_announce": "bn63TQYrzzK9H3XQwZ1gzGdNS91xkt5YaTinFPWyahR",
"multisig_ism_message_id": "2abS919HMBQ39GviSgjMQQf5L1G7cfdbAiNq61wQ8CvJ",
"multisig_ism_message_id": "HDXcKGggF4aLRE1Q6ApF2rLwriSQjpjrnE811cwH21mr",
"igp_program_id": "8cPZ6Lv49h1cYBb6q29E2pxn4xbWjJGxfarNE8LqP1Ks",
"overhead_igp_account": "5FG1TUuhXGKdMbbH8uHEnUghazD4aVfEPAgKLNGNx3SL",
"igp_account": "HkqbGqRX7Fi5pwqi8HkDaUhHK6mGWsy7Rt17fpgBrbP5"

@ -0,0 +1,10 @@
{
"eclipse": {
"hex": "0x6d8f3a8e68449b4c5fa5f09f9f9bf82607f6b2b1b6052260bf7c5c66aa5a5684",
"base58": "8Ng7TLE223sWcRjH7rEdUPoR3seaugqPw1djdQRRZ6Gj"
},
"solana": {
"hex": "0x6903def7c07b2844eb549e7037651c07f508884e0c962345c8bb2b9834633e15",
"base58": "84wESfpyisKVYwkpaJpkHS6XxkLPC5bLZrTD1jQw1TrL"
}
}

@ -0,0 +1,15 @@
{
"solana": {
"type": "native",
"decimals": 9,
"interchainGasPaymaster": "HkqbGqRX7Fi5pwqi8HkDaUhHK6mGWsy7Rt17fpgBrbP5"
},
"eclipse": {
"type": "synthetic",
"decimals": 9,
"name": "Solana",
"symbol": "SOL",
"uri": "https://github.com/hyperlane-xyz/hyperlane-registry/blob/b661127dd3dce5ea98b78ae0051fbd10c384b173/deployments/warp_routes/SOL/eclipse/metadata.json",
"interchainGasPaymaster": "AgjedtgQKTWGR77ULJ9j9AhLjNDk1D3BTtuxKmcZrJqE"
}
}

@ -0,0 +1,10 @@
{
"solana": {
"hex": "0xe9792265ec273ffc17731af890d3e9963e9d744e7b99f02491911ce1bb75b8cb",
"base58": "GiP8GwN1GsscVJvmKSD4muDEihRzZRa9mxnS1Toi64pa"
},
"ethereum": {
"hex": "0x0000000000000000000000001d622da2ce4c4d9d4b0611718cb3bcdcad008dd4",
"base58": "111111111111Qk6NeeudaXPNPB2XfB1yQ9furMh"
}
}

@ -0,0 +1,18 @@
{
"solana": {
"type": "synthetic",
"decimals": 9,
"name": "Renzo Restaked LST",
"symbol": "pzETH",
"uri": "https://raw.githubusercontent.com/hyperlane-xyz/hyperlane-registry/12660fd34d30e960a748d87408a8d88f634f7454/deployments/warp_routes/pzETH/ethereum-solana-metadata.json",
"interchainGasPaymaster": "5FG1TUuhXGKdMbbH8uHEnUghazD4aVfEPAgKLNGNx3SL",
"remoteDecimals": 18
},
"ethereum": {
"type": "collateral",
"decimals": 18,
"token": "0x8c9532a60e0e7c6bbd2b2c1303f63ace1c3e9811",
"foreignDeployment": "0x1D622da2ce4C4D9D4B0611718cb3BcDcAd008DD4",
"destinationGas": 200000
}
}

@ -43,37 +43,40 @@ pub fn get_multisig_ism_test_data() -> MultisigIsmTestData {
"0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
)
.unwrap(),
index: 69,
// Intentionally set to a different value than the message's nonce to test
// that the checkpoint in the ISM is constructed correctly using the metadata's
// merkle index.
index: message.nonce + 1,
},
message_id: message.id(),
};
// checkpoint.signing_hash() is equal to:
// 0x5c2b4cbafdd7319d9a3dea4aa078748ae53368e1521494a80cf4259b9c6dba6a
// 0x3fd308215a20af20b137372f8a69fd336ebf93d57d4076a7c46e13f315255257
// Validator 0:
// Address: 0xE3DCDBbc248cE191bDc271f3FCcd0d95911BFC5D
// Private Key: 0x788aa7213bd92ff92017d767fde0d75601425818c8e4b21e87314c2a4dcd6091
let validator_0 = H160::from_str("0xE3DCDBbc248cE191bDc271f3FCcd0d95911BFC5D").unwrap();
// > await (new ethers.Wallet('0x788aa7213bd92ff92017d767fde0d75601425818c8e4b21e87314c2a4dcd6091')).signMessage(ethers.utils.arrayify('0x5c2b4cbafdd7319d9a3dea4aa078748ae53368e1521494a80cf4259b9c6dba6a'))
// '0xb8875fb75adf471e43a943b78eb422ffe86a4291fa6324f9f875241605ca831d2b4225358936deee7501cf305aafc1677e3dc9bcfea4caec54f4cde49d416bd91b'
let signature_0 = hex::decode("b8875fb75adf471e43a943b78eb422ffe86a4291fa6324f9f875241605ca831d2b4225358936deee7501cf305aafc1677e3dc9bcfea4caec54f4cde49d416bd91b").unwrap();
// > await (new ethers.Wallet('0x788aa7213bd92ff92017d767fde0d75601425818c8e4b21e87314c2a4dcd6091')).signMessage(ethers.utils.arrayify('0x3fd308215a20af20b137372f8a69fd336ebf93d57d4076a7c46e13f315255257'))
// '0x081d398e1452ae12267f63f224d3037b4bb3f496cb55c14a2076c5e27ed944ad6d8e10d3164bc13b5820846a3f19e013e1c551b67a3c863882f7b951acdab96d1c'
let signature_0 = hex::decode("081d398e1452ae12267f63f224d3037b4bb3f496cb55c14a2076c5e27ed944ad6d8e10d3164bc13b5820846a3f19e013e1c551b67a3c863882f7b951acdab96d1c").unwrap();
// Validator 1:
// Address: 0xb25206874C24733F05CC0dD11924724A8E7175bd
// Private Key: 0x4a599de3915f404d84a2ebe522bfe7032ebb1ca76a65b55d6eb212b129043a0e
let validator_1 = H160::from_str("0xb25206874C24733F05CC0dD11924724A8E7175bd").unwrap();
// > await (new ethers.Wallet('0x4a599de3915f404d84a2ebe522bfe7032ebb1ca76a65b55d6eb212b129043a0e')).signMessage(ethers.utils.arrayify('0x5c2b4cbafdd7319d9a3dea4aa078748ae53368e1521494a80cf4259b9c6dba6a'))
// '0xfa8d61da2e0ac6f32c8432e75770d90483613efe96b442fbca9ca8d200447bf979f46529c7341879333a9c24a7d3fba08b53d13447618b71cf2ee4734e82f96e1c'
let signature_1 = hex::decode("fa8d61da2e0ac6f32c8432e75770d90483613efe96b442fbca9ca8d200447bf979f46529c7341879333a9c24a7d3fba08b53d13447618b71cf2ee4734e82f96e1c").unwrap();
// > await (new ethers.Wallet('0x4a599de3915f404d84a2ebe522bfe7032ebb1ca76a65b55d6eb212b129043a0e')).signMessage(ethers.utils.arrayify('0x3fd308215a20af20b137372f8a69fd336ebf93d57d4076a7c46e13f315255257'))
// '0x0c189e25dea6bb93292af16fd0516f3adc8a19556714c0b8d624016175bebcba7a5fe8218dad6fc86faeb8104fad8390ccdec989d992e852553ea6b61fbb2eda1b'
let signature_1 = hex::decode("0c189e25dea6bb93292af16fd0516f3adc8a19556714c0b8d624016175bebcba7a5fe8218dad6fc86faeb8104fad8390ccdec989d992e852553ea6b61fbb2eda1b").unwrap();
// Validator 2:
// Address: 0x28b8d0E2bBfeDe9071F8Ff3DaC9CcE3d3176DBd3
// Private Key: 0x2cc76d56db9924ddc3388164454dfea9edd2d5f5da81102fd3594fc7c5281515
let validator_2 = H160::from_str("0x28b8d0E2bBfeDe9071F8Ff3DaC9CcE3d3176DBd3").unwrap();
// > await (new ethers.Wallet('0x2cc76d56db9924ddc3388164454dfea9edd2d5f5da81102fd3594fc7c5281515')).signMessage(ethers.utils.arrayify('0x5c2b4cbafdd7319d9a3dea4aa078748ae53368e1521494a80cf4259b9c6dba6a'))
// '0x6c60c3744f6bf779017b8ab9aa8eed60bf53c317cf4d74d765015cc0a7dcad783dee39334bfc7e4baab944355914e7431e4286df8c0557a0c1f6ba867677da421b'
let signature_2 = hex::decode("6c60c3744f6bf779017b8ab9aa8eed60bf53c317cf4d74d765015cc0a7dcad783dee39334bfc7e4baab944355914e7431e4286df8c0557a0c1f6ba867677da421b").unwrap();
// > await (new ethers.Wallet('0x2cc76d56db9924ddc3388164454dfea9edd2d5f5da81102fd3594fc7c5281515')).signMessage(ethers.utils.arrayify('0x3fd308215a20af20b137372f8a69fd336ebf93d57d4076a7c46e13f315255257'))
// '0x5493449e8a09c1105195ecf913997de51bd50926a075ad98fe3e845e0a11126b5212a2cd1afdd35a44322146d31f8fa3d179d8a9822637d8db0e2fa8b3d292421b'
let signature_2 = hex::decode("5493449e8a09c1105195ecf913997de51bd50926a075ad98fe3e845e0a11126b5212a2cd1afdd35a44322146d31f8fa3d179d8a9822637d8db0e2fa8b3d292421b").unwrap();
MultisigIsmTestData {
message,

@ -8,12 +8,13 @@ use hyperlane_sealevel_token_lib::{
accounts::HyperlaneToken, message::TokenMessage, processor::HyperlaneSealevelTokenPlugin,
};
use serializable_account_meta::SerializableAccountMeta;
#[cfg(not(target_arch = "sbf"))]
use solana_program::program_pack::Pack as _;
use solana_program::{
account_info::{next_account_info, AccountInfo},
instruction::AccountMeta,
program::{invoke, invoke_signed},
program_error::ProgramError,
program_pack::Pack as _,
pubkey::Pubkey,
rent::Rent,
sysvar::Sysvar,
@ -74,6 +75,13 @@ impl SizedData for SyntheticPlugin {
}
impl SyntheticPlugin {
/// The size of the mint account.
// Need to hardcode this value because our `spl_token_2022` version doesn't include it.
// It was calculated by calling `ExtensionType::try_calculate_account_len::<Mint>(vec![ExtensionType::MetadataPointer]).unwrap()`
#[cfg(target_arch = "sbf")]
const MINT_ACCOUNT_SIZE: usize = 234;
/// The size of the mint account.
#[cfg(not(target_arch = "sbf"))]
const MINT_ACCOUNT_SIZE: usize = spl_token_2022::state::Mint::LEN;
/// Returns Ok(()) if the mint account info is valid.

@ -249,7 +249,7 @@ fn verify(
merkle_tree_hook_address: metadata.origin_merkle_tree_hook,
mailbox_domain: message.origin,
root: metadata.merkle_root,
index: message.nonce,
index: metadata.merkle_index,
},
message_id: message.id(),
},

@ -16,7 +16,7 @@ use crate::AGENT_BIN_PATH;
/// The Solana CLI tool version to download and use.
const SOLANA_CLI_VERSION: &str = "1.14.20";
const SOLANA_PROGRAM_LIBRARY_ARCHIVE: &str =
"https://github.com/hyperlane-xyz/solana-program-library/releases/download/2023-07-27-01/spl.tar.gz";
"https://github.com/hyperlane-xyz/solana-program-library/releases/download/2024-08-23/spl.tar.gz";
// Solana program tuples of:
// 0: Solana address or keypair for the bpf program

@ -164,8 +164,8 @@
"decimals": 9
},
"solana": {
"amount": "0.001",
"decimals": 9
"amount": "0.5",
"decimals": 1
},
"taiko": {
"amount": "0.050000001",

@ -1,11 +1,8 @@
{
"ancient8": {
"HypERC20": "0x97423A68BAe94b5De52d767a17aBCc54c157c0E5",
"synthetic": "0x97423A68BAe94b5De52d767a17aBCc54c157c0E5"
},
"ethereum": {
"HypERC20Collateral": "0x8b4192B9Ad1fCa440A5808641261e5289e6de95D",
"collateral": "0x8b4192B9Ad1fCa440A5808641261e5289e6de95D",
"proxyAdmin": "0x7d0c8b23c5b35091972023ccc689cfedcd881c7d"
"HypERC20Collateral": "0x1D622da2ce4C4D9D4B0611718cb3BcDcAd008DD4",
"collateral": "0x1D622da2ce4C4D9D4B0611718cb3BcDcAd008DD4",
"proxyAdmin": "0xe41a3270875f28A03312877cD95A01e9a53664b1",
"timelockController": "0x0000000000000000000000000000000000000000"
}
}

@ -0,0 +1,34 @@
import { ethers } from 'ethers';
import {
ChainMap,
RouterConfig,
TokenRouterConfig,
TokenType,
} from '@hyperlane-xyz/sdk';
import { DEPLOYER } from '../../owners.js';
export const getEthereumSolanaPzETHWarpConfig = async (
routerConfig: ChainMap<RouterConfig>,
): Promise<ChainMap<TokenRouterConfig>> => {
// @ts-ignore - foreignDeployment configs don't conform to the TokenRouterConfig
const solana: TokenRouterConfig = {
type: TokenType.synthetic,
foreignDeployment: 'GiP8GwN1GsscVJvmKSD4muDEihRzZRa9mxnS1Toi64pa',
gas: 300_000,
};
const ethereum: TokenRouterConfig = {
...routerConfig.ethereum,
type: TokenType.collateral,
interchainSecurityModule: ethers.constants.AddressZero,
token: '0x8c9532a60e0e7c6bbd2b2c1303f63ace1c3e9811',
owner: DEPLOYER,
};
return {
solana,
ethereum,
};
};

@ -111,6 +111,25 @@
"constructorArguments": "0000000000000000000000007d0c8b23c5b35091972023ccc689cfedcd881c7d000000000000000000000000ed96482bea3c51a33b4c1ada8b438e33a236741300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b00000000000000000000000019b2cf952b70b217c90fc408714fbc1acd29a6a8000000000000000000000000d17b4100cc66a2f1b9a452007ff26365aaeb7ec3000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000",
"isProxy": true,
"name": "TransparentUpgradeableProxy"
},
{
"address": "0xe41a3270875f28A03312877cD95A01e9a53664b1",
"constructorArguments": "",
"isProxy": false,
"name": "ProxyAdmin"
},
{
"address": "0xbE6501A4E68a3463A217eC0dEc862b1593C0A47D",
"constructorArguments": "0000000000000000000000008c9532a60e0e7c6bbd2b2c1303f63ace1c3e9811000000000000000000000000c005dc82818d67af737725bd4bf75435d065d239",
"isProxy": false,
"name": "HypERC20Collateral"
},
{
"address": "0x1D622da2ce4C4D9D4B0611718cb3BcDcAd008DD4",
"constructorArguments": "000000000000000000000000be6501a4e68a3463a217ec0dec862b1593c0a47d000000000000000000000000e41a3270875f28a03312877cd95a01e9a53664b100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000",
"expectedimplementation": "0xbE6501A4E68a3463A217eC0dEc862b1593C0A47D",
"isProxy": true,
"name": "TransparentUpgradeableProxy"
}
],
"inevm": [

@ -13,6 +13,7 @@ import { getArbitrumNeutronEclipWarpConfig } from './environments/mainnet3/warp/
import { getArbitrumNeutronTiaWarpConfig } from './environments/mainnet3/warp/configGetters/getArbitrumNeutronTiaWarpConfig.js';
import { getEthereumInevmUSDCWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumInevmUSDCWarpConfig.js';
import { getEthereumInevmUSDTWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumInevmUSDTWarpConfig.js';
import { getEthereumSolanaPzETHWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumSolanaPzETHWarpConfig.js';
import { getEthereumVictionETHWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumVictionETHWarpConfig.js';
import { getEthereumVictionUSDCWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumVictionUSDCWarpConfig.js';
import { getEthereumVictionUSDTWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumVictionUSDTWarpConfig.js';
@ -27,6 +28,7 @@ export enum WarpRouteIds {
ArbitrumNeutronTIA = 'TIA/arbitrum-neutron',
EthereumInevmUSDC = 'USDC/ethereum-inevm',
EthereumInevmUSDT = 'USDT/ethereum-inevm',
EthereumSolanaPzETH = 'pzETH/ethereum-solana',
EthereumVictionETH = 'ETH/ethereum-viction',
EthereumVictionUSDC = 'USDC/ethereum-viction',
EthereumVictionUSDT = 'USDT/ethereum-viction',
@ -56,6 +58,7 @@ export const warpConfigGetterMap: Record<
[WarpRouteIds.EthereumVictionUSDC]: getEthereumVictionUSDCWarpConfig,
[WarpRouteIds.EthereumVictionUSDT]: getEthereumVictionUSDTWarpConfig,
[WarpRouteIds.MantapacificNeutronTIA]: getMantapacificNeutronTiaWarpConfig,
[WarpRouteIds.EthereumSolanaPzETH]: getEthereumSolanaPzETHWarpConfig,
};
export async function getWarpConfig(

@ -126,7 +126,7 @@ export async function estimateTransactionFeeSolanaWeb3({
const { value } = await connection.simulateTransaction(
transaction.transaction,
);
assert(!value.err, `Solana gas estimation failed: ${value.err}`);
assert(!value.err, `Solana gas estimation failed: ${JSON.stringify(value)}`);
const gasUnits = BigInt(value.unitsConsumed || 0);
const recentFees = await connection.getRecentPrioritizationFees();
const gasPrice = BigInt(recentFees[0].prioritizationFee);

@ -40,7 +40,7 @@ export abstract class GasRouterDeployer<
const remoteConfigs = remoteDomains
.map((domain, i) => ({
domain,
gas: configMap[remoteChains[i]].gas ?? DEFAULT_GAS_OVERHEAD,
gas: configMap[remoteChains[i]]?.gas ?? DEFAULT_GAS_OVERHEAD,
}))
.filter(({ gas }, index) => !currentConfigs[index].eq(gas));
if (remoteConfigs.length == 0) {

Loading…
Cancel
Save