commit
4fe269dc01
@ -1,5 +0,0 @@ |
||||
--- |
||||
'@hyperlane-xyz/sdk': minor |
||||
--- |
||||
|
||||
Add ether's error reasoning handling to SmartProvider to show clearer error messages |
@ -0,0 +1,5 @@ |
||||
--- |
||||
'@hyperlane-xyz/cli': minor |
||||
--- |
||||
|
||||
re-enable space key for multiselect cli prompt |
@ -0,0 +1,5 @@ |
||||
--- |
||||
'@hyperlane-xyz/sdk': patch |
||||
--- |
||||
|
||||
Optimize HyperlaneRelayer routing config derivation |
@ -0,0 +1,6 @@ |
||||
--- |
||||
'@hyperlane-xyz/sdk': minor |
||||
'@hyperlane-xyz/core': minor |
||||
--- |
||||
|
||||
Checking for sufficient fees in `AbstractMessageIdAuthHook` and refund surplus |
@ -1,8 +0,0 @@ |
||||
--- |
||||
'@hyperlane-xyz/helloworld': minor |
||||
'@hyperlane-xyz/widgets': minor |
||||
'@hyperlane-xyz/infra': minor |
||||
'@hyperlane-xyz/cli': minor |
||||
--- |
||||
|
||||
Update to registry v2.5.0 |
@ -1,5 +0,0 @@ |
||||
--- |
||||
'@hyperlane-xyz/sdk': minor |
||||
--- |
||||
|
||||
Deploy to arbitrumsepolia, basesepolia, ecotestnet, optimismsepolia, polygonamoy |
@ -0,0 +1,6 @@ |
||||
--- |
||||
'@hyperlane-xyz/utils': patch |
||||
'@hyperlane-xyz/sdk': patch |
||||
--- |
||||
|
||||
Dedupe internals of hook and ISM module deploy code |
@ -1,5 +0,0 @@ |
||||
--- |
||||
'@hyperlane-xyz/cli': patch |
||||
--- |
||||
|
||||
Require at least 1 chain selection in warp init |
@ -1,5 +0,0 @@ |
||||
--- |
||||
'@hyperlane-xyz/sdk': minor |
||||
--- |
||||
|
||||
Deploy to zircuit |
@ -1,6 +0,0 @@ |
||||
--- |
||||
'@hyperlane-xyz/cli': patch |
||||
'@hyperlane-xyz/sdk': patch |
||||
--- |
||||
|
||||
feat: Add long-running CLI relayer |
@ -0,0 +1,5 @@ |
||||
--- |
||||
'@hyperlane-xyz/sdk': minor |
||||
--- |
||||
|
||||
Deploy to apechain, arbitrumnova, b3, fantom, gravity, harmony, kaia, morph, orderly, snaxchain, zeronetwork, zksync. Update default metadata in `HyperlaneCore` to `0x00001` to ensure empty metadata does not break on zksync. |
@ -1,7 +0,0 @@ |
||||
--- |
||||
'@hyperlane-xyz/cli': minor |
||||
'@hyperlane-xyz/sdk': minor |
||||
'@hyperlane-xyz/core': minor |
||||
--- |
||||
|
||||
Added SDK support for ArbL2ToL1Hook/ISM for selfrelay |
@ -0,0 +1,5 @@ |
||||
--- |
||||
'@hyperlane-xyz/utils': patch |
||||
--- |
||||
|
||||
fix median utils func + add test |
@ -0,0 +1,6 @@ |
||||
--- |
||||
'@hyperlane-xyz/cli': minor |
||||
'@hyperlane-xyz/sdk': minor |
||||
--- |
||||
|
||||
Add feat to allow updates to destination gas using warp apply |
@ -0,0 +1,5 @@ |
||||
--- |
||||
'@hyperlane-xyz/core': minor |
||||
--- |
||||
|
||||
Added msg.value to preverifyMessage to commit it as part of external hook payload |
@ -0,0 +1,6 @@ |
||||
--- |
||||
'@hyperlane-xyz/cli': minor |
||||
'@hyperlane-xyz/sdk': minor |
||||
--- |
||||
|
||||
Add optional proxy admin reuse in warp route deployments and admin proxy ownership transfer in warp apply |
@ -1,5 +0,0 @@ |
||||
--- |
||||
'@hyperlane-xyz/sdk': minor |
||||
--- |
||||
|
||||
Deploy to solana + eclipse |
@ -0,0 +1,11 @@ |
||||
--- |
||||
'@hyperlane-xyz/widgets': minor |
||||
--- |
||||
|
||||
Update widgets with components from explorer and warp ui |
||||
|
||||
- Add icons: Discord, Docs, Github, History, LinkedIn, Medium, Twitter, Wallet and Web |
||||
- Add animation component: Fade component |
||||
- Add components: DatetimeField and SelectField |
||||
- New stories: IconList and Fade |
||||
- Add "Icon" suffix for icons that did not have it |
@ -1,5 +0,0 @@ |
||||
--- |
||||
'@hyperlane-xyz/cli': minor |
||||
--- |
||||
|
||||
Add output of hyperlane warp read to ./configs/warp-route-deployment.yaml |
@ -0,0 +1,5 @@ |
||||
--- |
||||
'@hyperlane-xyz/widgets': patch |
||||
--- |
||||
|
||||
- Update ChainSearchMenu with improvements |
@ -0,0 +1,5 @@ |
||||
--- |
||||
'@hyperlane-xyz/sdk': minor |
||||
--- |
||||
|
||||
Update default validator sets. Throw in `InterchainAccount.getOrDeployAccount` if the origin router is the zero address. |
@ -1,5 +0,0 @@ |
||||
--- |
||||
'@hyperlane-xyz/core': patch |
||||
--- |
||||
|
||||
fix: only evaluate dynamic revert reasons in reverting branch |
@ -1,6 +0,0 @@ |
||||
--- |
||||
'@hyperlane-xyz/sdk': minor |
||||
'@hyperlane-xyz/core': minor |
||||
--- |
||||
|
||||
Added yield route with yield going to message recipient. |
@ -0,0 +1,6 @@ |
||||
--- |
||||
'@hyperlane-xyz/cli': minor |
||||
'@hyperlane-xyz/sdk': minor |
||||
--- |
||||
|
||||
Add `hyperlane warp verify` to allow post-deployment verification. |
@ -1,5 +0,0 @@ |
||||
--- |
||||
'@hyperlane-xyz/core': minor |
||||
--- |
||||
|
||||
feat: attributable fraud for signers |
@ -0,0 +1,5 @@ |
||||
--- |
||||
'@hyperlane-xyz/core': minor |
||||
--- |
||||
|
||||
disabled the ICARouter's ability to change hook given that the user doesn't expect the hook to change after they deploy their ICA account. Hook is not part of the derivation like ism on the destination chain and hence, cannot be configured custom by the user. |
@ -0,0 +1,5 @@ |
||||
--- |
||||
'@hyperlane-xyz/cli': minor |
||||
--- |
||||
|
||||
Enable configuration of IGP hooks in the CLI |
@ -1,5 +0,0 @@ |
||||
--- |
||||
'@hyperlane-xyz/cli': minor |
||||
--- |
||||
|
||||
Remove registry.getUri() from core read logging to prevent registry error |
@ -0,0 +1,5 @@ |
||||
--- |
||||
'@hyperlane-xyz/sdk': patch |
||||
--- |
||||
|
||||
Fix ICA ISM self relay |
@ -0,0 +1,5 @@ |
||||
--- |
||||
'@hyperlane-xyz/sdk': minor |
||||
--- |
||||
|
||||
Introduce utils that can be reused by the CLI and Infra for fetching token prices from Coingecko and gas prices from EVM/Cosmos chains. |
@ -0,0 +1,5 @@ |
||||
--- |
||||
'@hyperlane-xyz/utils': patch |
||||
--- |
||||
|
||||
Filter undefined/null values in invertKeysAndValues function |
@ -1,5 +0,0 @@ |
||||
--- |
||||
'@hyperlane-xyz/core': minor |
||||
--- |
||||
|
||||
Implement checkpoint fraud proofs for use in slashing |
@ -1,5 +0,0 @@ |
||||
--- |
||||
'@hyperlane-xyz/sdk': minor |
||||
--- |
||||
|
||||
Supprt passing foreignDeployments to HypERC20App constructor |
@ -1,5 +0,0 @@ |
||||
--- |
||||
'@hyperlane-xyz/cli': minor |
||||
--- |
||||
|
||||
Add check & confirm for existing mailbox to core deploy to allow users to decide if they want to deploy a new mailbox |
@ -1,5 +1,5 @@ |
||||
[codespell] |
||||
skip = .git,node_modules,yarn.lock,Cargo.lock,./typescript/helloworld,./rust/config |
||||
skip = .git,node_modules,yarn.lock,Cargo.lock,./typescript/helloworld,./rust/main/config,./rust/sealevel/environments/mainnet3/chain-config.json |
||||
count = |
||||
quiet-level = 3 |
||||
ignore-words = ./.codespell/ignore.txt |
||||
|
@ -1,4 +1,4 @@ |
||||
typescript/sdk/src/cw-types/*.types.ts linguist-generated=true |
||||
rust/chains/hyperlane-ethereum/abis/*.abi.json linguist-generated=true |
||||
rust/main/chains/hyperlane-ethereum/abis/*.abi.json linguist-generated=true |
||||
solidity/contracts/interfaces/avs/*.sol linguist-vendored=true |
||||
solidity/contracts/avs/ECDSA*.sol linguist-vendored=true |
||||
|
@ -1,29 +1,22 @@ |
||||
# File extension owners |
||||
|
||||
*.sol @yorhodes @tkporter @aroralanuk @nbayindirli |
||||
*.ts @yorhodes @jmrossy @nbayindirli |
||||
*.rs @tkporter @daniel-savu |
||||
*.md @Skunkchain @avious00 |
||||
*.sol @yorhodes @aroralanuk @ltyu |
||||
*.ts @yorhodes @jmrossy |
||||
*.rs @tkporter @daniel-savu @ameten |
||||
|
||||
# Package owners |
||||
|
||||
## Contracts |
||||
solidity/ @yorhodes @tkporter @aroralanuk @nbayindirli |
||||
solidity/ @yorhodes @tkporter @aroralanuk @ltyu |
||||
|
||||
## Agents |
||||
rust/ @tkporter @daniel-savu |
||||
|
||||
## SDK |
||||
typescript/sdk @yorhodes @jmrossy |
||||
|
||||
## Token |
||||
typescript/token @yorhodes @jmrossy @tkporter @aroralanuk @nbayindirli |
||||
|
||||
## Hello World |
||||
typescript/helloworld @yorhodes |
||||
typescript/sdk @yorhodes @jmrossy @ltyu @paulbalaji |
||||
|
||||
## CLI |
||||
typescript/cli @jmrossy @yorhodes @aroralanuk @nbayindirli |
||||
typescript/cli @jmrossy @yorhodes @ltyu |
||||
|
||||
## Infra |
||||
typescript/infra @tkporter |
||||
typescript/infra @tkporter @paulbalaji @Mo-Hussain |
||||
|
@ -0,0 +1,37 @@ |
||||
name: 'Yarn Build with Cache' |
||||
description: 'Run yarn build using yarn cache' |
||||
|
||||
inputs: |
||||
ref: |
||||
description: 'The Git ref to checkout' |
||||
required: true |
||||
|
||||
runs: |
||||
using: "composite" |
||||
steps: |
||||
- name: Cache |
||||
uses: buildjet/cache@v4 |
||||
id: cache |
||||
with: |
||||
path: | |
||||
**/node_modules |
||||
.yarn |
||||
key: ${{ runner.os }}-yarn-cache-${{ hashFiles('./yarn.lock') }} |
||||
|
||||
# Typically, the cache will be hit, but if there's a network error when |
||||
# restoring the cache, let's run the install step ourselves. |
||||
- name: Install dependencies |
||||
if: steps.cache.outputs.cache-hit != 'true' |
||||
shell: bash |
||||
run: | |
||||
yarn install |
||||
CHANGES=$(git status -s --ignore-submodules) |
||||
if [[ ! -z $CHANGES ]]; then |
||||
echo "Changes found: $CHANGES" |
||||
git diff |
||||
exit 1 |
||||
fi |
||||
|
||||
- name: Build |
||||
shell: bash |
||||
run: yarn build |
@ -1,106 +0,0 @@ |
||||
name: test |
||||
|
||||
on: |
||||
push: |
||||
branches: [main] |
||||
paths: |
||||
- '*.md' |
||||
- '!**/*' |
||||
pull_request: |
||||
branches: |
||||
- '*' |
||||
paths: |
||||
- '*.md' |
||||
- '!**/*' |
||||
merge_group: |
||||
|
||||
concurrency: |
||||
group: e2e-${{ github.ref }} |
||||
cancel-in-progress: ${{ github.ref_name != 'main' }} |
||||
|
||||
jobs: |
||||
yarn-install: |
||||
runs-on: ubuntu-latest |
||||
steps: |
||||
- name: Instant pass |
||||
run: echo "yarn-install job passed" |
||||
|
||||
yarn-build: |
||||
runs-on: ubuntu-latest |
||||
steps: |
||||
- name: Instant pass |
||||
run: echo "yarn-build job passed" |
||||
|
||||
lint-prettier: |
||||
runs-on: ubuntu-latest |
||||
steps: |
||||
- name: Instant pass |
||||
run: echo "lint-prettier job passed" |
||||
|
||||
yarn-test: |
||||
runs-on: ubuntu-latest |
||||
steps: |
||||
- name: Instant pass |
||||
run: echo "yarn-test job passed" |
||||
|
||||
agent-configs: |
||||
runs-on: ubuntu-latest |
||||
strategy: |
||||
fail-fast: false |
||||
matrix: |
||||
environment: [mainnet3, testnet4] |
||||
steps: |
||||
- name: Instant pass |
||||
run: echo "agent-configs job passed" |
||||
|
||||
e2e-matrix: |
||||
runs-on: ubuntu-latest |
||||
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.base_ref == 'main') || github.event_name == 'merge_group' |
||||
strategy: |
||||
matrix: |
||||
e2e-type: [cosmwasm, non-cosmwasm] |
||||
steps: |
||||
- name: Instant pass |
||||
run: echo "e2e-matrix job passed" |
||||
|
||||
e2e: |
||||
runs-on: ubuntu-latest |
||||
if: always() |
||||
steps: |
||||
- name: Instant pass |
||||
run: echo "e2e job passed" |
||||
|
||||
cli-advanced-e2e: |
||||
runs-on: ubuntu-latest |
||||
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.base_ref == 'main') || github.event_name == 'merge_group' |
||||
strategy: |
||||
matrix: |
||||
include: |
||||
- test-type: preset_hook_enabled |
||||
- test-type: configure_hook_enabled |
||||
- test-type: pi_with_core_chain |
||||
steps: |
||||
- name: Instant pass |
||||
run: echo "cli-advanced-e2e job passed" |
||||
|
||||
env-test: |
||||
runs-on: ubuntu-latest |
||||
strategy: |
||||
fail-fast: false |
||||
matrix: |
||||
environment: [mainnet3] |
||||
chain: [ethereum, arbitrum, optimism, inevm, viction] |
||||
module: [core, igp] |
||||
include: |
||||
- environment: testnet4 |
||||
chain: sepolia |
||||
module: core |
||||
steps: |
||||
- name: Instant pass |
||||
run: echo "env-test job passed" |
||||
|
||||
coverage: |
||||
runs-on: ubuntu-latest |
||||
steps: |
||||
- name: Instant pass |
||||
run: echo "coverage job passed" |
@ -1 +1 @@ |
||||
v2.5.0 |
||||
302be4817c063629cec70c0b02322b250df71122 |
||||
|
@ -0,0 +1,6 @@ |
||||
{ |
||||
"rust-analyzer.linkedProjects": [ |
||||
"./rust/main/Cargo.toml", |
||||
"./rust/sealevel/Cargo.toml", |
||||
], |
||||
} |
@ -1,170 +0,0 @@ |
||||
use axum::{ |
||||
extract::{Query, State}, |
||||
routing, Router, |
||||
}; |
||||
use derive_new::new; |
||||
use hyperlane_core::{ChainCommunicationError, QueueOperation, H256}; |
||||
use serde::Deserialize; |
||||
use std::str::FromStr; |
||||
use tokio::sync::broadcast::Sender; |
||||
|
||||
const MESSAGE_RETRY_API_BASE: &str = "/message_retry"; |
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)] |
||||
pub enum MessageRetryRequest { |
||||
MessageId(H256), |
||||
DestinationDomain(u32), |
||||
} |
||||
|
||||
impl PartialEq<QueueOperation> for &MessageRetryRequest { |
||||
fn eq(&self, other: &QueueOperation) -> bool { |
||||
match self { |
||||
MessageRetryRequest::MessageId(message_id) => message_id == &other.id(), |
||||
MessageRetryRequest::DestinationDomain(destination_domain) => { |
||||
destination_domain == &other.destination_domain().id() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[derive(new, Clone)] |
||||
pub struct MessageRetryApi { |
||||
tx: Sender<MessageRetryRequest>, |
||||
} |
||||
|
||||
#[derive(Deserialize)] |
||||
struct RawMessageRetryRequest { |
||||
message_id: Option<String>, |
||||
destination_domain: Option<u32>, |
||||
} |
||||
|
||||
impl TryFrom<RawMessageRetryRequest> for Vec<MessageRetryRequest> { |
||||
type Error = ChainCommunicationError; |
||||
|
||||
fn try_from(request: RawMessageRetryRequest) -> Result<Self, Self::Error> { |
||||
let mut retry_requests = Vec::new(); |
||||
if let Some(message_id) = request.message_id { |
||||
retry_requests.push(MessageRetryRequest::MessageId(H256::from_str(&message_id)?)); |
||||
} |
||||
if let Some(destination_domain) = request.destination_domain { |
||||
retry_requests.push(MessageRetryRequest::DestinationDomain(destination_domain)); |
||||
} |
||||
Ok(retry_requests) |
||||
} |
||||
} |
||||
|
||||
async fn retry_message( |
||||
State(tx): State<Sender<MessageRetryRequest>>, |
||||
Query(request): Query<RawMessageRetryRequest>, |
||||
) -> String { |
||||
let retry_requests: Vec<MessageRetryRequest> = match request.try_into() { |
||||
Ok(retry_requests) => retry_requests, |
||||
// Technically it's bad practice to print the error message to the user, but
|
||||
// this endpoint is for debugging purposes only.
|
||||
Err(err) => { |
||||
return format!("Failed to parse retry request: {}", err); |
||||
} |
||||
}; |
||||
|
||||
if retry_requests.is_empty() { |
||||
return "No retry requests found. Please provide either a message_id or destination_domain.".to_string(); |
||||
} |
||||
|
||||
if let Err(err) = retry_requests |
||||
.into_iter() |
||||
.map(|req| tx.send(req)) |
||||
.collect::<Result<Vec<_>, _>>() |
||||
{ |
||||
return format!("Failed to send retry request to the queue: {}", err); |
||||
} |
||||
|
||||
"Moved message(s) to the front of the queue".to_string() |
||||
} |
||||
|
||||
impl MessageRetryApi { |
||||
pub fn router(&self) -> Router { |
||||
Router::new() |
||||
.route("/", routing::get(retry_message)) |
||||
.with_state(self.tx.clone()) |
||||
} |
||||
|
||||
pub fn get_route(&self) -> (&'static str, Router) { |
||||
(MESSAGE_RETRY_API_BASE, self.router()) |
||||
} |
||||
} |
||||
|
||||
#[cfg(test)] |
||||
mod tests { |
||||
use crate::server::ENDPOINT_MESSAGES_QUEUE_SIZE; |
||||
|
||||
use super::*; |
||||
use axum::http::StatusCode; |
||||
use ethers::utils::hex::ToHex; |
||||
use std::net::SocketAddr; |
||||
use tokio::sync::broadcast::{Receiver, Sender}; |
||||
|
||||
fn setup_test_server() -> (SocketAddr, Receiver<MessageRetryRequest>) { |
||||
let broadcast_tx = Sender::<MessageRetryRequest>::new(ENDPOINT_MESSAGES_QUEUE_SIZE); |
||||
let message_retry_api = MessageRetryApi::new(broadcast_tx.clone()); |
||||
let (path, retry_router) = message_retry_api.get_route(); |
||||
let app = Router::new().nest(path, retry_router); |
||||
|
||||
// Running the app in the background using a test server
|
||||
let server = |
||||
axum::Server::bind(&"127.0.0.1:0".parse().unwrap()).serve(app.into_make_service()); |
||||
let addr = server.local_addr(); |
||||
tokio::spawn(server); |
||||
|
||||
(addr, broadcast_tx.subscribe()) |
||||
} |
||||
|
||||
#[tokio::test] |
||||
async fn test_message_id_retry() { |
||||
let (addr, mut rx) = setup_test_server(); |
||||
|
||||
// Create a random message ID
|
||||
let message_id = H256::random(); |
||||
|
||||
// Send a GET request to the server
|
||||
let response = reqwest::get(format!( |
||||
"http://{}{}?message_id={}", |
||||
addr, |
||||
MESSAGE_RETRY_API_BASE, |
||||
message_id.encode_hex::<String>() |
||||
)) |
||||
.await |
||||
.unwrap(); |
||||
|
||||
// Check that the response status code is OK
|
||||
assert_eq!(response.status(), StatusCode::OK); |
||||
|
||||
assert_eq!( |
||||
rx.try_recv().unwrap(), |
||||
MessageRetryRequest::MessageId(message_id) |
||||
); |
||||
} |
||||
|
||||
#[tokio::test] |
||||
async fn test_destination_domain_retry() { |
||||
let (addr, mut rx) = setup_test_server(); |
||||
|
||||
// Create a random destination domain
|
||||
let destination_domain = 42; |
||||
|
||||
// Send a GET request to the server
|
||||
let response = reqwest::get(format!( |
||||
"http://{}{}?destination_domain={}", |
||||
addr, MESSAGE_RETRY_API_BASE, destination_domain |
||||
)) |
||||
.await |
||||
.unwrap(); |
||||
|
||||
// Check that the response status code is OK
|
||||
assert_eq!(response.status(), StatusCode::OK); |
||||
|
||||
assert_eq!( |
||||
rx.try_recv().unwrap(), |
||||
MessageRetryRequest::DestinationDomain(destination_domain) |
||||
); |
||||
} |
||||
} |
@ -1,39 +0,0 @@ |
||||
use num_bigint::{BigInt, Sign}; |
||||
use sea_orm::prelude::BigDecimal; |
||||
|
||||
use hyperlane_core::{H256, U256}; |
||||
|
||||
// Creates a big-endian hex representation of the address
|
||||
pub fn address_to_bytes(data: &H256) -> Vec<u8> { |
||||
if hex::is_h160(data.as_fixed_bytes()) { |
||||
// take the last 20 bytes
|
||||
data.as_fixed_bytes()[12..32].into() |
||||
} else { |
||||
h256_to_bytes(data) |
||||
} |
||||
} |
||||
|
||||
// Creates a big-endian hex representation of the address
|
||||
pub fn bytes_to_address(data: Vec<u8>) -> eyre::Result<H256> { |
||||
if (data.len() != 20) && (data.len() != 32) { |
||||
return Err(eyre::eyre!("Invalid address length")); |
||||
} |
||||
if data.len() == 20 { |
||||
let mut prefix = vec![0; 12]; |
||||
prefix.extend(data); |
||||
Ok(H256::from_slice(&prefix[..])) |
||||
} else { |
||||
Ok(H256::from_slice(&data[..])) |
||||
} |
||||
} |
||||
|
||||
// Creates a big-endian hex representation of the address hash
|
||||
pub fn h256_to_bytes(data: &H256) -> Vec<u8> { |
||||
data.as_fixed_bytes().as_slice().into() |
||||
} |
||||
|
||||
pub fn u256_to_decimal(v: U256) -> BigDecimal { |
||||
let mut buf = [0u8; 32]; |
||||
v.to_little_endian(&mut buf); |
||||
BigDecimal::from(BigInt::from_bytes_le(Sign::Plus, &buf as &[u8])) |
||||
} |
@ -1,314 +0,0 @@ |
||||
use std::num::NonZeroU64; |
||||
use std::sync::Arc; |
||||
use std::time::{Duration, Instant}; |
||||
use std::vec; |
||||
|
||||
use hyperlane_core::rpc_clients::call_and_retry_indefinitely; |
||||
use hyperlane_core::{ChainResult, MerkleTreeHook}; |
||||
use prometheus::IntGauge; |
||||
use tokio::time::sleep; |
||||
use tracing::{debug, error, info}; |
||||
|
||||
use hyperlane_base::{db::HyperlaneRocksDB, CheckpointSyncer, CoreMetrics}; |
||||
use hyperlane_core::{ |
||||
accumulator::incremental::IncrementalMerkle, Checkpoint, CheckpointWithMessageId, |
||||
HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneSignerExt, |
||||
}; |
||||
use hyperlane_ethereum::SingletonSignerHandle; |
||||
|
||||
#[derive(Clone)] |
||||
pub(crate) struct ValidatorSubmitter { |
||||
interval: Duration, |
||||
reorg_period: Option<NonZeroU64>, |
||||
signer: SingletonSignerHandle, |
||||
merkle_tree_hook: Arc<dyn MerkleTreeHook>, |
||||
checkpoint_syncer: Arc<dyn CheckpointSyncer>, |
||||
message_db: HyperlaneRocksDB, |
||||
metrics: ValidatorSubmitterMetrics, |
||||
} |
||||
|
||||
impl ValidatorSubmitter { |
||||
pub(crate) fn new( |
||||
interval: Duration, |
||||
reorg_period: u64, |
||||
merkle_tree_hook: Arc<dyn MerkleTreeHook>, |
||||
signer: SingletonSignerHandle, |
||||
checkpoint_syncer: Arc<dyn CheckpointSyncer>, |
||||
message_db: HyperlaneRocksDB, |
||||
metrics: ValidatorSubmitterMetrics, |
||||
) -> Self { |
||||
Self { |
||||
reorg_period: NonZeroU64::new(reorg_period), |
||||
interval, |
||||
merkle_tree_hook, |
||||
signer, |
||||
checkpoint_syncer, |
||||
message_db, |
||||
metrics, |
||||
} |
||||
} |
||||
|
||||
pub(crate) fn checkpoint(&self, tree: &IncrementalMerkle) -> Checkpoint { |
||||
Checkpoint { |
||||
root: tree.root(), |
||||
index: tree.index(), |
||||
merkle_tree_hook_address: self.merkle_tree_hook.address(), |
||||
mailbox_domain: self.merkle_tree_hook.domain().id(), |
||||
} |
||||
} |
||||
|
||||
/// Submits signed checkpoints from index 0 until the target checkpoint (inclusive).
|
||||
/// Runs idly forever once the target checkpoint is reached to avoid exiting the task.
|
||||
pub(crate) async fn backfill_checkpoint_submitter(self, target_checkpoint: Checkpoint) { |
||||
let mut tree = IncrementalMerkle::default(); |
||||
self.submit_checkpoints_until_correctness_checkpoint(&mut tree, &target_checkpoint) |
||||
.await; |
||||
|
||||
info!( |
||||
?target_checkpoint, |
||||
"Backfill checkpoint submitter successfully reached target checkpoint" |
||||
); |
||||
} |
||||
|
||||
/// Submits signed checkpoints indefinitely, starting from the `tree`.
|
||||
pub(crate) async fn checkpoint_submitter(self, mut tree: IncrementalMerkle) { |
||||
// How often to log checkpoint info - once every minute
|
||||
let checkpoint_info_log_period = Duration::from_secs(60); |
||||
// The instant in which we last logged checkpoint info, if at all
|
||||
let mut latest_checkpoint_info_log: Option<Instant> = None; |
||||
// Returns whether checkpoint info should be logged based off the
|
||||
// checkpoint_info_log_period having elapsed since the last log.
|
||||
// Sets latest_checkpoint_info_log to the current instant if true.
|
||||
let mut should_log_checkpoint_info = || { |
||||
if let Some(instant) = latest_checkpoint_info_log { |
||||
if instant.elapsed() < checkpoint_info_log_period { |
||||
return false; |
||||
} |
||||
} |
||||
latest_checkpoint_info_log = Some(Instant::now()); |
||||
true |
||||
}; |
||||
|
||||
loop { |
||||
// Lag by reorg period because this is our correctness checkpoint.
|
||||
let latest_checkpoint = call_and_retry_indefinitely(|| { |
||||
let merkle_tree_hook = self.merkle_tree_hook.clone(); |
||||
Box::pin(async move { merkle_tree_hook.latest_checkpoint(self.reorg_period).await }) |
||||
}) |
||||
.await; |
||||
|
||||
self.metrics |
||||
.latest_checkpoint_observed |
||||
.set(latest_checkpoint.index as i64); |
||||
|
||||
if should_log_checkpoint_info() { |
||||
info!( |
||||
?latest_checkpoint, |
||||
tree_count = tree.count(), |
||||
"Latest checkpoint" |
||||
); |
||||
} |
||||
|
||||
// This may occur e.g. if RPC providers are unreliable and make calls against
|
||||
// inconsistent block tips.
|
||||
//
|
||||
// In this case, we just sleep a bit until we fetch a new latest checkpoint
|
||||
// that at least meets the tree.
|
||||
if tree_exceeds_checkpoint(&latest_checkpoint, &tree) { |
||||
debug!( |
||||
?latest_checkpoint, |
||||
tree_count = tree.count(), |
||||
"Latest checkpoint is behind tree, sleeping briefly" |
||||
); |
||||
sleep(self.interval).await; |
||||
continue; |
||||
} |
||||
self.submit_checkpoints_until_correctness_checkpoint(&mut tree, &latest_checkpoint) |
||||
.await; |
||||
|
||||
self.metrics |
||||
.latest_checkpoint_processed |
||||
.set(latest_checkpoint.index as i64); |
||||
|
||||
sleep(self.interval).await; |
||||
} |
||||
} |
||||
|
||||
/// Submits signed checkpoints relating to the given tree until the correctness checkpoint (inclusive).
|
||||
/// Only submits the signed checkpoints once the correctness checkpoint is reached.
|
||||
async fn submit_checkpoints_until_correctness_checkpoint( |
||||
&self, |
||||
tree: &mut IncrementalMerkle, |
||||
correctness_checkpoint: &Checkpoint, |
||||
) { |
||||
// This should never be called with a tree that is ahead of the correctness checkpoint.
|
||||
assert!( |
||||
!tree_exceeds_checkpoint(correctness_checkpoint, tree), |
||||
"tree (count: {}) is ahead of correctness checkpoint {:?}", |
||||
tree.count(), |
||||
correctness_checkpoint, |
||||
); |
||||
|
||||
// All intermediate checkpoints will be stored here and signed once the correctness
|
||||
// checkpoint is reached.
|
||||
let mut checkpoint_queue = vec![]; |
||||
|
||||
// If the correctness checkpoint is ahead of the tree, we need to ingest more messages.
|
||||
//
|
||||
// tree.index() will panic if the tree is empty, so we use tree.count() instead
|
||||
// and convert the correctness_checkpoint.index to a count by adding 1.
|
||||
while tree.count() as u32 <= correctness_checkpoint.index { |
||||
if let Some(insertion) = self |
||||
.message_db |
||||
.retrieve_merkle_tree_insertion_by_leaf_index(&(tree.count() as u32)) |
||||
.unwrap_or_else(|err| { |
||||
panic!( |
||||
"Error fetching merkle tree insertion for leaf index {}: {}", |
||||
tree.count(), |
||||
err |
||||
) |
||||
}) |
||||
{ |
||||
debug!( |
||||
index = insertion.index(), |
||||
queue_length = checkpoint_queue.len(), |
||||
"Ingesting leaf to tree" |
||||
); |
||||
let message_id = insertion.message_id(); |
||||
tree.ingest(message_id); |
||||
|
||||
let checkpoint = self.checkpoint(tree); |
||||
|
||||
checkpoint_queue.push(CheckpointWithMessageId { |
||||
checkpoint, |
||||
message_id, |
||||
}); |
||||
} else { |
||||
// If we haven't yet indexed the next merkle tree insertion but know that
|
||||
// it will soon exist (because we know the correctness checkpoint), wait a bit and
|
||||
// try again.
|
||||
sleep(Duration::from_millis(100)).await |
||||
} |
||||
} |
||||
|
||||
// At this point we know that correctness_checkpoint.index == tree.index().
|
||||
assert_eq!( |
||||
correctness_checkpoint.index, |
||||
tree.index(), |
||||
"correctness checkpoint index {} != tree index {}", |
||||
correctness_checkpoint.index, |
||||
tree.index(), |
||||
); |
||||
|
||||
let checkpoint = self.checkpoint(tree); |
||||
|
||||
// If the tree's checkpoint doesn't match the correctness checkpoint, something went wrong
|
||||
// and we bail loudly.
|
||||
if checkpoint != *correctness_checkpoint { |
||||
error!( |
||||
?checkpoint, |
||||
?correctness_checkpoint, |
||||
"Incorrect tree root, something went wrong" |
||||
); |
||||
panic!("Incorrect tree root, something went wrong"); |
||||
} |
||||
|
||||
if !checkpoint_queue.is_empty() { |
||||
info!( |
||||
index = checkpoint.index, |
||||
queue_len = checkpoint_queue.len(), |
||||
"Reached tree consistency" |
||||
); |
||||
self.sign_and_submit_checkpoints(checkpoint_queue).await; |
||||
|
||||
info!( |
||||
index = checkpoint.index, |
||||
"Signed all queued checkpoints until index" |
||||
); |
||||
} |
||||
} |
||||
|
||||
async fn sign_and_submit_checkpoint( |
||||
&self, |
||||
checkpoint: CheckpointWithMessageId, |
||||
) -> ChainResult<()> { |
||||
let existing = self |
||||
.checkpoint_syncer |
||||
.fetch_checkpoint(checkpoint.index) |
||||
.await?; |
||||
if existing.is_some() { |
||||
debug!(index = checkpoint.index, "Checkpoint already submitted"); |
||||
return Ok(()); |
||||
} |
||||
let signed_checkpoint = self.signer.sign(checkpoint).await?; |
||||
self.checkpoint_syncer |
||||
.write_checkpoint(&signed_checkpoint) |
||||
.await?; |
||||
debug!(index = checkpoint.index, "Signed and submitted checkpoint"); |
||||
|
||||
// TODO: move these into S3 implementations
|
||||
// small sleep before signing next checkpoint to avoid rate limiting
|
||||
sleep(Duration::from_millis(100)).await; |
||||
Ok(()) |
||||
} |
||||
|
||||
/// Signs and submits any previously unsubmitted checkpoints.
|
||||
async fn sign_and_submit_checkpoints(&self, checkpoints: Vec<CheckpointWithMessageId>) { |
||||
let last_checkpoint = checkpoints.as_slice()[checkpoints.len() - 1]; |
||||
// Submits checkpoints to the store in reverse order. This speeds up processing historic checkpoints (those before the validator is spun up),
|
||||
// since those are the most likely to make messages become processable.
|
||||
// A side effect is that new checkpoints will also be submitted in reverse order.
|
||||
for queued_checkpoint in checkpoints.into_iter().rev() { |
||||
// certain checkpoint stores rate limit very aggressively, so we retry indefinitely
|
||||
call_and_retry_indefinitely(|| { |
||||
let self_clone = self.clone(); |
||||
Box::pin(async move { |
||||
self_clone |
||||
.sign_and_submit_checkpoint(queued_checkpoint) |
||||
.await?; |
||||
Ok(()) |
||||
}) |
||||
}) |
||||
.await; |
||||
} |
||||
|
||||
call_and_retry_indefinitely(|| { |
||||
let self_clone = self.clone(); |
||||
Box::pin(async move { |
||||
self_clone |
||||
.checkpoint_syncer |
||||
.update_latest_index(last_checkpoint.index) |
||||
.await?; |
||||
Ok(()) |
||||
}) |
||||
}) |
||||
.await; |
||||
} |
||||
} |
||||
|
||||
/// Returns whether the tree exceeds the checkpoint.
|
||||
fn tree_exceeds_checkpoint(checkpoint: &Checkpoint, tree: &IncrementalMerkle) -> bool { |
||||
// tree.index() will panic if the tree is empty, so we use tree.count() instead
|
||||
// and convert the correctness_checkpoint.index to a count by adding 1.
|
||||
checkpoint.index + 1 < tree.count() as u32 |
||||
} |
||||
|
||||
#[derive(Clone)] |
||||
pub(crate) struct ValidatorSubmitterMetrics { |
||||
latest_checkpoint_observed: IntGauge, |
||||
latest_checkpoint_processed: IntGauge, |
||||
} |
||||
|
||||
impl ValidatorSubmitterMetrics { |
||||
pub fn new(metrics: &CoreMetrics, mailbox_chain: &HyperlaneDomain) -> Self { |
||||
let chain_name = mailbox_chain.name(); |
||||
Self { |
||||
latest_checkpoint_observed: metrics |
||||
.latest_checkpoint() |
||||
.with_label_values(&["validator_observed", chain_name]), |
||||
latest_checkpoint_processed: metrics |
||||
.latest_checkpoint() |
||||
.with_label_values(&["validator_processed", chain_name]), |
||||
} |
||||
} |
||||
} |
@ -1,2 +0,0 @@ |
||||
/// This module contains all the verification variables the libraries used by the Hyperlane Cosmos chain.
|
||||
pub mod address; |
@ -1,452 +0,0 @@ |
||||
use base64::{engine::general_purpose::STANDARD as BASE64, Engine}; |
||||
use std::{ |
||||
fmt::{Debug, Formatter}, |
||||
io::Cursor, |
||||
num::NonZeroU64, |
||||
ops::RangeInclusive, |
||||
str::FromStr, |
||||
}; |
||||
|
||||
use crate::payloads::{general, mailbox}; |
||||
use crate::rpc::{CosmosWasmIndexer, ParsedEvent, WasmIndexer}; |
||||
use crate::CosmosProvider; |
||||
use crate::{ |
||||
address::CosmosAddress, |
||||
payloads::mailbox::{GeneralMailboxQuery, ProcessMessageRequest, ProcessMessageRequestInner}, |
||||
utils::execute_and_parse_log_futures, |
||||
}; |
||||
use crate::{grpc::WasmProvider, HyperlaneCosmosError}; |
||||
use crate::{signers::Signer, utils::get_block_height_for_lag, ConnectionConf}; |
||||
use async_trait::async_trait; |
||||
use cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse; |
||||
use once_cell::sync::Lazy; |
||||
use tendermint::abci::EventAttribute; |
||||
|
||||
use crate::utils::{CONTRACT_ADDRESS_ATTRIBUTE_KEY, CONTRACT_ADDRESS_ATTRIBUTE_KEY_BASE64}; |
||||
use hyperlane_core::{ |
||||
utils::bytes_to_hex, ChainResult, HyperlaneChain, HyperlaneContract, HyperlaneDomain, |
||||
HyperlaneMessage, HyperlaneProvider, Indexed, Indexer, LogMeta, Mailbox, TxCostEstimate, |
||||
TxOutcome, H256, U256, |
||||
}; |
||||
use hyperlane_core::{ |
||||
ChainCommunicationError, ContractLocator, Decode, RawHyperlaneMessage, SequenceAwareIndexer, |
||||
}; |
||||
use tracing::{instrument, warn}; |
||||
|
||||
#[derive(Clone)] |
||||
/// A reference to a Mailbox contract on some Cosmos chain
|
||||
pub struct CosmosMailbox { |
||||
config: ConnectionConf, |
||||
domain: HyperlaneDomain, |
||||
address: H256, |
||||
provider: CosmosProvider, |
||||
} |
||||
|
||||
impl CosmosMailbox { |
||||
/// Create a new cosmos mailbox
|
||||
pub fn new( |
||||
conf: ConnectionConf, |
||||
locator: ContractLocator, |
||||
signer: Option<Signer>, |
||||
) -> ChainResult<Self> { |
||||
let provider = CosmosProvider::new( |
||||
locator.domain.clone(), |
||||
conf.clone(), |
||||
Some(locator.clone()), |
||||
signer, |
||||
)?; |
||||
|
||||
Ok(Self { |
||||
config: conf, |
||||
domain: locator.domain.clone(), |
||||
address: locator.address, |
||||
provider, |
||||
}) |
||||
} |
||||
|
||||
/// Prefix used in the bech32 address encoding
|
||||
pub fn bech32_prefix(&self) -> String { |
||||
self.config.get_bech32_prefix() |
||||
} |
||||
|
||||
fn contract_address_bytes(&self) -> usize { |
||||
self.config.get_contract_address_bytes() |
||||
} |
||||
} |
||||
|
||||
impl HyperlaneContract for CosmosMailbox { |
||||
fn address(&self) -> H256 { |
||||
self.address |
||||
} |
||||
} |
||||
|
||||
impl HyperlaneChain for CosmosMailbox { |
||||
fn domain(&self) -> &HyperlaneDomain { |
||||
&self.domain |
||||
} |
||||
|
||||
fn provider(&self) -> Box<dyn HyperlaneProvider> { |
||||
Box::new(self.provider.clone()) |
||||
} |
||||
} |
||||
|
||||
impl Debug for CosmosMailbox { |
||||
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { |
||||
todo!() |
||||
} |
||||
} |
||||
|
||||
#[async_trait] |
||||
impl Mailbox for CosmosMailbox { |
||||
#[instrument(level = "debug", err, ret, skip(self))] |
||||
async fn count(&self, lag: Option<NonZeroU64>) -> ChainResult<u32> { |
||||
let block_height = get_block_height_for_lag(self.provider.grpc(), lag).await?; |
||||
self.nonce_at_block(block_height).await |
||||
} |
||||
|
||||
#[instrument(level = "debug", err, ret, skip(self))] |
||||
async fn delivered(&self, id: H256) -> ChainResult<bool> { |
||||
let id = hex::encode(id); |
||||
let payload = mailbox::DeliveredRequest { |
||||
message_delivered: mailbox::DeliveredRequestInner { id }, |
||||
}; |
||||
|
||||
let delivered = match self |
||||
.provider |
||||
.grpc() |
||||
.wasm_query(GeneralMailboxQuery { mailbox: payload }, None) |
||||
.await |
||||
{ |
||||
Ok(v) => { |
||||
let response: mailbox::DeliveredResponse = serde_json::from_slice(&v)?; |
||||
|
||||
response.delivered |
||||
} |
||||
Err(err) => { |
||||
warn!( |
||||
"error while checking the message delivery status: {:?}", |
||||
err |
||||
); |
||||
|
||||
false |
||||
} |
||||
}; |
||||
|
||||
Ok(delivered) |
||||
} |
||||
|
||||
#[instrument(err, ret, skip(self))] |
||||
async fn default_ism(&self) -> ChainResult<H256> { |
||||
let payload = mailbox::DefaultIsmRequest { |
||||
default_ism: general::EmptyStruct {}, |
||||
}; |
||||
|
||||
let data = self |
||||
.provider |
||||
.grpc() |
||||
.wasm_query(GeneralMailboxQuery { mailbox: payload }, None) |
||||
.await?; |
||||
let response: mailbox::DefaultIsmResponse = serde_json::from_slice(&data)?; |
||||
|
||||
// convert bech32 to H256
|
||||
let ism = CosmosAddress::from_str(&response.default_ism)?; |
||||
Ok(ism.digest()) |
||||
} |
||||
|
||||
#[instrument(err, ret, skip(self))] |
||||
async fn recipient_ism(&self, recipient: H256) -> ChainResult<H256> { |
||||
let address = CosmosAddress::from_h256( |
||||
recipient, |
||||
&self.bech32_prefix(), |
||||
self.contract_address_bytes(), |
||||
)? |
||||
.address(); |
||||
|
||||
let payload = mailbox::RecipientIsmRequest { |
||||
recipient_ism: mailbox::RecipientIsmRequestInner { |
||||
recipient_addr: address, |
||||
}, |
||||
}; |
||||
|
||||
let data = self |
||||
.provider |
||||
.grpc() |
||||
.wasm_query(GeneralMailboxQuery { mailbox: payload }, None) |
||||
.await?; |
||||
let response: mailbox::RecipientIsmResponse = serde_json::from_slice(&data)?; |
||||
|
||||
// convert bech32 to H256
|
||||
let ism = CosmosAddress::from_str(&response.ism)?; |
||||
Ok(ism.digest()) |
||||
} |
||||
|
||||
#[instrument(err, ret, skip(self))] |
||||
async fn process( |
||||
&self, |
||||
message: &HyperlaneMessage, |
||||
metadata: &[u8], |
||||
tx_gas_limit: Option<U256>, |
||||
) -> ChainResult<TxOutcome> { |
||||
let process_message = ProcessMessageRequest { |
||||
process: ProcessMessageRequestInner { |
||||
message: hex::encode(RawHyperlaneMessage::from(message)), |
||||
metadata: hex::encode(metadata), |
||||
}, |
||||
}; |
||||
|
||||
let response: TxResponse = self |
||||
.provider |
||||
.grpc() |
||||
.wasm_send(process_message, tx_gas_limit) |
||||
.await?; |
||||
|
||||
Ok(response.try_into()?) |
||||
} |
||||
|
||||
#[instrument(err, ret, skip(self), fields(msg=%message, metadata=%bytes_to_hex(metadata)))] |
||||
async fn process_estimate_costs( |
||||
&self, |
||||
message: &HyperlaneMessage, |
||||
metadata: &[u8], |
||||
) -> ChainResult<TxCostEstimate> { |
||||
let process_message = ProcessMessageRequest { |
||||
process: ProcessMessageRequestInner { |
||||
message: hex::encode(RawHyperlaneMessage::from(message)), |
||||
metadata: hex::encode(metadata), |
||||
}, |
||||
}; |
||||
|
||||
let gas_limit = self |
||||
.provider |
||||
.grpc() |
||||
.wasm_estimate_gas(process_message) |
||||
.await?; |
||||
|
||||
let result = TxCostEstimate { |
||||
gas_limit: gas_limit.into(), |
||||
gas_price: self.provider.grpc().gas_price(), |
||||
l2_gas_limit: None, |
||||
}; |
||||
|
||||
Ok(result) |
||||
} |
||||
|
||||
fn process_calldata(&self, message: &HyperlaneMessage, metadata: &[u8]) -> Vec<u8> { |
||||
todo!() // not required
|
||||
} |
||||
} |
||||
|
||||
impl CosmosMailbox { |
||||
#[instrument(level = "debug", err, ret, skip(self))] |
||||
async fn nonce_at_block(&self, block_height: Option<u64>) -> ChainResult<u32> { |
||||
let payload = mailbox::NonceRequest { |
||||
nonce: general::EmptyStruct {}, |
||||
}; |
||||
|
||||
let data = self |
||||
.provider |
||||
.grpc() |
||||
.wasm_query(GeneralMailboxQuery { mailbox: payload }, block_height) |
||||
.await?; |
||||
|
||||
let response: mailbox::NonceResponse = serde_json::from_slice(&data)?; |
||||
|
||||
Ok(response.nonce) |
||||
} |
||||
} |
||||
|
||||
// ------------------ Indexer ------------------
|
||||
|
||||
const MESSAGE_ATTRIBUTE_KEY: &str = "message"; |
||||
static MESSAGE_ATTRIBUTE_KEY_BASE64: Lazy<String> = |
||||
Lazy::new(|| BASE64.encode(MESSAGE_ATTRIBUTE_KEY)); |
||||
|
||||
/// Struct that retrieves event data for a Cosmos Mailbox contract
|
||||
#[derive(Debug, Clone)] |
||||
pub struct CosmosMailboxIndexer { |
||||
mailbox: CosmosMailbox, |
||||
indexer: Box<CosmosWasmIndexer>, |
||||
} |
||||
|
||||
impl CosmosMailboxIndexer { |
||||
/// The message dispatch event type from the CW contract.
|
||||
const MESSAGE_DISPATCH_EVENT_TYPE: &str = "mailbox_dispatch"; |
||||
|
||||
/// Create a reference to a mailbox at a specific Cosmos address on some
|
||||
/// chain
|
||||
pub fn new( |
||||
conf: ConnectionConf, |
||||
locator: ContractLocator, |
||||
signer: Option<Signer>, |
||||
reorg_period: u32, |
||||
) -> ChainResult<Self> { |
||||
let mailbox = CosmosMailbox::new(conf.clone(), locator.clone(), signer.clone())?; |
||||
let indexer = CosmosWasmIndexer::new( |
||||
conf, |
||||
locator, |
||||
Self::MESSAGE_DISPATCH_EVENT_TYPE.into(), |
||||
reorg_period, |
||||
)?; |
||||
|
||||
Ok(Self { |
||||
mailbox, |
||||
indexer: Box::new(indexer), |
||||
}) |
||||
} |
||||
|
||||
#[instrument(err)] |
||||
fn hyperlane_message_parser( |
||||
attrs: &Vec<EventAttribute>, |
||||
) -> ChainResult<ParsedEvent<HyperlaneMessage>> { |
||||
let mut contract_address: Option<String> = None; |
||||
let mut message: Option<HyperlaneMessage> = None; |
||||
|
||||
for attr in attrs { |
||||
let key = attr.key.as_str(); |
||||
let value = attr.value.as_str(); |
||||
|
||||
match key { |
||||
CONTRACT_ADDRESS_ATTRIBUTE_KEY => { |
||||
contract_address = Some(value.to_string()); |
||||
} |
||||
v if *CONTRACT_ADDRESS_ATTRIBUTE_KEY_BASE64 == v => { |
||||
contract_address = Some(String::from_utf8( |
||||
BASE64 |
||||
.decode(value) |
||||
.map_err(Into::<HyperlaneCosmosError>::into)?, |
||||
)?); |
||||
} |
||||
|
||||
MESSAGE_ATTRIBUTE_KEY => { |
||||
// Intentionally using read_from to get a Result::Err if there's
|
||||
// an issue with the message.
|
||||
let mut reader = Cursor::new(hex::decode(value)?); |
||||
message = Some(HyperlaneMessage::read_from(&mut reader)?); |
||||
} |
||||
v if *MESSAGE_ATTRIBUTE_KEY_BASE64 == v => { |
||||
// Intentionally using read_from to get a Result::Err if there's
|
||||
// an issue with the message.
|
||||
let mut reader = Cursor::new(hex::decode(String::from_utf8( |
||||
BASE64 |
||||
.decode(value) |
||||
.map_err(Into::<HyperlaneCosmosError>::into)?, |
||||
)?)?); |
||||
message = Some(HyperlaneMessage::read_from(&mut reader)?); |
||||
} |
||||
|
||||
_ => {} |
||||
} |
||||
} |
||||
|
||||
let contract_address = contract_address |
||||
.ok_or_else(|| ChainCommunicationError::from_other_str("missing contract_address"))?; |
||||
let message = |
||||
message.ok_or_else(|| ChainCommunicationError::from_other_str("missing message"))?; |
||||
|
||||
Ok(ParsedEvent::new(contract_address, message)) |
||||
} |
||||
} |
||||
|
||||
#[async_trait] |
||||
impl Indexer<HyperlaneMessage> for CosmosMailboxIndexer { |
||||
async fn fetch_logs_in_range( |
||||
&self, |
||||
range: RangeInclusive<u32>, |
||||
) -> ChainResult<Vec<(Indexed<HyperlaneMessage>, LogMeta)>> { |
||||
let logs_futures: Vec<_> = range |
||||
.map(|block_number| { |
||||
let self_clone = self.clone(); |
||||
tokio::spawn(async move { |
||||
let logs = self_clone |
||||
.indexer |
||||
.get_logs_in_block( |
||||
block_number, |
||||
Self::hyperlane_message_parser, |
||||
"HyperlaneMessageCursor", |
||||
) |
||||
.await; |
||||
(logs, block_number) |
||||
}) |
||||
}) |
||||
.collect(); |
||||
|
||||
execute_and_parse_log_futures(logs_futures).await |
||||
} |
||||
|
||||
async fn get_finalized_block_number(&self) -> ChainResult<u32> { |
||||
self.indexer.get_finalized_block_number().await |
||||
} |
||||
} |
||||
|
||||
#[async_trait] |
||||
impl Indexer<H256> for CosmosMailboxIndexer { |
||||
async fn fetch_logs_in_range( |
||||
&self, |
||||
range: RangeInclusive<u32>, |
||||
) -> ChainResult<Vec<(Indexed<H256>, LogMeta)>> { |
||||
// TODO: implement when implementing Cosmos scraping
|
||||
todo!() |
||||
} |
||||
|
||||
async fn get_finalized_block_number(&self) -> ChainResult<u32> { |
||||
self.indexer.get_finalized_block_number().await |
||||
} |
||||
} |
||||
|
||||
#[async_trait] |
||||
impl SequenceAwareIndexer<H256> for CosmosMailboxIndexer { |
||||
async fn latest_sequence_count_and_tip(&self) -> ChainResult<(Option<u32>, u32)> { |
||||
let tip = Indexer::<H256>::get_finalized_block_number(&self).await?; |
||||
|
||||
// No sequence for message deliveries.
|
||||
Ok((None, tip)) |
||||
} |
||||
} |
||||
|
||||
#[async_trait] |
||||
impl SequenceAwareIndexer<HyperlaneMessage> for CosmosMailboxIndexer { |
||||
async fn latest_sequence_count_and_tip(&self) -> ChainResult<(Option<u32>, u32)> { |
||||
let tip = Indexer::<HyperlaneMessage>::get_finalized_block_number(&self).await?; |
||||
|
||||
let sequence = self.mailbox.nonce_at_block(Some(tip.into())).await?; |
||||
|
||||
Ok((Some(sequence), tip)) |
||||
} |
||||
} |
||||
|
||||
#[cfg(test)] |
||||
mod tests { |
||||
use hyperlane_core::HyperlaneMessage; |
||||
|
||||
use crate::{rpc::ParsedEvent, utils::event_attributes_from_str}; |
||||
|
||||
use super::*; |
||||
|
||||
#[test] |
||||
fn test_hyperlane_message_parser() { |
||||
// Examples from https://rpc-kralum.neutron-1.neutron.org/tx_search?query=%22tx.height%20%3E=%204000000%20AND%20tx.height%20%3C=%204100000%20AND%20wasm-mailbox_dispatch._contract_address%20=%20%27neutron1sjzzd4gwkggy6hrrs8kxxatexzcuz3jecsxm3wqgregkulzj8r7qlnuef4%27%22&prove=false&page=1&per_page=100
|
||||
|
||||
let expected = ParsedEvent::new( |
||||
"neutron1sjzzd4gwkggy6hrrs8kxxatexzcuz3jecsxm3wqgregkulzj8r7qlnuef4".into(), |
||||
HyperlaneMessage::from(hex::decode("03000000006e74726e0000000000000000000000006ba6343a09a60ac048d0e99f50b76fd99eff1063000000a9000000000000000000000000281973b53c9aacec128ac964a6f750fea40912aa48656c6c6f2066726f6d204e657574726f6e204d61696e6e657420746f204d616e74612050616369666963206f63742032392c2031323a353520616d").unwrap()), |
||||
); |
||||
|
||||
let assert_parsed_event = |attrs: &Vec<EventAttribute>| { |
||||
let parsed_event = CosmosMailboxIndexer::hyperlane_message_parser(attrs).unwrap(); |
||||
|
||||
assert_eq!(parsed_event, expected); |
||||
}; |
||||
|
||||
// Non-base64 version
|
||||
let non_base64_attrs = event_attributes_from_str( |
||||
r#"[{"key":"_contract_address","value":"neutron1sjzzd4gwkggy6hrrs8kxxatexzcuz3jecsxm3wqgregkulzj8r7qlnuef4","index":true},{"key":"sender","value":"0000000000000000000000006ba6343a09a60ac048d0e99f50b76fd99eff1063","index":true},{"key":"destination","value":"169","index":true},{"key":"recipient","value":"000000000000000000000000281973b53c9aacec128ac964a6f750fea40912aa","index":true},{"key":"message","value":"03000000006e74726e0000000000000000000000006ba6343a09a60ac048d0e99f50b76fd99eff1063000000a9000000000000000000000000281973b53c9aacec128ac964a6f750fea40912aa48656c6c6f2066726f6d204e657574726f6e204d61696e6e657420746f204d616e74612050616369666963206f63742032392c2031323a353520616d","index":true}]"#, |
||||
); |
||||
assert_parsed_event(&non_base64_attrs); |
||||
|
||||
// Base64 version
|
||||
let base64_attrs = event_attributes_from_str( |
||||
r#"[{"key":"X2NvbnRyYWN0X2FkZHJlc3M=","value":"bmV1dHJvbjFzanp6ZDRnd2tnZ3k2aHJyczhreHhhdGV4emN1ejNqZWNzeG0zd3FncmVna3Vsemo4cjdxbG51ZWY0","index":true},{"key":"c2VuZGVy","value":"MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNmJhNjM0M2EwOWE2MGFjMDQ4ZDBlOTlmNTBiNzZmZDk5ZWZmMTA2Mw==","index":true},{"key":"ZGVzdGluYXRpb24=","value":"MTY5","index":true},{"key":"cmVjaXBpZW50","value":"MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMjgxOTczYjUzYzlhYWNlYzEyOGFjOTY0YTZmNzUwZmVhNDA5MTJhYQ==","index":true},{"key":"bWVzc2FnZQ==","value":"MDMwMDAwMDAwMDZlNzQ3MjZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNmJhNjM0M2EwOWE2MGFjMDQ4ZDBlOTlmNTBiNzZmZDk5ZWZmMTA2MzAwMDAwMGE5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMjgxOTczYjUzYzlhYWNlYzEyOGFjOTY0YTZmNzUwZmVhNDA5MTJhYTQ4NjU2YzZjNmYyMDY2NzI2ZjZkMjA0ZTY1NzU3NDcyNmY2ZTIwNGQ2MTY5NmU2ZTY1NzQyMDc0NmYyMDRkNjE2ZTc0NjEyMDUwNjE2MzY5NjY2OTYzMjA2ZjYzNzQyMDMyMzkyYzIwMzEzMjNhMzUzNTIwNjE2ZA==","index":true}]"#, |
||||
); |
||||
assert_parsed_event(&base64_attrs); |
||||
} |
||||
} |
@ -1,106 +0,0 @@ |
||||
use async_trait::async_trait; |
||||
use hyperlane_core::{ |
||||
BlockInfo, ChainInfo, ChainResult, ContractLocator, HyperlaneChain, HyperlaneDomain, |
||||
HyperlaneProvider, TxnInfo, H256, U256, |
||||
}; |
||||
use tendermint_rpc::{client::CompatMode, HttpClient}; |
||||
|
||||
use crate::{ConnectionConf, CosmosAmount, HyperlaneCosmosError, Signer}; |
||||
|
||||
use self::grpc::WasmGrpcProvider; |
||||
|
||||
/// cosmos grpc provider
|
||||
pub mod grpc; |
||||
/// cosmos rpc provider
|
||||
pub mod rpc; |
||||
|
||||
/// Abstraction over a connection to a Cosmos chain
|
||||
#[derive(Debug, Clone)] |
||||
pub struct CosmosProvider { |
||||
domain: HyperlaneDomain, |
||||
canonical_asset: String, |
||||
grpc_client: WasmGrpcProvider, |
||||
rpc_client: HttpClient, |
||||
} |
||||
|
||||
impl CosmosProvider { |
||||
/// Create a reference to a Cosmos chain
|
||||
pub fn new( |
||||
domain: HyperlaneDomain, |
||||
conf: ConnectionConf, |
||||
locator: Option<ContractLocator>, |
||||
signer: Option<Signer>, |
||||
) -> ChainResult<Self> { |
||||
let gas_price = CosmosAmount::try_from(conf.get_minimum_gas_price().clone())?; |
||||
let grpc_client = WasmGrpcProvider::new( |
||||
domain.clone(), |
||||
conf.clone(), |
||||
gas_price.clone(), |
||||
locator, |
||||
signer, |
||||
)?; |
||||
let rpc_client = HttpClient::builder( |
||||
conf.get_rpc_url() |
||||
.parse() |
||||
.map_err(Into::<HyperlaneCosmosError>::into)?, |
||||
) |
||||
// Consider supporting different compatibility modes.
|
||||
.compat_mode(CompatMode::latest()) |
||||
.build() |
||||
.map_err(Into::<HyperlaneCosmosError>::into)?; |
||||
|
||||
Ok(Self { |
||||
domain, |
||||
rpc_client, |
||||
grpc_client, |
||||
canonical_asset: conf.get_canonical_asset(), |
||||
}) |
||||
} |
||||
|
||||
/// Get a grpc client
|
||||
pub fn grpc(&self) -> &WasmGrpcProvider { |
||||
&self.grpc_client |
||||
} |
||||
|
||||
/// Get an rpc client
|
||||
pub fn rpc(&self) -> &HttpClient { |
||||
&self.rpc_client |
||||
} |
||||
} |
||||
|
||||
impl HyperlaneChain for CosmosProvider { |
||||
fn domain(&self) -> &HyperlaneDomain { |
||||
&self.domain |
||||
} |
||||
|
||||
fn provider(&self) -> Box<dyn HyperlaneProvider> { |
||||
Box::new(self.clone()) |
||||
} |
||||
} |
||||
|
||||
#[async_trait] |
||||
impl HyperlaneProvider for CosmosProvider { |
||||
async fn get_block_by_hash(&self, _hash: &H256) -> ChainResult<BlockInfo> { |
||||
todo!() // FIXME
|
||||
} |
||||
|
||||
async fn get_txn_by_hash(&self, _hash: &H256) -> ChainResult<TxnInfo> { |
||||
todo!() // FIXME
|
||||
} |
||||
|
||||
async fn is_contract(&self, _address: &H256) -> ChainResult<bool> { |
||||
// FIXME
|
||||
Ok(true) |
||||
} |
||||
|
||||
async fn get_balance(&self, address: String) -> ChainResult<U256> { |
||||
Ok(self |
||||
.grpc_client |
||||
.get_balance(address, self.canonical_asset.clone()) |
||||
.await?) |
||||
} |
||||
|
||||
async fn get_chain_metrics(&self) -> ChainResult<Option<ChainInfo>> { |
||||
Ok(None) |
||||
} |
||||
} |
@ -1,54 +0,0 @@ |
||||
use hyperlane_core::{config::OperationBatchConfig, U256}; |
||||
use url::Url; |
||||
|
||||
/// Ethereum RPC connection configuration
|
||||
#[derive(Debug, Clone)] |
||||
pub enum RpcConnectionConf { |
||||
/// An HTTP-only quorum.
|
||||
HttpQuorum { |
||||
/// List of urls to connect to
|
||||
urls: Vec<Url>, |
||||
}, |
||||
/// An HTTP-only fallback set.
|
||||
HttpFallback { |
||||
/// List of urls to connect to in order of priority
|
||||
urls: Vec<Url>, |
||||
}, |
||||
/// HTTP connection details
|
||||
Http { |
||||
/// Url to connect to
|
||||
url: Url, |
||||
}, |
||||
/// Websocket connection details
|
||||
Ws { |
||||
/// Url to connect to
|
||||
url: Url, |
||||
}, |
||||
} |
||||
|
||||
/// Ethereum connection configuration
|
||||
#[derive(Debug, Clone)] |
||||
pub struct ConnectionConf { |
||||
/// RPC connection configuration
|
||||
pub rpc_connection: RpcConnectionConf, |
||||
/// Transaction overrides to use when sending transactions.
|
||||
pub transaction_overrides: TransactionOverrides, |
||||
/// Operation batching configuration
|
||||
pub operation_batch: OperationBatchConfig, |
||||
} |
||||
|
||||
/// Ethereum transaction overrides.
|
||||
#[derive(Debug, Clone, Default)] |
||||
pub struct TransactionOverrides { |
||||
/// Gas price to use for transactions, in wei.
|
||||
/// If specified, non-1559 transactions will be used with this gas price.
|
||||
pub gas_price: Option<U256>, |
||||
/// Gas limit to use for transactions.
|
||||
/// If unspecified, the gas limit will be estimated.
|
||||
/// If specified, transactions will use `max(estimated_gas, gas_limit)`
|
||||
pub gas_limit: Option<U256>, |
||||
/// Max fee per gas to use for EIP-1559 transactions.
|
||||
pub max_fee_per_gas: Option<U256>, |
||||
/// Max priority fee per gas to use for EIP-1559 transactions.
|
||||
pub max_priority_fee_per_gas: Option<U256>, |
||||
} |
@ -1,530 +0,0 @@ |
||||
{ |
||||
"types": [ |
||||
{ |
||||
"typeId": 0, |
||||
"type": "()", |
||||
"components": [], |
||||
"typeParameters": null |
||||
}, |
||||
{ |
||||
"typeId": 1, |
||||
"type": "(_, _)", |
||||
"components": [ |
||||
{ |
||||
"name": "__tuple_element", |
||||
"type": 2, |
||||
"typeArguments": null |
||||
}, |
||||
{ |
||||
"name": "__tuple_element", |
||||
"type": 20, |
||||
"typeArguments": null |
||||
} |
||||
], |
||||
"typeParameters": null |
||||
}, |
||||
{ |
||||
"typeId": 2, |
||||
"type": "b256", |
||||
"components": null, |
||||
"typeParameters": null |
||||
}, |
||||
{ |
||||
"typeId": 3, |
||||
"type": "bool", |
||||
"components": null, |
||||
"typeParameters": null |
||||
}, |
||||
{ |
||||
"typeId": 4, |
||||
"type": "enum Identity", |
||||
"components": [ |
||||
{ |
||||
"name": "Address", |
||||
"type": 14, |
||||
"typeArguments": null |
||||
}, |
||||
{ |
||||
"name": "ContractId", |
||||
"type": 15, |
||||
"typeArguments": null |
||||
} |
||||
], |
||||
"typeParameters": null |
||||
}, |
||||
{ |
||||
"typeId": 5, |
||||
"type": "enum Option", |
||||
"components": [ |
||||
{ |
||||
"name": "None", |
||||
"type": 0, |
||||
"typeArguments": null |
||||
}, |
||||
{ |
||||
"name": "Some", |
||||
"type": 6, |
||||
"typeArguments": null |
||||
} |
||||
], |
||||
"typeParameters": [ |
||||
6 |
||||
] |
||||
}, |
||||
{ |
||||
"typeId": 6, |
||||
"type": "generic T", |
||||
"components": null, |
||||
"typeParameters": null |
||||
}, |
||||
{ |
||||
"typeId": 7, |
||||
"type": "raw untyped ptr", |
||||
"components": null, |
||||
"typeParameters": null |
||||
}, |
||||
{ |
||||
"typeId": 8, |
||||
"type": "str[12]", |
||||
"components": null, |
||||
"typeParameters": null |
||||
}, |
||||
{ |
||||
"typeId": 9, |
||||
"type": "str[16]", |
||||
"components": null, |
||||
"typeParameters": null |
||||
}, |
||||
{ |
||||
"typeId": 10, |
||||
"type": "str[6]", |
||||
"components": null, |
||||
"typeParameters": null |
||||
}, |
||||
{ |
||||
"typeId": 11, |
||||
"type": "str[7]", |
||||
"components": null, |
||||
"typeParameters": null |
||||
}, |
||||
{ |
||||
"typeId": 12, |
||||
"type": "str[8]", |
||||
"components": null, |
||||
"typeParameters": null |
||||
}, |
||||
{ |
||||
"typeId": 13, |
||||
"type": "str[9]", |
||||
"components": null, |
||||
"typeParameters": null |
||||
}, |
||||
{ |
||||
"typeId": 14, |
||||
"type": "struct Address", |
||||
"components": [ |
||||
{ |
||||
"name": "value", |
||||
"type": 2, |
||||
"typeArguments": null |
||||
} |
||||
], |
||||
"typeParameters": null |
||||
}, |
||||
{ |
||||
"typeId": 15, |
||||
"type": "struct ContractId", |
||||
"components": [ |
||||
{ |
||||
"name": "value", |
||||
"type": 2, |
||||
"typeArguments": null |
||||
} |
||||
], |
||||
"typeParameters": null |
||||
}, |
||||
{ |
||||
"typeId": 16, |
||||
"type": "struct Message", |
||||
"components": [ |
||||
{ |
||||
"name": "version", |
||||
"type": 22, |
||||
"typeArguments": null |
||||
}, |
||||
{ |
||||
"name": "nonce", |
||||
"type": 20, |
||||
"typeArguments": null |
||||
}, |
||||
{ |
||||
"name": "origin", |
||||
"type": 20, |
||||
"typeArguments": null |
||||
}, |
||||
{ |
||||
"name": "sender", |
||||
"type": 2, |
||||
"typeArguments": null |
||||
}, |
||||
{ |
||||
"name": "destination", |
||||
"type": 20, |
||||
"typeArguments": null |
||||
}, |
||||
{ |
||||
"name": "recipient", |
||||
"type": 2, |
||||
"typeArguments": null |
||||
}, |
||||
{ |
||||
"name": "body", |
||||
"type": 19, |
||||
"typeArguments": [ |
||||
{ |
||||
"name": "", |
||||
"type": 22, |
||||
"typeArguments": null |
||||
} |
||||
] |
||||
} |
||||
], |
||||
"typeParameters": null |
||||
}, |
||||
{ |
||||
"typeId": 17, |
||||
"type": "struct OwnershipTransferredEvent", |
||||
"components": [ |
||||
{ |
||||
"name": "previous_owner", |
||||
"type": 5, |
||||
"typeArguments": [ |
||||
{ |
||||
"name": "", |
||||
"type": 4, |
||||
"typeArguments": null |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
"name": "new_owner", |
||||
"type": 5, |
||||
"typeArguments": [ |
||||
{ |
||||
"name": "", |
||||
"type": 4, |
||||
"typeArguments": null |
||||
} |
||||
] |
||||
} |
||||
], |
||||
"typeParameters": null |
||||
}, |
||||
{ |
||||
"typeId": 18, |
||||
"type": "struct RawVec", |
||||
"components": [ |
||||
{ |
||||
"name": "ptr", |
||||
"type": 7, |
||||
"typeArguments": null |
||||
}, |
||||
{ |
||||
"name": "cap", |
||||
"type": 21, |
||||
"typeArguments": null |
||||
} |
||||
], |
||||
"typeParameters": [ |
||||
6 |
||||
] |
||||
}, |
||||
{ |
||||
"typeId": 19, |
||||
"type": "struct Vec", |
||||
"components": [ |
||||
{ |
||||
"name": "buf", |
||||
"type": 18, |
||||
"typeArguments": [ |
||||
{ |
||||
"name": "", |
||||
"type": 6, |
||||
"typeArguments": null |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
"name": "len", |
||||
"type": 21, |
||||
"typeArguments": null |
||||
} |
||||
], |
||||
"typeParameters": [ |
||||
6 |
||||
] |
||||
}, |
||||
{ |
||||
"typeId": 20, |
||||
"type": "u32", |
||||
"components": null, |
||||
"typeParameters": null |
||||
}, |
||||
{ |
||||
"typeId": 21, |
||||
"type": "u64", |
||||
"components": null, |
||||
"typeParameters": null |
||||
}, |
||||
{ |
||||
"typeId": 22, |
||||
"type": "u8", |
||||
"components": null, |
||||
"typeParameters": null |
||||
} |
||||
], |
||||
"functions": [ |
||||
{ |
||||
"inputs": [], |
||||
"name": "count", |
||||
"output": { |
||||
"name": "", |
||||
"type": 20, |
||||
"typeArguments": null |
||||
} |
||||
}, |
||||
{ |
||||
"inputs": [ |
||||
{ |
||||
"name": "message_id", |
||||
"type": 2, |
||||
"typeArguments": null |
||||
} |
||||
], |
||||
"name": "delivered", |
||||
"output": { |
||||
"name": "", |
||||
"type": 3, |
||||
"typeArguments": null |
||||
} |
||||
}, |
||||
{ |
||||
"inputs": [ |
||||
{ |
||||
"name": "destination_domain", |
||||
"type": 20, |
||||
"typeArguments": null |
||||
}, |
||||
{ |
||||
"name": "recipient", |
||||
"type": 2, |
||||
"typeArguments": null |
||||
}, |
||||
{ |
||||
"name": "message_body", |
||||
"type": 19, |
||||
"typeArguments": [ |
||||
{ |
||||
"name": "", |
||||
"type": 22, |
||||
"typeArguments": null |
||||
} |
||||
] |
||||
} |
||||
], |
||||
"name": "dispatch", |
||||
"output": { |
||||
"name": "", |
||||
"type": 2, |
||||
"typeArguments": null |
||||
} |
||||
}, |
||||
{ |
||||
"inputs": [], |
||||
"name": "get_default_ism", |
||||
"output": { |
||||
"name": "", |
||||
"type": 15, |
||||
"typeArguments": null |
||||
} |
||||
}, |
||||
{ |
||||
"inputs": [], |
||||
"name": "latest_checkpoint", |
||||
"output": { |
||||
"name": "", |
||||
"type": 1, |
||||
"typeArguments": null |
||||
} |
||||
}, |
||||
{ |
||||
"inputs": [ |
||||
{ |
||||
"name": "metadata", |
||||
"type": 19, |
||||
"typeArguments": [ |
||||
{ |
||||
"name": "", |
||||
"type": 22, |
||||
"typeArguments": null |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
"name": "_message", |
||||
"type": 16, |
||||
"typeArguments": null |
||||
} |
||||
], |
||||
"name": "process", |
||||
"output": { |
||||
"name": "", |
||||
"type": 0, |
||||
"typeArguments": null |
||||
} |
||||
}, |
||||
{ |
||||
"inputs": [], |
||||
"name": "root", |
||||
"output": { |
||||
"name": "", |
||||
"type": 2, |
||||
"typeArguments": null |
||||
} |
||||
}, |
||||
{ |
||||
"inputs": [ |
||||
{ |
||||
"name": "module", |
||||
"type": 15, |
||||
"typeArguments": null |
||||
} |
||||
], |
||||
"name": "set_default_ism", |
||||
"output": { |
||||
"name": "", |
||||
"type": 0, |
||||
"typeArguments": null |
||||
} |
||||
}, |
||||
{ |
||||
"inputs": [], |
||||
"name": "owner", |
||||
"output": { |
||||
"name": "", |
||||
"type": 5, |
||||
"typeArguments": [ |
||||
{ |
||||
"name": "", |
||||
"type": 4, |
||||
"typeArguments": null |
||||
} |
||||
] |
||||
} |
||||
}, |
||||
{ |
||||
"inputs": [ |
||||
{ |
||||
"name": "new_owner", |
||||
"type": 5, |
||||
"typeArguments": [ |
||||
{ |
||||
"name": "", |
||||
"type": 4, |
||||
"typeArguments": null |
||||
} |
||||
] |
||||
} |
||||
], |
||||
"name": "transfer_ownership", |
||||
"output": { |
||||
"name": "", |
||||
"type": 0, |
||||
"typeArguments": null |
||||
} |
||||
} |
||||
], |
||||
"loggedTypes": [ |
||||
{ |
||||
"logId": 0, |
||||
"loggedType": { |
||||
"name": "", |
||||
"type": 8, |
||||
"typeArguments": null |
||||
} |
||||
}, |
||||
{ |
||||
"logId": 1, |
||||
"loggedType": { |
||||
"name": "", |
||||
"type": 9, |
||||
"typeArguments": null |
||||
} |
||||
}, |
||||
{ |
||||
"logId": 2, |
||||
"loggedType": { |
||||
"name": "", |
||||
"type": 12, |
||||
"typeArguments": null |
||||
} |
||||
}, |
||||
{ |
||||
"logId": 3, |
||||
"loggedType": { |
||||
"name": "", |
||||
"type": 8, |
||||
"typeArguments": null |
||||
} |
||||
}, |
||||
{ |
||||
"logId": 4, |
||||
"loggedType": { |
||||
"name": "", |
||||
"type": 13, |
||||
"typeArguments": null |
||||
} |
||||
}, |
||||
{ |
||||
"logId": 5, |
||||
"loggedType": { |
||||
"name": "", |
||||
"type": 11, |
||||
"typeArguments": null |
||||
} |
||||
}, |
||||
{ |
||||
"logId": 6, |
||||
"loggedType": { |
||||
"name": "", |
||||
"type": 2, |
||||
"typeArguments": null |
||||
} |
||||
}, |
||||
{ |
||||
"logId": 7, |
||||
"loggedType": { |
||||
"name": "", |
||||
"type": 10, |
||||
"typeArguments": null |
||||
} |
||||
}, |
||||
{ |
||||
"logId": 8, |
||||
"loggedType": { |
||||
"name": "", |
||||
"type": 10, |
||||
"typeArguments": null |
||||
} |
||||
}, |
||||
{ |
||||
"logId": 9, |
||||
"loggedType": { |
||||
"name": "", |
||||
"type": 17, |
||||
"typeArguments": [] |
||||
} |
||||
} |
||||
], |
||||
"messagesTypes": [] |
||||
} |
@ -1,164 +0,0 @@ |
||||
use std::collections::HashMap; |
||||
use std::fmt::{Debug, Formatter}; |
||||
use std::num::NonZeroU64; |
||||
use std::ops::RangeInclusive; |
||||
|
||||
use async_trait::async_trait; |
||||
use fuels::prelude::{Bech32ContractId, WalletUnlocked}; |
||||
use hyperlane_core::Indexed; |
||||
use tracing::instrument; |
||||
|
||||
use hyperlane_core::{ |
||||
utils::bytes_to_hex, ChainCommunicationError, ChainResult, ContractLocator, HyperlaneAbi, |
||||
HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, |
||||
Indexer, LogMeta, Mailbox, TxCostEstimate, TxOutcome, H256, U256, |
||||
}; |
||||
|
||||
use crate::{ |
||||
contracts::mailbox::Mailbox as FuelMailboxInner, conversions::*, make_provider, ConnectionConf, |
||||
}; |
||||
|
||||
/// A reference to a Mailbox contract on some Fuel chain
|
||||
pub struct FuelMailbox { |
||||
contract: FuelMailboxInner, |
||||
domain: HyperlaneDomain, |
||||
} |
||||
|
||||
impl FuelMailbox { |
||||
/// Create a new fuel mailbox
|
||||
pub fn new( |
||||
conf: &ConnectionConf, |
||||
locator: ContractLocator, |
||||
mut wallet: WalletUnlocked, |
||||
) -> ChainResult<Self> { |
||||
let provider = make_provider(conf)?; |
||||
wallet.set_provider(provider); |
||||
let address = Bech32ContractId::from_h256(&locator.address); |
||||
|
||||
Ok(FuelMailbox { |
||||
contract: FuelMailboxInner::new(address, wallet), |
||||
domain: locator.domain.clone(), |
||||
}) |
||||
} |
||||
} |
||||
|
||||
impl HyperlaneContract for FuelMailbox { |
||||
fn address(&self) -> H256 { |
||||
self.contract.contract_id().into_h256() |
||||
} |
||||
} |
||||
|
||||
impl HyperlaneChain for FuelMailbox { |
||||
fn domain(&self) -> &HyperlaneDomain { |
||||
&self.domain |
||||
} |
||||
|
||||
fn provider(&self) -> Box<dyn HyperlaneProvider> { |
||||
todo!() |
||||
} |
||||
} |
||||
|
||||
impl Debug for FuelMailbox { |
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
||||
write!(f, "{:?}", self as &dyn HyperlaneContract) |
||||
} |
||||
} |
||||
|
||||
#[async_trait] |
||||
impl Mailbox for FuelMailbox { |
||||
#[instrument(level = "debug", err, ret, skip(self))] |
||||
async fn count(&self, lag: Option<NonZeroU64>) -> ChainResult<u32> { |
||||
assert!( |
||||
lag.is_none(), |
||||
"Fuel does not support querying point-in-time" |
||||
); |
||||
self.contract |
||||
.methods() |
||||
.count() |
||||
.simulate() |
||||
.await |
||||
.map(|r| r.value) |
||||
.map_err(ChainCommunicationError::from_other) |
||||
} |
||||
|
||||
#[instrument(level = "debug", err, ret, skip(self))] |
||||
async fn delivered(&self, id: H256) -> ChainResult<bool> { |
||||
todo!() |
||||
} |
||||
|
||||
#[instrument(err, ret, skip(self))] |
||||
async fn default_ism(&self) -> ChainResult<H256> { |
||||
todo!() |
||||
} |
||||
|
||||
#[instrument(err, ret, skip(self))] |
||||
async fn recipient_ism(&self, recipient: H256) -> ChainResult<H256> { |
||||
todo!() |
||||
} |
||||
|
||||
#[instrument(err, ret, skip(self))] |
||||
async fn process( |
||||
&self, |
||||
message: &HyperlaneMessage, |
||||
metadata: &[u8], |
||||
tx_gas_limit: Option<U256>, |
||||
) -> ChainResult<TxOutcome> { |
||||
todo!() |
||||
} |
||||
|
||||
#[instrument(err, ret, skip(self), fields(msg=%message, metadata=%bytes_to_hex(metadata)))] |
||||
async fn process_estimate_costs( |
||||
&self, |
||||
message: &HyperlaneMessage, |
||||
metadata: &[u8], |
||||
) -> ChainResult<TxCostEstimate> { |
||||
todo!() |
||||
} |
||||
|
||||
fn process_calldata(&self, message: &HyperlaneMessage, metadata: &[u8]) -> Vec<u8> { |
||||
todo!() |
||||
} |
||||
} |
||||
|
||||
/// Struct that retrieves event data for a Fuel Mailbox contract
|
||||
#[derive(Debug)] |
||||
pub struct FuelMailboxIndexer {} |
||||
|
||||
#[async_trait] |
||||
impl Indexer<HyperlaneMessage> for FuelMailboxIndexer { |
||||
async fn fetch_logs_in_range( |
||||
&self, |
||||
range: RangeInclusive<u32>, |
||||
) -> ChainResult<Vec<(Indexed<HyperlaneMessage>, LogMeta)>> { |
||||
todo!() |
||||
} |
||||
|
||||
async fn get_finalized_block_number(&self) -> ChainResult<u32> { |
||||
todo!() |
||||
} |
||||
} |
||||
|
||||
#[async_trait] |
||||
impl Indexer<H256> for FuelMailboxIndexer { |
||||
async fn fetch_logs_in_range( |
||||
&self, |
||||
range: RangeInclusive<u32>, |
||||
) -> ChainResult<Vec<(Indexed<H256>, LogMeta)>> { |
||||
todo!() |
||||
} |
||||
|
||||
async fn get_finalized_block_number(&self) -> ChainResult<u32> { |
||||
todo!() |
||||
} |
||||
} |
||||
|
||||
struct FuelMailboxAbi; |
||||
|
||||
impl HyperlaneAbi for FuelMailboxAbi { |
||||
const SELECTOR_SIZE_BYTES: usize = 8; |
||||
|
||||
fn fn_map() -> HashMap<Vec<u8>, &'static str> { |
||||
// Can't support this without Fuels exporting it in the generated code
|
||||
todo!() |
||||
} |
||||
} |
@ -1,43 +0,0 @@ |
||||
use async_trait::async_trait; |
||||
|
||||
use hyperlane_core::{ |
||||
BlockInfo, ChainInfo, ChainResult, HyperlaneChain, HyperlaneDomain, HyperlaneProvider, TxnInfo, |
||||
H256, U256, |
||||
}; |
||||
|
||||
/// A wrapper around a fuel provider to get generic blockchain information.
|
||||
#[derive(Debug)] |
||||
pub struct FuelProvider {} |
||||
|
||||
impl HyperlaneChain for FuelProvider { |
||||
fn domain(&self) -> &HyperlaneDomain { |
||||
todo!() |
||||
} |
||||
|
||||
fn provider(&self) -> Box<dyn HyperlaneProvider> { |
||||
todo!() |
||||
} |
||||
} |
||||
|
||||
#[async_trait] |
||||
impl HyperlaneProvider for FuelProvider { |
||||
async fn get_block_by_hash(&self, hash: &H256) -> ChainResult<BlockInfo> { |
||||
todo!() |
||||
} |
||||
|
||||
async fn get_txn_by_hash(&self, hash: &H256) -> ChainResult<TxnInfo> { |
||||
todo!() |
||||
} |
||||
|
||||
async fn is_contract(&self, address: &H256) -> ChainResult<bool> { |
||||
todo!() |
||||
} |
||||
|
||||
async fn get_balance(&self, address: String) -> ChainResult<U256> { |
||||
todo!() |
||||
} |
||||
|
||||
async fn get_chain_metrics(&self) -> ChainResult<Option<ChainInfo>> { |
||||
Ok(None) |
||||
} |
||||
} |
@ -1,35 +0,0 @@ |
||||
cargo-features = ["workspace-inheritance"] |
||||
|
||||
[package] |
||||
name = "hyperlane-sealevel" |
||||
version = "0.1.0" |
||||
edition = "2021" |
||||
|
||||
[dependencies] |
||||
anyhow.workspace = true |
||||
async-trait.workspace = true |
||||
base64.workspace = true |
||||
borsh.workspace = true |
||||
derive-new.workspace = true |
||||
jsonrpc-core.workspace = true |
||||
num-traits.workspace = true |
||||
serde.workspace = true |
||||
solana-account-decoder.workspace = true |
||||
solana-client.workspace = true |
||||
solana-sdk.workspace = true |
||||
solana-transaction-status.workspace = true |
||||
thiserror.workspace = true |
||||
tracing-futures.workspace = true |
||||
tracing.workspace = true |
||||
url.workspace = true |
||||
|
||||
account-utils = { path = "../../sealevel/libraries/account-utils" } |
||||
hyperlane-core = { path = "../../hyperlane-core", features = ["solana", "async"] } |
||||
hyperlane-sealevel-interchain-security-module-interface = { path = "../../sealevel/libraries/interchain-security-module-interface" } |
||||
hyperlane-sealevel-mailbox = { path = "../../sealevel/programs/mailbox", features = ["no-entrypoint"] } |
||||
hyperlane-sealevel-igp = { path = "../../sealevel/programs/hyperlane-sealevel-igp", features = ["no-entrypoint"] } |
||||
hyperlane-sealevel-message-recipient-interface = { path = "../../sealevel/libraries/message-recipient-interface" } |
||||
hyperlane-sealevel-multisig-ism-message-id = { path = "../../sealevel/programs/ism/multisig-ism-message-id", features = ["no-entrypoint"] } |
||||
hyperlane-sealevel-validator-announce = { path = "../../sealevel/programs/validator-announce", features = ["no-entrypoint"] } |
||||
multisig-ism = { path = "../../sealevel/libraries/multisig-ism" } |
||||
serializable-account-meta = { path = "../../sealevel/libraries/serializable-account-meta" } |
@ -1,29 +0,0 @@ |
||||
use solana_client::nonblocking::rpc_client::RpcClient; |
||||
use solana_sdk::commitment_config::CommitmentConfig; |
||||
|
||||
/// Kludge to implement Debug for RpcClient.
|
||||
pub struct RpcClientWithDebug(RpcClient); |
||||
|
||||
impl RpcClientWithDebug { |
||||
pub fn new(rpc_endpoint: String) -> Self { |
||||
Self(RpcClient::new(rpc_endpoint)) |
||||
} |
||||
|
||||
pub fn new_with_commitment(rpc_endpoint: String, commitment: CommitmentConfig) -> Self { |
||||
Self(RpcClient::new_with_commitment(rpc_endpoint, commitment)) |
||||
} |
||||
} |
||||
|
||||
impl std::fmt::Debug for RpcClientWithDebug { |
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
||||
f.write_str("RpcClient { ... }") |
||||
} |
||||
} |
||||
|
||||
impl std::ops::Deref for RpcClientWithDebug { |
||||
type Target = RpcClient; |
||||
|
||||
fn deref(&self) -> &Self::Target { |
||||
&self.0 |
||||
} |
||||
} |
@ -1,23 +0,0 @@ |
||||
use hyperlane_core::ChainCommunicationError; |
||||
use solana_client::client_error::ClientError; |
||||
use solana_sdk::pubkey::ParsePubkeyError; |
||||
|
||||
/// Errors from the crates specific to the hyperlane-sealevel
|
||||
/// implementation.
|
||||
/// This error can then be converted into the broader error type
|
||||
/// in hyperlane-core using the `From` trait impl
|
||||
#[derive(Debug, thiserror::Error)] |
||||
pub enum HyperlaneSealevelError { |
||||
/// ParsePubkeyError error
|
||||
#[error("{0}")] |
||||
ParsePubkeyError(#[from] ParsePubkeyError), |
||||
/// ClientError error
|
||||
#[error("{0}")] |
||||
ClientError(#[from] ClientError), |
||||
} |
||||
|
||||
impl From<HyperlaneSealevelError> for ChainCommunicationError { |
||||
fn from(value: HyperlaneSealevelError) -> Self { |
||||
ChainCommunicationError::from_other(value) |
||||
} |
||||
} |
@ -1,84 +0,0 @@ |
||||
use std::{str::FromStr, sync::Arc}; |
||||
|
||||
use async_trait::async_trait; |
||||
|
||||
use hyperlane_core::{ |
||||
BlockInfo, ChainInfo, ChainResult, HyperlaneChain, HyperlaneDomain, HyperlaneProvider, TxnInfo, |
||||
H256, U256, |
||||
}; |
||||
use solana_sdk::{commitment_config::CommitmentConfig, pubkey::Pubkey}; |
||||
|
||||
use crate::{client::RpcClientWithDebug, error::HyperlaneSealevelError, ConnectionConf}; |
||||
|
||||
/// A wrapper around a Sealevel provider to get generic blockchain information.
|
||||
#[derive(Debug)] |
||||
pub struct SealevelProvider { |
||||
domain: HyperlaneDomain, |
||||
rpc_client: Arc<RpcClientWithDebug>, |
||||
} |
||||
|
||||
impl SealevelProvider { |
||||
/// Create a new Sealevel provider.
|
||||
pub fn new(domain: HyperlaneDomain, conf: &ConnectionConf) -> Self { |
||||
// Set the `processed` commitment at rpc level
|
||||
let rpc_client = Arc::new(RpcClientWithDebug::new_with_commitment( |
||||
conf.url.to_string(), |
||||
CommitmentConfig::processed(), |
||||
)); |
||||
|
||||
SealevelProvider { domain, rpc_client } |
||||
} |
||||
|
||||
/// Get an rpc client
|
||||
pub fn rpc(&self) -> &RpcClientWithDebug { |
||||
&self.rpc_client |
||||
} |
||||
|
||||
/// Get the balance of an address
|
||||
pub async fn get_balance(&self, address: String) -> ChainResult<U256> { |
||||
let pubkey = Pubkey::from_str(&address).map_err(Into::<HyperlaneSealevelError>::into)?; |
||||
let balance = self |
||||
.rpc_client |
||||
.get_balance(&pubkey) |
||||
.await |
||||
.map_err(Into::<HyperlaneSealevelError>::into)?; |
||||
Ok(balance.into()) |
||||
} |
||||
} |
||||
|
||||
impl HyperlaneChain for SealevelProvider { |
||||
fn domain(&self) -> &HyperlaneDomain { |
||||
&self.domain |
||||
} |
||||
|
||||
fn provider(&self) -> Box<dyn HyperlaneProvider> { |
||||
Box::new(SealevelProvider { |
||||
domain: self.domain.clone(), |
||||
rpc_client: self.rpc_client.clone(), |
||||
}) |
||||
} |
||||
} |
||||
|
||||
#[async_trait] |
||||
impl HyperlaneProvider for SealevelProvider { |
||||
async fn get_block_by_hash(&self, _hash: &H256) -> ChainResult<BlockInfo> { |
||||
todo!() // FIXME
|
||||
} |
||||
|
||||
async fn get_txn_by_hash(&self, _hash: &H256) -> ChainResult<TxnInfo> { |
||||
todo!() // FIXME
|
||||
} |
||||
|
||||
async fn is_contract(&self, _address: &H256) -> ChainResult<bool> { |
||||
// FIXME
|
||||
Ok(true) |
||||
} |
||||
|
||||
async fn get_balance(&self, address: String) -> ChainResult<U256> { |
||||
self.get_balance(address).await |
||||
} |
||||
|
||||
async fn get_chain_metrics(&self) -> ChainResult<Option<ChainInfo>> { |
||||
Ok(None) |
||||
} |
||||
} |
@ -1,93 +0,0 @@ |
||||
use base64::Engine; |
||||
use borsh::{BorshDeserialize, BorshSerialize}; |
||||
use hyperlane_core::{ChainCommunicationError, ChainResult}; |
||||
|
||||
use serializable_account_meta::{SerializableAccountMeta, SimulationReturnData}; |
||||
use solana_client::nonblocking::rpc_client::RpcClient; |
||||
use solana_sdk::{ |
||||
commitment_config::CommitmentConfig, |
||||
instruction::{AccountMeta, Instruction}, |
||||
message::Message, |
||||
signature::{Keypair, Signer}, |
||||
transaction::Transaction, |
||||
}; |
||||
use solana_transaction_status::UiReturnDataEncoding; |
||||
|
||||
use crate::client::RpcClientWithDebug; |
||||
|
||||
/// Simulates an instruction, and attempts to deserialize it into a T.
|
||||
/// If no return data at all was returned, returns Ok(None).
|
||||
/// If some return data was returned but deserialization was unsuccessful,
|
||||
/// an Err is returned.
|
||||
pub async fn simulate_instruction<T: BorshDeserialize + BorshSerialize>( |
||||
rpc_client: &RpcClient, |
||||
payer: &Keypair, |
||||
instruction: Instruction, |
||||
) -> ChainResult<Option<T>> { |
||||
let commitment = CommitmentConfig::finalized(); |
||||
let (recent_blockhash, _) = rpc_client |
||||
.get_latest_blockhash_with_commitment(commitment) |
||||
.await |
||||
.map_err(ChainCommunicationError::from_other)?; |
||||
let return_data = rpc_client |
||||
.simulate_transaction(&Transaction::new_unsigned(Message::new_with_blockhash( |
||||
&[instruction], |
||||
Some(&payer.pubkey()), |
||||
&recent_blockhash, |
||||
))) |
||||
.await |
||||
.map_err(ChainCommunicationError::from_other)? |
||||
.value |
||||
.return_data; |
||||
|
||||
if let Some(return_data) = return_data { |
||||
let bytes = match return_data.data.1 { |
||||
UiReturnDataEncoding::Base64 => base64::engine::general_purpose::STANDARD |
||||
.decode(return_data.data.0) |
||||
.map_err(ChainCommunicationError::from_other)?, |
||||
}; |
||||
|
||||
let decoded_data = |
||||
T::try_from_slice(bytes.as_slice()).map_err(ChainCommunicationError::from_other)?; |
||||
|
||||
return Ok(Some(decoded_data)); |
||||
} |
||||
|
||||
Ok(None) |
||||
} |
||||
|
||||
/// Simulates an Instruction that will return a list of AccountMetas.
|
||||
pub async fn get_account_metas( |
||||
rpc_client: &RpcClient, |
||||
payer: &Keypair, |
||||
instruction: Instruction, |
||||
) -> ChainResult<Vec<AccountMeta>> { |
||||
// If there's no data at all, default to an empty vec.
|
||||
let account_metas = simulate_instruction::<SimulationReturnData<Vec<SerializableAccountMeta>>>( |
||||
rpc_client, |
||||
payer, |
||||
instruction, |
||||
) |
||||
.await? |
||||
.map(|serializable_account_metas| { |
||||
serializable_account_metas |
||||
.return_data |
||||
.into_iter() |
||||
.map(|serializable_account_meta| serializable_account_meta.into()) |
||||
.collect() |
||||
}) |
||||
.unwrap_or_else(Vec::new); |
||||
|
||||
Ok(account_metas) |
||||
} |
||||
|
||||
pub async fn get_finalized_block_number(rpc_client: &RpcClientWithDebug) -> ChainResult<u32> { |
||||
let height = rpc_client |
||||
.get_block_height() |
||||
.await |
||||
.map_err(ChainCommunicationError::from_other)? |
||||
.try_into() |
||||
// FIXME solana block height is u64...
|
||||
.expect("sealevel block height exceeds u32::MAX"); |
||||
Ok(height) |
||||
} |
@ -1,974 +0,0 @@ |
||||
{ |
||||
"chains": { |
||||
"alfajores": { |
||||
"aggregationHook": "0xdBabD76358897E68E4964647C1fb8Bf524f5EFdB", |
||||
"blockExplorers": [ |
||||
{ |
||||
"apiUrl": "https://api-alfajores.celoscan.io/api", |
||||
"family": "etherscan", |
||||
"name": "CeloScan", |
||||
"url": "https://alfajores.celoscan.io" |
||||
}, |
||||
{ |
||||
"apiUrl": "https://explorer.celo.org/alfajores/api", |
||||
"family": "blockscout", |
||||
"name": "Blockscout", |
||||
"url": "https://explorer.celo.org/alfajores" |
||||
} |
||||
], |
||||
"blocks": { |
||||
"confirmations": 1, |
||||
"estimateBlockTime": 5, |
||||
"reorgPeriod": 0 |
||||
}, |
||||
"chainId": 44787, |
||||
"deployer": { |
||||
"name": "Abacus Works", |
||||
"url": "https://www.hyperlane.xyz" |
||||
}, |
||||
"displayName": "Alfajores", |
||||
"domainId": 44787, |
||||
"domainRoutingIsm": "0xD1DCBe1546bb911f2570E939a231A28F14C29638", |
||||
"domainRoutingIsmFactory": "0x30d9A03762431F8A917a0C469E7A62Bf55092Ca6", |
||||
"fallbackRoutingHook": "0x3528B1aeF3a3d29E0eae90ad777A2b4A6a48aC3F", |
||||
"index": { |
||||
"from": 20231908 |
||||
}, |
||||
"interchainAccountIsm": "0x6895d3916B94b386fAA6ec9276756e16dAe7480E", |
||||
"interchainAccountRouter": "0xEbA64c8a9b4a61a9210d5fe7E4375380999C821b", |
||||
"interchainGasPaymaster": "0x44769b0f4a6f01339e131a691cc2eebbb519d297", |
||||
"interchainSecurityModule": "0xEE179dd1b1beD39449e81c003D9629e92A5c0085", |
||||
"isTestnet": true, |
||||
"mailbox": "0xEf9F292fcEBC3848bF4bB92a96a04F9ECBb78E59", |
||||
"merkleTreeHook": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", |
||||
"name": "alfajores", |
||||
"nativeToken": { |
||||
"decimals": 18, |
||||
"name": "CELO", |
||||
"symbol": "CELO" |
||||
}, |
||||
"pausableHook": "0x4a2902395A40Ecf0B57CaB362b59bAffba9BB4aE", |
||||
"pausableIsm": "0xA4caB1565083D33899A6eE69B174cC7729b3EaDF", |
||||
"protocol": "ethereum", |
||||
"protocolFee": "0xC9D50584F08Bf6cCD1004d14c7062044b45E3b48", |
||||
"proxyAdmin": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", |
||||
"rpcUrls": [ |
||||
{ |
||||
"http": "https://alfajores-forno.celo-testnet.org" |
||||
} |
||||
], |
||||
"staticAggregationHookFactory": "0x71bB34Ee833467443628CEdFAA188B2387827Cee", |
||||
"staticAggregationIsm": "0x8B29157852340cC5d3d0E289be3B0344E8812173", |
||||
"staticAggregationIsmFactory": "0x4bE8AC22f506B1504C93C3A5b1579C5e7c550D9C", |
||||
"staticMerkleRootMultisigIsmFactory": "0xa9C7e306C0941896CA1fd528aA59089571D8D67E", |
||||
"staticMessageIdMultisigIsmFactory": "0xC1b8c0e56D6a34940Ee2B86172450B54AFd633A7", |
||||
"storageGasOracle": "0x8356113754C7aCa297Db3089b89F87CC125499fb", |
||||
"testRecipient": "0x6489d13AcAd3B8dce4c5B31f375DE4f9451E7b38", |
||||
"testTokenRecipient": "0x92dC0a76452a9D9358D2d2dEd8CddA209DF67c45", |
||||
"timelockController": "0x0000000000000000000000000000000000000000", |
||||
"validatorAnnounce": "0x3726EE36a2A9e11a40d1ffD7D9A1A16e0154cDA0" |
||||
}, |
||||
"arbitrumsepolia": { |
||||
"aggregationHook": "0xD2670EedcD21116c6F0B331Ce391eA4B3Bf1aB19", |
||||
"blockExplorers": [ |
||||
{ |
||||
"apiUrl": "https://api-sepolia.arbiscan.io/api", |
||||
"family": "etherscan", |
||||
"name": "Arbiscan", |
||||
"url": "https://sepolia.arbiscan.io" |
||||
} |
||||
], |
||||
"blocks": { |
||||
"confirmations": 1, |
||||
"estimateBlockTime": 3, |
||||
"reorgPeriod": 0 |
||||
}, |
||||
"chainId": 421614, |
||||
"deployer": { |
||||
"name": "Abacus Works", |
||||
"url": "https://www.hyperlane.xyz" |
||||
}, |
||||
"displayName": "Arbitrum Sepolia", |
||||
"domainId": 421614, |
||||
"domainRoutingIsm": "0xaCA2f65aFDa2cbC8BF28DdE096eCF83aCd121c0b", |
||||
"domainRoutingIsmFactory": "0xd785272D240B07719e417622cbd2cfA0E584d1bd", |
||||
"fallbackRoutingHook": "0xAb9B273366D794B7F80B4378bc8Aaca75C6178E2", |
||||
"index": { |
||||
"from": 49690504 |
||||
}, |
||||
"interchainGasPaymaster": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", |
||||
"interchainSecurityModule": "0x29cEAFEE1F76B9CE271750f86B2bD12C23F9dDb6", |
||||
"isTestnet": true, |
||||
"mailbox": "0x598facE78a4302f11E3de0bee1894Da0b2Cb71F8", |
||||
"merkleTreeHook": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", |
||||
"name": "arbitrumsepolia", |
||||
"nativeToken": { |
||||
"decimals": 18, |
||||
"name": "Ether", |
||||
"symbol": "ETH" |
||||
}, |
||||
"pausableHook": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", |
||||
"pausableIsm": "0x54ba950390670f27892AFFC670Ba6ed598E5B8Df", |
||||
"protocol": "ethereum", |
||||
"protocolFee": "0x75f3E2a4f424401195A5E176246Ecc9f7e7680ff", |
||||
"proxyAdmin": "0x666a24F62f7A97BA33c151776Eb3D9441a059eB8", |
||||
"rpcUrls": [ |
||||
{ |
||||
"http": "https://public.stackup.sh/api/v1/node/arbitrum-sepolia" |
||||
}, |
||||
{ |
||||
"http": "https://arbitrum-sepolia.blockpi.network/v1/rpc/public" |
||||
} |
||||
], |
||||
"staticAggregationHookFactory": "0x0526E47C49742C15F8817ef8cf0d8FFc72139D4F", |
||||
"staticAggregationIsm": "0x29cEAFEE1F76B9CE271750f86B2bD12C23F9dDb6", |
||||
"staticAggregationIsmFactory": "0xfc8d0D2E15A36f1A3F3aE3Cb127B706c1f23Aadc", |
||||
"staticMerkleRootMultisigIsmFactory": "0x1D5EbC3e15e9ECDe0e3530C85899556797eeaea5", |
||||
"staticMessageIdMultisigIsmFactory": "0xF7F0DaB0BECE4498dAc7eb616e288809D4499371", |
||||
"storageGasOracle": "0xddf4C3e791caCaFd26D7fb275549739B38ae6e75", |
||||
"technicalStack": "arbitrumnitro", |
||||
"testRecipient": "0x6c13643B3927C57DB92c790E4E3E7Ee81e13f78C", |
||||
"validatorAnnounce": "0x1b33611fCc073aB0737011d5512EF673Bff74962" |
||||
}, |
||||
"basesepolia": { |
||||
"aggregationHook": "0xccA408a6A9A6dc405C3278647421eb4317466943", |
||||
"blockExplorers": [ |
||||
{ |
||||
"apiUrl": "https://api-sepolia.basescan.org/api", |
||||
"family": "etherscan", |
||||
"name": "BaseScan", |
||||
"url": "https://sepolia.basescan.org" |
||||
} |
||||
], |
||||
"blocks": { |
||||
"confirmations": 1, |
||||
"estimateBlockTime": 2, |
||||
"reorgPeriod": 1 |
||||
}, |
||||
"chainId": 84532, |
||||
"deployer": { |
||||
"name": "Abacus Works", |
||||
"url": "https://www.hyperlane.xyz" |
||||
}, |
||||
"displayName": "Base Sepolia", |
||||
"domainId": 84532, |
||||
"domainRoutingIsm": "0x4ac19e0bafc2aF6B98094F0a1B817dF196551219", |
||||
"domainRoutingIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", |
||||
"fallbackRoutingHook": "0xddf4C3e791caCaFd26D7fb275549739B38ae6e75", |
||||
"index": { |
||||
"from": 13851043 |
||||
}, |
||||
"interchainGasPaymaster": "0x28B02B97a850872C4D33C3E024fab6499ad96564", |
||||
"interchainSecurityModule": "0x815a9642497Ee1E9F061f8b828C85Eb7193DecfC", |
||||
"isTestnet": true, |
||||
"mailbox": "0x6966b0E55883d49BFB24539356a2f8A673E02039", |
||||
"merkleTreeHook": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", |
||||
"name": "basesepolia", |
||||
"nativeToken": { |
||||
"decimals": 18, |
||||
"name": "Ether", |
||||
"symbol": "ETH" |
||||
}, |
||||
"pausableHook": "0x19Be55D859368e02d7b9C00803Eb677BDC1359Bd", |
||||
"pausableIsm": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", |
||||
"protocol": "ethereum", |
||||
"protocolFee": "0x1b33611fCc073aB0737011d5512EF673Bff74962", |
||||
"proxyAdmin": "0x44b764045BfDC68517e10e783E69B376cef196B2", |
||||
"rpcUrls": [ |
||||
{ |
||||
"http": "https://sepolia.base.org" |
||||
}, |
||||
{ |
||||
"http": "https://base-sepolia-rpc.publicnode.com" |
||||
} |
||||
], |
||||
"staticAggregationHookFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", |
||||
"staticAggregationIsm": "0x815a9642497Ee1E9F061f8b828C85Eb7193DecfC", |
||||
"staticAggregationIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", |
||||
"staticMerkleRootMultisigIsmFactory": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", |
||||
"staticMessageIdMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", |
||||
"storageGasOracle": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", |
||||
"testRecipient": "0x783c4a0bB6663359281aD4a637D5af68F83ae213", |
||||
"validatorAnnounce": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9" |
||||
}, |
||||
"bsctestnet": { |
||||
"aggregationHook": "0x3d675bB93250Ab7603F40cbb9194bae210784627", |
||||
"blockExplorers": [ |
||||
{ |
||||
"apiUrl": "https://api-testnet.bscscan.com/api", |
||||
"family": "etherscan", |
||||
"name": "BscScan", |
||||
"url": "https://testnet.bscscan.com" |
||||
} |
||||
], |
||||
"blocks": { |
||||
"confirmations": 1, |
||||
"estimateBlockTime": 3, |
||||
"reorgPeriod": 9 |
||||
}, |
||||
"chainId": 97, |
||||
"deployer": { |
||||
"name": "Abacus Works", |
||||
"url": "https://www.hyperlane.xyz" |
||||
}, |
||||
"displayName": "BSC Testnet", |
||||
"domainId": 97, |
||||
"domainRoutingIsm": "0x82A4aac9bED3DFDfb9569031E19d18431204681C", |
||||
"domainRoutingIsmFactory": "0xD2a0c68ed92D1Eb3C699D2808b06dd7b70367F92", |
||||
"fallbackRoutingHook": "0x2670ED2EC08cAd135307556685a96bD4c16b007b", |
||||
"index": { |
||||
"chunk": 1000, |
||||
"from": 34323977 |
||||
}, |
||||
"interchainAccountIsm": "0xa9D8Ec959F34272B1a56D09AF00eeee58970d3AE", |
||||
"interchainAccountRouter": "0x6d2B3e304E58c2a19f1492E7cf15CaF63Ce6e0d2", |
||||
"interchainGasPaymaster": "0x0dD20e410bdB95404f71c5a4e7Fa67B892A5f949", |
||||
"interchainSecurityModule": "0xE758870D4E50c2aF2b03341808d54d79F5ec3c1E", |
||||
"isTestnet": true, |
||||
"mailbox": "0xF9F6F5646F478d5ab4e20B0F910C92F1CCC9Cc6D", |
||||
"merkleTreeHook": "0xc6cbF39A747f5E28d1bDc8D9dfDAb2960Abd5A8f", |
||||
"name": "bsctestnet", |
||||
"nativeToken": { |
||||
"decimals": 18, |
||||
"name": "BNB", |
||||
"symbol": "BNB" |
||||
}, |
||||
"pausableHook": "0xA71E50eFd93600933650A324AE43d395a8aE4AC7", |
||||
"pausableIsm": "0xb98B1596897d3c122111ea6b6b9014A95920F459", |
||||
"protocol": "ethereum", |
||||
"protocolFee": "0x3eF0a63B8976b838704Bcc93C78C56b6653E5a39", |
||||
"proxyAdmin": "0xb12282d2E838Aa5f2A4F9Ee5f624a77b7199A078", |
||||
"rpcUrls": [ |
||||
{ |
||||
"http": "https://bsc-testnet.publicnode.com" |
||||
}, |
||||
{ |
||||
"http": "https://bsc-testnet.blockpi.network/v1/rpc/public" |
||||
} |
||||
], |
||||
"staticAggregationHookFactory": "0xa1145B39F1c7Ef9aA593BC1DB1634b00CC020942", |
||||
"staticAggregationIsm": "0x8fb481f65d04c590b8507F75D05Ed29594590376", |
||||
"staticAggregationIsmFactory": "0x40613dE82d672605Ab051C64079022Bb4F8bDE4f", |
||||
"staticMerkleRootMultisigIsmFactory": "0x3E235B90197E1D6b5DB5ad5aD49f2c1ED6406382", |
||||
"staticMessageIdMultisigIsmFactory": "0x0D96aF0c01c4bbbadaaF989Eb489c8783F35B763", |
||||
"storageGasOracle": "0x124EBCBC018A5D4Efe639f02ED86f95cdC3f6498", |
||||
"testRecipient": "0xfbcD1c00a3d809f36cC1A15918694B17B32c0b6c", |
||||
"testTokenRecipient": "0x260f6024119549a40595d0937471e607411E8ea5", |
||||
"timelockController": "0x0000000000000000000000000000000000000000", |
||||
"transactionOverrides": { |
||||
"gasPrice": 8000000000 |
||||
}, |
||||
"validatorAnnounce": "0xf09701B0a93210113D175461b6135a96773B5465" |
||||
}, |
||||
"connextsepolia": { |
||||
"aggregationHook": "0x331eb40963dc11F5BB271308c42d97ac6e41F124", |
||||
"blockExplorers": [ |
||||
{ |
||||
"apiUrl": "https://connext-sepolia.blockscout.com/api", |
||||
"family": "blockscout", |
||||
"name": "Connext Explorer", |
||||
"url": "https://connext-sepolia.blockscout.com" |
||||
} |
||||
], |
||||
"blocks": { |
||||
"confirmations": 1, |
||||
"estimateBlockTime": 10, |
||||
"reorgPeriod": 0 |
||||
}, |
||||
"chainId": 6398, |
||||
"deployer": { |
||||
"name": "Everclear", |
||||
"url": "https://everclear.org" |
||||
}, |
||||
"displayName": "Connext Sepolia", |
||||
"domainId": 6398, |
||||
"domainRoutingIsm": "0x4ac19e0bafc2aF6B98094F0a1B817dF196551219", |
||||
"domainRoutingIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", |
||||
"fallbackRoutingHook": "0x98AAE089CaD930C64a76dD2247a2aC5773a4B8cE", |
||||
"index": { |
||||
"from": 4950 |
||||
}, |
||||
"interchainGasPaymaster": "0xeC7eb4196Bd601DEa7585A744FbFB4CF11278450", |
||||
"interchainSecurityModule": "0x415a45C98288059Cce9c32AE4a09AB19C91d5056", |
||||
"isTestnet": true, |
||||
"mailbox": "0x6966b0E55883d49BFB24539356a2f8A673E02039", |
||||
"merkleTreeHook": "0x4926a10788306D84202A2aDbd290b7743146Cc17", |
||||
"name": "connextsepolia", |
||||
"nativeToken": { |
||||
"decimals": 18, |
||||
"name": "Ether", |
||||
"symbol": "ETH" |
||||
}, |
||||
"pausableHook": "0x07009DA2249c388aD0f416a235AfE90D784e1aAc", |
||||
"pausableIsm": "0x58483b754Abb1E8947BE63d6b95DF75b8249543A", |
||||
"protocol": "ethereum", |
||||
"protocolFee": "0x863E8c26621c52ACa1849C53500606e73BA272F0", |
||||
"proxyAdmin": "0x44b764045BfDC68517e10e783E69B376cef196B2", |
||||
"rpcUrls": [ |
||||
{ |
||||
"http": "https://rpc.connext-sepolia.gelato.digital" |
||||
} |
||||
], |
||||
"staticAggregationHookFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", |
||||
"staticAggregationIsm": "0x5e6Fe18eC7D4b159bDC5Fa0C32bB1996277B3ddF", |
||||
"staticAggregationIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", |
||||
"staticMerkleRootMultisigIsmFactory": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", |
||||
"staticMessageIdMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", |
||||
"storageGasOracle": "0xF7561c34f17A32D5620583A3397C304e7038a7F6", |
||||
"technicalStack": "arbitrumnitro", |
||||
"testRecipient": "0xAb9B273366D794B7F80B4378bc8Aaca75C6178E2", |
||||
"validatorAnnounce": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C" |
||||
}, |
||||
"eclipsetestnet": { |
||||
"blocks": { |
||||
"confirmations": 1, |
||||
"estimateBlockTime": 0.4, |
||||
"reorgPeriod": 0 |
||||
}, |
||||
"chainId": 239092742, |
||||
"deployer": { |
||||
"name": "Abacus Works", |
||||
"url": "https://www.hyperlane.xyz" |
||||
}, |
||||
"displayName": "Eclipse Testnet", |
||||
"domainId": 239092742, |
||||
"index": { |
||||
"from": 1, |
||||
"mode": "sequence" |
||||
}, |
||||
"interchainGasPaymaster": "9SQVtTNsbipdMzumhzi6X8GwojiSMwBfqAhS7FgyTcqy", |
||||
"isTestnet": true, |
||||
"mailbox": "75HBBLae3ddeneJVrZeyrDfv6vb7SMC3aCpBucSXS5aR", |
||||
"merkleTreeHook": "75HBBLae3ddeneJVrZeyrDfv6vb7SMC3aCpBucSXS5aR", |
||||
"name": "eclipsetestnet", |
||||
"nativeToken": { |
||||
"decimals": 9, |
||||
"name": "Ether", |
||||
"symbol": "ETH" |
||||
}, |
||||
"protocol": "sealevel", |
||||
"rpcUrls": [ |
||||
{ |
||||
"http": "https://testnet.dev2.eclipsenetwork.xyz" |
||||
} |
||||
], |
||||
"validatorAnnounce": "8qNYSi9EP1xSnRjtMpyof88A26GBbdcrsa61uSaHiwx3" |
||||
}, |
||||
"ecotestnet": { |
||||
"aggregationHook": "0xccA408a6A9A6dc405C3278647421eb4317466943", |
||||
"blockExplorers": [ |
||||
{ |
||||
"apiUrl": "https://eco-testnet.explorer.caldera.xyz/api", |
||||
"family": "blockscout", |
||||
"name": "ECO Testnet explorer", |
||||
"url": "https://eco-testnet.explorer.caldera.xyz/" |
||||
} |
||||
], |
||||
"blocks": { |
||||
"confirmations": 1, |
||||
"estimateBlockTime": 1, |
||||
"reorgPeriod": 0 |
||||
}, |
||||
"chainId": 471923, |
||||
"deployer": { |
||||
"name": "Abacus Works", |
||||
"url": "https://www.hyperlane.xyz" |
||||
}, |
||||
"displayName": "Eco Testnet", |
||||
"domainId": 471923, |
||||
"domainRoutingIsm": "0x4ac19e0bafc2aF6B98094F0a1B817dF196551219", |
||||
"domainRoutingIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", |
||||
"fallbackRoutingHook": "0xddf4C3e791caCaFd26D7fb275549739B38ae6e75", |
||||
"index": { |
||||
"from": 1606754 |
||||
}, |
||||
"interchainGasPaymaster": "0x28B02B97a850872C4D33C3E024fab6499ad96564", |
||||
"interchainSecurityModule": "0x815a9642497Ee1E9F061f8b828C85Eb7193DecfC", |
||||
"isTestnet": true, |
||||
"mailbox": "0x6966b0E55883d49BFB24539356a2f8A673E02039", |
||||
"merkleTreeHook": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", |
||||
"name": "ecotestnet", |
||||
"nativeToken": { |
||||
"decimals": 18, |
||||
"name": "Ether", |
||||
"symbol": "ETH" |
||||
}, |
||||
"pausableHook": "0x19Be55D859368e02d7b9C00803Eb677BDC1359Bd", |
||||
"pausableIsm": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", |
||||
"protocol": "ethereum", |
||||
"protocolFee": "0x1b33611fCc073aB0737011d5512EF673Bff74962", |
||||
"proxyAdmin": "0x44b764045BfDC68517e10e783E69B376cef196B2", |
||||
"rpcUrls": [ |
||||
{ |
||||
"http": "https://eco-testnet.rpc.caldera.xyz/http" |
||||
} |
||||
], |
||||
"staticAggregationHookFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", |
||||
"staticAggregationIsm": "0x815a9642497Ee1E9F061f8b828C85Eb7193DecfC", |
||||
"staticAggregationIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", |
||||
"staticMerkleRootMultisigIsmFactory": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", |
||||
"staticMessageIdMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", |
||||
"storageGasOracle": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", |
||||
"testRecipient": "0x783c4a0bB6663359281aD4a637D5af68F83ae213", |
||||
"validatorAnnounce": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9" |
||||
}, |
||||
"fuji": { |
||||
"aggregationHook": "0x8E9b4006171c6B75111823e7545Ee5400CEce0B3", |
||||
"blockExplorers": [ |
||||
{ |
||||
"apiUrl": "https://api.routescan.io/v2/network/testnet/evm/43113/etherscan/api", |
||||
"family": "etherscan", |
||||
"name": "SnowTrace", |
||||
"url": "https://testnet.snowtrace.io" |
||||
} |
||||
], |
||||
"blocks": { |
||||
"confirmations": 3, |
||||
"estimateBlockTime": 2, |
||||
"reorgPeriod": 3 |
||||
}, |
||||
"chainId": 43113, |
||||
"deployer": { |
||||
"name": "Abacus Works", |
||||
"url": "https://www.hyperlane.xyz" |
||||
}, |
||||
"displayName": "Fuji", |
||||
"domainId": 43113, |
||||
"domainRoutingIsm": "0xe82EbDCC1B546CEDa0cf5B5495728700f6dE41B4", |
||||
"domainRoutingIsmFactory": "0x683a81E0e1a238dcA7341e04c08d3bba6f0Cb74f", |
||||
"fallbackRoutingHook": "0xc684f7F50DB4b2563218512e021fBdd0BeD6b57E", |
||||
"index": { |
||||
"from": 26503317 |
||||
}, |
||||
"interchainAccountIsm": "0xfaB4815BDC5c60c6bD625459C8577aFdD79D9311", |
||||
"interchainAccountRouter": "0xeEF6933122894fF217a7dd07510b3D64b747e29b", |
||||
"interchainGasPaymaster": "0x6895d3916B94b386fAA6ec9276756e16dAe7480E", |
||||
"interchainSecurityModule": "0x3dA17519c799f86000FADb7bfCCdc0CaB0D36fDd", |
||||
"isTestnet": true, |
||||
"mailbox": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", |
||||
"merkleTreeHook": "0x9ff6ac3dAf63103620BBf76136eA1AFf43c2F612", |
||||
"name": "fuji", |
||||
"nativeToken": { |
||||
"decimals": 18, |
||||
"name": "Avalanche", |
||||
"symbol": "AVAX" |
||||
}, |
||||
"pausableHook": "0x495e9E119b2aa848b418EF6A4d30b42803de43A9", |
||||
"pausableIsm": "0x8e3D9139547be48942Aa4a489bc250FEa51679fa", |
||||
"protocol": "ethereum", |
||||
"protocolFee": "0xEbA64c8a9b4a61a9210d5fe7E4375380999C821b", |
||||
"proxyAdmin": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", |
||||
"rpcUrls": [ |
||||
{ |
||||
"http": "https://api.avax-test.network/ext/bc/C/rpc", |
||||
"pagination": { |
||||
"maxBlockRange": 2048 |
||||
} |
||||
} |
||||
], |
||||
"staticAggregationHookFactory": "0x99554CC33cBCd6EDDd2f3fc9c7C9194Cb3b5df1E", |
||||
"staticAggregationIsm": "0xdDB75f480841A8333A31c4198ecf1780936B222D", |
||||
"staticAggregationIsmFactory": "0xF588129ed84F219A1f0f921bE7Aa1B2176516858", |
||||
"staticMerkleRootMultisigIsmFactory": "0x93F50Ac4E5663DAAb03508008d592f6260964f62", |
||||
"staticMessageIdMultisigIsmFactory": "0x90e1F9918F304645e4F6324E5C0EAc70138C84Ce", |
||||
"storageGasOracle": "0x9305dE34306886d615B096Bdf23b94a978f6a6c0", |
||||
"testRecipient": "0x44a7e1d76fD8AfA244AdE7278336E3D5C658D398", |
||||
"testTokenRecipient": "0x9CC10c844B3Bbae2444E39991aB027C4A05D1F2e", |
||||
"timelockController": "0x0000000000000000000000000000000000000000", |
||||
"validatorAnnounce": "0x4f7179A691F8a684f56cF7Fed65171877d30739a" |
||||
}, |
||||
"holesky": { |
||||
"aggregationHook": "0xb1FfD51f03c69A0a3e5AFEBDE639752DB1d56bc9", |
||||
"blockExplorers": [ |
||||
{ |
||||
"apiUrl": "https://api-holesky.etherscan.io/api", |
||||
"family": "etherscan", |
||||
"name": "Etherscan", |
||||
"url": "https://holesky.etherscan.io" |
||||
} |
||||
], |
||||
"blocks": { |
||||
"confirmations": 1, |
||||
"estimateBlockTime": 13, |
||||
"reorgPeriod": 2 |
||||
}, |
||||
"chainId": 17000, |
||||
"deployer": { |
||||
"name": "Abacus Works", |
||||
"url": "https://www.hyperlane.xyz" |
||||
}, |
||||
"displayName": "Holesky", |
||||
"domainId": 17000, |
||||
"domainRoutingIsm": "0x51890869940b4B7eD7426A612676eC63223eF3Db", |
||||
"domainRoutingIsmFactory": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", |
||||
"fallbackRoutingHook": "0x07009DA2249c388aD0f416a235AfE90D784e1aAc", |
||||
"index": { |
||||
"from": 1543015 |
||||
}, |
||||
"interchainGasPaymaster": "0x5CBf4e70448Ed46c2616b04e9ebc72D29FF0cfA9", |
||||
"interchainSecurityModule": "0x7C32096A30Ef0bC550e3b8AcB0D87F72A3910D04", |
||||
"isTestnet": true, |
||||
"mailbox": "0x46f7C5D896bbeC89bE1B19e4485e59b4Be49e9Cc", |
||||
"merkleTreeHook": "0x98AAE089CaD930C64a76dD2247a2aC5773a4B8cE", |
||||
"name": "holesky", |
||||
"nativeToken": { |
||||
"decimals": 18, |
||||
"name": "Ether", |
||||
"symbol": "ETH" |
||||
}, |
||||
"pausableHook": "0xF7561c34f17A32D5620583A3397C304e7038a7F6", |
||||
"pausableIsm": "0x80fE4Cb8c70fc60B745d4ffD4403c27a8cBC9e02", |
||||
"protocol": "ethereum", |
||||
"protocolFee": "0x6b1bb4ce664Bb4164AEB4d3D2E7DE7450DD8084C", |
||||
"proxyAdmin": "0x33dB966328Ea213b0f76eF96CA368AB37779F065", |
||||
"rpcUrls": [ |
||||
{ |
||||
"http": "https://ethereum-holesky-rpc.publicnode.com" |
||||
} |
||||
], |
||||
"staticAggregationHookFactory": "0x589C201a07c26b4725A4A829d772f24423da480B", |
||||
"staticAggregationIsm": "0x296Be783e0b1CdDed847E34e6587C24dDb710cf9", |
||||
"staticAggregationIsmFactory": "0x54148470292C24345fb828B003461a9444414517", |
||||
"staticMerkleRootMultisigIsmFactory": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", |
||||
"staticMessageIdMultisigIsmFactory": "0x6966b0E55883d49BFB24539356a2f8A673E02039", |
||||
"storageGasOracle": "0x2b2a158B4059C840c7aC67399B153bb567D06303", |
||||
"testRecipient": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", |
||||
"validatorAnnounce": "0xAb9B273366D794B7F80B4378bc8Aaca75C6178E2" |
||||
}, |
||||
"optimismsepolia": { |
||||
"aggregationHook": "0xccA408a6A9A6dc405C3278647421eb4317466943", |
||||
"blockExplorers": [ |
||||
{ |
||||
"apiUrl": "https://api-sepolia-optimistic.etherscan.io/api", |
||||
"family": "etherscan", |
||||
"name": "OP Sepolia Explorer", |
||||
"url": "https://sepolia-optimistic.etherscan.io" |
||||
} |
||||
], |
||||
"blocks": { |
||||
"confirmations": 1, |
||||
"estimateBlockTime": 2, |
||||
"reorgPeriod": 0 |
||||
}, |
||||
"chainId": 11155420, |
||||
"deployer": { |
||||
"name": "Abacus Works", |
||||
"url": "https://www.hyperlane.xyz" |
||||
}, |
||||
"displayName": "Optimism Sepolia", |
||||
"domainId": 11155420, |
||||
"domainRoutingIsm": "0x4ac19e0bafc2aF6B98094F0a1B817dF196551219", |
||||
"domainRoutingIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", |
||||
"fallbackRoutingHook": "0xddf4C3e791caCaFd26D7fb275549739B38ae6e75", |
||||
"gasCurrencyCoinGeckoId": "ethereum", |
||||
"index": { |
||||
"from": 15833917 |
||||
}, |
||||
"interchainGasPaymaster": "0x28B02B97a850872C4D33C3E024fab6499ad96564", |
||||
"interchainSecurityModule": "0x815a9642497Ee1E9F061f8b828C85Eb7193DecfC", |
||||
"isTestnet": true, |
||||
"mailbox": "0x6966b0E55883d49BFB24539356a2f8A673E02039", |
||||
"merkleTreeHook": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", |
||||
"name": "optimismsepolia", |
||||
"nativeToken": { |
||||
"decimals": 18, |
||||
"name": "Ether", |
||||
"symbol": "ETH" |
||||
}, |
||||
"pausableHook": "0x19Be55D859368e02d7b9C00803Eb677BDC1359Bd", |
||||
"pausableIsm": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", |
||||
"protocol": "ethereum", |
||||
"protocolFee": "0x1b33611fCc073aB0737011d5512EF673Bff74962", |
||||
"proxyAdmin": "0x44b764045BfDC68517e10e783E69B376cef196B2", |
||||
"rpcUrls": [ |
||||
{ |
||||
"http": "https://sepolia.optimism.io" |
||||
} |
||||
], |
||||
"staticAggregationHookFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", |
||||
"staticAggregationIsm": "0x815a9642497Ee1E9F061f8b828C85Eb7193DecfC", |
||||
"staticAggregationIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", |
||||
"staticMerkleRootMultisigIsmFactory": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", |
||||
"staticMessageIdMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", |
||||
"storageGasOracle": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", |
||||
"testRecipient": "0x783c4a0bB6663359281aD4a637D5af68F83ae213", |
||||
"validatorAnnounce": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9" |
||||
}, |
||||
"plumetestnet": { |
||||
"aggregationHook": "0x31dF0EEE7Dc7565665468698a0da221225619a1B", |
||||
"blockExplorers": [ |
||||
{ |
||||
"apiUrl": "https://plume-testnet.explorer.caldera.xyz/api", |
||||
"family": "blockscout", |
||||
"name": "Plume Testnet Explorer", |
||||
"url": "https://plume-testnet.explorer.caldera.xyz" |
||||
} |
||||
], |
||||
"blocks": { |
||||
"confirmations": 1, |
||||
"estimateBlockTime": 5, |
||||
"reorgPeriod": 0 |
||||
}, |
||||
"chainId": 161221135, |
||||
"deployer": { |
||||
"name": "Abacus Works", |
||||
"url": "https://www.hyperlane.xyz" |
||||
}, |
||||
"displayName": "Plume Testnet", |
||||
"domainId": 161221135, |
||||
"domainRoutingIsm": "0xF554Be1611572dF824556e8060bf90Fe5bE7Ff08", |
||||
"domainRoutingIsmFactory": "0x54148470292C24345fb828B003461a9444414517", |
||||
"fallbackRoutingHook": "0x19Be55D859368e02d7b9C00803Eb677BDC1359Bd", |
||||
"index": { |
||||
"from": 5284139 |
||||
}, |
||||
"interchainAccountIsm": "0x7c115c16E34c74afdb88bd268EaB19bC705891FE", |
||||
"interchainAccountRouter": "0xB6F8aA9B1b314A6E6DFB465DD3e0E95936347517", |
||||
"interchainGasPaymaster": "0x28B02B97a850872C4D33C3E024fab6499ad96564", |
||||
"interchainSecurityModule": "0x5708e5CAd632898E16D6A7bA286EC8b8eFD3056b", |
||||
"isTestnet": true, |
||||
"mailbox": "0x33dB966328Ea213b0f76eF96CA368AB37779F065", |
||||
"merkleTreeHook": "0xddf4C3e791caCaFd26D7fb275549739B38ae6e75", |
||||
"name": "plumetestnet", |
||||
"nativeToken": { |
||||
"decimals": 18, |
||||
"name": "Ether", |
||||
"symbol": "ETH" |
||||
}, |
||||
"pausableHook": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", |
||||
"pausableIsm": "0xDaAc5C311d6e9134C725171Ef06a8a2BaAF4a10f", |
||||
"protocol": "ethereum", |
||||
"protocolFee": "0x1b33611fCc073aB0737011d5512EF673Bff74962", |
||||
"proxyAdmin": "0x589C201a07c26b4725A4A829d772f24423da480B", |
||||
"rpcConsensusType": "single", |
||||
"rpcUrls": [ |
||||
{ |
||||
"http": "https://plume-testnet.rpc.caldera.xyz/http" |
||||
} |
||||
], |
||||
"staticAggregationHookFactory": "0x6966b0E55883d49BFB24539356a2f8A673E02039", |
||||
"staticAggregationIsm": "0xDaE6E59fB970F8df1cCCC7d230a7cdeD8BDfCb95", |
||||
"staticAggregationIsmFactory": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", |
||||
"staticMerkleRootMultisigIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", |
||||
"staticMessageIdMultisigIsmFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", |
||||
"storageGasOracle": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", |
||||
"technicalStack": "arbitrumnitro", |
||||
"testRecipient": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F", |
||||
"timelockController": "0x0000000000000000000000000000000000000000", |
||||
"validatorAnnounce": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9" |
||||
}, |
||||
"polygonamoy": { |
||||
"aggregationHook": "0x06a54A2db82D37410C1383c51F96Bd7b3ABD243E", |
||||
"blockExplorers": [ |
||||
{ |
||||
"apiUrl": "https://api-amoy.polygonscan.com/api", |
||||
"family": "etherscan", |
||||
"name": "Polygon Amoy Explorer", |
||||
"url": "https://amoy.polygonscan.com" |
||||
} |
||||
], |
||||
"blocks": { |
||||
"confirmations": 5, |
||||
"estimateBlockTime": 2, |
||||
"reorgPeriod": 10 |
||||
}, |
||||
"chainId": 80002, |
||||
"deployer": { |
||||
"name": "Abacus Works", |
||||
"url": "https://www.hyperlane.xyz" |
||||
}, |
||||
"displayName": "Polygon Amoy", |
||||
"domainId": 80002, |
||||
"domainRoutingIsm": "0x2a2F4AAaf726abb4B969c2804D38e188555683b5", |
||||
"domainRoutingIsmFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", |
||||
"fallbackRoutingHook": "0x19Be55D859368e02d7b9C00803Eb677BDC1359Bd", |
||||
"index": { |
||||
"from": 10634605 |
||||
}, |
||||
"interchainGasPaymaster": "0x6c13643B3927C57DB92c790E4E3E7Ee81e13f78C", |
||||
"interchainSecurityModule": "0x831Ee59F524C94A320821f4d30B6581Ace69379d", |
||||
"isTestnet": true, |
||||
"mailbox": "0x54148470292C24345fb828B003461a9444414517", |
||||
"merkleTreeHook": "0xddf4C3e791caCaFd26D7fb275549739B38ae6e75", |
||||
"name": "polygonamoy", |
||||
"nativeToken": { |
||||
"decimals": 18, |
||||
"name": "MATIC", |
||||
"symbol": "MATIC" |
||||
}, |
||||
"pausableHook": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", |
||||
"pausableIsm": "0xAb9B273366D794B7F80B4378bc8Aaca75C6178E2", |
||||
"protocol": "ethereum", |
||||
"protocolFee": "0x66b71A4e18FbE09a6977A6520B47fEDdffA82a1c", |
||||
"proxyAdmin": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", |
||||
"rpcUrls": [ |
||||
{ |
||||
"http": "https://rpc-amoy.polygon.technology" |
||||
}, |
||||
{ |
||||
"http": "https://polygon-amoy-bor-rpc.publicnode.com" |
||||
}, |
||||
{ |
||||
"http": "https://polygon-amoy.blockpi.network/v1/rpc/public" |
||||
}, |
||||
{ |
||||
"http": "https://rpc.ankr.com/polygon_amoy" |
||||
} |
||||
], |
||||
"staticAggregationHookFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", |
||||
"staticAggregationIsm": "0x1D770b978d915bD96F2ad41b25824C4193EBAfA2", |
||||
"staticAggregationIsmFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", |
||||
"staticMerkleRootMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", |
||||
"staticMessageIdMultisigIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", |
||||
"storageGasOracle": "0xD0680F80F4f947968206806C2598Cbc5b6FE5b03", |
||||
"testRecipient": "0x04438ef7622f5412f82915F59caD4f704C61eA48", |
||||
"validatorAnnounce": "0x11918DC33E067C5DA83EEF58E50F856398b8Df4C" |
||||
}, |
||||
"scrollsepolia": { |
||||
"aggregationHook": "0x7b63Aa270335F8896717c2A809205F4b650E4268", |
||||
"blockExplorers": [ |
||||
{ |
||||
"apiUrl": "https://api-sepolia.scrollscan.com/api", |
||||
"family": "etherscan", |
||||
"name": "Scroll Explorer", |
||||
"url": "https://sepolia.scrollscan.dev/" |
||||
} |
||||
], |
||||
"blocks": { |
||||
"confirmations": 1, |
||||
"estimateBlockTime": 3, |
||||
"reorgPeriod": 1 |
||||
}, |
||||
"chainId": 534351, |
||||
"deployer": { |
||||
"name": "Abacus Works", |
||||
"url": "https://www.hyperlane.xyz" |
||||
}, |
||||
"displayName": "Scroll Sepolia", |
||||
"domainId": 534351, |
||||
"domainRoutingIsm": "0x606511c593fd78AFbeD8A2e02EDe2C6179722276", |
||||
"domainRoutingIsmFactory": "0x17866ebE0e503784a9461d3e753dEeD0d3F61153", |
||||
"fallbackRoutingHook": "0xE1CCB130389f687bf745Dd6dc05E50da17d9ea96", |
||||
"index": { |
||||
"from": 1344054 |
||||
}, |
||||
"interchainAccountIsm": "0xE023239c8dfc172FF008D8087E7442d3eBEd9350", |
||||
"interchainAccountRouter": "0xe17c37212d785760E8331D4A4395B17b34Ba8cDF", |
||||
"interchainGasPaymaster": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", |
||||
"interchainSecurityModule": "0x476a8f40c81E69f5d676ccaA9709c0dE61F907E1", |
||||
"isTestnet": true, |
||||
"mailbox": "0x3C5154a193D6e2955650f9305c8d80c18C814A68", |
||||
"merkleTreeHook": "0x863E8c26621c52ACa1849C53500606e73BA272F0", |
||||
"name": "scrollsepolia", |
||||
"nativeToken": { |
||||
"decimals": 18, |
||||
"name": "Ether", |
||||
"symbol": "ETH" |
||||
}, |
||||
"pausableHook": "0x390A48fC8Cb8F29B3ffE1B95aD3773414B8DD704", |
||||
"pausableIsm": "0xcD19Ff7306E04EA6b8f4B5Ab1c5A198c186aaB42", |
||||
"protocol": "ethereum", |
||||
"protocolFee": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", |
||||
"proxyAdmin": "0x598facE78a4302f11E3de0bee1894Da0b2Cb71F8", |
||||
"rpcUrls": [ |
||||
{ |
||||
"http": "https://sepolia-rpc.scroll.io" |
||||
}, |
||||
{ |
||||
"http": "https://rpc.ankr.com/scroll_sepolia_testnet" |
||||
}, |
||||
{ |
||||
"http": "https://scroll-sepolia.blockpi.network/v1/rpc/public" |
||||
}, |
||||
{ |
||||
"http": "https://scroll-sepolia.chainstacklabs.com" |
||||
}, |
||||
{ |
||||
"http": "https://scroll-public.scroll-testnet.quiknode.pro" |
||||
} |
||||
], |
||||
"staticAggregationHookFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", |
||||
"staticAggregationIsm": "0xcc5BAaa44A8749c7C566b1cb578a315427632053", |
||||
"staticAggregationIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", |
||||
"staticMerkleRootMultisigIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", |
||||
"staticMessageIdMultisigIsmFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", |
||||
"storageGasOracle": "0x6b1bb4ce664Bb4164AEB4d3D2E7DE7450DD8084C", |
||||
"testRecipient": "0xa3AB7E6cE24E6293bD5320A53329Ef2f4DE73fCA", |
||||
"testTokenRecipient": "0xc76E477437065093D353b7d56c81ff54D167B0Ab", |
||||
"timelockController": "0x0000000000000000000000000000000000000000", |
||||
"transactionOverrides": { |
||||
"gasPrice": 500000000 |
||||
}, |
||||
"validatorAnnounce": "0x527768930D889662Fe7ACF64294871e86e4C2381" |
||||
}, |
||||
"sepolia": { |
||||
"aggregationHook": "0xe3147d5618f5e2e100690B50ec923009a4cde14A", |
||||
"blockExplorers": [ |
||||
{ |
||||
"apiUrl": "https://api-sepolia.etherscan.io/api", |
||||
"family": "etherscan", |
||||
"name": "Etherscan", |
||||
"url": "https://sepolia.etherscan.io" |
||||
} |
||||
], |
||||
"blocks": { |
||||
"confirmations": 1, |
||||
"estimateBlockTime": 13, |
||||
"reorgPeriod": 2 |
||||
}, |
||||
"chainId": 11155111, |
||||
"deployer": { |
||||
"name": "Abacus Works", |
||||
"url": "https://www.hyperlane.xyz" |
||||
}, |
||||
"displayName": "Sepolia", |
||||
"domainId": 11155111, |
||||
"domainRoutingIsm": "0xfa9a26cCc5417d1C1D03C949b5013Bb5898dA905", |
||||
"domainRoutingIsmFactory": "0x3F100cBBE5FD5466BdB4B3a15Ac226957e7965Ad", |
||||
"fallbackRoutingHook": "0x17Dc724B7a2F09141C13b8AC33B396073785c2BC", |
||||
"gnosisSafeTransactionServiceUrl": "https://safe-transaction-sepolia.safe.global", |
||||
"index": { |
||||
"from": 4517401 |
||||
}, |
||||
"interchainAccountIsm": "0x83a3068B719F764d413625dA77468ED74789ae02", |
||||
"interchainAccountRouter": "0x8e131c8aE5BF1Ed38D05a00892b6001a7d37739d", |
||||
"interchainGasPaymaster": "0x6f2756380FD49228ae25Aa7F2817993cB74Ecc56", |
||||
"interchainSecurityModule": "0x76B21a2241f6A6FbaE624A7Fe5d7D3919C9ce3E3", |
||||
"isTestnet": true, |
||||
"mailbox": "0xfFAEF09B3cd11D9b20d1a19bECca54EEC2884766", |
||||
"merkleTreeHook": "0x4917a9746A7B6E0A57159cCb7F5a6744247f2d0d", |
||||
"name": "sepolia", |
||||
"nativeToken": { |
||||
"decimals": 18, |
||||
"name": "Ether", |
||||
"symbol": "ETH" |
||||
}, |
||||
"pausableHook": "0xa68022e53Fd28119D07C8336a8eC84A298Fd38Fd", |
||||
"pausableIsm": "0x21bdaB116d9DA77E312910fB53aD35dD82C8a76c", |
||||
"protocol": "ethereum", |
||||
"protocolFee": "0x13AC3349Cb159fE86A22cf42DdA803D9f7309DB5", |
||||
"proxyAdmin": "0x97Bbc6bBaFa5Ce3b2FA966c121Af63bD09e940f8", |
||||
"rpcUrls": [ |
||||
{ |
||||
"http": "https://ethereum-sepolia.publicnode.com" |
||||
}, |
||||
{ |
||||
"http": "https://ethereum-sepolia.blockpi.network/v1/rpc/public" |
||||
}, |
||||
{ |
||||
"http": "https://rpc.sepolia.org" |
||||
} |
||||
], |
||||
"staticAggregationHookFactory": "0x160C28C92cA453570aD7C031972b58d5Dd128F72", |
||||
"staticAggregationIsm": "0x7856078C4881e236660Be82fdb12473B4a33cCFf", |
||||
"staticAggregationIsmFactory": "0xC83e12EF2627ACE445C298e6eC418684918a6002", |
||||
"staticMerkleRootMultisigIsmFactory": "0x0a71AcC99967829eE305a285750017C4916Ca269", |
||||
"staticMessageIdMultisigIsmFactory": "0xFEb9585b2f948c1eD74034205a7439261a9d27DD", |
||||
"storageGasOracle": "0x71775B071F77F1ce52Ece810ce084451a3045FFe", |
||||
"testRecipient": "0xeDc1A3EDf87187085A3ABb7A9a65E1e7aE370C07", |
||||
"testTokenRecipient": "0x031AD9c560D37baC7d6Bd2d27A2443bAfd10101A", |
||||
"timelockController": "0x0000000000000000000000000000000000000000", |
||||
"validatorAnnounce": "0xE6105C59480a1B7DD3E4f28153aFdbE12F4CfCD9" |
||||
}, |
||||
"solanatestnet": { |
||||
"blockExplorers": [ |
||||
{ |
||||
"apiUrl": "https://explorer.solana.com?cluster=testnet", |
||||
"family": "other", |
||||
"name": "Solana Explorer", |
||||
"url": "https://explorer.solana.com?cluster=testnet" |
||||
} |
||||
], |
||||
"blocks": { |
||||
"confirmations": 1, |
||||
"estimateBlockTime": 0.4, |
||||
"reorgPeriod": 0 |
||||
}, |
||||
"chainId": 1399811150, |
||||
"deployer": { |
||||
"name": "Abacus Works", |
||||
"url": "https://www.hyperlane.xyz" |
||||
}, |
||||
"displayName": "Solana Testnet", |
||||
"displayNameShort": "Sol Testnet", |
||||
"domainId": 1399811150, |
||||
"index": { |
||||
"from": 1, |
||||
"mode": "sequence" |
||||
}, |
||||
"interchainGasPaymaster": "9SQVtTNsbipdMzumhzi6X8GwojiSMwBfqAhS7FgyTcqy", |
||||
"isTestnet": true, |
||||
"mailbox": "75HBBLae3ddeneJVrZeyrDfv6vb7SMC3aCpBucSXS5aR", |
||||
"merkleTreeHook": "75HBBLae3ddeneJVrZeyrDfv6vb7SMC3aCpBucSXS5aR", |
||||
"name": "solanatestnet", |
||||
"nativeToken": { |
||||
"decimals": 9, |
||||
"name": "Sol", |
||||
"symbol": "SOL" |
||||
}, |
||||
"protocol": "sealevel", |
||||
"rpcUrls": [ |
||||
{ |
||||
"http": "https://api.testnet.solana.com" |
||||
} |
||||
], |
||||
"validatorAnnounce": "8qNYSi9EP1xSnRjtMpyof88A26GBbdcrsa61uSaHiwx3" |
||||
}, |
||||
"superpositiontestnet": { |
||||
"aggregationHook": "0x331eb40963dc11F5BB271308c42d97ac6e41F124", |
||||
"blockExplorers": [ |
||||
{ |
||||
"apiUrl": "https://testnet-explorer.superposition.so/api", |
||||
"family": "blockscout", |
||||
"name": "CatScan", |
||||
"url": "https://testnet-explorer.superposition.so" |
||||
} |
||||
], |
||||
"blocks": { |
||||
"confirmations": 1, |
||||
"estimateBlockTime": 1, |
||||
"reorgPeriod": 1 |
||||
}, |
||||
"chainId": 98985, |
||||
"displayName": "Superposition Testnet", |
||||
"domainId": 98985, |
||||
"domainRoutingIsm": "0x4ac19e0bafc2aF6B98094F0a1B817dF196551219", |
||||
"domainRoutingIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", |
||||
"fallbackRoutingHook": "0x98AAE089CaD930C64a76dD2247a2aC5773a4B8cE", |
||||
"index": { |
||||
"from": 3111622 |
||||
}, |
||||
"interchainGasPaymaster": "0xeC7eb4196Bd601DEa7585A744FbFB4CF11278450", |
||||
"interchainSecurityModule": "0x415a45C98288059Cce9c32AE4a09AB19C91d5056", |
||||
"isTestnet": true, |
||||
"mailbox": "0x6966b0E55883d49BFB24539356a2f8A673E02039", |
||||
"merkleTreeHook": "0x4926a10788306D84202A2aDbd290b7743146Cc17", |
||||
"name": "superpositiontestnet", |
||||
"nativeToken": { |
||||
"decimals": 18, |
||||
"name": "Superposition", |
||||
"symbol": "SPN" |
||||
}, |
||||
"pausableHook": "0x07009DA2249c388aD0f416a235AfE90D784e1aAc", |
||||
"pausableIsm": "0x58483b754Abb1E8947BE63d6b95DF75b8249543A", |
||||
"protocol": "ethereum", |
||||
"protocolFee": "0x863E8c26621c52ACa1849C53500606e73BA272F0", |
||||
"proxyAdmin": "0x44b764045BfDC68517e10e783E69B376cef196B2", |
||||
"rpcUrls": [ |
||||
{ |
||||
"http": "https://testnet-rpc.superposition.so" |
||||
} |
||||
], |
||||
"staticAggregationHookFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", |
||||
"staticAggregationIsm": "0x5e6Fe18eC7D4b159bDC5Fa0C32bB1996277B3ddF", |
||||
"staticAggregationIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", |
||||
"staticMerkleRootMultisigIsmFactory": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", |
||||
"staticMessageIdMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", |
||||
"storageGasOracle": "0xF7561c34f17A32D5620583A3397C304e7038a7F6", |
||||
"technicalStack": "arbitrumnitro", |
||||
"testRecipient": "0xAb9B273366D794B7F80B4378bc8Aaca75C6178E2", |
||||
"validatorAnnounce": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C" |
||||
} |
||||
}, |
||||
"defaultRpcConsensusType": "fallback" |
||||
} |
@ -1,2 +0,0 @@ |
||||
pub use rocks::*; |
||||
mod rocks; |
File diff suppressed because it is too large
Load Diff
@ -1,3 +1,5 @@ |
||||
#![allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue
|
||||
|
||||
use async_trait::async_trait; |
||||
use derive_more::Deref; |
||||
use derive_new::new; |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue