Skip to content

Commit

Permalink
feat: Add new Sonic tokens, some SVM tooling drive-bys (#5334)
Browse files Browse the repository at this point in the history
### Description

Drive-bys for the SVM tooling:
- make sure that metadata for an SVM synthetic matches some basic
expectations, e.g.
  - points to a legit machine readable URL (we had an issue here once)
  - image is a valid URL that's queryable
  - symbol / name match the token config (we had an issue here once)
- No longer require explicitly specifying if a collateral token is SPL
Token or SPL Token 2022. We imply this instead by looking at the owner
of the collateral mint. Removes this from all previous configs

And adds the following tokens between Solana and Sonic: sSOL, USDStar,
USDC, USDT, SONIC

### 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
-->
  • Loading branch information
tkporter authored Feb 18, 2025
1 parent 4ddbd60 commit 1f559c3
Show file tree
Hide file tree
Showing 26 changed files with 304 additions and 50 deletions.
2 changes: 1 addition & 1 deletion .registryrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
26e513e8fe567428cb58e7634e0ed8c659430707
954b3b2982c7fe712b47bdc510b1414b3f27d2d7
16 changes: 16 additions & 0 deletions rust/sealevel/client/src/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ pub struct ChainMetadata {
name: String,
/// Collection of RPC endpoints
rpc_urls: Vec<RpcUrlConfig>,
pub is_testnet: Option<bool>,
}

impl ChainMetadata {
Expand Down Expand Up @@ -215,6 +216,16 @@ pub(crate) trait RouterDeployer<Config: RouterConfigGetter + std::fmt::Debug>:
program_id
}

fn verify_config(
&self,
_ctx: &mut Context,
_app_configs: &HashMap<String, Config>,
_app_configs_to_deploy: &HashMap<&String, &Config>,
_chain_configs: &HashMap<String, ChainMetadata>,
) {
// By default, do nothing.
}

fn init_program_idempotent(
&self,
ctx: &mut Context,
Expand Down Expand Up @@ -358,6 +369,11 @@ pub(crate) fn deploy_routers<
.filter(|(_, app_config)| app_config.router_config().foreign_deployment.is_none())
.collect::<HashMap<_, _>>();

// Verify the configuration.
println!("Verifying configuration...");
deployer.verify_config(ctx, &app_configs, &app_configs_to_deploy, &chain_configs);
println!("Configuration successfully verified!");

warp_route::install_spl_token_cli();

// Now we deploy to chains that don't have a foreign deployment
Expand Down
140 changes: 108 additions & 32 deletions rust/sealevel/client/src/warp_route.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ use std::{
process::{Command, Stdio},
};

use solana_client::{client_error::ClientError, rpc_client::RpcClient};
use solana_client::{
client_error::{reqwest, ClientError},
rpc_client::RpcClient,
};

use solana_sdk::{instruction::Instruction, program_error::ProgramError, pubkey::Pubkey};

Expand All @@ -18,7 +21,7 @@ use hyperlane_sealevel_connection_client::{
};
use hyperlane_sealevel_igp::accounts::InterchainGasPaymasterType;
use hyperlane_sealevel_token::{
hyperlane_token_mint_pda_seeds, plugin::SyntheticPlugin, spl_token, spl_token_2022,
hyperlane_token_mint_pda_seeds, plugin::SyntheticPlugin, spl_token_2022,
};
use hyperlane_sealevel_token_lib::{
accounts::{HyperlaneToken, HyperlaneTokenAccount},
Expand All @@ -39,6 +42,45 @@ use crate::{
Context, TokenType as FlatTokenType, WarpRouteCmd, WarpRouteSubCmd,
};

#[derive(Debug, Deserialize, Serialize, Clone)]
struct SplTokenOffchainMetadata {
name: String,
symbol: String,
description: Option<String>,
image: Option<String>,
website: Option<String>,
// Array of key-value pairs
attributes: Option<Vec<(String, String)>>,
}

impl SplTokenOffchainMetadata {
fn validate(&self) {
assert!(!self.name.is_empty(), "Name must not be empty");
assert!(
!self.symbol.is_empty(),
"Symbol must not be empty for token with name: {}",
self.name
);
assert!(
self.description.is_some(),
"Description must be provided for token with name: {}",
self.name
);
assert!(
self.image.is_some(),
"Image must be provided for token with name: {}",
self.name
);
let image_url = self.image.as_ref().unwrap();
let image = reqwest::blocking::get(image_url).unwrap();
assert!(
image.status().is_success(),
"Image URL must return a successful status code, url: {}",
image_url,
);
}
}

/// Configuration relating to decimals.
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
Expand Down Expand Up @@ -85,28 +127,11 @@ struct TokenMetadata {
uri: Option<String>,
}

#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
enum SplTokenProgramType {
Token,
Token2022,
}

impl SplTokenProgramType {
fn program_id(&self) -> Pubkey {
match &self {
SplTokenProgramType::Token => spl_token::id(),
SplTokenProgramType::Token2022 => spl_token_2022::id(),
}
}
}

#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
struct CollateralInfo {
#[serde(rename = "token")]
mint: String,
spl_token_program: Option<SplTokenProgramType>,
}

#[derive(Debug, Deserialize, Serialize, Clone)]
Expand Down Expand Up @@ -321,20 +346,24 @@ impl RouterDeployer<TokenConfig> for WarpRouteDeployer {
.unwrap(),
)
}
TokenType::Collateral(collateral_info) => ctx.new_txn().add(
hyperlane_sealevel_token_collateral::instruction::init_instruction(
program_id,
ctx.payer_pubkey,
init,
collateral_info
.spl_token_program
.as_ref()
.expect("Cannot initialize collateral warp route without SPL token program")
.program_id(),
collateral_info.mint.parse().expect("Invalid mint address"),
TokenType::Collateral(collateral_info) => {
let collateral_mint = collateral_info.mint.parse().expect("Invalid mint address");
let collateral_mint_account = client.get_account(&collateral_mint).unwrap();
// The owner of the mint account is the SPL Token program responsible for it
// (either spl-token or spl-token-2022).
let collateral_spl_token_program = collateral_mint_account.owner;

ctx.new_txn().add(
hyperlane_sealevel_token_collateral::instruction::init_instruction(
program_id,
ctx.payer_pubkey,
init,
collateral_spl_token_program,
collateral_mint,
)
.unwrap(),
)
.unwrap(),
),
}
}
.with_client(client)
.send_with_payer();
Expand Down Expand Up @@ -408,6 +437,53 @@ impl RouterDeployer<TokenConfig> for WarpRouteDeployer {
try_fund_ata_payer(ctx, client);
}

fn verify_config(
&self,
_ctx: &mut Context,
_app_configs: &HashMap<String, TokenConfig>,
app_configs_to_deploy: &HashMap<&String, &TokenConfig>,
chain_configs: &HashMap<String, ChainMetadata>,
) {
// We only have validations for SVM tokens at the moment.
for (chain, config) in app_configs_to_deploy.iter() {
if let TokenType::Synthetic(synthetic) = &config.token_type {
// Verify that the metadata URI provided points to a valid JSON file.
let metadata_uri = match synthetic.uri.as_ref() {
Some(uri) => uri,
None => {
if chain_configs
.get(*chain)
.unwrap()
.is_testnet
.unwrap_or(false)
{
// Skip validation for testnet chain
println!(
"Skipping metadata URI validation for testnet chain: {}",
chain
);
continue;
}
panic!("URI not provided for token: {}", chain);
}
};
println!("Validating metadata URI: {}", metadata_uri);
let metadata_response = reqwest::blocking::get(metadata_uri).unwrap();
let metadata_contents: SplTokenOffchainMetadata = metadata_response
.json()
.expect("Failed to parse metadata JSON");
metadata_contents.validate();

// Ensure that the metadata contents match the provided token config.
assert_eq!(metadata_contents.name, synthetic.name, "Name mismatch");
assert_eq!(
metadata_contents.symbol, synthetic.symbol,
"Symbol mismatch"
);
}
}
}

/// Sets gas router configs on all deployable chains.
fn post_deploy(
&self,
Expand Down
2 changes: 2 additions & 0 deletions rust/sealevel/environments/local-e2e/chain-config.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"sealeveltest1": {
"chainId": 13375,
"isTestnet": true,
"name": "sealeveltest1",
"rpcUrls": [
{
Expand All @@ -10,6 +11,7 @@
},
"sealeveltest2": {
"chainId": 13376,
"isTestnet": true,
"name": "sealeveltest2",
"rpcUrls": [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
"type": "collateral",
"decimals": 5,
"interchainGasPaymaster": "AkeHBbE5JkwVppujCQQ6WuxsVsJtruBAjUo6fDCFp6fF",
"token": "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
"splTokenProgram": "token"
"token": "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263"
},
"soon": {
"type": "synthetic",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"base": {
"hex": "0x00000000000000000000000022fd11f93f0303346c9b9070cc67c4bc7ab2dabb",
"base58": "111111111111VGnE2fqhvnn7b54X4ZEzKE9v28S"
},
"solanamainnet": {
"hex": "0xbf628e576bb2c600fb6934812c6a630c420e22625ad60b635b28fc867877d344",
"base58": "Dt62xRfWf7h6cURDsr19tqGWhjHMdobNVQ25cfHtZXg7"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"base": {
"type": "collateral",
"decimals": 18,
"token": "0xC0D3700000c0e32716863323bFd936b54a1633d1",
"foreignDeployment": "0x22Fd11F93F0303346c9b9070cc67C4Bc7aB2dABB"
},
"solanamainnet": {
"type": "synthetic",
"decimals": 9,
"remoteDecimals": 18,
"interchainGasPaymaster": "AkeHBbE5JkwVppujCQQ6WuxsVsJtruBAjUo6fDCFp6fF",
"name": "Cod3x Token",
"symbol": "CDX",
"uri": "https://raw.githubusercontent.com/hyperlane-xyz/hyperlane-registry/fef8869f17249bd561235e7e118ee5293b410fc3/deployments/warp_routes/CDX/metadata.json"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
"type": "collateral",
"decimals": 6,
"interchainGasPaymaster": "AkeHBbE5JkwVppujCQQ6WuxsVsJtruBAjUo6fDCFp6fF",
"token": "orcaEKTdK7LKz57vaAYr9QeNsVEPfiu6QeMU1kektZE",
"splTokenProgram": "token"
"token": "orcaEKTdK7LKz57vaAYr9QeNsVEPfiu6QeMU1kektZE"
},
"eclipsemainnet": {
"type": "synthetic",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"solanamainnet": {
"hex": "0x185ca10cb892c8fbc6857a218f9071ed3d860de9cf81eaf4aac68022f34fe57b",
"base58": "2e6hyJbUpbhqbJzorDZ1m4QVTj5oPhsn2H3KBaMFVXAz"
},
"sonicsvm": {
"hex": "0x4857253b685b7aead0442c6566a83b93b28eb0125adeb490a19ef0010e8e34ad",
"base58": "5sPRiRLfmohVmtkgiGV6scrzy2C6qEZRGRUiePZf1Fs2"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"solanamainnet": {
"type": "collateral",
"decimals": 9,
"interchainGasPaymaster": "AkeHBbE5JkwVppujCQQ6WuxsVsJtruBAjUo6fDCFp6fF",
"interchainSecurityModule": "NtVfGz6mMXe17Jy8Mt8pvStgwFbGKHkSvxPeWn1FMNu",
"token": "SonicxvLud67EceaEzCLRnMTBqzYUUYNr93DBkBdDES"
},
"sonicsvm": {
"type": "synthetic",
"decimals": 9,
"name": "Sonic SVM",
"symbol": "SONIC",
"uri": "https://raw.githubusercontent.com/hyperlane-xyz/hyperlane-registry/63ae6c0a0415d480c00880e64ec8a9c3724b4e37/deployments/warp_routes/SONIC/metadata.json",
"interchainGasPaymaster": "7VResHbw6jRVUa8qfD6e1cbzGmErcLGwgx4o7mLhZief"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
"decimals": 6,
"remoteDecimals": 18,
"token": "6p6xgHyF7AeE6TZkSmFsko444wqoP15icUSqi2jfGiPN",
"interchainGasPaymaster": "AkeHBbE5JkwVppujCQQ6WuxsVsJtruBAjUo6fDCFp6fF",
"splTokenProgram": "token"
"interchainGasPaymaster": "AkeHBbE5JkwVppujCQQ6WuxsVsJtruBAjUo6fDCFp6fF"
},
"base": {
"type": "synthetic",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
"decimals": 6,
"remoteDecimals": 18,
"token": "6p6xgHyF7AeE6TZkSmFsko444wqoP15icUSqi2jfGiPN",
"interchainGasPaymaster": "AkeHBbE5JkwVppujCQQ6WuxsVsJtruBAjUo6fDCFp6fF",
"splTokenProgram": "token"
"interchainGasPaymaster": "AkeHBbE5JkwVppujCQQ6WuxsVsJtruBAjUo6fDCFp6fF"
},
"trumpchain": {
"type": "native",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"solanamainnet": {
"hex": "0x78f010d70282133383abb61fa5d2c35274fceec9485b898ddb4c6bed2eeb61d0",
"base58": "996EsR8a7MC6odE6j7sjsArmDQVqB96qWu84wHdthahm"
},
"sonicsvm": {
"hex": "0xa246507f376539e22612cb216b549c958fc9a8eaa470ef9d5320e40493adc81e",
"base58": "BvTEYSqCcfMwpAriC1vDpNhzR1JWzasRFfCPCdgsYaCd"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"solanamainnet": {
"type": "collateral",
"decimals": 6,
"interchainGasPaymaster": "AkeHBbE5JkwVppujCQQ6WuxsVsJtruBAjUo6fDCFp6fF",
"interchainSecurityModule": "NtVfGz6mMXe17Jy8Mt8pvStgwFbGKHkSvxPeWn1FMNu",
"token": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
},
"sonicsvm": {
"type": "synthetic",
"decimals": 6,
"name": "USD Coin",
"symbol": "USDC",
"uri": "https://raw.githubusercontent.com/hyperlane-xyz/hyperlane-registry/63ae6c0a0415d480c00880e64ec8a9c3724b4e37/deployments/warp_routes/USDC/metadata.json",
"interchainGasPaymaster": "7VResHbw6jRVUa8qfD6e1cbzGmErcLGwgx4o7mLhZief"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"sonicsvm": {
"hex": "0x4c41c164c94a75760556a8c11d213ca882e00da697e50d004b27a462ec3451c6",
"base58": "68g96Cq16vKAqyG79BFVeJCgitjK9fQ1NRCRmcPkJtDB"
},
"solanamainnet": {
"hex": "0xd642f2e1c4e448fcc87316a4592673309fdcafa012c5f6b005e1bae508cb4d2b",
"base58": "FRPTWpmnNPm4LS2a5NUL1QenUrzGDAuifZBUi1mTWTdt"
}
}
Loading

0 comments on commit 1f559c3

Please # to comment.