diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 556587d6a..41ad02d32 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,7 +2,7 @@ *.sol @yorhodes @tkporter @aroralanuk *.ts @yorhodes @jmrossy @nambrot -*.rs @mattiecnvr @tkporter @daniel-savu +*.rs @tkporter @daniel-savu *.md @Skunkchain @nambrot @avious00 # Package owners @@ -11,7 +11,7 @@ solidity/ @yorhodes @tkporter @aroralanuk ## Agents -rust/ @mattiecnvr @tkporter @daniel-savu +rust/ @tkporter @daniel-savu ## SDK typescript/sdk @yorhodes @jmrossy @@ -23,4 +23,4 @@ typescript/token @yorhodes @jmrossy @tkporter @aroralanuk typescript/helloworld @yorhodes @nambrot ## Infra -typescript/infra @tkporter @nambrot @mattiecnvr +typescript/infra @tkporter @nambrot diff --git a/README.md b/README.md index 36566fc96..fd3400681 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Hyperlane is an interchain messaging protocol that allows applications to commun Developers can use Hyperlane to share state between blockchains, allowing them to build interchain applications that live natively across multiple chains. -To read more about interchain applications, how the protocol works, and how to integrate with Hyperlane, please see the [documentation](https://docs.hyperlane.xyz/). +To read more about interchain applications, how the protocol works, and how to integrate with Hyperlane, please see the [documentation](https://docs.hyperlane.xyz). ## Working on Hyperlane diff --git a/rust-toolchain b/rust-toolchain index 08e602710..83e24cc56 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "1.71.1" +channel = "1.72.1" profile = "default" diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 3671ef9e5..0754638d8 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -41,9 +41,9 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] @@ -120,15 +120,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ "cfg-if", + "getrandom 0.2.10", "once_cell", "version_check", ] [[package]] name = "aho-corasick" -version = "1.0.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" dependencies = [ "memchr", ] @@ -186,30 +187,29 @@ dependencies = [ [[package]] name = "anstream" -version = "0.3.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", - "is-terminal", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" dependencies = [ "utf8parse", ] @@ -225,9 +225,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "1.0.1" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", "windows-sys 0.48.0", @@ -235,9 +235,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.72" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "arrayref" @@ -270,7 +270,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.25", + "time", ] [[package]] @@ -279,8 +279,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", "synstructure", ] @@ -291,8 +291,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] @@ -304,9 +304,9 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-compression" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b74f44609f0f91493e3082d3734d98497e094777144380ea4db9f9905dd5b6" +checksum = "bb42b2197bf15ccb092b62c74515dbd8b86d0effd934795f6687c93b6e679a2c" dependencies = [ "brotli", "flate2", @@ -342,20 +342,20 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", - "syn 2.0.28", + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 2.0.37", ] [[package]] name = "async-trait" -version = "0.1.72" +version = "0.1.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" +checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", - "syn 2.0.28", + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 2.0.37", ] [[package]] @@ -371,9 +371,9 @@ dependencies = [ [[package]] name = "atoi" -version = "1.0.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c57d12312ff59c811c0643f4d80830505833c9ffaebd193d819392b265be8e" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" dependencies = [ "num-traits", ] @@ -405,8 +405,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7862e21c893d65a1650125d157eaeec691439379a1cee17ee49031b79236ada4" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] @@ -417,8 +417,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] @@ -430,9 +430,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", @@ -451,19 +451,6 @@ dependencies = [ "derive-new", ] -[[package]] -name = "bae" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b8de67cc41132507eeece2584804efcb15f85ba516e34c944b7667f480397a" -dependencies = [ - "heck 0.3.3", - "proc-macro-error", - "proc-macro2 1.0.66", - "quote 1.0.32", - "syn 1.0.109", -] - [[package]] name = "base16ct" version = "0.1.1" @@ -500,9 +487,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.2" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" [[package]] name = "base64ct" @@ -528,7 +515,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6773ddc0eafc0e509fb60e48dff7f450f8e674a0686ae8605e8d9901bd5eefa" dependencies = [ - "num-bigint 0.4.3", + "num-bigint 0.4.4", "num-integer", "num-traits", ] @@ -555,12 +542,12 @@ dependencies = [ "lazycell", "peeking_take_while", "prettyplease", - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "regex", "rustc-hash", "shlex", - "syn 2.0.28", + "syn 2.0.37", ] [[package]] @@ -571,9 +558,12 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +dependencies = [ + "serde", +] [[package]] name = "bitmaps" @@ -710,7 +700,7 @@ dependencies = [ "borsh-derive-internal 0.9.3", "borsh-schema-derive-internal 0.9.3", "proc-macro-crate 0.1.5", - "proc-macro2 1.0.66", + "proc-macro2 1.0.67", "syn 1.0.109", ] @@ -723,7 +713,7 @@ dependencies = [ "borsh-derive-internal 0.10.3", "borsh-schema-derive-internal 0.10.3", "proc-macro-crate 0.1.5", - "proc-macro2 1.0.66", + "proc-macro2 1.0.67", "syn 1.0.109", ] @@ -733,8 +723,8 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] @@ -744,8 +734,8 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] @@ -755,8 +745,8 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] @@ -766,8 +756,8 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] @@ -809,9 +799,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "bv" @@ -852,29 +842,29 @@ version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] [[package]] name = "bytemuck" -version = "1.13.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" +checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdde5c9cd29ebd706ce1b35600920a33550e402fc998a2e53ad3b42c3c47a192" +checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", - "syn 2.0.28", + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 2.0.37", ] [[package]] @@ -885,9 +875,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" dependencies = [ "serde", ] @@ -957,9 +947,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.82" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ "jobserver", "libc", @@ -982,18 +972,17 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.26" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "serde", - "time 0.1.45", "wasm-bindgen", - "winapi", + "windows-targets 0.48.5", ] [[package]] @@ -1058,9 +1047,8 @@ checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "atty", "bitflags 1.3.2", - "clap_derive 3.2.25", "clap_lex 0.2.4", - "indexmap", + "indexmap 1.9.3", "once_cell", "strsim 0.10.0", "termcolor", @@ -1069,50 +1057,36 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.19" +version = "4.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d" +checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" dependencies = [ "clap_builder", - "clap_derive 4.3.12", - "once_cell", + "clap_derive", ] [[package]] name = "clap_builder" -version = "4.3.19" +version = "4.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1" +checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" dependencies = [ "anstream", "anstyle", - "clap_lex 0.5.0", + "clap_lex 0.5.1", "strsim 0.10.0", ] [[package]] name = "clap_derive" -version = "3.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" -dependencies = [ - "heck 0.4.1", - "proc-macro-error", - "proc-macro2 1.0.66", - "quote 1.0.32", - "syn 1.0.109", -] - -[[package]] -name = "clap_derive" -version = "4.3.12" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" +checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.66", - "quote 1.0.32", - "syn 2.0.28", + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 2.0.37", ] [[package]] @@ -1126,9 +1100,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" [[package]] name = "cobs" @@ -1151,7 +1125,7 @@ dependencies = [ "k256", "lazy_static", "serde", - "sha2 0.10.7", + "sha2 0.10.8", "thiserror", ] @@ -1168,7 +1142,7 @@ dependencies = [ "hmac 0.12.1", "pbkdf2 0.11.0", "rand 0.8.5", - "sha2 0.10.7", + "sha2 0.10.8", "thiserror", ] @@ -1188,7 +1162,7 @@ dependencies = [ "ripemd", "serde", "serde_derive", - "sha2 0.10.7", + "sha2 0.10.8", "sha3 0.10.8", "thiserror", ] @@ -1299,9 +1273,9 @@ checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" [[package]] name = "const-oid" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "795bc6e66a8e340f075fcf6227e417a2dc976b92b91f3cdc778bb858778b6747" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" [[package]] name = "constant_time_eq" @@ -1331,7 +1305,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" dependencies = [ "percent-encoding", - "time 0.3.25", + "time", "version_check", ] @@ -1348,7 +1322,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "time 0.3.25", + "time", "url", ] @@ -1386,6 +1360,21 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" + [[package]] name = "crc32fast" version = "1.3.2" @@ -1397,9 +1386,9 @@ dependencies = [ [[package]] name = "critical-section" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6548a0ad5d2549e111e1f6a11a6c2e2d00ce6a3dafe22948d67c2b443f775e52" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" [[package]] name = "crossbeam-channel" @@ -1544,11 +1533,11 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.4.0" +version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a011bbe2c35ce9c1f143b7af6f94f29a167beb4cd1d29e6740ce836f723120e" +checksum = "82e95fbd621905b854affdc67943b043a0fbb6ed7385fd5a25650d19a8a6cfdf" dependencies = [ - "nix 0.26.2", + "nix 0.27.1", "windows-sys 0.48.0", ] @@ -1589,8 +1578,8 @@ dependencies = [ "darling 0.13.4", "graphql-parser", "once_cell", - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "strsim 0.10.0", "syn 1.0.109", ] @@ -1633,8 +1622,8 @@ checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "strsim 0.10.0", "syn 1.0.109", ] @@ -1647,8 +1636,8 @@ checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "strsim 0.10.0", "syn 1.0.109", ] @@ -1660,7 +1649,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ "darling_core 0.13.4", - "quote 1.0.32", + "quote 1.0.33", "syn 1.0.109", ] @@ -1671,7 +1660,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ "darling_core 0.14.4", - "quote 1.0.32", + "quote 1.0.33", "syn 1.0.109", ] @@ -1707,7 +1696,18 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" dependencies = [ - "const-oid 0.9.4", + "const-oid 0.9.5", + "zeroize", +] + +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid 0.9.5", + "pem-rfc7468", "zeroize", ] @@ -1720,16 +1720,16 @@ dependencies = [ "asn1-rs", "displaydoc", "nom", - "num-bigint 0.4.3", + "num-bigint 0.4.4", "num-traits", "rusticata-macros", ] [[package]] name = "deranged" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7684a49fb1af197853ef7b2ee694bc1f5b4179556f1e5710e1760c5db6f5e929" +checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" dependencies = [ "serde", ] @@ -1746,8 +1746,8 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] @@ -1757,8 +1757,8 @@ version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] @@ -1778,8 +1778,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" dependencies = [ "darling 0.14.4", - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] @@ -1800,8 +1800,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case 0.4.0", - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "rustc_version", "syn 1.0.109", ] @@ -1849,6 +1849,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", + "const-oid 0.9.5", "crypto-common", "subtle", ] @@ -1862,15 +1863,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "dirs" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" -dependencies = [ - "dirs-sys", -] - [[package]] name = "dirs-next" version = "2.0.0" @@ -1881,17 +1873,6 @@ dependencies = [ "dirs-sys-next", ] -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -1909,9 +1890,9 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", - "syn 2.0.28", + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 2.0.37", ] [[package]] @@ -1976,7 +1957,7 @@ dependencies = [ "der 0.6.1", "elliptic-curve 0.12.3", "rfc6979", - "signature", + "signature 1.6.4", ] [[package]] @@ -1995,7 +1976,7 @@ version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" dependencies = [ - "signature", + "signature 1.6.4", ] [[package]] @@ -2020,18 +2001,18 @@ dependencies = [ "derivation-path", "ed25519-dalek", "hmac 0.12.1", - "sha2 0.10.7", + "sha2 0.10.8", ] [[package]] name = "educe" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "079044df30bb07de7d846d41a184c4b00e66ebdac93ee459253474f3a47e50ae" +checksum = "0f0042ff8246a363dbe77d2ceedb073339e85a804b9a47636c6e016a9a32c05f" dependencies = [ "enum-ordinalize", - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] @@ -2040,6 +2021,9 @@ name = "either" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +dependencies = [ + "serde", +] [[package]] name = "elliptic-curve" @@ -2076,6 +2060,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + [[package]] name = "encode_unicode" version = "0.3.6" @@ -2084,9 +2074,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.32" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" dependencies = [ "cfg-if", ] @@ -2106,8 +2096,8 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8958699f9359f0b04e691a13850d48b7de329138023876d07cbd024c2c820598" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] @@ -2117,11 +2107,11 @@ version = "3.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4f76552f53cefc9a7f64987c3701b99d982f7690606fd67de1d09712fbf52f1" dependencies = [ - "num-bigint 0.4.3", + "num-bigint 0.4.4", "num-traits", - "proc-macro2 1.0.66", - "quote 1.0.32", - "syn 2.0.28", + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 2.0.37", ] [[package]] @@ -2131,9 +2121,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f33313078bb8d4d05a2733a94ac4c2d8a0df9a2b84424ebf4f33bfc224a890e" dependencies = [ "once_cell", - "proc-macro2 1.0.66", - "quote 1.0.32", - "syn 2.0.28", + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 2.0.37", ] [[package]] @@ -2162,11 +2152,17 @@ dependencies = [ "termcolor", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" +checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" dependencies = [ "errno-dragonfly", "libc", @@ -2183,6 +2179,17 @@ dependencies = [ "libc", ] +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + [[package]] name = "eth-keystore" version = "0.3.0" @@ -2221,7 +2228,7 @@ dependencies = [ "scrypt 0.10.0", "serde", "serde_json", - "sha2 0.10.7", + "sha2 0.10.8", "sha3 0.10.8", "thiserror", "uuid 0.8.2", @@ -2330,8 +2337,8 @@ dependencies = [ "eyre", "getrandom 0.2.10", "hex 0.4.3", - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "regex", "reqwest", "serde", @@ -2350,8 +2357,8 @@ dependencies = [ "ethers-contract-abigen", "ethers-core", "hex 0.4.3", - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "serde_json", "syn 1.0.109", ] @@ -2373,7 +2380,7 @@ dependencies = [ "k256", "once_cell", "open-fastrlp", - "proc-macro2 1.0.66", + "proc-macro2 1.0.67", "rand 0.8.5", "rlp", "rlp-derive", @@ -2499,7 +2506,7 @@ dependencies = [ "rand 0.8.5", "rusoto_core", "rusoto_kms", - "sha2 0.10.7", + "sha2 0.10.8", "spki 0.6.0", "thiserror", "tracing", @@ -2544,9 +2551,9 @@ checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] name = "fastrand" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "feature-probe" @@ -2576,6 +2583,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "finl_unicode" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" + [[package]] name = "fixed-hash" version = "0.8.0" @@ -2601,9 +2614,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" dependencies = [ "crc32fast", "miniz_oxide", @@ -2618,6 +2631,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "futures-core", + "futures-sink", + "spin 0.9.8", +] + [[package]] name = "fnv" version = "1.0.7" @@ -2762,7 +2786,7 @@ dependencies = [ "rand 0.8.5", "secp256k1", "serde", - "sha2 0.10.7", + "sha2 0.10.8", "zeroize", ] @@ -2776,7 +2800,7 @@ dependencies = [ "fuel-storage", "hashbrown 0.13.2", "hex 0.4.3", - "sha2 0.10.7", + "sha2 0.10.8", "thiserror", ] @@ -2863,8 +2887,8 @@ dependencies = [ "fuel-abi-types", "itertools 0.10.5", "lazy_static", - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "regex", "serde_json", "syn 1.0.109", @@ -2896,8 +2920,8 @@ dependencies = [ "fuels-code-gen", "itertools 0.10.5", "lazy_static", - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "rand 0.8.5", "regex", "serde_json", @@ -2921,7 +2945,7 @@ dependencies = [ "futures", "hex 0.4.3", "itertools 0.10.5", - "proc-macro2 1.0.66", + "proc-macro2 1.0.67", "rand 0.8.5", "regex", "serde", @@ -3005,7 +3029,7 @@ dependencies = [ "hex 0.4.3", "itertools 0.10.5", "lazy_static", - "proc-macro2 1.0.66", + "proc-macro2 1.0.67", "regex", "serde", "serde_json", @@ -3064,13 +3088,13 @@ dependencies = [ [[package]] name = "futures-intrusive" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a604f7a68fbf8103337523b1fadc8ade7361ee3f112f7c680ad179651616aed5" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" dependencies = [ "futures-core", "lock_api", - "parking_lot 0.11.2", + "parking_lot 0.12.1", ] [[package]] @@ -3095,9 +3119,9 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", - "syn 2.0.28", + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 2.0.37", ] [[package]] @@ -3203,9 +3227,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.27.3" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" [[package]] name = "glob" @@ -3247,9 +3271,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.20" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" +checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" dependencies = [ "bytes", "fnv", @@ -3257,10 +3281,10 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", - "tokio-util 0.7.8", + "tokio-util 0.7.9", "tracing", ] @@ -3302,9 +3326,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" dependencies = [ "ahash 0.8.3", "allocator-api2", @@ -3321,21 +3345,20 @@ dependencies = [ [[package]] name = "hashlink" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312f66718a2d7789ffef4f4b7b213138ed9f1eb3aa1d0d82fc99f88fb3ffd26f" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" dependencies = [ - "hashbrown 0.14.0", + "hashbrown 0.14.1", ] [[package]] name = "headers" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" +checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" dependencies = [ - "base64 0.13.1", - "bitflags 1.3.2", + "base64 0.21.4", "bytes", "headers-core", "http", @@ -3396,9 +3419,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "hex" @@ -3471,6 +3494,15 @@ dependencies = [ "hmac 0.8.1", ] +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys 0.48.0", +] + [[package]] name = "http" version = "0.2.9" @@ -3501,9 +3533,9 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" @@ -3528,7 +3560,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -3562,7 +3594,7 @@ dependencies = [ "futures-util", "http", "hyper", - "rustls 0.21.6", + "rustls 0.21.7", "tokio", "tokio-rustls 0.24.1", ] @@ -3723,7 +3755,7 @@ dependencies = [ "account-utils", "anyhow", "async-trait", - "base64 0.21.2", + "base64 0.21.4", "borsh 0.9.3", "derive-new", "hyperlane-core", @@ -3756,10 +3788,11 @@ dependencies = [ "bincode", "borsh 0.9.3", "bs58 0.5.0", - "clap 4.3.19", + "clap 4.4.6", "hex 0.4.3", "hyperlane-core", "hyperlane-sealevel-connection-client", + "hyperlane-sealevel-hello-world", "hyperlane-sealevel-igp", "hyperlane-sealevel-mailbox", "hyperlane-sealevel-multisig-ism-message-id", @@ -3791,6 +3824,26 @@ dependencies = [ "solana-program", ] +[[package]] +name = "hyperlane-sealevel-hello-world" +version = "0.1.0" +dependencies = [ + "access-control", + "account-utils", + "borsh 0.9.3", + "hyperlane-core", + "hyperlane-sealevel-connection-client", + "hyperlane-sealevel-igp", + "hyperlane-sealevel-mailbox", + "hyperlane-sealevel-message-recipient-interface", + "hyperlane-test-utils", + "serializable-account-meta", + "solana-program", + "solana-program-test", + "solana-sdk", + "spl-noop", +] + [[package]] name = "hyperlane-sealevel-igp" version = "0.1.0" @@ -3839,7 +3892,7 @@ version = "0.1.0" dependencies = [ "access-control", "account-utils", - "base64 0.21.2", + "base64 0.21.4", "blake3", "borsh 0.9.3", "getrandom 0.2.10", @@ -3863,7 +3916,7 @@ version = "0.1.0" dependencies = [ "access-control", "account-utils", - "base64 0.21.2", + "base64 0.21.4", "borsh 0.9.3", "hyperlane-core", "hyperlane-sealevel-interchain-security-module-interface", @@ -4242,8 +4295,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] @@ -4269,6 +4322,16 @@ dependencies = [ "hashbrown 0.12.3", ] +[[package]] +name = "indexmap" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad227c3af19d4914570ad36d30409928b75967c298feb9ea1969db3a610bb14e" +dependencies = [ + "equivalent", + "hashbrown 0.14.1", +] + [[package]] name = "indicatif" version = "0.16.2" @@ -4281,6 +4344,17 @@ dependencies = [ "regex", ] +[[package]] +name = "inherent" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce243b1bfa62ffc028f1cc3b6034ec63d649f3031bc8a4fbbb004e1ac17d1f68" +dependencies = [ + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 2.0.37", +] + [[package]] name = "inout" version = "0.1.3" @@ -4314,7 +4388,7 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ - "hermit-abi 0.3.2", + "hermit-abi 0.3.3", "rustix", "windows-sys 0.48.0", ] @@ -4396,7 +4470,7 @@ dependencies = [ "cfg-if", "ecdsa", "elliptic-curve 0.12.3", - "sha2 0.10.7", + "sha2 0.10.8", "sha3 0.10.8", ] @@ -4414,6 +4488,9 @@ name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin 0.5.2", +] [[package]] name = "lazycell" @@ -4423,9 +4500,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "libloading" @@ -4437,6 +4514,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "libm" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" + [[package]] name = "librocksdb-sys" version = "0.11.0+8.1.1" @@ -4501,6 +4584,17 @@ dependencies = [ "libsecp256k1-core", ] +[[package]] +name = "libsqlite3-sys" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "libz-sys" version = "1.1.12" @@ -4520,9 +4614,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" +checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" [[package]] name = "lock_api" @@ -4536,9 +4630,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "lru" @@ -4619,18 +4713,19 @@ dependencies = [ [[package]] name = "md-5" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ + "cfg-if", "digest 0.10.7", ] [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" [[package]] name = "memmap2" @@ -4678,7 +4773,7 @@ dependencies = [ "sea-orm", "sea-orm-migration", "serde", - "time 0.3.25", + "time", "tokio", "tracing", "tracing-subscriber", @@ -4748,8 +4843,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ "cfg-if", - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] @@ -4769,8 +4864,8 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] @@ -4837,14 +4932,13 @@ dependencies = [ [[package]] name = "nix" -version = "0.26.2" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "cfg-if", "libc", - "static_assertions", ] [[package]] @@ -4893,8 +4987,8 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" dependencies = [ - "num-bigint 0.4.3", - "num-complex 0.4.3", + "num-bigint 0.4.4", + "num-complex 0.4.4", "num-integer", "num-iter", "num-rational 0.4.1", @@ -4914,9 +5008,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ "autocfg", "num-integer", @@ -4924,6 +5018,23 @@ dependencies = [ "serde", ] +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", +] + [[package]] name = "num-complex" version = "0.2.4" @@ -4936,9 +5047,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" +checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" dependencies = [ "num-traits", "serde", @@ -4950,8 +5061,8 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] @@ -4961,9 +5072,9 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e6a0fd4f737c707bd9086cc16c925f294943eb62eb71499e9fd4cf71f8b9f4e" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", - "syn 2.0.28", + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 2.0.37", ] [[package]] @@ -5006,7 +5117,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ "autocfg", - "num-bigint 0.4.3", + "num-bigint 0.4.4", "num-integer", "num-traits", "serde", @@ -5019,6 +5130,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -5027,7 +5139,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.2", + "hermit-abi 0.3.3", "libc", ] @@ -5056,8 +5168,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" dependencies = [ "proc-macro-crate 1.2.1", - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] @@ -5068,9 +5180,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ "proc-macro-crate 1.2.1", - "proc-macro2 1.0.66", - "quote 1.0.32", - "syn 2.0.28", + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 2.0.37", ] [[package]] @@ -5081,9 +5193,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "object" -version = "0.31.1" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" dependencies = [ "memchr", ] @@ -5135,18 +5247,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" dependencies = [ "bytes", - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] [[package]] name = "openssl" -version = "0.10.56" +version = "0.10.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "729b745ad4a5575dd06a3e1af1414bd330ee561c01b3899eb584baeaa8def17e" +checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "cfg-if", "foreign-types", "libc", @@ -5161,9 +5273,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", - "syn 2.0.28", + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 2.0.37", ] [[package]] @@ -5174,9 +5286,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.91" +version = "0.9.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "866b5f16f90776b9bb8dc1e1802ac6f0513de3a7a7465867bfbc563dc737faac" +checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" dependencies = [ "cc", "libc", @@ -5203,6 +5315,15 @@ dependencies = [ "thiserror", ] +[[package]] +name = "ordered-float" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a54938017eacd63036332b4ae5c8a49fc8c0c1d6d629893057e4f13609edd06" +dependencies = [ + "num-traits", +] + [[package]] name = "ordered-multimap" version = "0.4.3" @@ -5226,7 +5347,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1358bd1558bd2a083fed428ffeda486fbfb323e698cdda7794259d592ca72db" dependencies = [ "aliasable", - "ouroboros_macro", + "ouroboros_macro 0.15.6", +] + +[[package]] +name = "ouroboros" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2ba07320d39dfea882faa70554b4bd342a5f273ed59ba7c1c6b4c840492c954" +dependencies = [ + "aliasable", + "ouroboros_macro 0.17.2", + "static_assertions", ] [[package]] @@ -5237,11 +5369,24 @@ checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7" dependencies = [ "Inflector", "proc-macro-error", - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] +[[package]] +name = "ouroboros_macro" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec4c6225c69b4ca778c0aea097321a64c421cf4577b331c61b229267edabb6f8" +dependencies = [ + "heck 0.4.1", + "proc-macro-error", + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 2.0.37", +] + [[package]] name = "overload" version = "0.1.1" @@ -5256,9 +5401,9 @@ checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" [[package]] name = "parity-scale-codec" -version = "3.6.4" +version = "3.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8e946cc0cc711189c0b0249fb8b599cbeeab9784d83c415719368bb8d4ac64" +checksum = "0dec8a8073036902368c2cdc0387e85ff9a37054d7e7c98e592145e0c92cd4fb" dependencies = [ "arrayvec", "bitvec 1.0.1", @@ -5270,13 +5415,13 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.6.4" +version = "3.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a296c3079b5fefbc499e1de58dc26c09b1b9a5952d26694ee89f04a43ebbb3e" +checksum = "312270ee71e1cd70289dacf597cab7b207aa107d2f28191c2ae45b2ece18a260" dependencies = [ "proc-macro-crate 1.2.1", - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] @@ -5325,7 +5470,7 @@ dependencies = [ "libc", "redox_syscall 0.3.5", "smallvec", - "windows-targets 0.48.1", + "windows-targets 0.48.5", ] [[package]] @@ -5393,7 +5538,7 @@ dependencies = [ "digest 0.10.7", "hmac 0.12.1", "password-hash 0.4.2", - "sha2 0.10.7", + "sha2 0.10.8", ] [[package]] @@ -5411,6 +5556,15 @@ dependencies = [ "base64 0.13.1", ] +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.0" @@ -5428,19 +5582,20 @@ dependencies = [ [[package]] name = "pest" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1acb4a4365a13f749a93f1a094a7805e5cfa0955373a9de860d962eaa3a5fe5a" +checksum = "c022f1e7b65d6a24c0dbbd5fb344c66881bc01f3e5ae74a1c8100f2f985d98a4" dependencies = [ + "memchr", "thiserror", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "666d00490d4ac815001da55838c500eafb0320019bbaa44444137c48b443a853" +checksum = "35513f630d46400a977c4cb58f78e1bfbe01434316e60c37d27b9ad6139c66d8" dependencies = [ "pest", "pest_generator", @@ -5448,26 +5603,26 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ca01446f50dbda87c1786af8770d535423fa8a53aec03b8f4e3d7eb10e0929" +checksum = "bc9fc1b9e7057baba189b5c626e2d6f40681ae5b6eb064dc7c7834101ec8123a" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.66", - "quote 1.0.32", - "syn 2.0.28", + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 2.0.37", ] [[package]] name = "pest_meta" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56af0a30af74d0445c0bf6d9d051c979b516a1a5af790d251daee76005420a48" +checksum = "1df74e9e7ec4053ceb980e7c0c8bd3594e977fde1af91daba9c928e8e8c6708d" dependencies = [ "once_cell", "pest", - "sha2 0.10.7", + "sha2 0.10.8", ] [[package]] @@ -5495,16 +5650,16 @@ version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", - "syn 2.0.28", + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 2.0.37", ] [[package]] name = "pin-project-lite" -version = "0.2.11" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c516611246607d0c04186886dbb3a754368ef82c79e9827a802c6d836dd111c" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -5513,12 +5668,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] -name = "pkcs8" -version = "0.8.0" +name = "pkcs1" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" dependencies = [ - "der 0.5.1", + "der 0.7.8", + "pkcs8 0.10.2", + "spki 0.7.2", +] + +[[package]] +name = "pkcs8" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" +dependencies = [ + "der 0.5.1", "spki 0.5.4", "zeroize", ] @@ -5533,6 +5699,16 @@ dependencies = [ "spki 0.6.0", ] +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der 0.7.8", + "spki 0.7.2", +] + [[package]] name = "pkg-config" version = "0.3.27" @@ -5568,11 +5744,12 @@ dependencies = [ [[package]] name = "postcard" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9ee729232311d3cd113749948b689627618133b1c5012b77342c1950b25eaeb" +checksum = "a55c51ee6c0db07e68448e336cf8ea4131a620edefebf9893e759b2d793420f8" dependencies = [ "cobs", + "embedded-io", "heapless", "serde", ] @@ -5625,12 +5802,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" +checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" dependencies = [ - "proc-macro2 1.0.66", - "syn 2.0.28", + "proc-macro2 1.0.67", + "syn 2.0.37", ] [[package]] @@ -5673,8 +5850,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", "version_check", ] @@ -5685,8 +5862,8 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "version_check", ] @@ -5701,9 +5878,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" dependencies = [ "unicode-ident", ] @@ -5750,8 +5927,8 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] @@ -5786,11 +5963,11 @@ dependencies = [ "fxhash", "quinn-proto", "quinn-udp", - "rustls 0.20.8", + "rustls 0.20.9", "thiserror", "tokio", "tracing", - "webpki 0.22.0", + "webpki 0.22.1", ] [[package]] @@ -5803,14 +5980,14 @@ dependencies = [ "fxhash", "rand 0.8.5", "ring", - "rustls 0.20.8", + "rustls 0.20.9", "rustls-native-certs 0.6.3", "rustls-pemfile 0.2.1", "slab", "thiserror", "tinyvec", "tracing", - "webpki 0.22.0", + "webpki 0.22.1", ] [[package]] @@ -5822,7 +5999,7 @@ dependencies = [ "futures-util", "libc", "quinn-proto", - "socket2", + "socket2 0.4.9", "tokio", "tracing", ] @@ -5838,11 +6015,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.32" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ - "proc-macro2 1.0.66", + "proc-macro2 1.0.67", ] [[package]] @@ -5939,9 +6116,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" dependencies = [ "either", "rayon-core", @@ -5949,14 +6126,12 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" dependencies = [ - "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "num_cpus", ] [[package]] @@ -5967,7 +6142,7 @@ checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" dependencies = [ "pem", "ring", - "time 0.3.25", + "time", "yasna", ] @@ -6002,14 +6177,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.3" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" +checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.3.6", - "regex-syntax 0.7.4", + "regex-automata 0.3.8", + "regex-syntax 0.7.5", ] [[package]] @@ -6023,13 +6198,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" +checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.4", + "regex-syntax 0.7.5", ] [[package]] @@ -6040,9 +6215,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "relayer" @@ -6080,21 +6255,21 @@ dependencies = [ [[package]] name = "rend" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581008d2099240d37fb08d77ad713bcaec2c4d89d50b5b21a8bb1996bbab68ab" +checksum = "a2571463863a6bd50c32f94402933f03457a3fbaf697a707c5be741e459f08fd" dependencies = [ "bytecheck", ] [[package]] name = "reqwest" -version = "0.11.18" +version = "0.11.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" +checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" dependencies = [ "async-compression", - "base64 0.21.2", + "base64 0.21.4", "bytes", "cookie", "cookie_store", @@ -6115,7 +6290,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.6", + "rustls 0.21.7", "rustls-pemfile 1.0.3", "serde", "serde_json", @@ -6123,13 +6298,13 @@ dependencies = [ "tokio", "tokio-native-tls", "tokio-rustls 0.24.1", - "tokio-util 0.7.8", + "tokio-util 0.7.9", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 0.22.6", + "webpki-roots 0.25.2", "winreg", ] @@ -6191,8 +6366,8 @@ version = "0.7.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2e06b915b5c230a17d7a736d1e2e63ee753c256a8614ef3f5147b13a4f5541d" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] @@ -6211,8 +6386,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] @@ -6249,6 +6424,28 @@ dependencies = [ "winapi", ] +[[package]] +name = "rsa" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ab43bb47d23c1a631b4b680199a45255dce26fa9ab2fa902581f624ff13e6a8" +dependencies = [ + "byteorder", + "const-oid 0.9.5", + "digest 0.10.7", + "num-bigint-dig", + "num-integer", + "num-iter", + "num-traits", + "pkcs1", + "pkcs8 0.10.2", + "rand_core 0.6.4", + "signature 2.1.0", + "spki 0.7.2", + "subtle", + "zeroize", +] + [[package]] name = "run-locally" version = "0.1.0" @@ -6258,7 +6455,7 @@ dependencies = [ "hyperlane-core", "macro_rules_attribute", "maplit", - "nix 0.26.2", + "nix 0.27.1", "regex", "tempfile", "ureq", @@ -6388,13 +6585,12 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.31.0" +version = "1.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a2ab0025103a60ecaaf3abf24db1db240a4e1c15837090d2c32f625ac98abea" +checksum = "a4c4216490d5a413bc6d10fa4742bd7d4955941d062c0ef873141d6b0e7b30fd" dependencies = [ "arrayvec", "borsh 0.10.3", - "byteorder", "bytes", "num-traits", "rand 0.8.5", @@ -6441,11 +6637,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.7" +version = "0.38.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "172891ebdceb05aa0005f533a6cbfca599ddd7d966f6f5d4d9b2e70478e70399" +checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.0", "errno", "libc", "linux-raw-sys", @@ -6467,21 +6663,21 @@ dependencies = [ [[package]] name = "rustls" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" dependencies = [ "log", "ring", "sct 0.7.0", - "webpki 0.22.0", + "webpki 0.22.1", ] [[package]] name = "rustls" -version = "0.21.6" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1feddffcfcc0b33f5c6ce9a29e341e4cd59c3f78e7ee45f4a40c038b1d6cbb" +checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" dependencies = [ "log", "ring", @@ -6528,14 +6724,14 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ - "base64 0.21.2", + "base64 0.21.4", ] [[package]] name = "rustls-webpki" -version = "0.101.2" +version = "0.101.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "513722fd73ad80a71f72b61009ea1b584bcfa1483ca93949c8f290298837fa59" +checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" dependencies = [ "ring", "untrusted", @@ -6599,8 +6795,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "912e55f6d20e0e80d63733872b40e1227c0bce1e1ab81ba67d696339bfd7fd29" dependencies = [ "proc-macro-crate 1.2.1", - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] @@ -6641,13 +6837,13 @@ dependencies = [ "hyperlane-test", "itertools 0.11.0", "migration", - "num-bigint 0.4.3", + "num-bigint 0.4.4", "prometheus", "sea-orm", "serde", "serde_json", "thiserror", - "time 0.3.25", + "time", "tokio", "tokio-test", "tracing", @@ -6669,9 +6865,9 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", - "syn 2.0.28", + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 2.0.37", ] [[package]] @@ -6697,7 +6893,7 @@ dependencies = [ "hmac 0.12.1", "pbkdf2 0.11.0", "salsa20 0.10.2", - "sha2 0.10.7", + "sha2 0.10.8", ] [[package]] @@ -6720,11 +6916,24 @@ dependencies = [ "untrusted", ] +[[package]] +name = "sea-bae" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bd3534a9978d0aa7edd2808dc1f8f31c4d0ecd31ddf71d997b3c98e9f3c9114" +dependencies = [ + "heck 0.4.1", + "proc-macro-error", + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 2.0.37", +] + [[package]] name = "sea-orm" -version = "0.11.3" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fade86e8d41fd1a4721f84cb834f4ca2783f973cc30e6212b7fafc134f169214" +checksum = "da5b2d70c255bc5cbe1d49f69c3c8eadae0fbbaeb18ee978edbf2f75775cb94d" dependencies = [ "async-stream", "async-trait", @@ -6732,17 +6941,17 @@ dependencies = [ "chrono", "futures", "log", - "ouroboros", + "ouroboros 0.17.2", "rust_decimal", "sea-orm-macros", "sea-query", "sea-query-binder", - "sea-strum", "serde", "serde_json", "sqlx", + "strum 0.25.0", "thiserror", - "time 0.3.25", + "time", "tracing", "url", "uuid 1.4.1", @@ -6750,13 +6959,14 @@ dependencies = [ [[package]] name = "sea-orm-cli" -version = "0.11.3" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbf34a2caf70c2e3be9bb1e674e9540f6dfd7c8f40f6f05daf3b9740e476005" +checksum = "6bef60732e6016c5643350c87f43a697e8c074e41e4e2a9d961c056cb1310915" dependencies = [ "chrono", - "clap 3.2.25", + "clap 4.4.6", "dotenvy", + "glob", "regex", "sea-schema", "tracing", @@ -6766,25 +6976,26 @@ dependencies = [ [[package]] name = "sea-orm-macros" -version = "0.11.3" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28936f26d62234ff0be16f80115dbdeb3237fe9c25cf18fbcd1e3b3592360f20" +checksum = "d7c8d455fad40194fb9774fdc4810c0f2700ff0dc0e93bd5ce9d641cc3f5dd75" dependencies = [ - "bae", - "heck 0.3.3", - "proc-macro2 1.0.66", - "quote 1.0.32", - "syn 1.0.109", + "heck 0.4.1", + "proc-macro2 1.0.67", + "quote 1.0.33", + "sea-bae", + "syn 2.0.37", + "unicode-ident", ] [[package]] name = "sea-orm-migration" -version = "0.11.3" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "278d3adfd0832b6ffc17d3cfbc574d3695a5c1b38814e0bc8ac238d33f3d87cf" +checksum = "7e53b6ddaf6dbb84e5dfc3fb78634ed0a4d6d64e7479500ab2585db239747031" dependencies = [ "async-trait", - "clap 3.2.25", + "clap 4.4.6", "dotenvy", "futures", "sea-orm", @@ -6796,24 +7007,27 @@ dependencies = [ [[package]] name = "sea-query" -version = "0.28.5" +version = "0.30.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbab99b8cd878ab7786157b7eb8df96333a6807cc6e45e8888c85b51534b401a" +checksum = "fb3e6bba153bb198646c8762c48414942a38db27d142e44735a133cabddcc820" dependencies = [ "bigdecimal", "chrono", + "derivative", + "inherent", + "ordered-float", "rust_decimal", "sea-query-derive", "serde_json", - "time 0.3.25", + "time", "uuid 1.4.1", ] [[package]] name = "sea-query-binder" -version = "0.3.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cea85029985b40dfbf18318d85fe985c04db7c1b4e5e8e0a0a0cdff5f1e30f9" +checksum = "36bbb68df92e820e4d5aeb17b4acd5cc8b5d18b2c36a4dd6f4626aabfa7ab1b9" dependencies = [ "bigdecimal", "chrono", @@ -6821,28 +7035,28 @@ dependencies = [ "sea-query", "serde_json", "sqlx", - "time 0.3.25", + "time", "uuid 1.4.1", ] [[package]] name = "sea-query-derive" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63f62030c60f3a691f5fe251713b4e220b306e50a71e1d6f9cce1f24bb781978" +checksum = "bd78f2e0ee8e537e9195d1049b752e0433e2cac125426bccb7b5c3e508096117" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", "thiserror", ] [[package]] name = "sea-schema" -version = "0.11.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeb2940bb5a10bc6cd05b450ce6cd3993e27fddd7eface2becb97fc5af3a040e" +checksum = "0cd9561232bd1b82ea748b581f15909d11de0db6563ddcf28c5d908aee8282f1" dependencies = [ "futures", "sea-query", @@ -6851,35 +7065,13 @@ dependencies = [ [[package]] name = "sea-schema-derive" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56821b7076f5096b8f726e2791ad255a99c82498e08ec477a65a96c461ff1927" -dependencies = [ - "heck 0.3.3", - "proc-macro2 1.0.66", - "quote 1.0.32", - "syn 1.0.109", -] - -[[package]] -name = "sea-strum" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391d06a6007842cfe79ac6f7f53911b76dfd69fc9a6769f1cf6569d12ce20e1b" -dependencies = [ - "sea-strum_macros", -] - -[[package]] -name = "sea-strum_macros" -version = "0.23.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69b4397b825df6ccf1e98bcdabef3bbcfc47ff5853983467850eeab878384f21" +checksum = "c6f686050f76bffc4f635cda8aea6df5548666b830b52387e8bc7de11056d11e" dependencies = [ - "heck 0.3.3", - "proc-macro2 1.0.66", - "quote 1.0.32", - "rustversion", + "heck 0.4.1", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] @@ -6956,9 +7148,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" dependencies = [ "serde", ] @@ -6971,9 +7163,9 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.183" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] @@ -6999,20 +7191,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.183" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", - "syn 2.0.28", + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 2.0.37", ] [[package]] name = "serde_json" -version = "1.0.104" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", @@ -7049,8 +7241,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" dependencies = [ "darling 0.13.4", - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] @@ -7060,7 +7252,7 @@ version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" dependencies = [ - "indexmap", + "indexmap 1.9.3", "ryu", "serde", "yaml-rust", @@ -7087,9 +7279,9 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", @@ -7123,9 +7315,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.7" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", @@ -7156,9 +7348,9 @@ dependencies = [ [[package]] name = "sharded-slab" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +checksum = "c1b21f559e07218024e7e9f90f96f601825397de0e25420135f7f952453fed0b" dependencies = [ "lazy_static", ] @@ -7171,9 +7363,9 @@ checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" [[package]] name = "shlex" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" +checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" [[package]] name = "signal-hook-registry" @@ -7194,6 +7386,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "signature" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + [[package]] name = "simdutf8" version = "0.1.4" @@ -7212,18 +7414,18 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "socket2" @@ -7235,6 +7437,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "solana-account-decoder" version = "1.14.13" @@ -7404,7 +7616,7 @@ dependencies = [ "enum_dispatch", "futures", "futures-util", - "indexmap", + "indexmap 1.9.3", "indicatif", "itertools 0.10.5", "jsonrpc-core", @@ -7416,7 +7628,7 @@ dependencies = [ "rand_chacha 0.2.2", "rayon", "reqwest", - "rustls 0.20.8", + "rustls 0.20.9", "semver", "serde", "serde_derive", @@ -7513,7 +7725,7 @@ dependencies = [ "serde_bytes", "serde_derive", "serde_json", - "sha2 0.10.7", + "sha2 0.10.8", "solana-frozen-abi-macro", "subtle", "thiserror", @@ -7524,8 +7736,8 @@ name = "solana-frozen-abi-macro" version = "1.14.13" source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "rustc_version", "syn 1.0.109", ] @@ -7575,7 +7787,7 @@ dependencies = [ "rand 0.7.3", "serde", "serde_derive", - "socket2", + "socket2 0.4.9", "solana-logger", "solana-sdk", "solana-version", @@ -7646,7 +7858,7 @@ dependencies = [ "serde_bytes", "serde_derive", "serde_json", - "sha2 0.10.7", + "sha2 0.10.8", "sha3 0.10.8", "solana-frozen-abi", "solana-frozen-abi-macro", @@ -7763,7 +7975,7 @@ dependencies = [ "num-traits", "num_cpus", "once_cell", - "ouroboros", + "ouroboros 0.15.6", "rand 0.7.3", "rayon", "regex", @@ -7832,7 +8044,7 @@ dependencies = [ "serde_bytes", "serde_derive", "serde_json", - "sha2 0.10.7", + "sha2 0.10.8", "sha3 0.10.8", "solana-frozen-abi", "solana-frozen-abi-macro", @@ -7850,8 +8062,8 @@ version = "1.14.13" source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" dependencies = [ "bs58 0.4.0", - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "rustversion", "syn 1.0.109", ] @@ -7900,7 +8112,7 @@ dependencies = [ "crossbeam-channel", "futures-util", "histogram", - "indexmap", + "indexmap 1.9.3", "itertools 0.10.5", "libc", "log", @@ -7911,7 +8123,7 @@ dependencies = [ "quinn", "rand 0.7.3", "rcgen", - "rustls 0.20.8", + "rustls 0.20.9", "solana-metrics", "solana-perf", "solana-sdk", @@ -8080,6 +8292,16 @@ dependencies = [ "der 0.6.1", ] +[[package]] +name = "spki" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +dependencies = [ + "base64ct", + "der 0.7.8", +] + [[package]] name = "spl-associated-token-account" version = "1.1.2" @@ -8167,109 +8389,228 @@ dependencies = [ [[package]] name = "sqlformat" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c12bc9199d1db8234678b7051747c07f517cdcf019262d1847b94ec8b1aee3e" +checksum = "6b7b278788e7be4d0d29c0f39497a0eef3fba6bbc8e70d8bf7fde46edeaa9e85" dependencies = [ - "itertools 0.10.5", + "itertools 0.11.0", "nom", "unicode_categories", ] [[package]] name = "sqlx" -version = "0.6.3" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8de3b03a925878ed54a954f621e64bf55a3c1bd29652d0d1a17830405350188" +checksum = "0e50c216e3624ec8e7ecd14c6a6a6370aad6ee5d8cfc3ab30b5162eeeef2ed33" dependencies = [ "sqlx-core", "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", ] [[package]] name = "sqlx-core" -version = "0.6.3" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa8241483a83a3f33aa5fff7e7d9def398ff9990b2752b6c6112b83c6d246029" +checksum = "8d6753e460c998bbd4cd8c6f0ed9a64346fcca0723d6e75e52fdc351c5d2169d" dependencies = [ - "ahash 0.7.6", + "ahash 0.8.3", "atoi", - "base64 0.13.1", "bigdecimal", - "bitflags 1.3.2", "byteorder", "bytes", "chrono", + "crc", "crossbeam-queue", - "dirs", "dotenvy", "either", "event-listener", "futures-channel", "futures-core", "futures-intrusive", + "futures-io", "futures-util", "hashlink", "hex 0.4.3", - "hkdf", - "hmac 0.12.1", - "indexmap", - "itoa", - "libc", + "indexmap 2.0.1", "log", - "md-5 0.10.5", "memchr", - "num-bigint 0.4.3", + "native-tls", "once_cell", "paste", "percent-encoding", - "rand 0.8.5", "rust_decimal", "serde", "serde_json", - "sha1", - "sha2 0.10.7", + "sha2 0.10.8", "smallvec", "sqlformat", - "sqlx-rt", - "stringprep", "thiserror", - "time 0.3.25", + "time", + "tokio", "tokio-stream", + "tracing", "url", "uuid 1.4.1", - "whoami", ] [[package]] name = "sqlx-macros" -version = "0.6.3" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a793bb3ba331ec8359c1853bd39eed32cdd7baaf22c35ccf5c92a7e8d1189ec" +dependencies = [ + "proc-macro2 1.0.67", + "quote 1.0.33", + "sqlx-core", + "sqlx-macros-core", + "syn 1.0.109", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9966e64ae989e7e575b19d7265cb79d7fc3cbbdf179835cb0d716f294c2049c9" +checksum = "0a4ee1e104e00dedb6aa5ffdd1343107b0a4702e862a84320ee7cc74782d96fc" dependencies = [ "dotenvy", "either", "heck 0.4.1", + "hex 0.4.3", "once_cell", - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", + "serde", "serde_json", + "sha2 0.10.8", "sqlx-core", - "sqlx-rt", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", "syn 1.0.109", + "tempfile", + "tokio", "url", ] [[package]] -name = "sqlx-rt" -version = "0.6.3" +name = "sqlx-mysql" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804d3f245f894e61b1e6263c84b23ca675d96753b5abfd5cc8597d86806e8024" +checksum = "864b869fdf56263f4c95c45483191ea0af340f9f3e3e7b4d57a61c7c87a970db" dependencies = [ - "native-tls", + "atoi", + "base64 0.21.4", + "bigdecimal", + "bitflags 2.4.0", + "byteorder", + "bytes", + "chrono", + "crc", + "digest 0.10.7", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array 0.14.7", + "hex 0.4.3", + "hkdf", + "hmac 0.12.1", + "itoa", + "log", + "md-5 0.10.6", + "memchr", "once_cell", - "tokio", - "tokio-native-tls", + "percent-encoding", + "rand 0.8.5", + "rsa", + "rust_decimal", + "serde", + "sha1", + "sha2 0.10.8", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "time", + "tracing", + "uuid 1.4.1", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb7ae0e6a97fb3ba33b23ac2671a5ce6e3cabe003f451abd5a56e7951d975624" +dependencies = [ + "atoi", + "base64 0.21.4", + "bigdecimal", + "bitflags 2.4.0", + "byteorder", + "chrono", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "hex 0.4.3", + "hkdf", + "hmac 0.12.1", + "home", + "itoa", + "log", + "md-5 0.10.6", + "memchr", + "num-bigint 0.4.4", + "once_cell", + "rand 0.8.5", + "rust_decimal", + "serde", + "serde_json", + "sha1", + "sha2 0.10.8", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "time", + "tracing", + "uuid 1.4.1", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59dc83cf45d89c555a577694534fcd1b55c545a816c816ce51f20bbe56a4f3f" +dependencies = [ + "atoi", + "chrono", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "sqlx-core", + "time", + "tracing", + "url", + "uuid 1.4.1", ] [[package]] @@ -8286,10 +8627,11 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stringprep" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3737bde7edce97102e0e2b15365bf7a20bfdb5f60f4f9e8d7004258a51a8da" +checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6" dependencies = [ + "finl_unicode", "unicode-bidi", "unicode-normalization", ] @@ -8337,8 +8679,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" dependencies = [ "heck 0.3.3", - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] @@ -8349,8 +8691,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "rustversion", "syn 1.0.109", ] @@ -8362,10 +8704,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "rustversion", - "syn 2.0.28", + "syn 2.0.37", ] [[package]] @@ -8397,19 +8739,19 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.28" +version = "2.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" +checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "unicode-ident", ] @@ -8419,8 +8761,8 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", "unicode-xid 0.2.4", ] @@ -8481,16 +8823,16 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee42b4e559f17bce0385ebf511a7beb67d5cc33c12c96b7f4e9789919d9c10f" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", + "proc-macro2 1.0.67", + "quote 1.0.33", "syn 1.0.109", ] [[package]] name = "tempfile" -version = "3.7.1" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if", "fastrand", @@ -8501,9 +8843,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" dependencies = [ "winapi-util", ] @@ -8531,22 +8873,22 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.44" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" +checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.44" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" +checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", - "syn 2.0.28", + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 2.0.37", ] [[package]] @@ -8561,20 +8903,9 @@ dependencies = [ [[package]] name = "time" -version = "0.1.45" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - -[[package]] -name = "time" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fdd63d58b18d663fbdf70e049f00a22c8e42be082203be7f26589213cd75ea" +checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe" dependencies = [ "deranged", "itoa", @@ -8585,15 +8916,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.11" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb71511c991639bb078fd5bf97757e03914361c48100d52878b8e52b46fb92cd" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" dependencies = [ "time-core", ] @@ -8643,11 +8974,10 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.29.1" +version = "1.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" dependencies = [ - "autocfg", "backtrace", "bytes", "libc", @@ -8656,7 +8986,7 @@ dependencies = [ "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.5.4", "tokio-macros", "windows-sys 0.48.0", ] @@ -8677,9 +9007,9 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", - "syn 2.0.28", + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 2.0.37", ] [[package]] @@ -8709,9 +9039,9 @@ version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ - "rustls 0.20.8", + "rustls 0.20.9", "tokio", - "webpki 0.22.0", + "webpki 0.22.1", ] [[package]] @@ -8720,7 +9050,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.6", + "rustls 0.21.7", "tokio", ] @@ -8753,9 +9083,9 @@ dependencies = [ [[package]] name = "tokio-test" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53474327ae5e166530d17f2d956afcb4f8a004de581b3cae10f12006bc8163e3" +checksum = "e89b3cbabd3ae862100094ae433e1def582cf86451b4e9bf83aa7ac1d8a7d719" dependencies = [ "async-stream", "bytes", @@ -8772,24 +9102,24 @@ checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" dependencies = [ "futures-util", "log", - "rustls 0.20.8", + "rustls 0.20.9", "tokio", "tokio-rustls 0.23.4", "tungstenite 0.17.3", - "webpki 0.22.0", + "webpki 0.22.1", "webpki-roots 0.22.6", ] [[package]] name = "tokio-tungstenite" -version = "0.18.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54319c93411147bced34cb5609a80e0a8e44c5999c93903a81cd866630ec0bfd" +checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" dependencies = [ "futures-util", "log", "tokio", - "tungstenite 0.18.0", + "tungstenite 0.20.1", ] [[package]] @@ -8809,9 +9139,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" dependencies = [ "bytes", "futures-core", @@ -8855,9 +9185,9 @@ version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", - "syn 2.0.28", + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 2.0.37", ] [[package]] @@ -8951,24 +9281,24 @@ dependencies = [ "httparse", "log", "rand 0.8.5", - "rustls 0.20.8", + "rustls 0.20.9", "sha-1", "thiserror", "url", "utf-8", - "webpki 0.22.0", + "webpki 0.22.1", "webpki-roots 0.22.6", ] [[package]] name = "tungstenite" -version = "0.18.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788" +checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" dependencies = [ - "base64 0.13.1", "byteorder", "bytes", + "data-encoding", "http", "httparse", "log", @@ -8981,9 +9311,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" @@ -9005,9 +9335,9 @@ dependencies = [ [[package]] name = "unicase" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" dependencies = [ "version_check", ] @@ -9020,9 +9350,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" @@ -9041,9 +9371,9 @@ checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unicode-width" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "unicode-xid" @@ -9094,7 +9424,7 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b11c96ac7ee530603dcdf68ed1557050f374ce55a5a07193ebf8cbc9f8927e9" dependencies = [ - "base64 0.21.2", + "base64 0.21.4", "log", "once_cell", "url", @@ -9112,9 +9442,9 @@ dependencies = [ [[package]] name = "url" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" dependencies = [ "form_urlencoded", "idna 0.4.0", @@ -9208,9 +9538,9 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "walkdir" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" dependencies = [ "same-file", "winapi-util", @@ -9227,9 +9557,9 @@ dependencies = [ [[package]] name = "warp" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba431ef570df1287f7f8b07e376491ad54f84d26ac473489427231e1718e1f69" +checksum = "c1e92e22e03ff1230c03a1a8ee37d2f89cd489e2e541b7550d6afad96faed169" dependencies = [ "bytes", "futures-channel", @@ -9250,8 +9580,8 @@ dependencies = [ "serde_urlencoded", "tokio", "tokio-stream", - "tokio-tungstenite 0.18.0", - "tokio-util 0.7.8", + "tokio-tungstenite 0.20.1", + "tokio-util 0.7.9", "tower-service", "tracing", ] @@ -9262,12 +9592,6 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -9293,9 +9617,9 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.66", - "quote 1.0.32", - "syn 2.0.28", + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 2.0.37", "wasm-bindgen-shared", ] @@ -9317,7 +9641,7 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ - "quote 1.0.32", + "quote 1.0.33", "wasm-bindgen-macro-support", ] @@ -9327,9 +9651,9 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", - "syn 2.0.28", + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 2.0.37", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -9377,9 +9701,9 @@ dependencies = [ [[package]] name = "webpki" -version = "0.22.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +checksum = "f0e74f82d49d545ad128049b7e88f6576df2da6b02e9ce565c6f533be576957e" dependencies = [ "ring", "untrusted", @@ -9400,18 +9724,25 @@ version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" dependencies = [ - "webpki 0.22.0", + "webpki 0.22.1", ] +[[package]] +name = "webpki-roots" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" + [[package]] name = "which" -version = "4.4.0" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" dependencies = [ "either", - "libc", + "home", "once_cell", + "rustix", ] [[package]] @@ -9419,10 +9750,6 @@ name = "whoami" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" -dependencies = [ - "wasm-bindgen", - "web-sys", -] [[package]] name = "winapi" @@ -9442,9 +9769,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] @@ -9461,7 +9788,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets 0.48.1", + "windows-targets 0.48.5", ] [[package]] @@ -9479,7 +9806,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.1", + "windows-targets 0.48.5", ] [[package]] @@ -9499,17 +9826,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -9520,9 +9847,9 @@ checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" @@ -9532,9 +9859,9 @@ checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" @@ -9544,9 +9871,9 @@ checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" @@ -9556,9 +9883,9 @@ checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" @@ -9568,9 +9895,9 @@ checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" @@ -9580,9 +9907,9 @@ checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" @@ -9592,17 +9919,18 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winreg" -version = "0.10.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi", + "cfg-if", + "windows-sys 0.48.0", ] [[package]] @@ -9648,7 +9976,7 @@ dependencies = [ "oid-registry", "rusticata-macros", "thiserror", - "time 0.3.25", + "time", ] [[package]] @@ -9662,9 +9990,9 @@ dependencies = [ [[package]] name = "xml-rs" -version = "0.8.16" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47430998a7b5d499ccee752b41567bc3afc57e1327dc855b1a2aa44ce29b5fa1" +checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" [[package]] name = "yaml-rust" @@ -9681,7 +10009,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" dependencies = [ - "time 0.3.25", + "time", ] [[package]] @@ -9699,9 +10027,9 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2 1.0.66", - "quote 1.0.32", - "syn 2.0.28", + "proc-macro2 1.0.67", + "quote 1.0.33", + "syn 2.0.37", ] [[package]] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 3608f3f1f..b1400bbf8 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -22,6 +22,7 @@ members = [ "sealevel/libraries/serializable-account-meta", "sealevel/libraries/test-transaction-utils", "sealevel/libraries/test-utils", + "sealevel/programs/helloworld", "sealevel/programs/hyperlane-sealevel-token", "sealevel/programs/hyperlane-sealevel-token-collateral", "sealevel/programs/hyperlane-sealevel-token-native", @@ -55,7 +56,7 @@ auto_impl = "1.0" backtrace = "0.3" base64 = "0.21.2" bincode = "1.3" -borsh = "0.9" +borsh = "0.9" # 0.9 is needed for solana 1.14 bs58 = "0.5.0" bytes = "1" clap = "4" @@ -76,7 +77,7 @@ fuels = "0.38" fuels-code-gen = "0.38" futures = "0.3" futures-util = "0.3" -generic-array = { version = "0.14", features = ["serde", "more_lengths"] } +generic-array = { version = "1.0", features = ["serde", "more_lengths"] } getrandom = { version = "0.2", features = ["js"] } # Required for WASM support https://docs.rs/getrandom/latest/getrandom/#webassembly-support hex = "0.4" itertools = "0.11.0" @@ -85,7 +86,7 @@ log = "0.4" macro_rules_attribute = "0.2" maplit = "1.0" mockall = "0.11" -nix = { version = "0.26", default-features = false } +nix = { version = "0.27", default-features = false } num = "0.4" num-bigint = "0.4" num-derive = "0.4.0" @@ -99,8 +100,8 @@ regex = "1.5" reqwest = "0.11" rlp = "=0.5.2" rocksdb = "0.21.0" -sea-orm = { version = "0.11.1", features = ["sqlx-postgres", "runtime-tokio-native-tls", "with-bigdecimal", "with-time", "macros"] } -sea-orm-migration = { version = "0.11.1", features = ["sqlx-postgres", "runtime-tokio-native-tls"] } +sea-orm = { version = "0.12.3", features = ["sqlx-postgres", "runtime-tokio-native-tls", "with-bigdecimal", "with-time", "macros"] } +sea-orm-migration = { version = "0.12.3", features = ["sqlx-postgres", "runtime-tokio-native-tls"] } semver = "1.0" serde = { version = "1.0", features = ["derive"] } serde_bytes = "0.11" diff --git a/rust/Dockerfile b/rust/Dockerfile index 72d0c94d3..f931c0fa7 100644 --- a/rust/Dockerfile +++ b/rust/Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:experimental -FROM rust:1.71.1 as builder +FROM rust:1.72.1 as builder WORKDIR /usr/src # 1a: Prepare for static linking @@ -34,7 +34,7 @@ RUN \ cp /usr/src/target/release/scraper /release ## 2: Copy the binaries to release image -FROM ubuntu:20.04 +FROM ubuntu:22.04 RUN apt-get update && \ apt-get install -y \ openssl \ diff --git a/rust/README.md b/rust/README.md index b51066795..ae73e2418 100644 --- a/rust/README.md +++ b/rust/README.md @@ -5,14 +5,14 @@ - install `rustup` - [link here](https://rustup.rs/) -Note: You should be running >= version `1.71.1` of the rustc compiler, you can see that version with this command and +Note: You should be running >= version `1.72.1` of the rustc compiler, you can see that version with this command and should see similar output: ``` $ rustup --version rustup 1.26.0 (5af9b9484 2023-04-05) info: This is the version for the rustup toolchain manager, not the rustc compiler. -info: The currently active `rustc` version is `rustc 1.71.1 (eb26296b5 2023-08-03)` +info: The currently active `rustc` version is `rustc 1.72.1 (d5c2e9c34 2023-09-13)` ``` ### Running Locally diff --git a/rust/agents/relayer/src/msg/metadata/base.rs b/rust/agents/relayer/src/msg/metadata/base.rs index c05c1e631..e08faeda7 100644 --- a/rust/agents/relayer/src/msg/metadata/base.rs +++ b/rust/agents/relayer/src/msg/metadata/base.rs @@ -76,9 +76,14 @@ impl MetadataBuilder for BaseMetadataBuilder { ism_address: H256, message: &HyperlaneMessage, ) -> Result>> { - const CTX: &str = "When fetching module type"; - let ism = self.build_ism(ism_address).await.context(CTX)?; - let module_type = ism.module_type().await.context(CTX)?; + let ism = self + .build_ism(ism_address) + .await + .context("When building ISM")?; + let module_type = ism + .module_type() + .await + .context("When fetching module type")?; let base = self.clone_with_incremented_depth()?; let metadata_builder: Box = match module_type { @@ -96,7 +101,7 @@ impl MetadataBuilder for BaseMetadataBuilder { metadata_builder .build(ism_address, message) .await - .context(CTX) + .context("When building metadata") } } @@ -193,8 +198,12 @@ impl BaseMetadataBuilder { for (&validator, validator_storage_locations) in validators.iter().zip(storage_locations) { for storage_location in validator_storage_locations.iter().rev() { let Ok(config) = CheckpointSyncerConf::from_str(storage_location) else { - debug!(?validator, ?storage_location, "Could not parse checkpoint syncer config for validator"); - continue + debug!( + ?validator, + ?storage_location, + "Could not parse checkpoint syncer config for validator" + ); + continue; }; // If this is a LocalStorage based checkpoint syncer and it's not diff --git a/rust/agents/relayer/src/msg/pending_message.rs b/rust/agents/relayer/src/msg/pending_message.rs index a7add49bd..860c1017a 100644 --- a/rust/agents/relayer/src/msg/pending_message.rs +++ b/rust/agents/relayer/src/msg/pending_message.rs @@ -1,17 +1,17 @@ -use std::fmt::{Debug, Formatter}; -use std::sync::Arc; -use std::time::{Duration, Instant}; +use std::{ + fmt::{Debug, Formatter}, + sync::Arc, + time::{Duration, Instant}, +}; use async_trait::async_trait; use derive_new::new; use eyre::{Context, Result}; -use hyperlane_base::db::HyperlaneRocksDB; +use hyperlane_base::{db::HyperlaneRocksDB, CoreMetrics}; +use hyperlane_core::{HyperlaneChain, HyperlaneDomain, HyperlaneMessage, Mailbox, U256}; use prometheus::{IntCounter, IntGauge}; use tracing::{debug, error, info, instrument, trace, warn}; -use hyperlane_base::CoreMetrics; -use hyperlane_core::{HyperlaneChain, HyperlaneDomain, HyperlaneMessage, Mailbox, U256}; - use super::{ gas_payment::GasPaymentEnforcer, metadata::{BaseMetadataBuilder, MetadataBuilder}, @@ -183,11 +183,10 @@ impl PendingOperation for PendingMessage { .message_meets_gas_payment_requirement(&self.message, &tx_cost_estimate) .await, "checking if message meets gas payment requirement" - ) - else { - info!(?tx_cost_estimate, "Gas payment requirement not met yet"); - return self.on_reprepare(); - }; + ) else { + info!(?tx_cost_estimate, "Gas payment requirement not met yet"); + return self.on_reprepare(); + }; // Go ahead and attempt processing of message to destination chain. debug!( @@ -393,9 +392,12 @@ impl PendingMessage { i if (1..12).contains(&i) => 10, // wait 90s to 19.5min with a linear increase i if (12..24).contains(&i) => (i as u64 - 11) * 90, - // exponential increase + 30 min; -21 makes it so that at i = 32 it will be - // ~60min timeout (64min to be more precise). - i => (2u64).pow(i - 21) + 60 * 30, + // wait 30min for the next 12 attempts + i if (24..36).contains(&i) => 60 * 30, + // wait 60min for the next 12 attempts + i if (36..48).contains(&i) => 60 * 60, + // wait 3h for the next 12 attempts, + _ => 60 * 60 * 3, })) } } diff --git a/rust/agents/relayer/src/msg/pending_operation.rs b/rust/agents/relayer/src/msg/pending_operation.rs index 31d6d23b5..a1219a98b 100644 --- a/rust/agents/relayer/src/msg/pending_operation.rs +++ b/rust/agents/relayer/src/msg/pending_operation.rs @@ -1,10 +1,8 @@ -use std::cmp::Ordering; -use std::time::Instant; +use std::{cmp::Ordering, time::Instant}; use async_trait::async_trait; use enum_dispatch::enum_dispatch; use eyre::Report; - use hyperlane_core::HyperlaneDomain; #[allow(unused_imports)] // required for enum_dispatch @@ -123,29 +121,30 @@ macro_rules! make_op_try { /// Handle a result and either return early with retry or a critical failure on /// error. macro_rules! op_try { - (critical: $e:expr, $ctx:literal) => { - match $e { - Ok(v) => v, - Err(e) => { - error!(error=?e, concat!("Error when ", $ctx)); - return PendingOperationResult::CriticalFailure( - Err::<(), _>(e) - .context(concat!("When ", $ctx)) - .unwrap_err() - ); + (critical: $e:expr, $ctx:literal) => { + match $e { + Ok(v) => v, + Err(e) => { + error!(error=?e, concat!("Error when ", $ctx)); + return PendingOperationResult::CriticalFailure( + Err::<(), _>(e) + .context(concat!("When ", $ctx)) + .unwrap_err() + ); + } } - } - }; - ($e:expr, $ctx:literal) => { - match $e { - Ok(v) => v, - Err(e) => { - warn!(error=?e, concat!("Error when ", $ctx)); - return $on_retry(); + }; + ($e:expr, $ctx:literal) => { + match $e { + Ok(v) => v, + Err(e) => { + warn!(error=?e, concat!("Error when ", $ctx)); + #[allow(clippy::redundant_closure_call)] + return $on_retry(); + } } - } - }; - } + }; + } }; } diff --git a/rust/agents/relayer/src/msg/processor.rs b/rust/agents/relayer/src/msg/processor.rs index 2028595cc..56b5bd017 100644 --- a/rust/agents/relayer/src/msg/processor.rs +++ b/rust/agents/relayer/src/msg/processor.rs @@ -390,7 +390,7 @@ mod test { .await; // Set some retry counts. This should update HyperlaneDB entries too. - let msg_retries_to_set = vec![3, 0, 10]; + let msg_retries_to_set = [3, 0, 10]; pending_messages .into_iter() .enumerate() diff --git a/rust/agents/scraper/migration/Cargo.toml b/rust/agents/scraper/migration/Cargo.toml index b90819d83..6d21643fc 100644 --- a/rust/agents/scraper/migration/Cargo.toml +++ b/rust/agents/scraper/migration/Cargo.toml @@ -18,7 +18,7 @@ sea-orm.workspace = true sea-orm-migration.workspace = true serde.workspace = true time.workspace = true -tokio = { workspace = true, features = ["rt", "macros", "parking_lot"] } +tokio = { workspace = true, features = ["rt", "process", "macros", "parking_lot"] } # bin-only deps tracing-subscriber.workspace = true diff --git a/rust/agents/scraper/migration/bin/generate_entities.rs b/rust/agents/scraper/migration/bin/generate_entities.rs index f2436e679..5ec3e6c66 100644 --- a/rust/agents/scraper/migration/bin/generate_entities.rs +++ b/rust/agents/scraper/migration/bin/generate_entities.rs @@ -1,18 +1,13 @@ -use std::path::Path; -use std::process::Stdio; -use std::time::Duration; - -use tokio::fs::remove_dir_all; -use tokio::process::Command; -use tokio::time::sleep; +use std::{path::Path, process::Stdio, time::Duration}; use common::*; +use tokio::{fs::remove_dir_all, process::Command, time::sleep}; mod common; const RAW_DB_PATH: &str = "./agents/scraper/src/db/generated"; const DOCKER_NAME: &str = "scraper-entity-generator"; -const CLI_VERSION: &str = "0.11.1"; +const CLI_VERSION: &str = "0.12.3"; struct PostgresDockerContainer; @@ -58,7 +53,7 @@ impl Drop for PostgresDockerContainer { } } -#[tokio::main] +#[tokio::main(flavor = "current_thread")] async fn main() -> Result<(), DbErr> { assert_eq!( std::env::current_dir().unwrap().file_name().unwrap(), diff --git a/rust/agents/scraper/src/db/generated/block.rs b/rust/agents/scraper/src/db/generated/block.rs index 928926cda..098cb839c 100644 --- a/rust/agents/scraper/src/db/generated/block.rs +++ b/rust/agents/scraper/src/db/generated/block.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.1 +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3 use sea_orm::entity::prelude::*; @@ -56,9 +56,7 @@ impl ColumnTrait for Column { Self::Id => ColumnType::BigInteger.def(), Self::TimeCreated => ColumnType::DateTime.def(), Self::Domain => ColumnType::Integer.def(), - Self::Hash => ColumnType::Binary(sea_orm::sea_query::BlobSize::Blob(None)) - .def() - .unique(), + Self::Hash => ColumnType::Binary(BlobSize::Blob(None)).def().unique(), Self::Height => ColumnType::BigInteger.def(), Self::Timestamp => ColumnType::DateTime.def(), } diff --git a/rust/agents/scraper/src/db/generated/cursor.rs b/rust/agents/scraper/src/db/generated/cursor.rs index 1afb88111..f04b47428 100644 --- a/rust/agents/scraper/src/db/generated/cursor.rs +++ b/rust/agents/scraper/src/db/generated/cursor.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.1 +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3 use sea_orm::entity::prelude::*; diff --git a/rust/agents/scraper/src/db/generated/delivered_message.rs b/rust/agents/scraper/src/db/generated/delivered_message.rs index c8c7f3195..542d1c8e7 100644 --- a/rust/agents/scraper/src/db/generated/delivered_message.rs +++ b/rust/agents/scraper/src/db/generated/delivered_message.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.1 +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3 use sea_orm::entity::prelude::*; @@ -55,13 +55,9 @@ impl ColumnTrait for Column { match self { Self::Id => ColumnType::BigInteger.def(), Self::TimeCreated => ColumnType::DateTime.def(), - Self::MsgId => ColumnType::Binary(sea_orm::sea_query::BlobSize::Blob(None)) - .def() - .unique(), + Self::MsgId => ColumnType::Binary(BlobSize::Blob(None)).def().unique(), Self::Domain => ColumnType::Integer.def(), - Self::DestinationMailbox => { - ColumnType::Binary(sea_orm::sea_query::BlobSize::Blob(None)).def() - } + Self::DestinationMailbox => ColumnType::Binary(BlobSize::Blob(None)).def(), Self::DestinationTxId => ColumnType::BigInteger.def(), } } diff --git a/rust/agents/scraper/src/db/generated/domain.rs b/rust/agents/scraper/src/db/generated/domain.rs index 3e70e4b8f..25f521929 100644 --- a/rust/agents/scraper/src/db/generated/domain.rs +++ b/rust/agents/scraper/src/db/generated/domain.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.1 +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3 use sea_orm::entity::prelude::*; diff --git a/rust/agents/scraper/src/db/generated/gas_payment.rs b/rust/agents/scraper/src/db/generated/gas_payment.rs index 44064d703..d4e3d53ac 100644 --- a/rust/agents/scraper/src/db/generated/gas_payment.rs +++ b/rust/agents/scraper/src/db/generated/gas_payment.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.1 +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3 use sea_orm::entity::prelude::*; @@ -60,7 +60,7 @@ impl ColumnTrait for Column { Self::Id => ColumnType::BigInteger.def(), Self::TimeCreated => ColumnType::DateTime.def(), Self::Domain => ColumnType::Integer.def(), - Self::MsgId => ColumnType::Binary(sea_orm::sea_query::BlobSize::Blob(None)).def(), + Self::MsgId => ColumnType::Binary(BlobSize::Blob(None)).def(), Self::Payment => ColumnType::Decimal(Some((78u32, 0u32))).def(), Self::GasAmount => ColumnType::Decimal(Some((78u32, 0u32))).def(), Self::TxId => ColumnType::BigInteger.def(), diff --git a/rust/agents/scraper/src/db/generated/message.rs b/rust/agents/scraper/src/db/generated/message.rs index f7308fd91..e06ea5de1 100644 --- a/rust/agents/scraper/src/db/generated/message.rs +++ b/rust/agents/scraper/src/db/generated/message.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.1 +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3 use sea_orm::entity::prelude::*; @@ -65,18 +65,14 @@ impl ColumnTrait for Column { match self { Self::Id => ColumnType::BigInteger.def(), Self::TimeCreated => ColumnType::DateTime.def(), - Self::MsgId => ColumnType::Binary(sea_orm::sea_query::BlobSize::Blob(None)).def(), + Self::MsgId => ColumnType::Binary(BlobSize::Blob(None)).def(), Self::Origin => ColumnType::Integer.def(), Self::Destination => ColumnType::Integer.def(), Self::Nonce => ColumnType::Integer.def(), - Self::Sender => ColumnType::Binary(sea_orm::sea_query::BlobSize::Blob(None)).def(), - Self::Recipient => ColumnType::Binary(sea_orm::sea_query::BlobSize::Blob(None)).def(), - Self::MsgBody => ColumnType::Binary(sea_orm::sea_query::BlobSize::Blob(None)) - .def() - .null(), - Self::OriginMailbox => { - ColumnType::Binary(sea_orm::sea_query::BlobSize::Blob(None)).def() - } + Self::Sender => ColumnType::Binary(BlobSize::Blob(None)).def(), + Self::Recipient => ColumnType::Binary(BlobSize::Blob(None)).def(), + Self::MsgBody => ColumnType::Binary(BlobSize::Blob(None)).def().null(), + Self::OriginMailbox => ColumnType::Binary(BlobSize::Blob(None)).def(), Self::OriginTxId => ColumnType::BigInteger.def(), } } diff --git a/rust/agents/scraper/src/db/generated/mod.rs b/rust/agents/scraper/src/db/generated/mod.rs index 73d12071f..929adca9d 100644 --- a/rust/agents/scraper/src/db/generated/mod.rs +++ b/rust/agents/scraper/src/db/generated/mod.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.1 +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3 pub mod prelude; diff --git a/rust/agents/scraper/src/db/generated/prelude.rs b/rust/agents/scraper/src/db/generated/prelude.rs index 93116af36..2bee035aa 100644 --- a/rust/agents/scraper/src/db/generated/prelude.rs +++ b/rust/agents/scraper/src/db/generated/prelude.rs @@ -1,9 +1,8 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.1 +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3 -pub use super::block::Entity as Block; -pub use super::cursor::Entity as Cursor; -pub use super::delivered_message::Entity as DeliveredMessage; -pub use super::domain::Entity as Domain; -pub use super::gas_payment::Entity as GasPayment; -pub use super::message::Entity as Message; -pub use super::transaction::Entity as Transaction; +pub use super::{ + block::Entity as Block, cursor::Entity as Cursor, + delivered_message::Entity as DeliveredMessage, domain::Entity as Domain, + gas_payment::Entity as GasPayment, message::Entity as Message, + transaction::Entity as Transaction, +}; diff --git a/rust/agents/scraper/src/db/generated/transaction.rs b/rust/agents/scraper/src/db/generated/transaction.rs index 74ab6645e..216545b37 100644 --- a/rust/agents/scraper/src/db/generated/transaction.rs +++ b/rust/agents/scraper/src/db/generated/transaction.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.1 +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3 use sea_orm::entity::prelude::*; @@ -73,9 +73,7 @@ impl ColumnTrait for Column { match self { Self::Id => ColumnType::BigInteger.def(), Self::TimeCreated => ColumnType::DateTime.def(), - Self::Hash => ColumnType::Binary(sea_orm::sea_query::BlobSize::Blob(None)) - .def() - .unique(), + Self::Hash => ColumnType::Binary(BlobSize::Blob(None)).def().unique(), Self::BlockId => ColumnType::BigInteger.def(), Self::GasLimit => ColumnType::Decimal(Some((78u32, 0u32))).def(), Self::MaxPriorityFeePerGas => ColumnType::Decimal(Some((78u32, 0u32))).def().null(), @@ -83,10 +81,8 @@ impl ColumnTrait for Column { Self::GasPrice => ColumnType::Decimal(Some((78u32, 0u32))).def().null(), Self::EffectiveGasPrice => ColumnType::Decimal(Some((78u32, 0u32))).def().null(), Self::Nonce => ColumnType::BigInteger.def(), - Self::Sender => ColumnType::Binary(sea_orm::sea_query::BlobSize::Blob(None)).def(), - Self::Recipient => ColumnType::Binary(sea_orm::sea_query::BlobSize::Blob(None)) - .def() - .null(), + Self::Sender => ColumnType::Binary(BlobSize::Blob(None)).def(), + Self::Recipient => ColumnType::Binary(BlobSize::Blob(None)).def().null(), Self::GasUsed => ColumnType::Decimal(Some((78u32, 0u32))).def(), Self::CumulativeGasUsed => ColumnType::Decimal(Some((78u32, 0u32))).def(), } diff --git a/rust/chains/hyperlane-ethereum/src/tx.rs b/rust/chains/hyperlane-ethereum/src/tx.rs index bb295346e..2850bf93c 100644 --- a/rust/chains/hyperlane-ethereum/src/tx.rs +++ b/rust/chains/hyperlane-ethereum/src/tx.rs @@ -2,16 +2,18 @@ use std::num::NonZeroU64; use std::sync::Arc; use std::time::Duration; -use ethers::abi::Detokenize; -use ethers::prelude::{NameOrAddress, TransactionReceipt}; -use ethers::types::Eip1559TransactionRequest; +use ethers::{ + abi::Detokenize, + prelude::{NameOrAddress, TransactionReceipt}, + types::Eip1559TransactionRequest, +}; use ethers_contract::builders::ContractCall; use ethers_core::types::BlockNumber; +use hyperlane_core::{ + utils::fmt_bytes, ChainCommunicationError, ChainResult, KnownHyperlaneDomain, H256, U256, +}; use tracing::{error, info}; -use hyperlane_core::utils::fmt_bytes; -use hyperlane_core::{ChainCommunicationError, ChainResult, KnownHyperlaneDomain, H256, U256}; - use crate::Middleware; /// An amount of gas to add to the estimated gas @@ -87,7 +89,7 @@ where }; let Ok((max_fee, max_priority_fee)) = provider.estimate_eip1559_fees(None).await else { // Is not EIP 1559 chain - return Ok(tx.gas(gas_limit)) + return Ok(tx.gas(gas_limit)); }; let max_priority_fee = if matches!( KnownHyperlaneDomain::try_from(domain), diff --git a/rust/chains/hyperlane-ethereum/src/validator_announce.rs b/rust/chains/hyperlane-ethereum/src/validator_announce.rs index 2d455fc06..8bce341c2 100644 --- a/rust/chains/hyperlane-ethereum/src/validator_announce.rs +++ b/rust/chains/hyperlane-ethereum/src/validator_announce.rs @@ -1,26 +1,25 @@ #![allow(clippy::enum_variant_names)] #![allow(missing_docs)] -use std::collections::HashMap; -use std::sync::Arc; +use std::{collections::HashMap, sync::Arc}; use async_trait::async_trait; use ethers::providers::Middleware; - use ethers_contract::builders::ContractCall; use hyperlane_core::{ Announcement, ChainResult, ContractLocator, HyperlaneAbi, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneProvider, SignedType, TxOutcome, ValidatorAnnounce, H160, H256, U256, }; -use tracing::instrument; -use tracing::log::trace; - -use crate::contracts::i_validator_announce::{ - IValidatorAnnounce as EthereumValidatorAnnounceInternal, IVALIDATORANNOUNCE_ABI, +use tracing::{instrument, log::trace}; + +use crate::{ + contracts::i_validator_announce::{ + IValidatorAnnounce as EthereumValidatorAnnounceInternal, IVALIDATORANNOUNCE_ABI, + }, + trait_builder::BuildableWithProvider, + tx::{fill_tx_gas_params, report_tx}, + EthereumProvider, }; -use crate::trait_builder::BuildableWithProvider; -use crate::tx::{fill_tx_gas_params, report_tx}; -use crate::EthereumProvider; impl std::fmt::Display for EthereumValidatorAnnounceInternal where @@ -140,22 +139,17 @@ where let validator = announcement.value.validator; let eth_h160: ethers::types::H160 = validator.into(); - let Ok(contract_call) = self - .announce_contract_call(announcement, None) - .await - else { - trace!("Unable to get announce contract call"); - return None; + let Ok(contract_call) = self.announce_contract_call(announcement, None).await else { + trace!("Unable to get announce contract call"); + return None; }; - let Ok(balance) = self.provider.get_balance(eth_h160, None).await - else { + let Ok(balance) = self.provider.get_balance(eth_h160, None).await else { trace!("Unable to query balance"); return None; }; - let Some(max_cost) = contract_call.tx.max_cost() - else { + let Some(max_cost) = contract_call.tx.max_cost() else { trace!("Unable to get announce max cost"); return None; }; diff --git a/rust/chains/hyperlane-sealevel/src/interchain_gas.rs b/rust/chains/hyperlane-sealevel/src/interchain_gas.rs index c7c2f3a75..3c3adedd2 100644 --- a/rust/chains/hyperlane-sealevel/src/interchain_gas.rs +++ b/rust/chains/hyperlane-sealevel/src/interchain_gas.rs @@ -23,7 +23,9 @@ use solana_sdk::{commitment_config::CommitmentConfig, pubkey::Pubkey}; use derive_new::new; -/// The offset to get the `unique_gas_payment_pubkey` field from the serialized GasPaymentData +/// The offset to get the `unique_gas_payment_pubkey` field from the serialized GasPaymentData. +/// The account data includes prefixes that are accounted for here: a 1 byte initialized flag +/// and an 8 byte discriminator. const UNIQUE_GAS_PAYMENT_PUBKEY_OFFSET: usize = 1 + 8 + 8 + 32 + 4 + 32 + 8 + 8; /// A reference to an IGP contract on some Sealevel chain @@ -126,6 +128,7 @@ impl SealevelInterchainGasPaymasterIndexer { Ok(Self { rpc_client, igp }) } + #[instrument(err, skip(self))] async fn get_payment_with_sequence( &self, sequence_number: u64, @@ -163,12 +166,15 @@ impl SealevelInterchainGasPaymasterIndexer { }, with_context: Some(false), }; + tracing::debug!(config=?config, "Fetching program accounts"); let accounts = self .rpc_client .get_program_accounts_with_config(&self.igp.program_id, config) .await .map_err(ChainCommunicationError::from_other)?; + tracing::debug!(accounts=?accounts, "Fetched program accounts"); + // Now loop through matching accounts and find the one with a valid account pubkey // that proves it's an actual gas payment PDA. let mut valid_payment_pda_pubkey = Option::::None; @@ -208,6 +214,8 @@ impl SealevelInterchainGasPaymasterIndexer { .map_err(ChainCommunicationError::from_other)? .into_inner(); + tracing::debug!(gas_payment_account=?gas_payment_account, "Found gas payment account"); + let igp_payment = InterchainGasPayment { message_id: gas_payment_account.message_id, destination: gas_payment_account.destination_domain, @@ -251,6 +259,8 @@ impl Indexer for SealevelInterchainGasPaymasterIndexer { let igp_account_filter = self.igp.igp_account; if igp_account_filter == sealevel_payment.igp_account_pubkey { payments.push((sealevel_payment.payment, sealevel_payment.log_meta)); + } else { + tracing::debug!(sealevel_payment=?sealevel_payment, igp_account_filter=?igp_account_filter, "Found interchain gas payment for a different IGP account, skipping"); } } } diff --git a/rust/hyperlane-base/Cargo.toml b/rust/hyperlane-base/Cargo.toml index db98e22af..3d28ab0e0 100644 --- a/rust/hyperlane-base/Cargo.toml +++ b/rust/hyperlane-base/Cargo.toml @@ -41,7 +41,7 @@ backtrace = { workspace = true, optional = true } backtrace-oneline = { path = "../utils/backtrace-oneline", optional = true } ethers-prometheus = { path = "../ethers-prometheus", features = ["serde"] } -hyperlane-core = { path = "../hyperlane-core", features = ["agent"] } +hyperlane-core = { path = "../hyperlane-core", features = ["agent", "float"] } hyperlane-ethereum = { path = "../chains/hyperlane-ethereum" } hyperlane-fuel = { path = "../chains/hyperlane-fuel" } hyperlane-sealevel = { path = "../chains/hyperlane-sealevel" } diff --git a/rust/hyperlane-base/src/contract_sync/cursor.rs b/rust/hyperlane-base/src/contract_sync/cursor.rs index cdbbffaf9..14cff9b02 100644 --- a/rust/hyperlane-base/src/contract_sync/cursor.rs +++ b/rust/hyperlane-base/src/contract_sync/cursor.rs @@ -1,7 +1,7 @@ -use std::cmp::Ordering; -use std::fmt::Debug; -use std::ops::RangeInclusive; use std::{ + cmp::Ordering, + fmt::Debug, + ops::RangeInclusive, sync::Arc, time::{Duration, Instant}, }; @@ -9,14 +9,13 @@ use std::{ use async_trait::async_trait; use derive_new::new; use eyre::Result; -use tokio::time::sleep; -use tracing::{debug, warn}; - use hyperlane_core::{ ChainCommunicationError, ChainResult, ContractSyncCursor, CursorAction, HyperlaneMessage, HyperlaneMessageStore, HyperlaneWatermarkedLogStore, IndexMode, Indexer, LogMeta, SequenceIndexer, }; +use tokio::time::sleep; +use tracing::{debug, warn}; use crate::contract_sync::eta_calculator::SyncerEtaCalculator; @@ -67,7 +66,7 @@ impl SyncState { "Sequence indexing requires a max sequence", ) })?; - if let Some(range) = self.sequence_range(tip, max_sequence)? { + if let Some(range) = self.sequence_range(max_sequence)? { range } else { return Ok(None); @@ -107,11 +106,7 @@ impl SyncState { /// * `max_sequence` - The maximum sequence that should be indexed. /// `max_sequence` is the exclusive upper bound of the range to be indexed. /// (e.g. `0..max_sequence`) - fn sequence_range( - &mut self, - tip: u32, - max_sequence: u32, - ) -> ChainResult>> { + fn sequence_range(&mut self, max_sequence: u32) -> ChainResult>> { let (from, to) = match self.direction { SyncDirection::Forward => { let sequence_start = self.next_sequence; @@ -120,7 +115,6 @@ impl SyncState { return Ok(None); } sequence_end = u32::min(sequence_end, max_sequence.saturating_sub(1)); - self.next_block = tip; self.next_sequence = sequence_end + 1; (sequence_start, sequence_end) } @@ -236,10 +230,9 @@ impl ForwardMessageSyncCursor { self.cursor.sync_state.next_sequence += 1; } - let (Some(mailbox_count), tip) = self.cursor.indexer.sequence_and_tip().await? - else { - return Ok(None); - }; + let (Some(mailbox_count), tip) = self.cursor.indexer.sequence_and_tip().await? else { + return Ok(None); + }; let cursor_count = self.cursor.sync_state.next_sequence; Ok(match cursor_count.cmp(&mailbox_count) { Ordering::Equal => { @@ -476,6 +469,7 @@ pub(crate) struct RateLimitedContractSyncCursor { indexer: Arc>, db: Arc>, tip: u32, + max_sequence: Option, last_tip_update: Instant, eta_calculator: SyncerEtaCalculator, sync_state: SyncState, @@ -490,11 +484,12 @@ impl RateLimitedContractSyncCursor { initial_height: u32, mode: IndexMode, ) -> Result { - let tip = indexer.get_finalized_block_number().await?; + let (max_sequence, tip) = indexer.sequence_and_tip().await?; Ok(Self { indexer, db, tip, + max_sequence, last_tip_update: Instant::now(), eta_calculator: SyncerEtaCalculator::new(initial_height, tip, ETA_TIME_WINDOW), sync_state: SyncState::new( @@ -539,6 +534,32 @@ impl RateLimitedContractSyncCursor { } } } + + fn sync_end(&self) -> ChainResult { + match self.sync_state.mode { + IndexMode::Block => Ok(self.tip), + IndexMode::Sequence => { + self.max_sequence + .ok_or(ChainCommunicationError::from_other_str( + "Sequence indexing requires a max sequence", + )) + } + } + } + + fn sync_position(&self) -> u32 { + match self.sync_state.mode { + IndexMode::Block => self.sync_state.next_block, + IndexMode::Sequence => self.sync_state.next_sequence, + } + } + + fn sync_step(&self) -> u32 { + match self.sync_state.mode { + IndexMode::Block => self.sync_state.chunk_size, + IndexMode::Sequence => MAX_SEQUENCE_RANGE, + } + } } #[async_trait] @@ -547,13 +568,11 @@ where T: Send + Debug + 'static, { async fn next_action(&mut self) -> ChainResult<(CursorAction, Duration)> { - let to = u32::min( - self.tip, - self.sync_state.next_block + self.sync_state.chunk_size, - ); - let from = to.saturating_sub(self.sync_state.chunk_size); - let eta = if to < self.tip { - self.eta_calculator.calculate(from, self.tip) + let sync_end = self.sync_end()?; + let to = u32::min(sync_end, self.sync_position() + self.sync_step()); + let from = self.sync_position(); + let eta = if to < sync_end { + self.eta_calculator.calculate(from, sync_end) } else { Duration::from_secs(0) }; @@ -562,8 +581,10 @@ where if let Some(rate_limit) = rate_limit { return Ok((CursorAction::Sleep(rate_limit), eta)); } - let (count, tip) = self.indexer.sequence_and_tip().await?; - if let Some(range) = self.sync_state.get_next_range(count, tip).await? { + let (max_sequence, tip) = self.indexer.sequence_and_tip().await?; + self.tip = tip; + self.max_sequence = max_sequence; + if let Some(range) = self.sync_state.get_next_range(max_sequence, tip).await? { return Ok((CursorAction::Query(range), eta)); } diff --git a/rust/hyperlane-base/src/contract_sync/eta_calculator.rs b/rust/hyperlane-base/src/contract_sync/eta_calculator.rs index 7fa2a5bf5..3b0664d89 100644 --- a/rust/hyperlane-base/src/contract_sync/eta_calculator.rs +++ b/rust/hyperlane-base/src/contract_sync/eta_calculator.rs @@ -1,7 +1,7 @@ use std::time::{Duration, Instant}; use derive_new::new; -use tracing::debug; +use tracing::warn; /// Calculates the expected time to catch up to the tip of the blockchain. #[derive(new)] @@ -38,10 +38,13 @@ impl SyncerEtaCalculator { self.last_block = current_block; self.last_tip = current_tip; + + // The block-processing rate, minus the tip-progression rate, measured in + // blocks per second. let new_rate = (blocks_processed - tip_progression) / elapsed; // Calculate the effective rate using a moving average. Only set the past - // effective rate once we have seen a move to prevent it taking a long + // effective rate once we have seen a move, to prevent it taking a long // time to normalize. let effective_rate = if let Some(old_rate) = self.effective_rate { let new_coeff = f64::min(elapsed / self.time_window, 0.9); @@ -66,7 +69,7 @@ impl SyncerEtaCalculator { { Ok(eta) => eta, Err(e) => { - debug!(error=?e, tip=?current_tip, block=?current_block, rate=?effective_rate, "Failed to calculate the eta"); + warn!(error=?e, tip=?current_tip, block=?current_block, rate=?effective_rate, "Failed to calculate the eta"); default_duration } } diff --git a/rust/hyperlane-base/src/contract_sync/mod.rs b/rust/hyperlane-base/src/contract_sync/mod.rs index 44f8ef21d..d569d2c14 100644 --- a/rust/hyperlane-base/src/contract_sync/mod.rs +++ b/rust/hyperlane-base/src/contract_sync/mod.rs @@ -59,7 +59,9 @@ where loop { indexed_height.set(cursor.latest_block() as i64); - let Ok((action, eta)) = cursor.next_action().await else { continue }; + let Ok((action, eta)) = cursor.next_action().await else { + continue; + }; match action { CursorAction::Query(range) => { debug!(?range, "Looking for for events in index range"); diff --git a/rust/hyperlane-base/src/settings/loader/case_adapter.rs b/rust/hyperlane-base/src/settings/loader/case_adapter.rs index 0f8e1e081..b6a8e5561 100644 --- a/rust/hyperlane-base/src/settings/loader/case_adapter.rs +++ b/rust/hyperlane-base/src/settings/loader/case_adapter.rs @@ -36,14 +36,14 @@ fn recase_pair(key: String, mut val: Value, case: Case) -> (String, Value) { .drain() .map(|(k, v)| recase_pair(k, v, case)) .collect_vec(); - table.extend(tmp.into_iter()); + table.extend(tmp); } ValueKind::Array(ary) => { let tmp = ary .drain(..) .map(|v| recase_pair(String::new(), v, case).1) .collect_vec(); - ary.extend(tmp.into_iter()) + ary.extend(tmp) } _ => {} } diff --git a/rust/hyperlane-base/src/types/local_storage.rs b/rust/hyperlane-base/src/types/local_storage.rs index 79765a852..d536b924e 100644 --- a/rust/hyperlane-base/src/types/local_storage.rs +++ b/rust/hyperlane-base/src/types/local_storage.rs @@ -2,9 +2,8 @@ use std::path::PathBuf; use async_trait::async_trait; use eyre::{Context, Result}; -use prometheus::IntGauge; - use hyperlane_core::{SignedAnnouncement, SignedCheckpoint, SignedCheckpointWithMessageId}; +use prometheus::IntGauge; use crate::traits::CheckpointSyncer; @@ -87,7 +86,7 @@ impl CheckpointSyncer for LocalStorage { async fn fetch_checkpoint(&self, index: u32) -> Result> { let Ok(data) = tokio::fs::read(self.checkpoint_file_path(index)).await else { - return Ok(None) + return Ok(None); }; let checkpoint = serde_json::from_slice(&data)?; Ok(Some(checkpoint)) diff --git a/rust/hyperlane-base/src/types/multisig.rs b/rust/hyperlane-base/src/types/multisig.rs index 820c13feb..c41aa4447 100644 --- a/rust/hyperlane-base/src/types/multisig.rs +++ b/rust/hyperlane-base/src/types/multisig.rs @@ -355,6 +355,7 @@ impl MultisigCheckpointSyncer { continue; } } + debug!("No quorum checkpoint found for message"); Ok(None) } } diff --git a/rust/hyperlane-core/Cargo.toml b/rust/hyperlane-core/Cargo.toml index b5bc50baf..648997400 100644 --- a/rust/hyperlane-core/Cargo.toml +++ b/rust/hyperlane-core/Cargo.toml @@ -45,6 +45,7 @@ tokio = { workspace = true, features = ["rt", "time"] } [features] default = [] +float = [] test-utils = ["dep:config"] agent = ["ethers", "strum"] strum = ["dep:strum"] diff --git a/rust/hyperlane-core/src/traits/indexer.rs b/rust/hyperlane-core/src/traits/indexer.rs index afed80ce3..a9a3752bf 100644 --- a/rust/hyperlane-core/src/traits/indexer.rs +++ b/rust/hyperlane-core/src/traits/indexer.rs @@ -38,7 +38,7 @@ pub trait Indexer: Send + Sync + Debug { /// Interface for indexing data in sequence. #[async_trait] #[auto_impl(&, Box, Arc)] -pub trait SequenceIndexer: Indexer + 'static { +pub trait SequenceIndexer: Indexer { /// Return the latest finalized sequence (if any) and block number async fn sequence_and_tip(&self) -> ChainResult<(Option, u32)>; } diff --git a/rust/hyperlane-core/src/traits/interchain_security_module.rs b/rust/hyperlane-core/src/traits/interchain_security_module.rs index 1d6907fad..2fa7263a3 100644 --- a/rust/hyperlane-core/src/traits/interchain_security_module.rs +++ b/rust/hyperlane-core/src/traits/interchain_security_module.rs @@ -4,12 +4,23 @@ use async_trait::async_trait; use auto_impl::auto_impl; use borsh::{BorshDeserialize, BorshSerialize}; use num_derive::FromPrimitive; +use serde::{Deserialize, Serialize}; use crate::{ChainResult, HyperlaneContract, HyperlaneMessage, U256}; /// Enumeration of all known module types #[derive( - FromPrimitive, Clone, Debug, Default, Copy, PartialEq, Eq, BorshDeserialize, BorshSerialize, + FromPrimitive, + Clone, + Debug, + Default, + Copy, + PartialEq, + Eq, + BorshDeserialize, + BorshSerialize, + Serialize, + Deserialize, )] #[cfg_attr(feature = "strum", derive(strum::Display))] pub enum ModuleType { diff --git a/rust/hyperlane-core/src/types/primitive_types.rs b/rust/hyperlane-core/src/types/primitive_types.rs index 2b45a93ce..58dd9bffa 100644 --- a/rust/hyperlane-core/src/types/primitive_types.rs +++ b/rust/hyperlane-core/src/types/primitive_types.rs @@ -3,11 +3,12 @@ #![allow(clippy::assign_op_pattern)] #![allow(clippy::reversed_empty_ranges)] -use crate::types::serialize; use borsh::{BorshDeserialize, BorshSerialize}; -use fixed_hash::{construct_fixed_hash, impl_fixed_hash_conversions}; +use fixed_hash::impl_fixed_hash_conversions; use uint::construct_uint; +use crate::types::serialize; + /// Error type for conversion. #[derive(Debug, PartialEq, Eq)] pub enum Error { @@ -32,29 +33,38 @@ construct_uint! { pub struct U512(8); } -construct_fixed_hash! { - /// 128-bit hash type. - #[derive(BorshSerialize, BorshDeserialize)] - pub struct H128(16); -} +mod fixed_hashes { + // we can't change how they made the macro, so ignore the lint + #![allow(clippy::incorrect_clone_impl_on_copy_type)] -construct_fixed_hash! { - /// 160-bit hash type. - #[derive(BorshSerialize, BorshDeserialize)] - pub struct H160(20); -} + use borsh::{BorshDeserialize, BorshSerialize}; + use fixed_hash::construct_fixed_hash; -construct_fixed_hash! { - /// 256-bit hash type. - #[derive(BorshSerialize, BorshDeserialize)] - pub struct H256(32); -} + construct_fixed_hash! { + /// 128-bit hash type. + #[derive(BorshSerialize, BorshDeserialize)] + pub struct H128(16); + } -construct_fixed_hash! { - /// 512-bit hash type. - #[derive(BorshSerialize, BorshDeserialize)] - pub struct H512(64); + construct_fixed_hash! { + /// 160-bit hash type. + #[derive(BorshSerialize, BorshDeserialize)] + pub struct H160(20); + } + + construct_fixed_hash! { + /// 256-bit hash type. + #[derive(BorshSerialize, BorshDeserialize)] + pub struct H256(32); + } + + construct_fixed_hash! { + /// 512-bit hash type. + #[derive(BorshSerialize, BorshDeserialize)] + pub struct H512(64); + } } +pub use fixed_hashes::*; #[cfg(feature = "ethers")] type EthersH160 = ethers_core::types::H160; @@ -143,6 +153,7 @@ impl_fixed_uint_conversions!(ethers_core::types::U512, U256); #[cfg(feature = "ethers")] impl_fixed_uint_conversions!(ethers_core::types::U512, U128); +#[cfg(feature = "float")] macro_rules! impl_f64_conversions { ($ty:ty) => { impl $ty { @@ -190,8 +201,11 @@ macro_rules! impl_f64_conversions { }; } +#[cfg(feature = "float")] impl_f64_conversions!(U128); +#[cfg(feature = "float")] impl_f64_conversions!(U256); +#[cfg(feature = "float")] impl_f64_conversions!(U512); #[cfg(feature = "ethers")] diff --git a/rust/hyperlane-core/src/utils.rs b/rust/hyperlane-core/src/utils.rs index 7752d670f..9d8b8b001 100644 --- a/rust/hyperlane-core/src/utils.rs +++ b/rust/hyperlane-core/src/utils.rs @@ -75,6 +75,7 @@ pub fn fmt_domain(domain: u32) -> String { } /// Formats the duration in the most appropriate time units. +#[cfg(feature = "float")] pub fn fmt_duration(dur: Duration) -> String { const MIN: f64 = 60.; const HOUR: f64 = MIN * 60.; @@ -97,6 +98,7 @@ pub fn fmt_duration(dur: Duration) -> String { /// Formats the duration in the most appropriate time units and says "synced" if /// the duration is 0. +#[cfg(feature = "float")] pub fn fmt_sync_time(dur: Duration) -> String { if dur.as_secs() == 0 { "synced".into() diff --git a/rust/sealevel/client/Cargo.toml b/rust/sealevel/client/Cargo.toml index 6f4b67220..976bf1163 100644 --- a/rust/sealevel/client/Cargo.toml +++ b/rust/sealevel/client/Cargo.toml @@ -31,4 +31,5 @@ hyperlane-sealevel-igp = { path = "../programs/hyperlane-sealevel-igp", features hyperlane-sealevel-token-collateral = { path = "../programs/hyperlane-sealevel-token-collateral", features = ["no-entrypoint"] } hyperlane-sealevel-token-lib = { path = "../libraries/hyperlane-sealevel-token" } hyperlane-sealevel-token-native = { path = "../programs/hyperlane-sealevel-token-native", features = ["no-entrypoint"] } -hyperlane-sealevel-validator-announce = { path = "../programs/validator-announce", features = ["no-entrypoint"] } \ No newline at end of file +hyperlane-sealevel-validator-announce = { path = "../programs/validator-announce", features = ["no-entrypoint"] } +hyperlane-sealevel-hello-world = { path = "../programs/helloworld" } \ No newline at end of file diff --git a/rust/sealevel/client/src/artifacts.rs b/rust/sealevel/client/src/artifacts.rs new file mode 100644 index 000000000..7caf4806f --- /dev/null +++ b/rust/sealevel/client/src/artifacts.rs @@ -0,0 +1,59 @@ +use serde::{de::DeserializeOwned, Deserialize, Serialize}; + +use hyperlane_core::H256; +use solana_program::pubkey::Pubkey; + +use std::{fs::File, io::Write, path::Path, str::FromStr}; + +#[derive(Debug, Serialize, Deserialize)] +pub(crate) struct SingularProgramIdArtifact { + #[serde(with = "crate::serde::serde_pubkey")] + pub program_id: Pubkey, +} + +impl From for SingularProgramIdArtifact { + fn from(val: Pubkey) -> Self { + SingularProgramIdArtifact { program_id: val } + } +} + +#[derive(Serialize, Deserialize)] +pub(crate) struct HexAndBase58ProgramIdArtifact { + hex: String, + base58: String, +} + +impl From for HexAndBase58ProgramIdArtifact { + fn from(val: H256) -> Self { + HexAndBase58ProgramIdArtifact { + hex: format!("0x{}", hex::encode(val)), + base58: Pubkey::new_from_array(val.to_fixed_bytes()).to_string(), + } + } +} + +impl From<&HexAndBase58ProgramIdArtifact> for Pubkey { + fn from(val: &HexAndBase58ProgramIdArtifact) -> Self { + Pubkey::from_str(&val.base58).unwrap() + } +} + +pub(crate) fn write_json(path: &Path, program_id: T) +where + T: Serialize, +{ + let json = serde_json::to_string_pretty(&program_id).unwrap(); + println!("Writing to file {} contents:\n{}", path.display(), json); + + let mut file = File::create(path).expect("Failed to create file"); + file.write_all(json.as_bytes()) + .expect("Failed write JSON to file"); +} + +pub(crate) fn read_json(path: &Path) -> T +where + T: DeserializeOwned, +{ + let file = File::open(path).expect("Failed to open JSON file"); + serde_json::from_reader(file).expect("Failed to read JSON file") +} diff --git a/rust/sealevel/client/src/context.rs b/rust/sealevel/client/src/context.rs index bc1bd5190..80b0ea9e2 100644 --- a/rust/sealevel/client/src/context.rs +++ b/rust/sealevel/client/src/context.rs @@ -26,6 +26,7 @@ pub(crate) struct Context { payer_keypair: Option, pub commitment: CommitmentConfig, pub initial_instructions: RefCell>, + pub require_tx_approval: bool, } pub(crate) struct InstructionWithDescription { @@ -58,6 +59,7 @@ impl Context { payer_keypair: Option, commitment: CommitmentConfig, initial_instructions: RefCell>, + require_tx_approval: bool, ) -> Self { Self { client, @@ -65,6 +67,7 @@ impl Context { payer_keypair, commitment, initial_instructions, + require_tx_approval, } } @@ -183,6 +186,13 @@ impl<'ctx, 'rpc> TxnBuilder<'ctx, 'rpc> { return None; } + // Print the tx as an indication for what's about to happen + self.pretty_print_transaction(); + + if self.ctx.require_tx_approval { + wait_for_user_confirmation(); + } + let client = self.client.unwrap_or(&self.ctx.client); let recent_blockhash = client.get_latest_blockhash().unwrap(); diff --git a/rust/sealevel/client/src/core.rs b/rust/sealevel/client/src/core.rs index 2f0f29f77..8aaf51adb 100644 --- a/rust/sealevel/client/src/core.rs +++ b/rust/sealevel/client/src/core.rs @@ -4,10 +4,12 @@ use solana_program::pubkey::Pubkey; use solana_sdk::signature::Signer; use std::collections::HashMap; -use std::{fs::File, io::Write, path::Path}; +use std::{fs::File, path::Path}; use crate::{ + artifacts::{read_json, write_json}, cmd_utils::{create_and_write_keypair, create_new_directory, deploy_program}, + multisig_ism::deploy_multisig_ism_message_id, Context, CoreCmd, CoreDeploy, CoreSubCmd, }; use hyperlane_core::H256; @@ -21,7 +23,12 @@ pub(crate) fn process_core_cmd(mut ctx: Context, cmd: CoreCmd) { let core_dir = create_new_directory(&chain_dir, "core"); let key_dir = create_new_directory(&core_dir, "keys"); - let ism_program_id = deploy_multisig_ism_message_id(&mut ctx, &core, &key_dir); + let ism_program_id = deploy_multisig_ism_message_id( + &mut ctx, + &core.built_so_dir, + core.use_existing_keys, + &key_dir, + ); let mailbox_program_id = deploy_mailbox(&mut ctx, &core, &key_dir, ism_program_id); @@ -44,43 +51,6 @@ pub(crate) fn process_core_cmd(mut ctx: Context, cmd: CoreCmd) { } } -fn deploy_multisig_ism_message_id(ctx: &mut Context, cmd: &CoreDeploy, key_dir: &Path) -> Pubkey { - let (keypair, keypair_path) = create_and_write_keypair( - key_dir, - "hyperlane_sealevel_multisig_ism_message_id-keypair.json", - cmd.use_existing_keys, - ); - let program_id = keypair.pubkey(); - - deploy_program( - ctx.payer_keypair_path(), - keypair_path.to_str().unwrap(), - cmd.built_so_dir - .join("hyperlane_sealevel_multisig_ism_message_id.so") - .to_str() - .unwrap(), - &ctx.client.url(), - ); - - println!( - "Deployed Multisig ISM Message ID at program ID {}", - program_id - ); - - // Initialize - let instruction = hyperlane_sealevel_multisig_ism_message_id::instruction::init_instruction( - program_id, - ctx.payer_pubkey, - ) - .unwrap(); - - ctx.new_txn().add(instruction).send_with_payer(); - - println!("Initialized Multisig ISM Message ID "); - - program_id -} - fn deploy_mailbox( ctx: &mut Context, core: &CoreDeploy, @@ -325,55 +295,23 @@ fn deploy_igp(ctx: &mut Context, core: &CoreDeploy, key_dir: &Path) -> (Pubkey, } #[derive(Debug, Serialize, Deserialize)] -pub(crate) struct CoreProgramIds { - #[serde(with = "serde_pubkey")] +pub struct CoreProgramIds { + #[serde(with = "crate::serde::serde_pubkey")] pub mailbox: Pubkey, - #[serde(with = "serde_pubkey")] + #[serde(with = "crate::serde::serde_pubkey")] pub validator_announce: Pubkey, - #[serde(with = "serde_pubkey")] + #[serde(with = "crate::serde::serde_pubkey")] pub multisig_ism_message_id: Pubkey, - #[serde(with = "serde_pubkey")] + #[serde(with = "crate::serde::serde_pubkey")] pub igp_program_id: Pubkey, - #[serde(with = "serde_pubkey")] + #[serde(with = "crate::serde::serde_pubkey")] pub overhead_igp_account: Pubkey, - #[serde(with = "serde_pubkey")] + #[serde(with = "crate::serde::serde_pubkey")] pub igp_account: Pubkey, } -mod serde_pubkey { - use borsh::BorshDeserialize; - use serde::{Deserialize, Deserializer, Serializer}; - use solana_sdk::pubkey::Pubkey; - use std::str::FromStr; - - #[derive(Deserialize)] - #[serde(untagged)] - enum RawPubkey { - String(String), - Bytes(Vec), - } - - pub fn serialize(k: &Pubkey, ser: S) -> Result { - ser.serialize_str(&k.to_string()) - } - - pub fn deserialize<'de, D: Deserializer<'de>>(de: D) -> Result { - match RawPubkey::deserialize(de)? { - RawPubkey::String(s) => Pubkey::from_str(&s).map_err(serde::de::Error::custom), - RawPubkey::Bytes(b) => Pubkey::try_from_slice(&b).map_err(serde::de::Error::custom), - } - } -} - fn write_program_ids(core_dir: &Path, program_ids: CoreProgramIds) { - let json = serde_json::to_string_pretty(&program_ids).unwrap(); - let path = core_dir.join("program-ids.json"); - - println!("Writing program IDs to {}:\n{}", path.display(), json); - - let mut file = File::create(path).expect("Failed to create keypair file"); - file.write_all(json.as_bytes()) - .expect("Failed to write program IDs to file"); + write_json(&core_dir.join("program-ids.json"), program_ids); } pub(crate) fn read_core_program_ids( @@ -386,6 +324,5 @@ pub(crate) fn read_core_program_ids( .join(chain) .join("core") .join("program-ids.json"); - let file = File::open(path).expect("Failed to open program IDs file"); - serde_json::from_reader(file).expect("Failed to read program IDs file") + read_json(&path) } diff --git a/rust/sealevel/client/src/helloworld.rs b/rust/sealevel/client/src/helloworld.rs new file mode 100644 index 000000000..6226b4828 --- /dev/null +++ b/rust/sealevel/client/src/helloworld.rs @@ -0,0 +1,197 @@ +use std::collections::HashMap; + +use hyperlane_core::H256; +use hyperlane_sealevel_connection_client::router::RemoteRouterConfig; +use hyperlane_sealevel_hello_world::{ + accounts::{HelloWorldStorage, HelloWorldStorageAccount}, + instruction::{ + enroll_remote_routers_instruction, init_instruction, + set_interchain_security_module_instruction, + }, + program_storage_pda_seeds, +}; +use serde::{Deserialize, Serialize}; +use solana_sdk::{instruction::Instruction, pubkey::Pubkey}; + +use crate::{ + cmd_utils::account_exists, + router::{ + deploy_routers, ChainMetadata, ConnectionClient, Ownable, RouterConfig, RouterConfigGetter, + RouterDeployer, + }, + Context, CoreProgramIds, HelloWorldCmd, HelloWorldDeploy, HelloWorldSubCmd, RpcClient, +}; + +pub(crate) fn process_helloworld_cmd(mut ctx: Context, cmd: HelloWorldCmd) { + match cmd.cmd { + HelloWorldSubCmd::Deploy(deploy) => { + deploy_helloworld(&mut ctx, deploy); + } + HelloWorldSubCmd::Query(query) => { + let program_storage_key = + Pubkey::find_program_address(program_storage_pda_seeds!(), &query.program_id); + let account = ctx.client.get_account(&program_storage_key.0).unwrap(); + let storage = HelloWorldStorageAccount::fetch(&mut &account.data[..]) + .unwrap() + .into_inner(); + println!("HelloWorld storage: {:?}", storage); + } + } +} + +#[derive(Debug, Deserialize, Serialize, Clone)] +struct HelloWorldConfig { + #[serde(flatten)] + router_config: RouterConfig, +} + +struct HelloWorldDeployer {} + +impl HelloWorldDeployer { + fn new() -> Self { + Self {} + } + + fn get_storage(&self, client: &RpcClient, program_id: &Pubkey) -> HelloWorldStorage { + let (program_storage_account, _program_storage_bump) = + Pubkey::find_program_address(program_storage_pda_seeds!(), program_id); + + let account = client.get_account(&program_storage_account).unwrap(); + *HelloWorldStorageAccount::fetch(&mut &account.data[..]) + .unwrap() + .into_inner() + } +} + +impl RouterDeployer for HelloWorldDeployer { + fn program_name(&self, _config: &HelloWorldConfig) -> &str { + "hyperlane_sealevel_hello_world" + } + + fn enroll_remote_routers_instruction( + &self, + program_id: Pubkey, + payer: Pubkey, + router_configs: Vec, + ) -> Instruction { + enroll_remote_routers_instruction(program_id, payer, router_configs).unwrap() + } + + fn get_routers(&self, client: &RpcClient, program_id: &Pubkey) -> HashMap { + let storage = self.get_storage(client, program_id); + + storage.routers + } + + fn init_program_idempotent( + &self, + ctx: &mut Context, + client: &RpcClient, + core_program_ids: &CoreProgramIds, + chain_config: &ChainMetadata, + app_config: &HelloWorldConfig, + program_id: Pubkey, + ) { + let (program_storage_account, _program_storage_bump) = + Pubkey::find_program_address(program_storage_pda_seeds!(), &program_id); + if account_exists(client, &program_storage_account).unwrap() { + println!("HelloWorld storage already exists, skipping init"); + return; + } + + let domain_id = chain_config.domain_id(); + let mailbox = app_config + .router_config() + .connection_client + .mailbox(core_program_ids.mailbox); + let ism = app_config + .router_config() + .connection_client + .interchain_security_module(); + let owner = Some(app_config.router_config().ownable.owner(ctx.payer_pubkey)); + + ctx.new_txn() + .add_with_description( + init_instruction( + program_id, + ctx.payer_pubkey, + domain_id, + mailbox, + ism, + // TODO revisit this when we want to deploy with IGPs + None, + owner, + ) + .unwrap(), + format!( + "Initializing HelloWorld program: domain_id: {}, mailbox: {}, ism: {:?}, owner: {:?}", + domain_id, mailbox, ism, owner + ) + ) + .with_client(client) + .send_with_payer(); + } +} + +impl RouterConfigGetter for HelloWorldConfig { + fn router_config(&self) -> &RouterConfig { + &self.router_config + } +} + +impl Ownable for HelloWorldDeployer { + /// Gets the owner configured on-chain. + fn get_owner(&self, client: &RpcClient, program_id: &Pubkey) -> Option { + let storage = self.get_storage(client, program_id); + + storage.owner + } + + /// Gets an instruction to set the owner. + fn set_owner_instruction( + &self, + _client: &RpcClient, + _program_id: &Pubkey, + _new_owner: Option, + ) -> Instruction { + unimplemented!("HelloWorld does not support changing the owner") + } +} + +impl ConnectionClient for HelloWorldDeployer { + fn get_interchain_security_module( + &self, + client: &RpcClient, + program_id: &Pubkey, + ) -> Option { + let storage = self.get_storage(client, program_id); + + storage.ism + } + + fn set_interchain_security_module_instruction( + &self, + client: &RpcClient, + program_id: &Pubkey, + ism: Option, + ) -> Instruction { + let storage = self.get_storage(client, program_id); + + set_interchain_security_module_instruction(*program_id, storage.owner.unwrap(), ism) + .unwrap() + } +} + +fn deploy_helloworld(ctx: &mut Context, deploy: HelloWorldDeploy) { + deploy_routers( + ctx, + HelloWorldDeployer::new(), + "helloworld", + &deploy.context, + deploy.config_file, + deploy.chain_config_file, + deploy.environments_dir, + &deploy.environment, + deploy.built_so_dir, + ) +} diff --git a/rust/sealevel/client/src/main.rs b/rust/sealevel/client/src/main.rs index 3736e6d7b..bef6cc209 100644 --- a/rust/sealevel/client/src/main.rs +++ b/rust/sealevel/client/src/main.rs @@ -20,32 +20,26 @@ use solana_sdk::{ }; use account_utils::DiscriminatorEncode; -use hyperlane_core::{Encode, HyperlaneMessage, H160, H256}; +use hyperlane_core::{H160, H256}; use hyperlane_sealevel_connection_client::router::RemoteRouterConfig; use hyperlane_sealevel_igp::{ accounts::{ - GasOracle, IgpAccount, InterchainGasPaymasterType, OverheadIgpAccount, RemoteGasData, + GasOracle, GasPaymentAccount, IgpAccount, InterchainGasPaymasterType, OverheadIgpAccount, + ProgramDataAccount as IgpProgramDataAccount, RemoteGasData, }, igp_gas_payment_pda_seeds, igp_program_data_pda_seeds, instruction::{GasOracleConfig, GasOverheadConfig}, }; use hyperlane_sealevel_mailbox::{ accounts::{InboxAccount, OutboxAccount}, - instruction::{InboxProcess, Instruction as MailboxInstruction, OutboxDispatch, VERSION}, + instruction::{Instruction as MailboxInstruction, OutboxDispatch}, mailbox_dispatched_message_pda_seeds, mailbox_inbox_pda_seeds, mailbox_message_dispatch_authority_pda_seeds, mailbox_outbox_pda_seeds, mailbox_processed_message_pda_seeds, spl_noop, }; -use hyperlane_sealevel_multisig_ism_message_id::{ - access_control_pda_seeds as multisig_ism_message_id_access_control_pda_seeds, - accounts::AccessControlAccount, - domain_data_pda_seeds as multisig_ism_message_id_domain_data_pda_seeds, - instruction::{ - Domained, Instruction as MultisigIsmMessageIdInstruction, ValidatorsAndThreshold, - }, -}; + use hyperlane_sealevel_token::{ - hyperlane_token_ata_payer_pda_seeds, hyperlane_token_mint_pda_seeds, plugin::SyntheticPlugin, + hyperlane_token_ata_payer_pda_seeds, hyperlane_token_mint_pda_seeds, spl_associated_token_account::get_associated_token_address_with_program_id, spl_token_2022, }; use hyperlane_sealevel_token_collateral::{ @@ -56,9 +50,7 @@ use hyperlane_sealevel_token_lib::{ hyperlane_token_pda_seeds, instruction::{Instruction as HtInstruction, TransferRemote as HtTransferRemote}, }; -use hyperlane_sealevel_token_native::{ - hyperlane_token_native_collateral_pda_seeds, plugin::NativePlugin, -}; +use hyperlane_sealevel_token_native::hyperlane_token_native_collateral_pda_seeds; use hyperlane_sealevel_validator_announce::{ accounts::ValidatorStorageLocationsAccount, instruction::{ @@ -68,15 +60,23 @@ use hyperlane_sealevel_validator_announce::{ replay_protection_pda_seeds, validator_announce_pda_seeds, validator_storage_locations_pda_seeds, }; +use warp_route::parse_token_account_data; -use crate::warp_route::process_warp_route_cmd; -pub(crate) use crate::{context::*, core::*}; - +mod artifacts; mod cmd_utils; mod context; mod r#core; +mod helloworld; +mod multisig_ism; +mod router; +mod serde; mod warp_route; +use crate::helloworld::process_helloworld_cmd; +use crate::multisig_ism::process_multisig_ism_message_id_cmd; +use crate::warp_route::process_warp_route_cmd; +pub(crate) use crate::{context::*, core::*}; + // Note: from solana_program_runtime::compute_budget const DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT: u32 = 200_000; const MAX_COMPUTE_UNIT_LIMIT: u32 = 1_400_000; @@ -99,6 +99,8 @@ struct Cli { heap_size: Option, #[arg(long, short = 'C')] config: Option, + #[arg(long, default_value_t = false)] + require_tx_approval: bool, } #[derive(Subcommand)] @@ -110,6 +112,7 @@ enum HyperlaneSealevelCmd { ValidatorAnnounce(ValidatorAnnounceCmd), MultisigIsmMessageId(MultisigIsmMessageIdCmd), WarpRoute(WarpRouteCmd), + HelloWorld(HelloWorldCmd), } #[derive(Args)] @@ -194,9 +197,9 @@ enum MailboxSubCmd { Init(Init), Query(Query), Send(Outbox), - Receive(Inbox), Delivered(Delivered), TransferOwnership(TransferOwnership), + SetDefaultIsm(SetDefaultIsm), } const MAILBOX_PROG_ID: Pubkey = pubkey!("692KZJaoe2KRcD6uhCQDLLXnLNA5ZLnfvdqjE4aX9iu1"); @@ -221,6 +224,14 @@ struct Query { program_id: Pubkey, } +#[derive(Args)] +struct SetDefaultIsm { + #[arg(long, short)] + program_id: Pubkey, + #[arg(long, short)] + default_ism: Pubkey, +} + #[derive(Args)] struct Outbox { #[arg(long, short, default_value_t = ECLIPSE_DOMAIN)] @@ -259,32 +270,6 @@ struct Delivered { message_id: H256, } -// Actual content depends on which ISM is used. -struct ExampleMetadata { - pub root: H256, - pub index: u32, - pub leaf_index: u32, - // pub proof: [H256; 32], - pub signatures: Vec, -} -impl Encode for ExampleMetadata { - fn write_to(&self, writer: &mut W) -> std::io::Result - where - W: std::io::Write, - { - writer.write_all(self.root.as_ref())?; - writer.write_all(&self.index.to_be_bytes())?; - writer.write_all(&self.leaf_index.to_be_bytes())?; - // for hash in self.proof { - // writer.write_all(hash.as_ref())?; - // } - for signature in &self.signatures { - writer.write_all(signature.as_ref())?; - } - Ok(32 + 4 + 4 + (32 * 32) + (self.signatures.len() * 32)) - } -} - #[derive(Args)] struct TokenCmd { #[command(subcommand)] @@ -297,10 +282,12 @@ enum TokenSubCmd { TransferRemote(TokenTransferRemote), EnrollRemoteRouter(TokenEnrollRemoteRouter), TransferOwnership(TransferOwnership), + SetInterchainSecurityModule(SetInterchainSecurityModule), + Igp(Igp), } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] -enum TokenType { +pub enum TokenType { Native, Synthetic, Collateral, @@ -336,6 +323,14 @@ struct TokenEnrollRemoteRouter { router: H256, } +#[derive(Args)] +struct SetInterchainSecurityModule { + #[arg(long, short)] + program_id: Pubkey, + #[arg(long, short)] + ism: Option, +} + #[derive(Args)] struct TransferOwnership { #[arg(long, short)] @@ -345,6 +340,39 @@ struct TransferOwnership { new_owner: Pubkey, } +#[derive(Args)] +struct Igp { + #[arg(long, short, default_value_t = HYPERLANE_TOKEN_PROG_ID)] + program_id: Pubkey, + #[command(subcommand)] + cmd: GetSetCmd, +} + +#[derive(Subcommand)] +enum GetSetCmd { + Get(G), + Set(S), +} + +#[derive(Args)] +struct SetIgpArgs { + igp_program: Pubkey, + #[arg(value_enum)] + igp_type: IgpType, + igp_account: Pubkey, +} + +#[derive(Args)] +struct GetIgpArgs { + token_type: TokenType, +} + +#[derive(ValueEnum, Clone)] +enum IgpType { + Igp, + OverheadIgp, +} + #[derive(Args)] struct IgpCmd { #[command(subcommand)] @@ -353,6 +381,7 @@ struct IgpCmd { #[derive(Subcommand)] enum IgpSubCmd { + Query(IgpQueryArgs), PayForGas(PayForGasArgs), GasOracleConfig(GasOracleConfigArgs), DestinationGasOverhead(DestinationGasOverheadArgs), @@ -360,6 +389,16 @@ enum IgpSubCmd { TransferOverheadIgpOwnership(TransferIgpOwnership), } +#[derive(Args)] +struct IgpQueryArgs { + #[arg(long)] + program_id: Pubkey, + #[arg(long)] + igp_account: Pubkey, + #[arg(long)] + gas_payment_account: Option, +} + #[derive(Args)] struct TransferIgpOwnership { #[arg(long, short)] @@ -373,8 +412,14 @@ struct TransferIgpOwnership { #[derive(Args)] struct PayForGasArgs { + #[arg(long)] program_id: Pubkey, + #[arg(long)] message_id: String, + #[arg(long)] + destination_domain: u32, + #[arg(long)] + gas: u64, } #[derive(Args)] @@ -388,13 +433,7 @@ struct GasOracleConfigArgs { #[arg(long)] remote_domain: u32, #[command(subcommand)] - cmd: GasOracleSubCmd, -} - -#[derive(Subcommand)] -enum GasOracleSubCmd { - Set(SetGasOracleArgs), - Get, + cmd: GetSetCmd, } #[derive(Args)] @@ -407,6 +446,9 @@ struct SetGasOracleArgs { token_decimals: u8, } +#[derive(Args)] +struct GetGasOracleArgs; + #[derive(Args)] struct DestinationGasOverheadArgs { #[arg(long)] @@ -483,10 +525,36 @@ struct MultisigIsmMessageIdCmd { #[derive(Subcommand)] enum MultisigIsmMessageIdSubCmd { + Deploy(MultisigIsmMessageIdDeploy), Init(MultisigIsmMessageIdInit), SetValidatorsAndThreshold(MultisigIsmMessageIdSetValidatorsAndThreshold), - Query(MultisigIsmMessageIdInit), + Query(MultisigIsmMessageIdQuery), TransferOwnership(TransferOwnership), + Configure(MultisigIsmMessageIdConfigure), +} + +#[derive(Args)] +struct MultisigIsmMessageIdDeploy { + #[arg(long)] + environment: String, + #[arg(long)] + environments_dir: PathBuf, + #[arg(long)] + built_so_dir: PathBuf, + #[arg(long)] + chain: String, + #[arg(long)] + context: String, +} + +#[derive(Args)] +struct MultisigIsmMessageIdConfigure { + #[arg(long)] + program_id: Pubkey, + #[arg(long)] + multisig_config_file: PathBuf, + #[arg(long)] + chain_config_file: PathBuf, } #[derive(Args)] @@ -495,6 +563,14 @@ struct MultisigIsmMessageIdInit { program_id: Pubkey, } +#[derive(Args)] +struct MultisigIsmMessageIdQuery { + #[arg(long, short)] + program_id: Pubkey, + #[arg(long, value_delimiter = ',')] + domains: Option>, +} + #[derive(Args)] struct MultisigIsmMessageIdSetValidatorsAndThreshold { #[arg(long, short, default_value_t = MULTISIG_ISM_MESSAGE_ID_PROG_ID)] @@ -507,6 +583,40 @@ struct MultisigIsmMessageIdSetValidatorsAndThreshold { threshold: u8, } +#[derive(Args)] +pub(crate) struct HelloWorldCmd { + #[command(subcommand)] + cmd: HelloWorldSubCmd, +} + +#[derive(Subcommand)] +pub(crate) enum HelloWorldSubCmd { + Deploy(HelloWorldDeploy), + Query(HelloWorldQuery), +} + +#[derive(Args)] +pub(crate) struct HelloWorldDeploy { + #[arg(long)] + environment: String, + #[arg(long)] + environments_dir: PathBuf, + #[arg(long)] + built_so_dir: PathBuf, + #[arg(long)] + config_file: PathBuf, + #[arg(long)] + chain_config_file: PathBuf, + #[arg(long)] + context: String, +} + +#[derive(Args)] +pub(crate) struct HelloWorldQuery { + #[arg(long)] + program_id: Pubkey, +} + fn main() { pretty_env_logger::init(); @@ -569,6 +679,7 @@ fn main() { payer_keypair, commitment, instructions.into(), + cli.require_tx_approval, ); match cli.cmd { HyperlaneSealevelCmd::Mailbox(cmd) => process_mailbox_cmd(ctx, cmd), @@ -579,6 +690,7 @@ fn main() { } HyperlaneSealevelCmd::Core(cmd) => process_core_cmd(ctx, cmd), HyperlaneSealevelCmd::WarpRoute(cmd) => process_warp_route_cmd(ctx, cmd), + HyperlaneSealevelCmd::HelloWorld(cmd) => process_helloworld_cmd(ctx, cmd), HyperlaneSealevelCmd::Igp(cmd) => process_igp_cmd(ctx, cmd), } } @@ -642,7 +754,6 @@ fn process_mailbox_cmd(ctx: Context, cmd: MailboxCmd) { destination_domain: outbox.destination, recipient: H256(outbox.recipient.to_bytes()), message_body: outbox.message.into(), - // message_body: std::iter::repeat(0x41).take(outbox.message_len).collect(), }); let outbox_instruction = Instruction { program_id: outbox.program_id, @@ -655,50 +766,6 @@ fn process_mailbox_cmd(ctx: Context, cmd: MailboxCmd) { }; ctx.new_txn().add(outbox_instruction).send_with_payer(); } - MailboxSubCmd::Receive(inbox) => { - // TODO this probably needs some love - - let (inbox_account, _inbox_bump) = - Pubkey::find_program_address(mailbox_inbox_pda_seeds!(), &inbox.program_id); - let hyperlane_message = HyperlaneMessage { - version: VERSION, - nonce: inbox.nonce, - origin: inbox.origin, - sender: H256::repeat_byte(123), - destination: inbox.local_domain, - recipient: H256::from(inbox.recipient.to_bytes()), - body: inbox.message.bytes().collect(), - }; - let mut encoded_message = vec![]; - hyperlane_message.write_to(&mut encoded_message).unwrap(); - let metadata = ExampleMetadata { - root: Default::default(), - index: 1, - leaf_index: 0, - // proof: Default::default(), - signatures: vec![], - }; - let mut encoded_metadata = vec![]; - metadata.write_to(&mut encoded_metadata).unwrap(); - - let ixn = MailboxInstruction::InboxProcess(InboxProcess { - metadata: encoded_metadata, - message: encoded_message, - }); - let inbox_instruction = Instruction { - program_id: inbox.program_id, - data: ixn.into_instruction_data().unwrap(), - accounts: vec![ - AccountMeta::new(inbox_account, false), - AccountMeta::new_readonly(spl_noop::id(), false), - AccountMeta::new_readonly(inbox.ism, false), - AccountMeta::new_readonly(inbox.recipient, false), - // Note: we would have to provide ism accounts and recipient accounts here if - // they were to use other accounts. - ], - }; - ctx.new_txn().add(inbox_instruction).send_with_payer(); - } MailboxSubCmd::Delivered(delivered) => { let (processed_message_account_key, _processed_message_account_bump) = Pubkey::find_program_address( @@ -731,6 +798,20 @@ fn process_mailbox_cmd(ctx: Context, cmd: MailboxCmd) { ) .send_with_payer(); } + MailboxSubCmd::SetDefaultIsm(set_default_ism) => { + let instruction = hyperlane_sealevel_mailbox::instruction::set_default_ism_instruction( + set_default_ism.program_id, + ctx.payer_pubkey, + set_default_ism.default_ism, + ) + .unwrap(); + ctx.new_txn() + .add_with_description( + instruction, + format!("Setting default ISM to {}", set_default_ism.default_ism), + ) + .send_with_payer(); + } }; } @@ -785,32 +866,7 @@ fn process_token_cmd(ctx: Context, cmd: TokenCmd) { ); if let Some(info) = &accounts[0] { println!("{:#?}", info); - - match query.token_type { - TokenType::Native => { - match HyperlaneTokenAccount::::fetch(&mut info.data.as_ref()) - { - Ok(token) => println!("{:#?}", token.into_inner()), - Err(err) => println!("Failed to deserialize account data: {}", err), - } - } - TokenType::Synthetic => { - match HyperlaneTokenAccount::::fetch( - &mut info.data.as_ref(), - ) { - Ok(token) => println!("{:#?}", token.into_inner()), - Err(err) => println!("Failed to deserialize account data: {}", err), - } - } - TokenType::Collateral => { - match HyperlaneTokenAccount::::fetch( - &mut info.data.as_ref(), - ) { - Ok(token) => println!("{:#?}", token.into_inner()), - Err(err) => println!("Failed to deserialize account data: {}", err), - } - } - } + parse_token_account_data(query.token_type, &mut info.data.as_ref()); } else { println!("Not yet created?"); } @@ -1108,6 +1164,59 @@ fn process_token_cmd(ctx: Context, cmd: TokenCmd) { ) .send_with_payer(); } + TokenSubCmd::SetInterchainSecurityModule(set_ism) => { + let instruction = + hyperlane_sealevel_token_lib::instruction::set_interchain_security_module_instruction( + set_ism.program_id, + ctx.payer_pubkey, + set_ism.ism + ) + .unwrap(); + + ctx.new_txn() + .add_with_description(instruction, format!("Set ISM to {:?}", set_ism.ism)) + .send_with_payer(); + } + TokenSubCmd::Igp(args) => match args.cmd { + GetSetCmd::Set(set_args) => { + let igp_type: InterchainGasPaymasterType = match set_args.igp_type { + IgpType::Igp => InterchainGasPaymasterType::Igp(set_args.igp_account), + IgpType::OverheadIgp => { + InterchainGasPaymasterType::OverheadIgp(set_args.igp_account) + } + }; + let instruction = hyperlane_sealevel_token_lib::instruction::set_igp_instruction( + args.program_id, + ctx.payer_pubkey, + Some((set_args.igp_program, igp_type.clone())), + ) + .unwrap(); + + ctx.new_txn() + .add_with_description( + instruction, + format!( + "Set IGP of {} to program {}, type {:?}", + args.program_id, set_args.igp_program, igp_type + ), + ) + .send_with_payer(); + } + GetSetCmd::Get(get_args) => { + let (token_account, _token_bump) = + Pubkey::find_program_address(hyperlane_token_pda_seeds!(), &args.program_id); + let token_account = ctx + .client + .get_account_with_commitment(&token_account, ctx.commitment) + .unwrap() + .value + .expect( + "Token account not found. Make sure you are connected to the right RPC.", + ); + + parse_token_account_data(get_args.token_type, &mut &token_account.data[..]); + } + }, } } @@ -1205,93 +1314,47 @@ fn process_validator_announce_cmd(ctx: Context, cmd: ValidatorAnnounceCmd) { } } -fn process_multisig_ism_message_id_cmd(ctx: Context, cmd: MultisigIsmMessageIdCmd) { +fn process_igp_cmd(ctx: Context, cmd: IgpCmd) { match cmd.cmd { - MultisigIsmMessageIdSubCmd::Init(init) => { - let init_instruction = - hyperlane_sealevel_multisig_ism_message_id::instruction::init_instruction( - init.program_id, - ctx.payer_pubkey, - ) - .unwrap(); - ctx.new_txn().add(init_instruction).send_with_payer(); - } - MultisigIsmMessageIdSubCmd::SetValidatorsAndThreshold(set_config) => { - let (access_control_pda_key, _access_control_pda_bump) = Pubkey::find_program_address( - multisig_ism_message_id_access_control_pda_seeds!(), - &set_config.program_id, - ); - - let (domain_data_pda_key, _domain_data_pda_bump) = Pubkey::find_program_address( - multisig_ism_message_id_domain_data_pda_seeds!(set_config.domain), - &set_config.program_id, - ); - - let ixn = MultisigIsmMessageIdInstruction::SetValidatorsAndThreshold(Domained { - domain: set_config.domain, - data: ValidatorsAndThreshold { - validators: set_config.validators, - threshold: set_config.threshold, - }, - }); - - // Accounts: - // 0. `[signer]` The access control owner and payer of the domain PDA. - // 1. `[]` The access control PDA account. - // 2. `[writable]` The PDA relating to the provided domain. - // 3. `[executable]` OPTIONAL - The system program account. Required if creating the domain PDA. - let accounts = vec![ - AccountMeta::new(ctx.payer_pubkey, true), - AccountMeta::new_readonly(access_control_pda_key, false), - AccountMeta::new(domain_data_pda_key, false), - AccountMeta::new_readonly(system_program::id(), false), - ]; - - let set_instruction = Instruction { - program_id: set_config.program_id, - data: ixn.encode().unwrap(), - accounts, - }; - ctx.new_txn().add(set_instruction).send_with_payer(); - } - MultisigIsmMessageIdSubCmd::Query(query) => { - let (access_control_pda_key, _access_control_pda_bump) = Pubkey::find_program_address( - multisig_ism_message_id_access_control_pda_seeds!(), - &query.program_id, - ); + IgpSubCmd::Query(query) => { + let (program_data_account_pda, _program_data_account_bump) = + Pubkey::find_program_address(igp_program_data_pda_seeds!(), &query.program_id); let accounts = ctx .client - .get_multiple_accounts_with_commitment(&[access_control_pda_key], ctx.commitment) + .get_multiple_accounts_with_commitment( + &[program_data_account_pda, query.igp_account], + ctx.commitment, + ) .unwrap() .value; - let access_control = - AccessControlAccount::fetch(&mut &accounts[0].as_ref().unwrap().data[..]) + + let igp_program_data = + IgpProgramDataAccount::fetch(&mut &accounts[0].as_ref().unwrap().data[..]) .unwrap() .into_inner(); - println!("Access control: {:#?}", access_control); - } - MultisigIsmMessageIdSubCmd::TransferOwnership(transfer_ownership) => { - let instruction = - hyperlane_sealevel_multisig_ism_message_id::instruction::transfer_ownership_instruction( - transfer_ownership.program_id, - ctx.payer_pubkey, - Some(transfer_ownership.new_owner), - ) - .unwrap(); - ctx.new_txn() - .add_with_description( - instruction, - format!("Transfer ownership to {}", transfer_ownership.new_owner), - ) - .send_with_payer(); - } - } -} + println!("IGP program data: {:?}", igp_program_data); -fn process_igp_cmd(ctx: Context, cmd: IgpCmd) { - match cmd.cmd { + let igp = IgpAccount::fetch(&mut &accounts[1].as_ref().unwrap().data[..]) + .unwrap() + .into_inner(); + + println!("IGP account: {:?}", igp); + + if let Some(gas_payment_account_pubkey) = query.gas_payment_account { + let account = ctx + .client + .get_account_with_commitment(&gas_payment_account_pubkey, ctx.commitment) + .unwrap() + .value + .unwrap(); + let gas_payment_account = GasPaymentAccount::fetch(&mut &account.data[..]) + .unwrap() + .into_inner(); + println!("Gas payment account: {:?}", gas_payment_account); + } + } IgpSubCmd::PayForGas(payment_details) => { let unique_gas_payment_keypair = Keypair::new(); let salt = H256::zero(); @@ -1312,8 +1375,8 @@ fn process_igp_cmd(ctx: Context, cmd: IgpCmd) { Some(overhead_igp_account), unique_gas_payment_keypair.pubkey(), H256::from_str(&payment_details.message_id).unwrap(), - 13376, - 100000, + payment_details.destination_domain, + payment_details.gas, ) .unwrap(); @@ -1330,7 +1393,7 @@ fn process_igp_cmd(ctx: Context, cmd: IgpCmd) { let core_program_ids = read_core_program_ids(&args.environments_dir, &args.environment, &args.chain_name); match args.cmd { - GasOracleSubCmd::Set(set_args) => { + GetSetCmd::Set(set_args) => { let remote_gas_data = RemoteGasData { token_exchange_rate: set_args.token_exchange_rate, gas_price: set_args.gas_price, @@ -1351,8 +1414,7 @@ fn process_igp_cmd(ctx: Context, cmd: IgpCmd) { ctx.new_txn().add(instruction).send_with_payer(); println!("Set gas oracle for remote domain {:?}", args.remote_domain); } - GasOracleSubCmd::Get => { - // Read the gas oracle config + GetSetCmd::Get(_) => { let igp_account = ctx .client .get_account_with_commitment(&core_program_ids.igp_account, ctx.commitment) diff --git a/rust/sealevel/client/src/multisig_ism.rs b/rust/sealevel/client/src/multisig_ism.rs new file mode 100644 index 000000000..d5676b281 --- /dev/null +++ b/rust/sealevel/client/src/multisig_ism.rs @@ -0,0 +1,312 @@ +use std::collections::{HashMap, HashSet}; +use std::{fs::File, path::Path}; + +use serde::{Deserialize, Serialize}; +use solana_program::pubkey::Pubkey; +use solana_sdk::signature::Signer; + +use crate::{ + artifacts::{write_json, SingularProgramIdArtifact}, + cmd_utils::{create_and_write_keypair, create_new_directory, deploy_program}, + router::ChainMetadata, + Context, MultisigIsmMessageIdCmd, MultisigIsmMessageIdSubCmd, +}; + +use hyperlane_core::H160; + +use hyperlane_sealevel_multisig_ism_message_id::{ + access_control_pda_seeds, + accounts::{AccessControlAccount, DomainDataAccount}, + domain_data_pda_seeds, + instruction::{set_validators_and_threshold_instruction, ValidatorsAndThreshold}, +}; + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct MultisigIsmConfig { + /// Note this type is ignored in this tooling. It'll always assume this + /// relates to a multisig-ism-message-id variant, which is the only type + /// implemented in Sealevel. + #[serde(rename = "type")] + pub module_type: u8, + pub validators: Vec, + pub threshold: u8, +} + +impl From for ValidatorsAndThreshold { + fn from(val: MultisigIsmConfig) -> Self { + ValidatorsAndThreshold { + validators: val.validators, + threshold: val.threshold, + } + } +} + +pub(crate) fn process_multisig_ism_message_id_cmd(mut ctx: Context, cmd: MultisigIsmMessageIdCmd) { + match cmd.cmd { + MultisigIsmMessageIdSubCmd::Deploy(deploy) => { + let environments_dir = + create_new_directory(&deploy.environments_dir, &deploy.environment); + let ism_dir = create_new_directory(&environments_dir, "multisig-ism-message-id"); + let chain_dir = create_new_directory(&ism_dir, &deploy.chain); + let context_dir = create_new_directory(&chain_dir, &deploy.context); + let key_dir = create_new_directory(&context_dir, "keys"); + + let ism_program_id = + deploy_multisig_ism_message_id(&mut ctx, &deploy.built_so_dir, true, &key_dir); + + write_json::( + &context_dir.join("program-ids.json"), + ism_program_id.into(), + ); + } + MultisigIsmMessageIdSubCmd::Init(init) => { + let init_instruction = + hyperlane_sealevel_multisig_ism_message_id::instruction::init_instruction( + init.program_id, + ctx.payer_pubkey, + ) + .unwrap(); + ctx.new_txn().add(init_instruction).send_with_payer(); + } + MultisigIsmMessageIdSubCmd::SetValidatorsAndThreshold(set_config) => { + set_validators_and_threshold( + &mut ctx, + set_config.program_id, + set_config.domain, + ValidatorsAndThreshold { + validators: set_config.validators, + threshold: set_config.threshold, + }, + ); + } + MultisigIsmMessageIdSubCmd::Query(query) => { + let (access_control_pda_key, _access_control_pda_bump) = + Pubkey::find_program_address(access_control_pda_seeds!(), &query.program_id); + + let accounts = ctx + .client + .get_multiple_accounts_with_commitment(&[access_control_pda_key], ctx.commitment) + .unwrap() + .value; + let access_control = + AccessControlAccount::fetch(&mut &accounts[0].as_ref().unwrap().data[..]) + .unwrap() + .into_inner(); + println!("Access control: {:#?}", access_control); + + if let Some(domains) = query.domains { + for domain in domains { + println!("Querying domain data for origin domain: {}", domain); + + let (domain_data_pda_key, _domain_data_pda_bump) = Pubkey::find_program_address( + domain_data_pda_seeds!(domain), + &query.program_id, + ); + + let accounts = ctx + .client + .get_multiple_accounts_with_commitment( + &[domain_data_pda_key], + ctx.commitment, + ) + .unwrap() + .value; + + if let Some(account) = &accounts[0] { + let domain_data = DomainDataAccount::fetch(&mut &account.data[..]) + .unwrap() + .into_inner(); + println!("Domain data for {}:\n{:#?}", domain, domain_data); + } else { + println!("No domain data for domain {}", domain); + } + } + } + } + MultisigIsmMessageIdSubCmd::TransferOwnership(transfer_ownership) => { + let instruction = + hyperlane_sealevel_multisig_ism_message_id::instruction::transfer_ownership_instruction( + transfer_ownership.program_id, + ctx.payer_pubkey, + Some(transfer_ownership.new_owner), + ) + .unwrap(); + + ctx.new_txn() + .add_with_description( + instruction, + format!("Transfer ownership to {}", transfer_ownership.new_owner), + ) + .send_with_payer(); + } + MultisigIsmMessageIdSubCmd::Configure(configure) => { + configure_multisig_ism_message_id( + &mut ctx, + configure.program_id, + &configure.multisig_config_file, + &configure.chain_config_file, + ); + } + } +} + +pub(crate) fn deploy_multisig_ism_message_id( + ctx: &mut Context, + built_so_dir: &Path, + use_existing_keys: bool, + key_dir: &Path, +) -> Pubkey { + let (keypair, keypair_path) = create_and_write_keypair( + key_dir, + "hyperlane_sealevel_multisig_ism_message_id-keypair.json", + use_existing_keys, + ); + let program_id = keypair.pubkey(); + + deploy_program( + ctx.payer_keypair_path(), + keypair_path.to_str().unwrap(), + built_so_dir + .join("hyperlane_sealevel_multisig_ism_message_id.so") + .to_str() + .unwrap(), + &ctx.client.url(), + ); + + println!( + "Deployed Multisig ISM Message ID at program ID {}", + program_id + ); + + // Initialize + let instruction = hyperlane_sealevel_multisig_ism_message_id::instruction::init_instruction( + program_id, + ctx.payer_pubkey, + ) + .unwrap(); + + ctx.new_txn() + .add_with_description( + instruction, + format!( + "Initializing Multisig ISM Message ID with payer & owner {}", + ctx.payer_pubkey + ), + ) + .send_with_payer(); + + program_id +} + +/// Configures the multisig-ism-message-id program +/// with the validators and thresholds for each of the domains +/// specified in the multisig config file. +fn configure_multisig_ism_message_id( + ctx: &mut Context, + program_id: Pubkey, + multisig_config_file_path: &Path, + chain_config_path: &Path, +) { + let multisig_config_file = + File::open(multisig_config_file_path).expect("Failed to open config file"); + let multisig_configs: HashMap = + serde_json::from_reader(multisig_config_file).expect("Failed to read config file"); + + let chain_config_file = File::open(chain_config_path).unwrap(); + let chain_configs: HashMap = + serde_json::from_reader(chain_config_file).unwrap(); + + for (chain_name, multisig_ism_config) in multisig_configs { + println!( + "Configuring Multisig ISM Message ID for chain {} and config {:?}", + chain_name, multisig_ism_config + ); + let chain_config = chain_configs.get(&chain_name).unwrap(); + + let matches = multisig_ism_config_matches_chain( + ctx, + program_id, + chain_config.domain_id(), + &multisig_ism_config, + ); + + if matches { + println!( + "Multisig ISM Message ID already correctly configured for chain {}", + chain_name + ); + } else { + println!( + "Multisig ISM Message ID incorrectly configured for chain {}, configuring now", + chain_name + ); + set_validators_and_threshold( + ctx, + program_id, + chain_config.domain_id(), + multisig_ism_config.into(), + ); + } + } +} + +fn multisig_ism_config_matches_chain( + ctx: &mut Context, + program_id: Pubkey, + remote_domain: u32, + expected: &MultisigIsmConfig, +) -> bool { + let (domain_data_key, _domain_data_bump) = + Pubkey::find_program_address(domain_data_pda_seeds!(remote_domain), &program_id); + + let domain_data_account = ctx + .client + .get_account_with_commitment(&domain_data_key, ctx.commitment) + .expect("Failed to get domain data account") + .value; + + if let Some(domain_data_account) = domain_data_account { + let domain_data = DomainDataAccount::fetch(&mut &domain_data_account.data[..]) + .unwrap() + .into_inner(); + let expected_validator_set = + HashSet::::from_iter(expected.validators.iter().cloned()); + let actual_validator_set = HashSet::::from_iter( + domain_data + .validators_and_threshold + .validators + .iter() + .cloned(), + ); + + expected_validator_set == actual_validator_set + && expected.threshold == domain_data.validators_and_threshold.threshold + } else { + false + } +} + +pub(crate) fn set_validators_and_threshold( + ctx: &mut Context, + program_id: Pubkey, + domain: u32, + validators_and_threshold: ValidatorsAndThreshold, +) { + let description = format!( + "Set for remote domain {} validators and threshold: {:?}", + domain, validators_and_threshold + ); + ctx.new_txn() + .add_with_description( + set_validators_and_threshold_instruction( + program_id, + ctx.payer_pubkey, + domain, + validators_and_threshold, + ) + .unwrap(), + description, + ) + .send_with_payer(); +} diff --git a/rust/sealevel/client/src/router.rs b/rust/sealevel/client/src/router.rs new file mode 100644 index 000000000..4eaf4cae7 --- /dev/null +++ b/rust/sealevel/client/src/router.rs @@ -0,0 +1,593 @@ +use hyperlane_core::{utils::hex_or_base58_to_h256, H256}; +use serde::{Deserialize, Serialize}; +use std::{ + collections::HashMap, + fs::File, + path::{Path, PathBuf}, +}; + +use solana_client::rpc_client::RpcClient; +use solana_program::instruction::Instruction; +use solana_sdk::{commitment_config::CommitmentConfig, pubkey::Pubkey, signature::Signer}; + +use account_utils::DiscriminatorData; +use hyperlane_sealevel_connection_client::router::RemoteRouterConfig; +use hyperlane_sealevel_igp::accounts::{Igp, InterchainGasPaymasterType, OverheadIgp}; + +use crate::{ + artifacts::{write_json, HexAndBase58ProgramIdArtifact}, + cmd_utils::{create_and_write_keypair, create_new_directory, deploy_program_idempotent}, + read_core_program_ids, Context, CoreProgramIds, +}; + +/// Optional connection client configuration. +#[derive(Debug, Deserialize, Serialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct OptionalConnectionClientConfig { + #[serde(default)] + #[serde(with = "crate::serde::serde_option_pubkey")] + mailbox: Option, + #[serde(default)] + #[serde(with = "crate::serde::serde_option_pubkey")] + interchain_gas_paymaster: Option, + #[serde(default)] + #[serde(with = "crate::serde::serde_option_pubkey")] + interchain_security_module: Option, +} + +impl OptionalConnectionClientConfig { + pub fn mailbox(&self, default: Pubkey) -> Pubkey { + self.mailbox.unwrap_or(default) + } + + pub fn interchain_security_module(&self) -> Option { + self.interchain_security_module + } + + /// Uses the configured IGP account, if Some, to get the IGP program ID + /// and generate a config of the form Some((program_id, Igp account)). + pub fn interchain_gas_paymaster_config( + &self, + client: &RpcClient, + ) -> Option<(Pubkey, InterchainGasPaymasterType)> { + if let Some(igp_pubkey) = self.interchain_gas_paymaster { + let account = client + .get_account(&self.interchain_gas_paymaster.unwrap()) + .unwrap(); + + match &account.data[1..9] { + Igp::DISCRIMINATOR_SLICE => { + Some((account.owner, InterchainGasPaymasterType::Igp(igp_pubkey))) + } + OverheadIgp::DISCRIMINATOR_SLICE => Some(( + account.owner, + InterchainGasPaymasterType::OverheadIgp(igp_pubkey), + )), + _ => { + panic!("Invalid IGP account configured {}", igp_pubkey); + } + } + } else { + None + } + } +} + +/// Optional ownable configuration. +#[derive(Debug, Deserialize, Serialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct OptionalOwnableConfig { + #[serde(default)] + #[serde(with = "crate::serde::serde_option_pubkey")] + pub owner: Option, +} + +impl OptionalOwnableConfig { + pub fn owner(&self, default: Pubkey) -> Pubkey { + self.owner.unwrap_or(default) + } +} + +/// Router configuration. +#[derive(Debug, Deserialize, Serialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct RouterConfig { + // Kept as a string to allow for hex or base58 + pub foreign_deployment: Option, + #[serde(flatten)] + pub ownable: OptionalOwnableConfig, + #[serde(flatten)] + pub connection_client: OptionalConnectionClientConfig, +} + +#[derive(Debug, Deserialize, Serialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct RpcUrlConfig { + pub http: String, +} + +/// An abridged version of the Typescript ChainMetadata +#[derive(Debug, Deserialize, Serialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct ChainMetadata { + chain_id: u32, + /// Hyperlane domain, only required if differs from id above + domain_id: Option, + name: String, + /// Collection of RPC endpoints + rpc_urls: Vec, +} + +impl ChainMetadata { + pub fn client(&self) -> RpcClient { + RpcClient::new_with_commitment(self.rpc_urls[0].http.clone(), CommitmentConfig::confirmed()) + } + + pub fn domain_id(&self) -> u32 { + self.domain_id.unwrap_or(self.chain_id) + } +} + +pub trait RouterConfigGetter { + fn router_config(&self) -> &RouterConfig; +} + +pub(crate) trait RouterDeployer: + ConnectionClient +{ + #[allow(clippy::too_many_arguments)] + fn deploy( + &self, + ctx: &mut Context, + key_dir: &Path, + environments_dir: &Path, + environment: &str, + built_so_dir: &Path, + chain_config: &ChainMetadata, + app_config: &Config, + existing_program_ids: Option<&HashMap>, + ) -> Pubkey { + let program_name = self.program_name(app_config); + + println!( + "Attempting deploy {} on chain: {}\nApp config: {:?}", + program_name, chain_config.name, app_config + ); + + let program_id = existing_program_ids + .and_then(|existing_program_ids| { + existing_program_ids.get(&chain_config.name).and_then(|id| { + chain_config + .client() + .get_account_with_commitment(id, ctx.commitment) + .unwrap() + .value + .map(|_| { + println!("Recovered existing program id {}", id); + *id + }) + }) + }) + .unwrap_or_else(|| { + let (keypair, keypair_path) = create_and_write_keypair( + key_dir, + format!("{}-{}.json", program_name, chain_config.name).as_str(), + true, + ); + let program_id = keypair.pubkey(); + + deploy_program_idempotent( + ctx.payer_keypair_path(), + &keypair, + keypair_path.to_str().unwrap(), + built_so_dir + .join(format!("{}.so", program_name)) + .to_str() + .unwrap(), + &chain_config.rpc_urls[0].http, + ) + .unwrap(); + + program_id + }); + + let core_program_ids = + read_core_program_ids(environments_dir, environment, &chain_config.name); + self.init_program_idempotent( + ctx, + &chain_config.client(), + &core_program_ids, + chain_config, + app_config, + program_id, + ); + + program_id + } + + fn init_program_idempotent( + &self, + ctx: &mut Context, + client: &RpcClient, + core_program_ids: &CoreProgramIds, + chain_config: &ChainMetadata, + app_config: &Config, + program_id: Pubkey, + ); + + fn post_deploy( + &self, + _ctx: &mut Context, + _app_configs: &HashMap, + _app_configs_to_deploy: &HashMap<&String, &Config>, + _chain_configs: &HashMap, + _routers: &HashMap, + ) { + // By default, do nothing. + } + + /// The program's name, i.e. the name of the program's .so file (without the .so suffix) + /// and the name that will be used to create the keypair file + fn program_name(&self, config: &Config) -> &str; + + fn enroll_remote_routers_instruction( + &self, + program_id: Pubkey, + payer: Pubkey, + router_configs: Vec, + ) -> Instruction; + + fn get_routers(&self, rpc_client: &RpcClient, program_id: &Pubkey) -> HashMap; +} + +pub(crate) trait Ownable { + /// Gets the owner configured on-chain. + fn get_owner(&self, client: &RpcClient, program_id: &Pubkey) -> Option; + + /// Gets an instruction to set the owner. + fn set_owner_instruction( + &self, + client: &RpcClient, + program_id: &Pubkey, + new_owner: Option, + ) -> Instruction; +} + +pub(crate) trait ConnectionClient: Ownable { + /// Gets the interchain security module configured on-chain. + fn get_interchain_security_module( + &self, + client: &RpcClient, + program_id: &Pubkey, + ) -> Option; + + /// Gets an instruction to set the interchain security module. + fn set_interchain_security_module_instruction( + &self, + client: &RpcClient, + program_id: &Pubkey, + ism: Option, + ) -> Instruction; +} + +/// Idempotently deploys routers on multiple Sealevel chains and enrolls all routers (including +/// foreign deployments) on each Sealevel chain. +#[allow(clippy::too_many_arguments)] +pub(crate) fn deploy_routers< + Config: for<'a> Deserialize<'a> + RouterConfigGetter + std::fmt::Debug + Clone, + Deployer: RouterDeployer, +>( + ctx: &mut Context, + deployer: Deployer, + app_name: &str, + deploy_name: &str, + app_config_file_path: PathBuf, + chain_config_file_path: PathBuf, + environments_dir_path: PathBuf, + environment: &str, + built_so_dir_path: PathBuf, +) { + // Load the app configs from the app config file. + let app_config_file = File::open(app_config_file_path).unwrap(); + let app_configs: HashMap = serde_json::from_reader(app_config_file).unwrap(); + + // Load the chain configs from the chain config file. + let chain_config_file = File::open(chain_config_file_path).unwrap(); + let chain_configs: HashMap = + serde_json::from_reader(chain_config_file).unwrap(); + + let environments_dir = create_new_directory(&environments_dir_path, environment); + + let artifacts_dir = create_new_directory(&environments_dir, app_name); + let deploy_dir = create_new_directory(&artifacts_dir, deploy_name); + let keys_dir = create_new_directory(&deploy_dir, "keys"); + + let existing_program_ids = read_router_program_ids(&deploy_dir); + + // Builds a HashMap of all the foreign deployments from the app config. + // These domains with foreign deployments will not have any txs / deployments + // made directly to them, but the routers will be enrolled on the other chains. + let foreign_deployments = app_configs + .iter() + .filter_map(|(chain_name, app_config)| { + app_config + .router_config() + .foreign_deployment + .as_ref() + .map(|foreign_deployment| { + let chain_config = chain_configs.get(chain_name).unwrap(); + ( + chain_config.domain_id(), + hex_or_base58_to_h256(foreign_deployment).unwrap(), + ) + }) + }) + .collect::>(); + + // A map of all the routers, including the foreign deployments. + let mut routers: HashMap = foreign_deployments; + + // Non-foreign app configs to deploy to. + let app_configs_to_deploy = app_configs + .iter() + .filter(|(_, app_config)| app_config.router_config().foreign_deployment.is_none()) + .collect::>(); + + // Now we deploy to chains that don't have a foreign deployment + for (chain_name, app_config) in app_configs_to_deploy.iter() { + let chain_config = chain_configs + .get(*chain_name) + .unwrap_or_else(|| panic!("Chain config not found for chain: {}", chain_name)); + + if let Some(configured_owner) = app_config.router_config().ownable.owner { + if configured_owner != ctx.payer_pubkey { + println!("WARNING: Ownership transfer is not yet supported in this deploy tooling, ownership is granted to the payer account"); + } + } + + // Deploy - this is idempotent. + let program_id = deployer.deploy( + ctx, + &keys_dir, + &environments_dir_path, + environment, + &built_so_dir_path, + chain_config, + app_config, + existing_program_ids.as_ref(), + ); + + // Add the router to the list of routers. + routers.insert( + chain_config.domain_id(), + H256::from_slice(&program_id.to_bytes()[..]), + ); + + configure_connection_client( + ctx, + &deployer, + &program_id, + app_config.router_config(), + chain_config, + ); + + configure_owner( + ctx, + &deployer, + &program_id, + app_config.router_config(), + chain_config, + ); + } + + // Now enroll all the routers. + enroll_all_remote_routers( + &deployer, + ctx, + &app_configs_to_deploy, + &chain_configs, + &routers, + ); + + // Call the post-deploy hook. + deployer.post_deploy( + ctx, + &app_configs, + &app_configs_to_deploy, + &chain_configs, + &routers, + ); + + // Now write the program ids to a file! + let routers_by_name: HashMap = routers + .iter() + .map(|(domain_id, router)| { + ( + chain_configs + .iter() + .find(|(_, chain_config)| chain_config.domain_id() == *domain_id) + .unwrap() + .0 + .clone(), + *router, + ) + }) + .collect::>(); + write_router_program_ids(&deploy_dir, &routers_by_name); +} + +// Idempotent. +// TODO: This should really be brought out into some nicer abstraction, and we should +// also look for IGP inconsistency etc. +fn configure_connection_client( + ctx: &mut Context, + deployer: &impl ConnectionClient, + program_id: &Pubkey, + router_config: &RouterConfig, + chain_config: &ChainMetadata, +) { + // Just ISM for now + + let client = chain_config.client(); + + let actual_ism = deployer.get_interchain_security_module(&client, program_id); + let expected_ism = router_config.connection_client.interchain_security_module(); + + if actual_ism != expected_ism { + ctx.new_txn() + .add_with_description( + deployer.set_interchain_security_module_instruction( + &client, + program_id, + expected_ism, + ), + format!( + "Setting ISM for chain: {} ({}) to {:?}", + chain_config.name, + chain_config.domain_id(), + expected_ism + ), + ) + .with_client(&client) + .send_with_payer(); + } +} + +// Idempotent. +// TODO: This should really be brought out into some nicer abstraction +fn configure_owner( + ctx: &mut Context, + deployer: &impl ConnectionClient, + program_id: &Pubkey, + router_config: &RouterConfig, + chain_config: &ChainMetadata, +) { + let client = chain_config.client(); + + let actual_owner = deployer.get_owner(&client, program_id); + let expected_owner = Some(router_config.ownable.owner(ctx.payer_pubkey)); + + if actual_owner != expected_owner { + ctx.new_txn() + .add_with_description( + deployer.set_owner_instruction(&client, program_id, expected_owner), + format!( + "Setting owner for chain: {} ({}) to {:?}", + chain_config.name, + chain_config.domain_id(), + expected_owner, + ), + ) + .with_client(&client) + .send_with_payer(); + } +} + +/// For each chain in app_configs_to_deploy, enrolls all the remote routers. +/// Idempotent. +fn enroll_all_remote_routers< + Config: for<'a> Deserialize<'a> + RouterConfigGetter + std::fmt::Debug + Clone, +>( + deployer: &impl RouterDeployer, + ctx: &mut Context, + app_configs_to_deploy: &HashMap<&String, &Config>, + chain_configs: &HashMap, + routers: &HashMap, +) { + for (chain_name, _) in app_configs_to_deploy.iter() { + let chain_config = chain_configs + .get(*chain_name) + .unwrap_or_else(|| panic!("Chain config not found for chain: {}", chain_name)); + + let domain_id = chain_config.domain_id(); + let program_id: Pubkey = + Pubkey::new_from_array(*routers.get(&domain_id).unwrap().as_fixed_bytes()); + + let enrolled_routers = deployer.get_routers(&chain_config.client(), &program_id); + let expected_routers = routers + .iter() + .filter(|(router_domain_id, _)| *router_domain_id != &domain_id) + .map(|(domain, router)| { + ( + *domain, + RemoteRouterConfig { + domain: *domain, + router: Some(*router), + }, + ) + }) + .collect::>(); + + // Routers to enroll (or update to a Some value) + let routers_to_enroll = expected_routers + .iter() + .filter(|(domain, router_config)| { + enrolled_routers.get(domain) != router_config.router.as_ref() + }) + .map(|(_, router_config)| router_config.clone()); + + // Routers to remove + let routers_to_unenroll = enrolled_routers + .iter() + .filter(|(domain, _)| !expected_routers.contains_key(domain)) + .map(|(domain, _)| RemoteRouterConfig { + domain: *domain, + router: None, + }); + + // All router config changes + let router_configs = routers_to_enroll + .chain(routers_to_unenroll) + .collect::>(); + + if !router_configs.is_empty() { + println!( + "Enrolling routers for chain: {}, program_id {}, routers: {:?}", + chain_name, program_id, router_configs, + ); + + ctx.new_txn() + .add(deployer.enroll_remote_routers_instruction( + program_id, + ctx.payer_pubkey, + router_configs, + )) + .with_client(&chain_config.client()) + .send_with_payer(); + } else { + println!( + "No router changes for chain: {}, program_id {}", + chain_name, program_id + ); + } + } +} + +// Writes router program IDs as hex and base58. +fn write_router_program_ids(deploy_dir: &Path, routers: &HashMap) { + let serialized_program_ids = routers + .iter() + .map(|(chain_name, router)| (chain_name.clone(), (*router).into())) + .collect::>(); + + let program_ids_file = deploy_dir.join("program-ids.json"); + write_json(&program_ids_file, serialized_program_ids); +} + +fn read_router_program_ids(deploy_dir: &Path) -> Option> { + let program_ids_file = deploy_dir.join("program-ids.json"); + + if !program_ids_file.exists() { + return None; + } + + let serialized_program_ids: HashMap = + serde_json::from_reader(File::open(program_ids_file).unwrap()).unwrap(); + + let existing_program_ids = serialized_program_ids + .iter() + .map(|(chain_name, program_id)| (chain_name.clone(), program_id.into())) + .collect::>(); + + Some(existing_program_ids) +} diff --git a/rust/sealevel/client/src/serde.rs b/rust/sealevel/client/src/serde.rs new file mode 100644 index 000000000..b73a25bd5 --- /dev/null +++ b/rust/sealevel/client/src/serde.rs @@ -0,0 +1,68 @@ +/// For serializing and deserializing Pubkey +pub(crate) mod serde_pubkey { + use borsh::BorshDeserialize; + use serde::{Deserialize, Deserializer, Serializer}; + use solana_sdk::pubkey::Pubkey; + use std::str::FromStr; + + #[derive(Deserialize)] + #[serde(untagged)] + enum RawPubkey { + String(String), + Bytes(Vec), + } + + pub fn serialize(k: &Pubkey, ser: S) -> Result { + ser.serialize_str(&k.to_string()) + } + + pub fn deserialize<'de, D: Deserializer<'de>>(de: D) -> Result { + match RawPubkey::deserialize(de)? { + RawPubkey::String(s) => Pubkey::from_str(&s).map_err(serde::de::Error::custom), + RawPubkey::Bytes(b) => Pubkey::try_from_slice(&b).map_err(serde::de::Error::custom), + } + } +} + +/// For serializing and deserializing Option +pub(crate) mod serde_option_pubkey { + use borsh::BorshDeserialize; + use serde::{Deserialize, Deserializer, Serializer}; + use solana_sdk::pubkey::Pubkey; + use std::str::FromStr; + + #[derive(Deserialize)] + #[serde(untagged)] + enum RawPubkey { + String(String), + Bytes(Vec), + } + + pub fn serialize(k: &Option, ser: S) -> Result { + ser.serialize_str(&k.map(|k| k.to_string()).unwrap_or_default()) + } + + pub fn deserialize<'de, D: Deserializer<'de>>(de: D) -> Result, D::Error> { + match Option::::deserialize(de)? { + Some(RawPubkey::String(s)) => { + if s.is_empty() { + Ok(None) + } else { + Pubkey::from_str(&s) + .map_err(serde::de::Error::custom) + .map(Some) + } + } + Some(RawPubkey::Bytes(b)) => { + if b.is_empty() { + Ok(None) + } else { + Pubkey::try_from_slice(&b) + .map_err(serde::de::Error::custom) + .map(Some) + } + } + None => Ok(None), + } + } +} diff --git a/rust/sealevel/client/src/warp_route.rs b/rust/sealevel/client/src/warp_route.rs index 33cc38b2e..5e67fcf37 100644 --- a/rust/sealevel/client/src/warp_route.rs +++ b/rust/sealevel/client/src/warp_route.rs @@ -1,30 +1,41 @@ -use hyperlane_core::{utils::hex_or_base58_to_h256, H256}; +use borsh::{BorshDeserialize, BorshSerialize}; +use hyperlane_core::H256; +use hyperlane_sealevel_token_collateral::plugin::CollateralPlugin; +use hyperlane_sealevel_token_native::plugin::NativePlugin; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, fs::File, path::Path, str::FromStr}; +use std::{collections::HashMap, fmt::Debug}; use solana_client::{client_error::ClientError, rpc_client::RpcClient}; -use solana_program::program_error::ProgramError; -use solana_sdk::{commitment_config::CommitmentConfig, pubkey::Pubkey, signature::Signer}; + +use solana_sdk::{instruction::Instruction, program_error::ProgramError, pubkey::Pubkey}; use hyperlane_sealevel_connection_client::{ gas_router::GasRouterConfig, router::RemoteRouterConfig, }; use hyperlane_sealevel_igp::accounts::InterchainGasPaymasterType; -use hyperlane_sealevel_token::{hyperlane_token_mint_pda_seeds, spl_token, spl_token_2022}; +use hyperlane_sealevel_token::{ + hyperlane_token_mint_pda_seeds, plugin::SyntheticPlugin, spl_token, spl_token_2022, +}; use hyperlane_sealevel_token_lib::{ - accounts::HyperlaneTokenAccount, + accounts::{HyperlaneToken, HyperlaneTokenAccount}, hyperlane_token_pda_seeds, - instruction::{enroll_remote_routers_instruction, set_destination_gas_configs, Init}, + instruction::{ + enroll_remote_routers_instruction, set_destination_gas_configs, + set_interchain_security_module_instruction, transfer_ownership_instruction, Init, + }, }; use crate::{ - cmd_utils::{ - account_exists, create_and_write_keypair, create_new_directory, deploy_program_idempotent, + cmd_utils::account_exists, + core::CoreProgramIds, + router::{ + deploy_routers, ChainMetadata, ConnectionClient, Ownable, RouterConfig, RouterConfigGetter, + RouterDeployer, }, - core::{read_core_program_ids, CoreProgramIds}, - Context, WarpRouteCmd, WarpRouteSubCmd, + Context, TokenType as FlatTokenType, WarpRouteCmd, WarpRouteSubCmd, }; +/// Configuration relating to decimals. #[derive(Debug, Deserialize, Serialize, Clone)] #[serde(rename_all = "camelCase")] struct DecimalMetadata { @@ -38,6 +49,7 @@ impl DecimalMetadata { } } +/// Configuration relating to a Warp Route token. #[derive(Clone, Serialize, Deserialize, Debug)] #[serde(tag = "type", rename_all = "camelCase")] enum TokenType { @@ -47,14 +59,6 @@ enum TokenType { } impl TokenType { - fn program_name(&self) -> &str { - match self { - TokenType::Native => "hyperlane_sealevel_token_native", - TokenType::Synthetic(_) => "hyperlane_sealevel_token", - TokenType::Collateral(_) => "hyperlane_sealevel_token_collateral", - } - } - // Borrowed from HypERC20Deployer's `gasOverheadDefault`. fn gas_overhead_default(&self) -> u64 { // TODO: note these are the amounts specific to the EVM. @@ -100,406 +104,361 @@ struct CollateralInfo { spl_token_program: Option, } -#[derive(Debug, Deserialize, Serialize, Clone)] -#[serde(rename_all = "camelCase")] -struct InterchainGasPaymasterConfig { - program_id: Pubkey, - igp_account: InterchainGasPaymasterType, -} - -#[derive(Debug, Deserialize, Serialize, Clone)] -#[serde(rename_all = "camelCase")] -struct OptionalConnectionClientConfig { - mailbox: Option, - interchain_gas_paymaster: Option, - interchain_security_module: Option, -} - -#[derive(Debug, Deserialize, Serialize, Clone)] -#[serde(rename_all = "camelCase")] -struct OptionalOwnableConfig { - owner: Option, -} - #[derive(Debug, Deserialize, Serialize, Clone)] #[serde(rename_all = "camelCase")] struct TokenConfig { #[serde(flatten)] token_type: TokenType, - foreign_deployment: Option, #[serde(flatten)] decimal_metadata: DecimalMetadata, #[serde(flatten)] - ownable: OptionalOwnableConfig, - #[serde(flatten)] - connection_client: OptionalConnectionClientConfig, + router_config: RouterConfig, } -#[derive(Debug, Deserialize, Serialize, Clone)] -#[serde(rename_all = "camelCase")] -pub struct RpcUrlConfig { - pub http: String, +pub(crate) fn process_warp_route_cmd(mut ctx: Context, cmd: WarpRouteCmd) { + match cmd.cmd { + WarpRouteSubCmd::Deploy(deploy) => { + deploy_routers( + &mut ctx, + WarpRouteDeployer::new(deploy.ata_payer_funding_amount), + "warp-routes", + &deploy.warp_route_name, + deploy.token_config_file, + deploy.chain_config_file, + deploy.environments_dir, + &deploy.environment, + deploy.built_so_dir, + ); + } + WarpRouteSubCmd::DestinationGas(args) => { + let destination_gas = get_destination_gas(&ctx.client, &args.program_id).unwrap(); + println!( + "Destination gas: {:?}", + destination_gas[&args.destination_domain] + ); + } + } } -/// An abridged version of the Typescript ChainMetadata -#[derive(Debug, Deserialize, Serialize, Clone)] -#[serde(rename_all = "camelCase")] -pub struct ChainMetadata { - chain_id: u32, - /// Hyperlane domain, only required if differs from id above - domain_id: Option, - name: String, - /// Collection of RPC endpoints - public_rpc_urls: Vec, +struct WarpRouteDeployer { + ata_payer_funding_amount: Option, } -impl ChainMetadata { - fn client(&self) -> RpcClient { - RpcClient::new_with_commitment( - self.public_rpc_urls[0].http.clone(), - CommitmentConfig::confirmed(), - ) +impl WarpRouteDeployer { + fn new(ata_payer_funding_amount: Option) -> Self { + Self { + ata_payer_funding_amount, + } } +} - fn domain_id(&self) -> u32 { - self.domain_id.unwrap_or(self.chain_id) +impl WarpRouteDeployer {} + +impl RouterDeployer for WarpRouteDeployer { + fn program_name(&self, config: &TokenConfig) -> &str { + match config.token_type { + TokenType::Native => "hyperlane_sealevel_token_native", + TokenType::Synthetic(_) => "hyperlane_sealevel_token", + TokenType::Collateral(_) => "hyperlane_sealevel_token_collateral", + } } -} -pub(crate) fn process_warp_route_cmd(mut ctx: Context, cmd: WarpRouteCmd) { - match cmd.cmd { - WarpRouteSubCmd::Deploy(deploy) => { - let token_config_file = File::open(deploy.token_config_file).unwrap(); - let token_configs: HashMap = - serde_json::from_reader(token_config_file).unwrap(); + fn enroll_remote_routers_instruction( + &self, + program_id: Pubkey, + payer: Pubkey, + router_configs: Vec, + ) -> Instruction { + enroll_remote_routers_instruction(program_id, payer, router_configs).unwrap() + } + + fn get_routers(&self, client: &RpcClient, program_id: &Pubkey) -> HashMap { + let token_data = get_token_data::<()>(client, program_id); - let chain_config_file = File::open(deploy.chain_config_file).unwrap(); - let chain_configs: HashMap = - serde_json::from_reader(chain_config_file).unwrap(); + token_data.remote_routers + } - let environments_dir = - create_new_directory(&deploy.environments_dir, &deploy.environment); + fn init_program_idempotent( + &self, + ctx: &mut Context, + client: &RpcClient, + core_program_ids: &CoreProgramIds, + chain_config: &ChainMetadata, + app_config: &TokenConfig, + program_id: Pubkey, + ) { + if let Some(ata_payer_funding_amount) = self.ata_payer_funding_amount { + if matches!( + app_config.token_type, + TokenType::Collateral(_) | TokenType::Synthetic(_) + ) { + fund_ata_payer_up_to(ctx, client, program_id, ata_payer_funding_amount); + } + } - let artifacts_dir = create_new_directory(&environments_dir, "warp-routes"); - let warp_route_dir = create_new_directory(&artifacts_dir, &deploy.warp_route_name); - let keys_dir = create_new_directory(&warp_route_dir, "keys"); + let (token_pda, _token_bump) = + Pubkey::find_program_address(hyperlane_token_pda_seeds!(), &program_id); + if account_exists(client, &token_pda).unwrap() { + println!("Warp route token already exists, skipping init"); + return; + } - let foreign_deployments = token_configs - .iter() - .filter(|(_, token_config)| token_config.foreign_deployment.is_some()) - .map(|(chain_name, token_config)| { - let chain_config = chain_configs.get(chain_name).unwrap(); - ( - chain_config.domain_id(), - hex_or_base58_to_h256(token_config.foreign_deployment.as_ref().unwrap()) - .unwrap(), - ) - }) - .collect::>(); - - let mut routers: HashMap = foreign_deployments; - - let token_configs_to_deploy = token_configs - .clone() - .into_iter() - .filter(|(_, token_config)| token_config.foreign_deployment.is_none()) - .collect::>(); - - // Deploy to chains that don't have a foreign deployment - for (chain_name, token_config) in token_configs_to_deploy.iter() { - let chain_config = chain_configs - .get(chain_name) - .unwrap_or_else(|| panic!("Chain config not found for chain: {}", chain_name)); - - if token_config.ownable.owner.is_some() { - println!("WARNING: Ownership transfer is not yet supported in this deploy tooling, ownership is granted to the payer account"); - } - - let program_id = deploy_warp_route( - &mut ctx, - &keys_dir, - &deploy.environments_dir, - &deploy.environment, - &deploy.built_so_dir, - chain_config, - token_config, - deploy.ata_payer_funding_amount, - ); + let domain_id = chain_config.domain_id(); + + // TODO: consider pulling the setting of defaults into router.rs, + // and possibly have a more distinct connection client abstration. + + let mailbox = app_config + .router_config() + .connection_client + .mailbox(core_program_ids.mailbox); + let interchain_security_module = app_config + .router_config() + .connection_client + .interchain_security_module(); + let owner = Some(app_config.router_config().ownable.owner(ctx.payer_pubkey)); + + // Default to the Overhead IGP + let interchain_gas_paymaster = Some( + app_config + .router_config() + .connection_client + .interchain_gas_paymaster_config(client) + .unwrap_or(( + core_program_ids.igp_program_id, + InterchainGasPaymasterType::OverheadIgp(core_program_ids.overhead_igp_account), + )), + ); + + println!( + "Initializing Warp Route program: domain_id: {}, mailbox: {}, ism: {:?}, owner: {:?}, igp: {:?}", + domain_id, mailbox, interchain_security_module, owner, interchain_gas_paymaster + ); + + let init = Init { + mailbox, + interchain_security_module, + interchain_gas_paymaster, + decimals: app_config.decimal_metadata.decimals, + remote_decimals: app_config.decimal_metadata.remote_decimals(), + }; + + match &app_config.token_type { + TokenType::Native => ctx.new_txn().add( + hyperlane_sealevel_token_native::instruction::init_instruction( + program_id, + ctx.payer_pubkey, + init, + ) + .unwrap(), + ), + TokenType::Synthetic(_token_metadata) => { + let decimals = init.decimals; - routers.insert( - chain_config.domain_id(), - H256::from_slice(&program_id.to_bytes()[..]), + let init_txn = ctx.new_txn().add( + hyperlane_sealevel_token::instruction::init_instruction( + program_id, + ctx.payer_pubkey, + init, + ) + .unwrap(), ); - } - // Now enroll routers - for (chain_name, _) in token_configs_to_deploy { - let chain_config = chain_configs - .get(&chain_name) - .unwrap_or_else(|| panic!("Chain config not found for chain: {}", chain_name)); - - let domain_id = chain_config.domain_id(); - let program_id: Pubkey = - Pubkey::new_from_array(*routers.get(&domain_id).unwrap().as_fixed_bytes()); - - let enrolled_routers = get_routers(&chain_config.client(), &program_id).unwrap(); - - let expected_routers = routers - .iter() - .filter(|(router_domain_id, _)| *router_domain_id != &domain_id) - .map(|(domain, router)| { - ( - *domain, - RemoteRouterConfig { - domain: *domain, - router: Some(*router), - }, - ) - }) - .collect::>(); - - // Routers to enroll (or update to a Some value) - let routers_to_enroll = expected_routers - .iter() - .filter(|(domain, router_config)| { - enrolled_routers.get(domain) != router_config.router.as_ref() - }) - .map(|(_, router_config)| router_config.clone()); - - // Routers to remove - let routers_to_unenroll = enrolled_routers - .iter() - .filter(|(domain, _)| !expected_routers.contains_key(domain)) - .map(|(domain, _)| RemoteRouterConfig { - domain: *domain, - router: None, - }); - - // All router config changes - let router_configs = routers_to_enroll - .chain(routers_to_unenroll) - .collect::>(); - - if !router_configs.is_empty() { - println!( - "Enrolling routers for chain: {}, program_id {}, routers: {:?}", - chain_name, program_id, router_configs, - ); - - ctx.new_txn() - .add( - enroll_remote_routers_instruction( - program_id, - ctx.payer_pubkey, - router_configs, - ) - .unwrap(), - ) - .with_client(&chain_config.client()) - .send_with_payer(); - } else { - println!( - "No router changes for chain: {}, program_id {}", - chain_name, program_id - ); - } - - // And set destination gas - let configured_destination_gas = - get_destination_gas(&chain_config.client(), &program_id).unwrap(); - - let expected_destination_gas = token_configs - .iter() - // filter out local chain - .filter(|(dest_chain_name, _)| *dest_chain_name != &chain_name) - .map(|(dest_chain_name, token_config)| { - let domain = chain_configs.get(dest_chain_name).unwrap().domain_id(); - ( - domain, - GasRouterConfig { - domain, - gas: Some(token_config.token_type.gas_overhead_default()), - }, - ) - }) - .collect::>(); - - // Destination gas to set or update to a Some value - let destination_gas_to_set = expected_destination_gas - .iter() - .filter(|(domain, expected_config)| { - configured_destination_gas.get(domain) != expected_config.gas.as_ref() - }) - .map(|(_, expected_config)| expected_config.clone()); - - // Destination gas to remove - let destination_gas_to_unset = configured_destination_gas - .iter() - .filter(|(domain, _)| !expected_destination_gas.contains_key(domain)) - .map(|(domain, _)| GasRouterConfig { - domain: *domain, - gas: None, - }); - - // All destination gas config changes - let destination_gas_configs = destination_gas_to_set - .chain(destination_gas_to_unset) - .collect::>(); - - if !destination_gas_configs.is_empty() { - println!( - "Setting destination gas amounts for chain: {}, program_id {}, destination gas: {:?}", - chain_name, program_id, destination_gas_configs, - ); - - ctx.new_txn() - .add( - set_destination_gas_configs( - program_id, - ctx.payer_pubkey, - destination_gas_configs, - ) - .unwrap(), - ) - .with_client(&chain_config.client()) - .send_with_payer(); - } else { - println!( - "No destination gas amount changes for chain: {}, program_id {}", - chain_name, program_id - ); - } + let (mint_account, _mint_bump) = + Pubkey::find_program_address(hyperlane_token_mint_pda_seeds!(), &program_id); + // TODO: Also set Metaplex metadata? + init_txn.add( + spl_token_2022::instruction::initialize_mint2( + &spl_token_2022::id(), + &mint_account, + &mint_account, + None, + decimals, + ) + .unwrap(), + ) } + TokenType::Collateral(collateral_info) => ctx.new_txn().add( + hyperlane_sealevel_token_collateral::instruction::init_instruction( + program_id, + ctx.payer_pubkey, + init, + collateral_info + .spl_token_program + .as_ref() + .expect("Cannot initalize collateral warp route without SPL token program") + .program_id(), + collateral_info.mint.parse().expect("Invalid mint address"), + ) + .unwrap(), + ), + } + .with_client(client) + .send_with_payer(); + } - let routers_by_name: HashMap = routers + /// Sets gas router configs on all deployable chains. + fn post_deploy( + &self, + ctx: &mut Context, + app_configs: &HashMap, + app_configs_to_deploy: &HashMap<&String, &TokenConfig>, + chain_configs: &HashMap, + routers: &HashMap, + ) { + // Set gas amounts for each destination chain + for chain_name in app_configs_to_deploy.keys() { + let chain_config = chain_configs + .get(*chain_name) + .unwrap_or_else(|| panic!("Chain config not found for chain: {}", chain_name)); + + let domain_id = chain_config.domain_id(); + let program_id: Pubkey = + Pubkey::new_from_array(*routers.get(&domain_id).unwrap().as_fixed_bytes()); + + // And set destination gas + let configured_destination_gas = + get_destination_gas(&chain_config.client(), &program_id).unwrap(); + + let expected_destination_gas = app_configs .iter() - .map(|(domain_id, router)| { + // filter out local chain + .filter(|(dest_chain_name, _)| dest_chain_name != chain_name) + .map(|(dest_chain_name, app_config)| { + let domain = chain_configs.get(dest_chain_name).unwrap().domain_id(); ( - chain_configs - .iter() - .find(|(_, chain_config)| chain_config.domain_id() == *domain_id) - .unwrap() - .0 - .clone(), - *router, + domain, + GasRouterConfig { + domain, + gas: Some(app_config.token_type.gas_overhead_default()), + }, ) }) - .collect::>(); - write_program_ids(&warp_route_dir, &routers_by_name); - } - WarpRouteSubCmd::DestinationGas(args) => { - let destination_gas = get_destination_gas(&ctx.client, &args.program_id).unwrap(); - println!( - "Destination gas: {:?}", - destination_gas[&args.destination_domain] - ); + .collect::>(); + + // Destination gas to set or update to a Some value + let destination_gas_to_set = expected_destination_gas + .iter() + .filter(|(domain, expected_config)| { + configured_destination_gas.get(domain) != expected_config.gas.as_ref() + }) + .map(|(_, expected_config)| expected_config.clone()); + + // Destination gas to remove + let destination_gas_to_unset = configured_destination_gas + .iter() + .filter(|(domain, _)| !expected_destination_gas.contains_key(domain)) + .map(|(domain, _)| GasRouterConfig { + domain: *domain, + gas: None, + }); + + // All destination gas config changes + let destination_gas_configs = destination_gas_to_set + .chain(destination_gas_to_unset) + .collect::>(); + + if !destination_gas_configs.is_empty() { + let description = format!( + "Setting destination gas amounts for chain: {}, program_id {}, destination gas: {:?}", + chain_name, program_id, destination_gas_configs, + ); + ctx.new_txn() + .add_with_description( + set_destination_gas_configs( + program_id, + ctx.payer_pubkey, + destination_gas_configs, + ) + .unwrap(), + description, + ) + .with_client(&chain_config.client()) + .send_with_payer(); + } else { + println!( + "No destination gas amount changes for chain: {}, program_id {}", + chain_name, program_id + ); + } } } } -#[allow(clippy::too_many_arguments)] -fn deploy_warp_route( - ctx: &mut Context, - key_dir: &Path, - environments_dir: &Path, - environment: &str, - built_so_dir: &Path, - chain_config: &ChainMetadata, - token_config: &TokenConfig, - ata_payer_funding_amount: Option, -) -> Pubkey { - println!( - "Attempting deploy on chain: {}\nToken config: {:?}", - chain_config.name, token_config - ); +impl RouterConfigGetter for TokenConfig { + fn router_config(&self) -> &RouterConfig { + &self.router_config + } +} - let (keypair, keypair_path) = create_and_write_keypair( - key_dir, - format!( - "{}-{}.json", - token_config.token_type.program_name(), - chain_config.name - ) - .as_str(), - true, - ); - let program_id = keypair.pubkey(); - - deploy_program_idempotent( - ctx.payer_keypair_path(), - &keypair, - keypair_path.to_str().unwrap(), - built_so_dir - .join(format!("{}.so", token_config.token_type.program_name())) - .to_str() - .unwrap(), - &chain_config.public_rpc_urls[0].http, - ) - .unwrap(); - - let core_program_ids = read_core_program_ids(environments_dir, environment, &chain_config.name); - init_warp_route_idempotent( - ctx, - &chain_config.client(), - &core_program_ids, - chain_config, - token_config, - program_id, - ata_payer_funding_amount, - ) - .unwrap(); - - match &token_config.token_type { - TokenType::Native => { - println!("Deploying native token"); - } - TokenType::Synthetic(_token_metadata) => { - println!("Deploying synthetic token"); - } - TokenType::Collateral(_collateral_info) => { - println!("Deploying collateral token"); - } +impl Ownable for WarpRouteDeployer { + /// Gets the owner configured on-chain. + fn get_owner(&self, client: &RpcClient, program_id: &Pubkey) -> Option { + let token = get_token_data::<()>(client, program_id); + + token.owner } - program_id + /// Gets an instruction to set the owner. + fn set_owner_instruction( + &self, + client: &RpcClient, + program_id: &Pubkey, + new_owner: Option, + ) -> Instruction { + let token = get_token_data::<()>(client, program_id); + + transfer_ownership_instruction(*program_id, token.owner.unwrap(), new_owner).unwrap() + } } -fn init_warp_route_idempotent( - ctx: &mut Context, - client: &RpcClient, - core_program_ids: &CoreProgramIds, - _chain_config: &ChainMetadata, - token_config: &TokenConfig, - program_id: Pubkey, - ata_payer_funding_amount: Option, -) -> Result<(), ProgramError> { - let (token_pda, _token_bump) = - Pubkey::find_program_address(hyperlane_token_pda_seeds!(), &program_id); - - if let Some(ata_payer_funding_amount) = ata_payer_funding_amount { - if matches!( - token_config.token_type, - TokenType::Collateral(_) | TokenType::Synthetic(_) - ) { - fund_ata_payer_up_to(ctx, client, program_id, ata_payer_funding_amount); - } +impl ConnectionClient for WarpRouteDeployer { + fn get_interchain_security_module( + &self, + client: &RpcClient, + program_id: &Pubkey, + ) -> Option { + let token_data = get_token_data::<()>(client, program_id); + + token_data.interchain_security_module } - if account_exists(client, &token_pda).unwrap() { - println!("Token PDA already exists, skipping init"); - return Ok(()); + fn set_interchain_security_module_instruction( + &self, + client: &RpcClient, + program_id: &Pubkey, + ism: Option, + ) -> Instruction { + let token_data = get_token_data::<()>(client, program_id); + + set_interchain_security_module_instruction(*program_id, token_data.owner.unwrap(), ism) + .unwrap() } +} + +fn get_token_data(client: &RpcClient, program_id: &Pubkey) -> HyperlaneToken +where + T: BorshDeserialize + BorshSerialize + Default + account_utils::Data, +{ + let (token_pda, _token_bump) = + Pubkey::find_program_address(hyperlane_token_pda_seeds!(), program_id); + + let account = client.get_account(&token_pda).unwrap(); + *HyperlaneTokenAccount::::fetch(&mut &account.data[..]) + .unwrap() + .into_inner() +} + +fn get_destination_gas( + client: &RpcClient, + program_id: &Pubkey, +) -> Result, ClientError> { + let token_data = get_token_data::<()>(client, program_id); - init_warp_route( - ctx, - client, - core_program_ids, - _chain_config, - token_config, - program_id, - ) + Ok(token_data.destination_gas) } +// Funds the ATA payer up to the specified amount. fn fund_ata_payer_up_to( ctx: &mut Context, client: &RpcClient, @@ -520,168 +479,42 @@ fn fund_ata_payer_up_to( return; } - println!( - "Funding ATA payer {} with funding_amount {} to reach total balance of {}", - ata_payer_account, funding_amount, ata_payer_funding_amount - ); ctx.new_txn() - .add(solana_program::system_instruction::transfer( - &ctx.payer_pubkey, - &ata_payer_account, - funding_amount, - )) + .add_with_description( + solana_program::system_instruction::transfer( + &ctx.payer_pubkey, + &ata_payer_account, + funding_amount, + ), + format!( + "Funding ATA payer {} with funding_amount {} to reach total balance of {}", + ata_payer_account, funding_amount, ata_payer_funding_amount + ), + ) .with_client(client) .send_with_payer(); } -fn init_warp_route( - ctx: &mut Context, - client: &RpcClient, - core_program_ids: &CoreProgramIds, - _chain_config: &ChainMetadata, - token_config: &TokenConfig, - program_id: Pubkey, -) -> Result<(), ProgramError> { - // If the Mailbox was provided as configuration, use that. Otherwise, default to - // the Mailbox found in the core program ids. - let mailbox = token_config - .connection_client - .mailbox - .as_ref() - .map(|s| Pubkey::from_str(s).unwrap()) - .unwrap_or(core_program_ids.mailbox); - - // TODO for now not specifying an IGP for compatibility with the warp route UI. - - // let interchain_gas_paymaster = Some(token_config - // .connection_client - // .interchain_gas_paymaster - // .clone() - // .map(|config| (config.program_id, config.igp_account)) - // .unwrap_or(( - // core_program_ids.igp_program_id, - // InterchainGasPaymasterType::OverheadIgp(core_program_ids.overhead_igp_account), - // )) - // ); - - let interchain_gas_paymaster = None; - - let init = Init { - mailbox, - interchain_security_module: token_config - .connection_client - .interchain_security_module - .as_ref() - .map(|s| Pubkey::from_str(s).unwrap()), - interchain_gas_paymaster, - decimals: token_config.decimal_metadata.decimals, - remote_decimals: token_config.decimal_metadata.remote_decimals(), - }; - - match &token_config.token_type { - TokenType::Native => ctx.new_txn().add( - hyperlane_sealevel_token_native::instruction::init_instruction( - program_id, - ctx.payer_pubkey, - init, - )?, - ), - TokenType::Synthetic(_token_metadata) => { - let decimals = init.decimals; - - let init_txn = - ctx.new_txn() - .add(hyperlane_sealevel_token::instruction::init_instruction( - program_id, - ctx.payer_pubkey, - init, - )?); - - let (mint_account, _mint_bump) = - Pubkey::find_program_address(hyperlane_token_mint_pda_seeds!(), &program_id); - // TODO: Also set Metaplex metadata? - init_txn.add( - spl_token_2022::instruction::initialize_mint2( - &spl_token_2022::id(), - &mint_account, - &mint_account, - None, - decimals, - ) - .unwrap(), - ) +pub fn parse_token_account_data(token_type: FlatTokenType, data: &mut &[u8]) { + fn print_data_or_err(data: Result) { + match data { + Ok(data) => println!("{:#?}", data), + Err(err) => println!("Failed to deserialize account data: {}", err), } - TokenType::Collateral(collateral_info) => ctx.new_txn().add( - hyperlane_sealevel_token_collateral::instruction::init_instruction( - program_id, - ctx.payer_pubkey, - init, - collateral_info - .spl_token_program - .as_ref() - .expect("Cannot initalize collateral warp route without SPL token program") - .program_id(), - collateral_info.mint.parse().expect("Invalid mint address"), - )?, - ), } - .with_client(client) - .send_with_payer(); - - Ok(()) -} - -fn get_routers( - client: &RpcClient, - token_program_id: &Pubkey, -) -> Result, ClientError> { - let (token_pda, _token_bump) = - Pubkey::find_program_address(hyperlane_token_pda_seeds!(), token_program_id); - - let account = client.get_account(&token_pda)?; - let token_data = HyperlaneTokenAccount::<()>::fetch(&mut &account.data[..]) - .unwrap() - .into_inner(); - - Ok(token_data.remote_routers) -} - -fn get_destination_gas( - client: &RpcClient, - token_program_id: &Pubkey, -) -> Result, ClientError> { - let (token_pda, _token_bump) = - Pubkey::find_program_address(hyperlane_token_pda_seeds!(), token_program_id); - let account = client.get_account(&token_pda)?; - let token_data = HyperlaneTokenAccount::<()>::fetch(&mut &account.data[..]) - .unwrap() - .into_inner(); - - Ok(token_data.destination_gas) -} - -#[derive(Serialize, Deserialize)] -struct SerializedProgramId { - hex: String, - base58: String, -} - -fn write_program_ids(warp_route_dir: &Path, routers: &HashMap) { - let serialized_program_ids = routers - .iter() - .map(|(chain_name, router)| { - ( - chain_name.clone(), - SerializedProgramId { - hex: format!("0x{}", hex::encode(router)), - base58: Pubkey::new_from_array(router.to_fixed_bytes()).to_string(), - }, - ) - }) - .collect::>(); - - let program_ids_file = warp_route_dir.join("program-ids.json"); - let program_ids_file = File::create(program_ids_file).unwrap(); - serde_json::to_writer_pretty(program_ids_file, &serialized_program_ids).unwrap(); + match token_type { + FlatTokenType::Native => { + let res = HyperlaneTokenAccount::::fetch(data); + print_data_or_err(res); + } + FlatTokenType::Synthetic => { + let res = HyperlaneTokenAccount::::fetch(data); + print_data_or_err(res); + } + FlatTokenType::Collateral => { + let res = HyperlaneTokenAccount::::fetch(data); + print_data_or_err(res); + } + } } diff --git a/rust/sealevel/environments/devnet/solanadevnet/core/keys/hyperlane_sealevel_mailbox-keypair.json b/rust/sealevel/environments/devnet/solanadevnet/core/keys/hyperlane_sealevel_mailbox-keypair.json deleted file mode 100644 index 484e2fb18..000000000 --- a/rust/sealevel/environments/devnet/solanadevnet/core/keys/hyperlane_sealevel_mailbox-keypair.json +++ /dev/null @@ -1 +0,0 @@ -[113,244,152,170,85,122,42,51,10,74,244,18,91,8,135,77,156,19,172,122,139,50,248,3,186,184,186,140,110,165,78,161,76,88,146,213,185,127,121,92,132,2,249,73,19,192,73,170,105,85,247,241,48,175,67,28,165,29,224,252,173,165,38,140] \ No newline at end of file diff --git a/rust/sealevel/environments/devnet/solanadevnet/core/keys/hyperlane_sealevel_multisig_ism_message_id-keypair.json b/rust/sealevel/environments/devnet/solanadevnet/core/keys/hyperlane_sealevel_multisig_ism_message_id-keypair.json deleted file mode 100644 index 243fcbd9a..000000000 --- a/rust/sealevel/environments/devnet/solanadevnet/core/keys/hyperlane_sealevel_multisig_ism_message_id-keypair.json +++ /dev/null @@ -1 +0,0 @@ -[135,153,145,193,50,88,169,205,206,171,48,1,17,242,3,43,225,72,101,163,93,126,105,165,159,44,243,196,182,240,4,87,22,253,47,198,217,75,23,60,181,129,251,103,140,170,111,35,152,97,16,23,64,17,198,239,79,225,120,141,55,38,60,86] \ No newline at end of file diff --git a/rust/sealevel/environments/devnet/solanadevnet/core/keys/hyperlane_sealevel_validator_announce-keypair.json b/rust/sealevel/environments/devnet/solanadevnet/core/keys/hyperlane_sealevel_validator_announce-keypair.json deleted file mode 100644 index 3428c9c49..000000000 --- a/rust/sealevel/environments/devnet/solanadevnet/core/keys/hyperlane_sealevel_validator_announce-keypair.json +++ /dev/null @@ -1 +0,0 @@ -[252,76,67,201,250,68,86,32,216,136,163,46,192,20,249,175,209,94,101,235,24,240,204,4,246,159,180,138,253,20,48,146,182,104,250,124,231,168,239,248,95,199,219,250,126,156,57,113,83,209,232,171,10,90,153,238,72,138,186,34,77,87,172,211] \ No newline at end of file diff --git a/rust/sealevel/environments/devnet/solanadevnet/core/program-ids.json b/rust/sealevel/environments/devnet/solanadevnet/core/program-ids.json deleted file mode 100644 index a9acf6dfd..000000000 --- a/rust/sealevel/environments/devnet/solanadevnet/core/program-ids.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "mailbox": "692KZJaoe2KRcD6uhCQDLLXnLNA5ZLnfvdqjE4aX9iu1", - "validator_announce": "DH43ae1LwemXAboWwSh8zc9pG8j72gKUEXNi57w8fEnn", - "multisig_ism_message_id": "2YjtZDiUoptoSsA5eVrDCcX6wxNK6YoEVW7y82x5Z2fw" -} \ No newline at end of file diff --git a/rust/sealevel/environments/devnet/solanadevnet1/core/keys/hyperlane_sealevel_mailbox-keypair.json b/rust/sealevel/environments/devnet/solanadevnet1/core/keys/hyperlane_sealevel_mailbox-keypair.json deleted file mode 100644 index 31a9bdeb3..000000000 --- a/rust/sealevel/environments/devnet/solanadevnet1/core/keys/hyperlane_sealevel_mailbox-keypair.json +++ /dev/null @@ -1 +0,0 @@ -[6,132,236,247,134,194,48,96,56,63,48,146,121,215,228,1,80,199,79,128,232,145,31,24,170,246,162,253,52,12,244,198,141,84,17,12,114,138,14,65,37,185,65,155,156,209,188,73,159,63,157,69,158,103,155,16,217,78,19,53,6,226,115,117] \ No newline at end of file diff --git a/rust/sealevel/environments/devnet/solanadevnet1/core/keys/hyperlane_sealevel_multisig_ism_message_id-keypair.json b/rust/sealevel/environments/devnet/solanadevnet1/core/keys/hyperlane_sealevel_multisig_ism_message_id-keypair.json deleted file mode 100644 index 4e8c405ce..000000000 --- a/rust/sealevel/environments/devnet/solanadevnet1/core/keys/hyperlane_sealevel_multisig_ism_message_id-keypair.json +++ /dev/null @@ -1 +0,0 @@ -[75,122,156,10,143,146,130,96,41,203,245,228,178,140,170,105,167,226,18,171,187,4,70,210,1,234,232,194,206,26,65,248,243,199,245,54,127,196,31,152,114,133,16,172,1,103,105,249,111,240,129,216,26,184,14,131,242,197,189,46,163,142,2,120] \ No newline at end of file diff --git a/rust/sealevel/environments/devnet/solanadevnet1/core/keys/hyperlane_sealevel_validator_announce-keypair.json b/rust/sealevel/environments/devnet/solanadevnet1/core/keys/hyperlane_sealevel_validator_announce-keypair.json deleted file mode 100644 index 153a856ce..000000000 --- a/rust/sealevel/environments/devnet/solanadevnet1/core/keys/hyperlane_sealevel_validator_announce-keypair.json +++ /dev/null @@ -1 +0,0 @@ -[60,166,246,212,217,15,197,101,188,59,172,187,217,44,158,58,65,180,5,179,193,73,206,199,134,54,56,70,26,169,141,82,49,9,182,63,146,255,211,243,158,55,120,3,60,23,151,134,195,85,195,50,62,205,7,162,107,106,40,106,220,117,82,91] \ No newline at end of file diff --git a/rust/sealevel/environments/devnet/solanadevnet1/core/program-ids.json b/rust/sealevel/environments/devnet/solanadevnet1/core/program-ids.json deleted file mode 100644 index 105a160e3..000000000 --- a/rust/sealevel/environments/devnet/solanadevnet1/core/program-ids.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "mailbox": "AWgqPcY1vjHRoFLHNgs15fdvy4bqEakHmYXW78B8GgYk", - "validator_announce": "4JRZrYJnXJn6KPSCG4tA6GBomP2zwQv8bD65anWnHmNz", - "multisig_ism_message_id": "HQcv2ibNRuJdHU8Lt9t655YUjXj4Rp9nW8mbcA26cYqM" -} \ No newline at end of file diff --git a/rust/sealevel/environments/devnet/warp-routes/chain-config.json b/rust/sealevel/environments/devnet/warp-routes/chain-config.json deleted file mode 100644 index 96691fdff..000000000 --- a/rust/sealevel/environments/devnet/warp-routes/chain-config.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "solanadevnet": { - "chainId": 13375, - "name": "solanadevnet", - "publicRpcUrls": [ - { - "http": "https://api.devnet.solana.com" - } - ] - }, - "solanadevnet1": { - "chainId": 13376, - "name": "solanadevnet1", - "publicRpcUrls": [ - { - "http": "https://api.devnet.solana.com" - } - ] - }, - "fuji": { - "chainId": 43113, - "name": "fuji", - "publicRpcUrls": [ - { - "http": "https://api.avax-test.network/ext/bc/C/rpc" - } - ] - } -} \ No newline at end of file diff --git a/rust/sealevel/environments/devnet/warp-routes/collateraltest/keys/hyperlane_sealevel_token-solanadevnet1.json b/rust/sealevel/environments/devnet/warp-routes/collateraltest/keys/hyperlane_sealevel_token-solanadevnet1.json deleted file mode 100644 index 7ef8d00db..000000000 --- a/rust/sealevel/environments/devnet/warp-routes/collateraltest/keys/hyperlane_sealevel_token-solanadevnet1.json +++ /dev/null @@ -1 +0,0 @@ -[234,24,62,69,201,85,23,105,162,73,39,96,54,24,252,131,65,61,204,71,240,230,98,153,79,12,102,57,135,254,59,159,71,62,216,28,221,183,176,75,40,167,248,151,145,3,242,74,196,153,147,167,98,202,124,87,70,27,115,81,78,50,199,68] \ No newline at end of file diff --git a/rust/sealevel/environments/devnet/warp-routes/collateraltest/keys/hyperlane_sealevel_token_collateral-solanadevnet.json b/rust/sealevel/environments/devnet/warp-routes/collateraltest/keys/hyperlane_sealevel_token_collateral-solanadevnet.json deleted file mode 100644 index 5bd77b561..000000000 --- a/rust/sealevel/environments/devnet/warp-routes/collateraltest/keys/hyperlane_sealevel_token_collateral-solanadevnet.json +++ /dev/null @@ -1 +0,0 @@ -[39,54,74,37,67,213,195,70,204,38,146,230,111,25,95,162,197,128,223,145,57,112,78,217,51,236,68,252,254,70,26,37,135,224,112,242,167,101,9,162,147,37,98,70,138,147,6,126,136,247,145,107,228,139,68,251,82,120,107,18,4,102,190,221] \ No newline at end of file diff --git a/rust/sealevel/environments/devnet/warp-routes/collateraltest/program-ids.json b/rust/sealevel/environments/devnet/warp-routes/collateraltest/program-ids.json deleted file mode 100644 index 0a7a1eab0..000000000 --- a/rust/sealevel/environments/devnet/warp-routes/collateraltest/program-ids.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "solanadevnet1": { - "hex": "0x473ed81cddb7b04b28a7f8979103f24ac49993a762ca7c57461b73514e32c744", - "base58": "5o7XXLy8N67cgCjSt4zKNnzAbDRpkVruu8BbpPNehBrK" - }, - "fuji": { - "hex": "0x000000000000000000000000b3af04fc8b461138eca4f5fc1d5955bbe6d20fca", - "base58": "1111111111113WC5zqqJzmcsiZZcai6ZxbvW84do" - }, - "solanadevnet": { - "hex": "0x87e070f2a76509a2932562468a93067e88f7916be48b44fb52786b120466bedd", - "base58": "A9QY6ZQ3t1T3Pk58gTx1vsSeH2B2AywwK2V7SpH2w2cC" - } -} \ No newline at end of file diff --git a/rust/sealevel/environments/devnet/warp-routes/collateraltest/token-config.json b/rust/sealevel/environments/devnet/warp-routes/collateraltest/token-config.json deleted file mode 100644 index d63f7a11e..000000000 --- a/rust/sealevel/environments/devnet/warp-routes/collateraltest/token-config.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "solanadevnet": { - "type": "collateral", - "token": "Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr", - "splTokenProgram": "token", - "decimals": 6, - "name": "USD Coin Dev", - "symbol": "USDC" - }, - "solanadevnet1": { - "type": "synthetic", - "decimals": 6, - "name": "USD Coin Dev", - "symbol": "USDC" - }, - "fuji": { - "type": "synthetic", - "decimals": 6, - "name": "USD Coin Dev", - "symbol": "USDC", - "foreignDeployment": "0xb3AF04Fc8b461138eCA4F5fC1D5955Bbe6D20Fca" - } -} \ No newline at end of file diff --git a/rust/sealevel/environments/devnet/warp-routes/nativetest/keys/hyperlane_sealevel_token-solanadevnet1.json b/rust/sealevel/environments/devnet/warp-routes/nativetest/keys/hyperlane_sealevel_token-solanadevnet1.json deleted file mode 100644 index 85b4ecd61..000000000 --- a/rust/sealevel/environments/devnet/warp-routes/nativetest/keys/hyperlane_sealevel_token-solanadevnet1.json +++ /dev/null @@ -1 +0,0 @@ -[108,3,96,252,94,52,97,146,193,61,252,4,209,156,21,178,42,234,170,70,97,252,167,156,146,209,57,48,52,224,211,72,40,108,141,225,165,106,19,22,48,134,115,13,111,173,228,116,229,5,197,167,246,245,139,19,60,75,183,152,49,124,190,33] \ No newline at end of file diff --git a/rust/sealevel/environments/devnet/warp-routes/nativetest/keys/hyperlane_sealevel_token_native-solanadevnet.json b/rust/sealevel/environments/devnet/warp-routes/nativetest/keys/hyperlane_sealevel_token_native-solanadevnet.json deleted file mode 100644 index b9d79c759..000000000 --- a/rust/sealevel/environments/devnet/warp-routes/nativetest/keys/hyperlane_sealevel_token_native-solanadevnet.json +++ /dev/null @@ -1 +0,0 @@ -[169,25,77,166,171,9,74,84,180,104,209,80,36,170,223,85,56,255,50,104,185,250,53,188,65,168,235,7,176,81,99,182,113,69,170,191,248,224,191,67,181,13,107,166,133,126,157,101,165,157,24,202,25,96,195,132,107,100,86,78,48,232,7,142] \ No newline at end of file diff --git a/rust/sealevel/environments/devnet/warp-routes/nativetest/program-ids.json b/rust/sealevel/environments/devnet/warp-routes/nativetest/program-ids.json deleted file mode 100644 index 811f6d7ec..000000000 --- a/rust/sealevel/environments/devnet/warp-routes/nativetest/program-ids.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "solanadevnet": { - "hex": "0x7145aabff8e0bf43b50d6ba6857e9d65a59d18ca1960c3846b64564e30e8078e", - "base58": "8dAgevgBAnhvxoB5mWNUfmXi6H8WLC3ZaP8poaRHkzaR" - }, - "solanadevnet1": { - "hex": "0x286c8de1a56a13163086730d6fade474e505c5a7f6f58b133c4bb798317cbe21", - "base58": "3ioKCgR4pyrtkJvsB3zketopnR3mqjjBszSgtiQXQz7i" - }, - "fuji": { - "hex": "0x00000000000000000000000011cf63c916263d6bbd710f43816ee6703e1c5da3", - "base58": "111111111111FPh8wLMbV6vLtqUcvxKR496PAXL" - } -} \ No newline at end of file diff --git a/rust/sealevel/environments/devnet/warp-routes/nativetest/token-config.json b/rust/sealevel/environments/devnet/warp-routes/nativetest/token-config.json deleted file mode 100644 index 712b1f31a..000000000 --- a/rust/sealevel/environments/devnet/warp-routes/nativetest/token-config.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "solanadevnet": { - "type": "native", - "decimals": 9 - }, - "solanadevnet1": { - "type": "synthetic", - "decimals": 9, - "name": "Solana (solanadevnet)", - "symbol": "SOL" - }, - "fuji": { - "type": "synthetic", - "decimals": 9, - "name": "Solana (solanadevnet)", - "symbol": "SOL", - "foreignDeployment": "0x11CF63c916263d6BBD710F43816ee6703e1C5da3" - } -} \ No newline at end of file diff --git a/rust/sealevel/environments/local-e2e/warp-routes/chain-config.json b/rust/sealevel/environments/local-e2e/chain-config.json similarity index 85% rename from rust/sealevel/environments/local-e2e/warp-routes/chain-config.json rename to rust/sealevel/environments/local-e2e/chain-config.json index 5914c12d6..eadbdf940 100644 --- a/rust/sealevel/environments/local-e2e/warp-routes/chain-config.json +++ b/rust/sealevel/environments/local-e2e/chain-config.json @@ -2,7 +2,7 @@ "sealeveltest1": { "chainId": 13375, "name": "sealeveltest1", - "publicRpcUrls": [ + "rpcUrls": [ { "http": "http://localhost:8899" } @@ -11,7 +11,7 @@ "sealeveltest2": { "chainId": 13376, "name": "sealeveltest2", - "publicRpcUrls": [ + "rpcUrls": [ { "http": "http://localhost:8899" } diff --git a/rust/sealevel/environments/mainnet2/chain-config.json b/rust/sealevel/environments/mainnet2/chain-config.json new file mode 100644 index 000000000..c8c314c77 --- /dev/null +++ b/rust/sealevel/environments/mainnet2/chain-config.json @@ -0,0 +1,350 @@ +{ + "bsc": { + "chainId": 56, + "domainId": 56, + "name": "bsc", + "protocol": "ethereum", + "displayName": "Binance Smart Chain", + "displayNameShort": "Binance", + "nativeToken": { + "decimals": 18, + "name": "BNB", + "symbol": "BNB" + }, + "rpcUrls": [ + { + "http": "https://bsc-dataseed.binance.org" + }, + { + "http": "https://rpc.ankr.com/bsc" + } + ], + "blockExplorers": [ + { + "name": "BscScan", + "url": "https://bscscan.com", + "apiUrl": "https://api.bscscan.com/api", + "family": "etherscan" + } + ], + "blocks": { + "confirmations": 1, + "reorgPeriod": 15, + "estimateBlockTime": 3 + }, + "gasCurrencyCoinGeckoId": "binancecoin", + "gnosisSafeTransactionServiceUrl": "https://safe-transaction-bsc.safe.global/", + "transactionOverrides": { + "gasPrice": 7000000000 + } + }, + "avalanche": { + "chainId": 43114, + "domainId": 43114, + "name": "avalanche", + "protocol": "ethereum", + "displayName": "Avalanche", + "nativeToken": { + "decimals": 18, + "name": "Avalanche", + "symbol": "AVAX" + }, + "rpcUrls": [ + { + "http": "https://api.avax.network/ext/bc/C/rpc", + "pagination": { + "maxBlockRange": 100000, + "minBlockNumber": 6765067 + } + } + ], + "blockExplorers": [ + { + "name": "SnowTrace", + "url": "https://snowtrace.io", + "apiUrl": "https://api.snowtrace.io/api", + "family": "other" + } + ], + "blocks": { + "confirmations": 3, + "reorgPeriod": 3, + "estimateBlockTime": 2 + }, + "gasCurrencyCoinGeckoId": "avalanche-2", + "gnosisSafeTransactionServiceUrl": "https://safe-transaction-avalanche.safe.global/" + }, + "polygon": { + "chainId": 137, + "domainId": 137, + "name": "polygon", + "protocol": "ethereum", + "displayName": "Polygon", + "nativeToken": { + "name": "Ether", + "symbol": "ETH", + "decimals": 18 + }, + "rpcUrls": [ + { + "http": "https://rpc-mainnet.matic.quiknode.pro", + "pagination": { + "maxBlockRange": 10000, + "minBlockNumber": 19657100 + } + }, + { + "http": "https://polygon-rpc.com" + } + ], + "blockExplorers": [ + { + "name": "PolygonScan", + "url": "https://polygonscan.com", + "apiUrl": "https://api.polygonscan.com/api", + "family": "etherscan" + } + ], + "blocks": { + "confirmations": 3, + "reorgPeriod": 256, + "estimateBlockTime": 2 + }, + "gasCurrencyCoinGeckoId": "matic-network", + "gnosisSafeTransactionServiceUrl": "https://safe-transaction-polygon.safe.global/", + "transactionOverrides": { + "maxFeePerGas": 500000000000, + "maxPriorityFeePerGas": 100000000000 + } + }, + "celo": { + "chainId": 42220, + "domainId": 42220, + "name": "celo", + "protocol": "ethereum", + "displayName": "Celo", + "nativeToken": { + "decimals": 18, + "name": "CELO", + "symbol": "CELO" + }, + "rpcUrls": [ + { + "http": "https://forno.celo.org" + } + ], + "blockExplorers": [ + { + "name": "CeloScan", + "url": "https://celoscan.io", + "apiUrl": "https://api.celoscan.io/api", + "family": "etherscan" + }, + { + "name": "Blockscout", + "url": "https://explorer.celo.org", + "apiUrl": "https://explorer.celo.org/mainnet/api", + "family": "blockscout" + } + ], + "blocks": { + "confirmations": 1, + "reorgPeriod": 0, + "estimateBlockTime": 5 + }, + "gnosisSafeTransactionServiceUrl": "https://mainnet-tx-svc.celo-safe-prod.celo-networks-dev.org/" + }, + "arbitrum": { + "chainId": 42161, + "domainId": 42161, + "name": "arbitrum", + "protocol": "ethereum", + "displayName": "Arbitrum", + "nativeToken": { + "name": "Ether", + "symbol": "ETH", + "decimals": 18 + }, + "rpcUrls": [ + { + "http": "https://arb1.arbitrum.io/rpc" + } + ], + "blockExplorers": [ + { + "name": "Arbiscan", + "url": "https://arbiscan.io", + "apiUrl": "https://api.arbiscan.io/api", + "family": "etherscan" + } + ], + "blocks": { + "confirmations": 1, + "reorgPeriod": 0, + "estimateBlockTime": 3 + }, + "gasCurrencyCoinGeckoId": "ethereum", + "gnosisSafeTransactionServiceUrl": "https://safe-transaction-arbitrum.safe.global/" + }, + "optimism": { + "chainId": 10, + "domainId": 10, + "name": "optimism", + "protocol": "ethereum", + "displayName": "Optimism", + "nativeToken": { + "name": "Ether", + "symbol": "ETH", + "decimals": 18 + }, + "rpcUrls": [ + { + "http": "https://mainnet.optimism.io" + } + ], + "blockExplorers": [ + { + "name": "Etherscan", + "url": "https://optimistic.etherscan.io", + "apiUrl": "https://api-optimistic.etherscan.io/api", + "family": "etherscan" + } + ], + "blocks": { + "confirmations": 1, + "reorgPeriod": 0, + "estimateBlockTime": 3 + }, + "gasCurrencyCoinGeckoId": "ethereum", + "gnosisSafeTransactionServiceUrl": "https://safe-transaction-optimism.safe.global/" + }, + "ethereum": { + "chainId": 1, + "domainId": 1, + "name": "ethereum", + "protocol": "ethereum", + "displayName": "Ethereum", + "nativeToken": { + "name": "Ether", + "symbol": "ETH", + "decimals": 18 + }, + "rpcUrls": [ + { + "http": "https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161" + }, + { + "http": "https://cloudflare-eth.com" + } + ], + "blockExplorers": [ + { + "name": "Etherscan", + "url": "https://etherscan.io", + "apiUrl": "https://api.etherscan.io/api", + "family": "etherscan" + }, + { + "name": "Blockscout", + "url": "https://blockscout.com/eth/mainnet", + "apiUrl": "https://blockscout.com/eth/mainnet/api", + "family": "blockscout" + } + ], + "blocks": { + "confirmations": 3, + "reorgPeriod": 14, + "estimateBlockTime": 13 + }, + "gnosisSafeTransactionServiceUrl": "https://safe-transaction-mainnet.safe.global/", + "transactionOverrides": { + "maxFeePerGas": 150000000000, + "maxPriorityFeePerGas": 5000000000 + } + }, + "moonbeam": { + "chainId": 1284, + "domainId": 1284, + "name": "moonbeam", + "protocol": "ethereum", + "displayName": "Moonbeam", + "nativeToken": { + "decimals": 18, + "name": "GLMR", + "symbol": "GLMR" + }, + "rpcUrls": [ + { + "http": "https://rpc.api.moonbeam.network" + } + ], + "blockExplorers": [ + { + "name": "MoonScan", + "url": "https://moonscan.io", + "apiUrl": "https://api-moonbeam.moonscan.io/api", + "family": "etherscan" + } + ], + "blocks": { + "confirmations": 2, + "reorgPeriod": 2, + "estimateBlockTime": 12 + }, + "gnosisSafeTransactionServiceUrl": "https://transaction.multisig.moonbeam.network" + }, + "gnosis": { + "chainId": 100, + "domainId": 100, + "name": "gnosis", + "protocol": "ethereum", + "displayName": "Gnosis", + "nativeToken": { + "name": "xDai", + "symbol": "xDai", + "decimals": 18 + }, + "rpcUrls": [ + { + "http": "https://rpc.gnosischain.com", + "pagination": { + "maxBlockRange": 10000, + "minBlockNumber": 25997478 + } + } + ], + "blockExplorers": [ + { + "name": "GnosisScan", + "url": "https://gnosisscan.io", + "apiUrl": "https://api.gnosisscan.io/api", + "family": "etherscan" + } + ], + "blocks": { + "confirmations": 1, + "reorgPeriod": 14, + "estimateBlockTime": 5 + }, + "gasCurrencyCoinGeckoId": "xdai", + "gnosisSafeTransactionServiceUrl": "https://safe-transaction-gnosis-chain.safe.global/" + }, + "solana": { + "chainId": 1399811149, + "name": "solana", + "rpcUrls": [ + { + "http": "https://api.mainnet-beta.solana.com" + } + ] + }, + "nautilus": { + "chainId": 22222, + "name": "nautilus", + "rpcUrls": [ + { + "http": "https://api.nautilus.nautchain.xyz" + } + ] + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet2/helloworld/hyperlane/helloworld-config.json b/rust/sealevel/environments/mainnet2/helloworld/hyperlane/helloworld-config.json new file mode 100644 index 000000000..42d35e1bc --- /dev/null +++ b/rust/sealevel/environments/mainnet2/helloworld/hyperlane/helloworld-config.json @@ -0,0 +1,30 @@ +{ + "solana": {}, + "bsc": { + "foreignDeployment": "0xB97d3bF2fC296c2cAC4056bBC8A783ff39408e20" + }, + "avalanche": { + "foreignDeployment": "0x2A925CD8a5d919c5c6599633090c37fe38A561b6" + }, + "polygon": { + "foreignDeployment": "0x6c0aC8cEA75232aa7BeD8cbe9C4f820E7a77a9C3" + }, + "celo": { + "foreignDeployment": "0x4151773Db70C0b2D4c43Ea44A5FB5803ff1d3e0B" + }, + "arbitrum": { + "foreignDeployment": "0x96271cA0ab9eeFB3Ca481749c0Ca4c705fD4F523" + }, + "optimism": { + "foreignDeployment": "0xA6f0A37DFDe9C2c8F46F010989C47d9edB3a9FA8" + }, + "ethereum": { + "foreignDeployment": "0x9311cEE522A7C122B843b66cC31C6a63e2F92641" + }, + "moonbeam": { + "foreignDeployment": "0xAe067C08703508230357025B38c35Cd12793628c" + }, + "gnosis": { + "foreignDeployment": "0x26f32245fCF5Ad53159E875d5Cae62aEcf19c2d4" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet2/helloworld/hyperlane/program-ids.json b/rust/sealevel/environments/mainnet2/helloworld/hyperlane/program-ids.json new file mode 100644 index 000000000..24dbe8648 --- /dev/null +++ b/rust/sealevel/environments/mainnet2/helloworld/hyperlane/program-ids.json @@ -0,0 +1,42 @@ +{ + "moonbeam": { + "hex": "0x000000000000000000000000ae067c08703508230357025b38c35cd12793628c", + "base58": "1111111111113RcuHPfDctyAnFWHvj8tS1q8UHPh" + }, + "bsc": { + "hex": "0x000000000000000000000000b97d3bf2fc296c2cac4056bbc8a783ff39408e20", + "base58": "1111111111113atAoP8gQ2GYeue77ETUPAf8w9zw" + }, + "optimism": { + "hex": "0x000000000000000000000000a6f0a37dfde9c2c8f46f010989c47d9edb3a9fa8", + "base58": "1111111111113KtqevvpYv7NCiadmp6tRRfivB8K" + }, + "avalanche": { + "hex": "0x0000000000000000000000002a925cd8a5d919c5c6599633090c37fe38a561b6", + "base58": "111111111111bQB6b7XVDHSyvi7XmLrQMT8C3xH" + }, + "ethereum": { + "hex": "0x0000000000000000000000009311cee522a7c122b843b66cc31c6a63e2f92641", + "base58": "11111111111133qb6DzNiJ7whNaYGud2WqqtjxFS" + }, + "solana": { + "hex": "0x3797d0096b18b5b645c346a66d7f18c6c5738782c6bce24da57a3462bdef82b1", + "base58": "4k1gruSdH1r57V9QQK4aunzfMYzLFfF83jdYkkEwyem6" + }, + "celo": { + "hex": "0x0000000000000000000000004151773db70c0b2d4c43ea44a5fb5803ff1d3e0b", + "base58": "111111111111unDVQcjdeHntE83qvf1vsKCZ4av" + }, + "polygon": { + "hex": "0x0000000000000000000000006c0ac8cea75232aa7bed8cbe9c4f820e7a77a9c3", + "base58": "1111111111112WJXE3PCAsCXYZxU9Kh51sSZEa5G" + }, + "arbitrum": { + "hex": "0x00000000000000000000000096271ca0ab9eefb3ca481749c0ca4c705fd4f523", + "base58": "11111111111136L61X7cdT9tPZ4GKBtzJtrjFAd8" + }, + "gnosis": { + "hex": "0x00000000000000000000000026f32245fcf5ad53159e875d5cae62aecf19c2d4", + "base58": "111111111111YURfyMRiiTWy8X6pYHAqmYPmBpf" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet2/helloworld/rc/helloworld-config.json b/rust/sealevel/environments/mainnet2/helloworld/rc/helloworld-config.json new file mode 100644 index 000000000..e6e92d919 --- /dev/null +++ b/rust/sealevel/environments/mainnet2/helloworld/rc/helloworld-config.json @@ -0,0 +1,32 @@ +{ + "solana": { + "interchainSecurityModule": "BYTsxBuKVbwgsZFswzB91nrxveQySghwXzaKqn8exNnC" + }, + "gnosis": { + "foreignDeployment": "0x99ca8c74cE7Cfa9d72A51fbb05F9821f5f826b3a" + }, + "bsc": { + "foreignDeployment": "0xe5554478F167936dB253f79f57c41770bfa00Bae" + }, + "avalanche": { + "foreignDeployment": "0xe1De9910fe71cC216490AC7FCF019e13a34481D7" + }, + "polygon": { + "foreignDeployment": "0xAb65C41a1BC580a52f0b166879122EFdce0cB868" + }, + "celo": { + "foreignDeployment": "0xfE29f6a4468536029Fc9c97d3a9669b9fe38E114" + }, + "arbitrum": { + "foreignDeployment": "0x414B67F62b143d6db6E9b633168Dd6fd4DA20642" + }, + "optimism": { + "foreignDeployment": "0xB4caf2CA864B413DAA502fA18A8D48cD0740fC52" + }, + "ethereum": { + "foreignDeployment": "0xed31c20c5517EaC05decD5F6dCd01Fe6d16fD09D" + }, + "moonbeam": { + "foreignDeployment": "0x3eB9eE2CFC8DCB6F58B5869D33336CFcBf1dC354" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet2/helloworld/rc/program-ids.json b/rust/sealevel/environments/mainnet2/helloworld/rc/program-ids.json new file mode 100644 index 000000000..201236ac6 --- /dev/null +++ b/rust/sealevel/environments/mainnet2/helloworld/rc/program-ids.json @@ -0,0 +1,42 @@ +{ + "solana": { + "hex": "0x29dacc0e7124ea39b1fd43ab0fd30e038cf405c0229890229d0086d0b6516f9c", + "base58": "3pPDp16iVTJFge2sm85Q61hW61UN5xNqeG24gqFhzLFV" + }, + "avalanche": { + "hex": "0x000000000000000000000000e1de9910fe71cc216490ac7fcf019e13a34481d7", + "base58": "11111111111149We9K5tM8ijcyNy9zDMG9RyDBCJ" + }, + "arbitrum": { + "hex": "0x000000000000000000000000414b67f62b143d6db6e9b633168dd6fd4da20642", + "base58": "111111111111um79Yc6Evs5e1m2fdD2x7T1cpXb" + }, + "moonbeam": { + "hex": "0x0000000000000000000000003eb9ee2cfc8dcb6f58b5869d33336cfcbf1dc354", + "base58": "111111111111sgjzaeuHfqhExkdPQ1gJdhcSr4j" + }, + "optimism": { + "hex": "0x000000000000000000000000b4caf2ca864b413daa502fa18a8d48cd0740fc52", + "base58": "1111111111113X64nhkfMi9X5MbxKsiDTeeTmjsw" + }, + "ethereum": { + "hex": "0x000000000000000000000000ed31c20c5517eac05decd5f6dcd01fe6d16fd09d", + "base58": "1111111111114JfPmRiKEsR445qonVzCpsAvXCR2" + }, + "gnosis": { + "hex": "0x00000000000000000000000099ca8c74ce7cfa9d72a51fbb05f9821f5f826b3a", + "base58": "11111111111139Gc7eyQjpZrmWkkYQRyA2Grcvmf" + }, + "bsc": { + "hex": "0x000000000000000000000000e5554478f167936db253f79f57c41770bfa00bae", + "base58": "1111111111114CJxuV4VoAh5NsJy9qCGHqryoTCy" + }, + "polygon": { + "hex": "0x000000000000000000000000ab65c41a1bc580a52f0b166879122efdce0cb868", + "base58": "1111111111113PVkHAU9H7moDSoQvhC3Y2wgmovX" + }, + "celo": { + "hex": "0x000000000000000000000000fe29f6a4468536029fc9c97d3a9669b9fe38e114", + "base58": "1111111111114YNh3uhCWh2NjyPttobeNRyuDHYo" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/hyperlane/multisig-config.json b/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/hyperlane/multisig-config.json new file mode 100644 index 000000000..49cb69474 --- /dev/null +++ b/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/hyperlane/multisig-config.json @@ -0,0 +1,106 @@ +{ + "celo": { + "type": 3, + "threshold": 4, + "validators": [ + "0x1f20274b1210046769d48174c2f0e7c25ca7d5c5", + "0x3bc014bafa43f93d534aed34f750997cdffcf007", + "0xd79d506d741fa735938f7b7847a926e34a6fe6b0", + "0xe4a258bc61e65914c2a477b2a8a433ab4ebdf44b", + "0x6aea63b0be4679c1385c26a92a3ff8aa6a8379f2", + "0xc0085e1a49bcc69e534272adb82c74c0e007e1ca" + ] + }, + "ethereum": { + "type": 3, + "threshold": 4, + "validators": [ + "0x4c327ccb881a7542be77500b2833dc84c839e7b7", + "0x84cb373148ef9112b277e68acf676fefa9a9a9a0", + "0x0d860c2b28bec3af4fd3a5997283e460ff6f2789", + "0xd4c1211f0eefb97a846c4e6d6589832e52fc03db", + "0x600c90404d5c9df885404d2cc5350c9b314ea3a2", + "0x892DC66F5B2f8C438E03f6323394e34A9C24F2D6" + ] + }, + "avalanche": { + "type": 3, + "threshold": 4, + "validators": [ + "0xa7aa52623fe3d78c343008c95894be669e218b8d", + "0xb6004433fb04f643e2d48ae765c0e7f890f0bc0c", + "0xa07e213e0985b21a6128e6c22ab5fb73948b0cc2", + "0x73853ed9a5f6f2e4c521970a94d43469e3cdaea6", + "0xbd2e136cda02ba627ca882e49b184cbe976081c8", + "0x1418126f944a44dad9edbab32294a8c890e7a9e3" + ] + }, + "polygon": { + "type": 3, + "threshold": 4, + "validators": [ + "0x59a001c3451e7f9f3b4759ea215382c1e9aa5fc1", + "0x009fb042d28944017177920c1d40da02bfebf474", + "0xba4b13e23705a5919c1901150d9697e8ffb3ea71", + "0x2faa4071b718972f9b4beec1d8cbaa4eb6cca6c6", + "0x5ae9b0f833dfe09ef455562a1f603f1634504dd6", + "0x6a163d312f7352a95c9b81dca15078d5bf77a442" + ] + }, + "bsc": { + "type": 3, + "threshold": 4, + "validators": [ + "0xcc84b1eb711e5076b2755cf4ad1d2b42c458a45e", + "0xefe34eae2bca1846b895d2d0762ec21796aa196a", + "0x662674e80e189b0861d6835c287693f50ee0c2ff", + "0x8a0f59075af466841808c529624807656309c9da", + "0xdd2ff046ccd748a456b4757a73d47f165469669f", + "0x034c4924c30ec4aa1b7f3ad58548988f0971e1bf" + ] + }, + "arbitrum": { + "type": 3, + "threshold": 4, + "validators": [ + "0xbcb815f38d481a5eba4d7ac4c9e74d9d0fc2a7e7", + "0xd839424e2e5ace0a81152298dc2b1e3bb3c7fb20", + "0xb8085c954b75b7088bcce69e61d12fcef797cd8d", + "0x9856dcb10fd6e5407fa74b5ab1d3b96cc193e9b7", + "0x505dff4e0827aa5065f5e001db888e0569d46490", + "0x25c6779d4610f940bf2488732e10bcffb9d36f81" + ] + }, + "optimism": { + "type": 3, + "threshold": 4, + "validators": [ + "0x9f2296d5cfc6b5176adc7716c7596898ded13d35", + "0x9c10bbe8efa03a8f49dfdb5c549258e3a8dca097", + "0x62144d4a52a0a0335ea5bb84392ef9912461d9dd", + "0xaff4718d5d637466ad07441ee3b7c4af8e328dbd", + "0xc64d1efeab8ae222bc889fe669f75d21b23005d9", + "0xfa174eb2b4921bb652bc1ada3e8b00e7e280bf3c" + ] + }, + "moonbeam": { + "type": 3, + "threshold": 3, + "validators": [ + "0x237243d32d10e3bdbbf8dbcccc98ad44c1c172ea", + "0x9509c8cf0a06955f27342262af501b74874e98fb", + "0xb7113c999e4d587b162dd1a28c73f3f51c6bdcdc", + "0x26725501597d47352a23cd26f122709f69ad53bc" + ] + }, + "gnosis": { + "type": 3, + "threshold": 3, + "validators": [ + "0xd0529ec8df08d0d63c0f023786bfa81e4bb51fd6", + "0x8a72ff8571c53c62c7ca02e8c97a443cd5674383", + "0x4075c2f6bd6d9562067cfe551d49c2bcafa7d692", + "0xa18580444eaeb1c5957e7b66a6bf84b6519f904d" + ] + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/nautilus/multisig-config.json b/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/nautilus/multisig-config.json new file mode 100644 index 000000000..5e08f27c0 --- /dev/null +++ b/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/nautilus/multisig-config.json @@ -0,0 +1,18 @@ +{ + "bsc": { + "type": 3, + "threshold": 1, + "validators": [ + "0x0000000000000000000000000000000000000001" + ] + }, + "nautilus": { + "type": 3, + "threshold": 2, + "validators": [ + "0x9c920af9467595a23cb3433adefc3854d498a437", + "0x87611503e37ce041527c11c24263e8760fccf81f", + "0x573443248cf9929af0001b88f62131f2de29fe9f" + ] + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/nautilus/program-ids.json b/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/nautilus/program-ids.json new file mode 100644 index 000000000..23f0cdd55 --- /dev/null +++ b/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/nautilus/program-ids.json @@ -0,0 +1,3 @@ +{ + "program_id": "9k74DkJvS2x9QhG4XfnKsLkqaCDyVfaj8s6FyJyhAeEP" +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/rc/multisig-config.json b/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/rc/multisig-config.json new file mode 100644 index 000000000..fc090e25d --- /dev/null +++ b/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/rc/multisig-config.json @@ -0,0 +1,65 @@ +{ + "celo": { + "type": 3, + "threshold": 1, + "validators": [ + "0xe7a82e210f512f8e9900d6bc2acbf7981c63e66e" + ] + }, + "ethereum": { + "type": 3, + "threshold": 1, + "validators": [ + "0xaea1adb1c687b061e5b60b9da84cb69e7b5fab44" + ] + }, + "avalanche": { + "type": 3, + "threshold": 1, + "validators": [ + "0x706976391e23dea28152e0207936bd942aba01ce" + ] + }, + "polygon": { + "type": 3, + "threshold": 1, + "validators": [ + "0xef372f6ff7775989b3ac884506ee31c79638c989" + ] + }, + "bsc": { + "type": 3, + "threshold": 1, + "validators": [ + "0x0823081031a4a6f97c6083775c191d17ca96d0ab" + ] + }, + "arbitrum": { + "type": 3, + "threshold": 1, + "validators": [ + "0x1a95b35fb809d57faf1117c1cc29a6c5df289df1" + ] + }, + "optimism": { + "type": 3, + "threshold": 1, + "validators": [ + "0x60e938bf280bbc21bacfd8bf435459d9003a8f98" + ] + }, + "moonbeam": { + "type": 3, + "threshold": 1, + "validators": [ + "0x0df7140811e309dc69638352545151ebb9d5e0fd" + ] + }, + "gnosis": { + "type": 3, + "threshold": 1, + "validators": [ + "0x15f48e78092a4f79febface509cfd76467c6cdbb" + ] + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/rc/program-ids.json b/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/rc/program-ids.json new file mode 100644 index 000000000..0bf925699 --- /dev/null +++ b/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/rc/program-ids.json @@ -0,0 +1,3 @@ +{ + "program_id": "BYTsxBuKVbwgsZFswzB91nrxveQySghwXzaKqn8exNnC" +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet2/warp-routes/chain-config.json b/rust/sealevel/environments/mainnet2/warp-routes/chain-config.json deleted file mode 100644 index d6029dfc5..000000000 --- a/rust/sealevel/environments/mainnet2/warp-routes/chain-config.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "solana": { - "chainId": 1399811149, - "name": "solana", - "publicRpcUrls": [ - { - "http": "https://api.mainnet-beta.solana.com" - } - ] - }, - "bsc": { - "chainId": 56, - "name": "bsc", - "publicRpcUrls": [ - { - "http": "https://bsc-dataseed.binance.org" - } - ] - }, - "nautilus": { - "chainId": 22222, - "name": "nautilus", - "publicRpcUrls": [ - { - "http": "https://api.nautilus.nautchain.xyz" - } - ] - } - } \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet2/warp-routes/zbc/program-ids.json b/rust/sealevel/environments/mainnet2/warp-routes/zbc/program-ids.json index 76bf385a9..3df636e1f 100644 --- a/rust/sealevel/environments/mainnet2/warp-routes/zbc/program-ids.json +++ b/rust/sealevel/environments/mainnet2/warp-routes/zbc/program-ids.json @@ -3,10 +3,6 @@ "hex": "0xc5ba229fa2822fe65ac2bd0a93d8371d75292c3415dd381923c1088a3308528b", "base58": "EJqwFjvVJSAxH8Ur2PYuMfdvoJeutjmH6GkoEFQ4MdSa" }, - "bsc": { - "hex": "0x000000000000000000000000c27980812e2e66491fd457d488509b7e04144b98", - "base58": "1111111111113i9HKBuaFrAQeGvhv3fJnCCDkg7h" - }, "nautilus": { "hex": "0x0000000000000000000000004501bbe6e731a4bc5c60c03a77435b2f6d5e9fe7", "base58": "111111111111xm5qkrK7gZ8Cmjr4ggPLRxy2T8a" diff --git a/rust/sealevel/environments/mainnet2/warp-routes/zbc/token-config.json b/rust/sealevel/environments/mainnet2/warp-routes/zbc/token-config.json index af0d1439b..51e235a3e 100644 --- a/rust/sealevel/environments/mainnet2/warp-routes/zbc/token-config.json +++ b/rust/sealevel/environments/mainnet2/warp-routes/zbc/token-config.json @@ -4,15 +4,9 @@ "decimals": 9, "remoteDecimals": 9, "token": "wzbcJyhGhQDLTV1S99apZiiBdE4jmYfbw99saMMdP59", - "splTokenProgram": "token" - }, - "bsc": { - "type": "collateral", - "decimals": 9, - "token": "0x37a56cdcD83Dce2868f721De58cB3830C44C6303", - "name": "Zebec", - "symbol": "ZBC", - "foreignDeployment": "0xC27980812E2E66491FD457D488509b7E04144b98" + "splTokenProgram": "token", + "interchainSecurityModule": "9k74DkJvS2x9QhG4XfnKsLkqaCDyVfaj8s6FyJyhAeEP", + "owner": "EzppBFV2taxWw8kEjxNYvby6q7W1biJEqwP3iC7YgRe3" }, "nautilus": { "type": "native", diff --git a/rust/sealevel/environments/testnet3/chain-config.json b/rust/sealevel/environments/testnet3/chain-config.json new file mode 100644 index 000000000..ae5158a8b --- /dev/null +++ b/rust/sealevel/environments/testnet3/chain-config.json @@ -0,0 +1,335 @@ +{ + "alfajores": { + "chainId": 44787, + "domainId": 44787, + "name": "alfajores", + "protocol": "ethereum", + "displayName": "Alfajores", + "nativeToken": { + "decimals": 18, + "name": "CELO", + "symbol": "CELO" + }, + "rpcUrls": [ + { + "http": "https://alfajores-forno.celo-testnet.org" + } + ], + "blockExplorers": [ + { + "name": "CeloScan", + "url": "https://alfajores.celoscan.io", + "apiUrl": "https://api-alfajores.celoscan.io/api", + "family": "etherscan" + }, + { + "name": "Blockscout", + "url": "https://explorer.celo.org/alfajores", + "apiUrl": "https://explorer.celo.org/alfajores/api", + "family": "blockscout" + } + ], + "blocks": { + "confirmations": 1, + "reorgPeriod": 0, + "estimateBlockTime": 5 + }, + "isTestnet": true + }, + "fuji": { + "chainId": 43113, + "domainId": 43113, + "name": "fuji", + "protocol": "ethereum", + "displayName": "Fuji", + "nativeToken": { + "decimals": 18, + "name": "Avalanche", + "symbol": "AVAX" + }, + "rpcUrls": [ + { + "http": "https://api.avax-test.network/ext/bc/C/rpc", + "pagination": { + "maxBlockRange": 2048 + } + } + ], + "blockExplorers": [ + { + "name": "SnowTrace", + "url": "https://testnet.snowtrace.io", + "apiUrl": "https://api-testnet.snowtrace.io/api", + "family": "etherscan" + } + ], + "blocks": { + "confirmations": 3, + "reorgPeriod": 3, + "estimateBlockTime": 2 + }, + "isTestnet": true + }, + "mumbai": { + "chainId": 80001, + "domainId": 80001, + "name": "mumbai", + "protocol": "ethereum", + "displayName": "Mumbai", + "nativeToken": { + "name": "MATIC", + "symbol": "MATIC", + "decimals": 18 + }, + "rpcUrls": [ + { + "http": "https://rpc.ankr.com/polygon_mumbai", + "pagination": { + "maxBlockRange": 10000, + "minBlockNumber": 22900000 + } + }, + { + "http": "https://matic-mumbai.chainstacklabs.com" + } + ], + "blockExplorers": [ + { + "name": "PolygonScan", + "url": "https://mumbai.polygonscan.com", + "apiUrl": "https://api-testnet.polygonscan.com/api", + "family": "etherscan" + } + ], + "blocks": { + "confirmations": 3, + "reorgPeriod": 32, + "estimateBlockTime": 5 + }, + "isTestnet": true + }, + "bsctestnet": { + "chainId": 97, + "domainId": 97, + "name": "bsctestnet", + "protocol": "ethereum", + "displayName": "BSC Testnet", + "nativeToken": { + "decimals": 18, + "name": "BNB", + "symbol": "BNB" + }, + "rpcUrls": [ + { + "http": "https://data-seed-prebsc-1-s3.binance.org:8545" + } + ], + "blockExplorers": [ + { + "name": "BscScan", + "url": "https://testnet.bscscan.com", + "apiUrl": "https://api-testnet.bscscan.com/api", + "family": "etherscan" + } + ], + "blocks": { + "confirmations": 1, + "reorgPeriod": 9, + "estimateBlockTime": 3 + }, + "isTestnet": true + }, + "goerli": { + "chainId": 5, + "domainId": 5, + "name": "goerli", + "protocol": "ethereum", + "displayName": "Goerli", + "nativeToken": { + "name": "Ether", + "symbol": "ETH", + "decimals": 18 + }, + "rpcUrls": [ + { + "http": "https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161" + }, + { + "http": "https://rpc.ankr.com/eth_goerli" + }, + { + "http": "https://eth-goerli.public.blastapi.io" + } + ], + "blockExplorers": [ + { + "name": "Etherscan", + "url": "https://goerli.etherscan.io", + "apiUrl": "https://api-goerli.etherscan.io/api", + "family": "etherscan" + } + ], + "blocks": { + "confirmations": 1, + "reorgPeriod": 2, + "estimateBlockTime": 13 + }, + "isTestnet": true + }, + "moonbasealpha": { + "chainId": 1287, + "domainId": 1287, + "name": "moonbasealpha", + "protocol": "ethereum", + "displayName": "Moonbase Alpha", + "displayNameShort": "Moonbase", + "nativeToken": { + "decimals": 18, + "name": "DEV", + "symbol": "DEV" + }, + "rpcUrls": [ + { + "http": "https://rpc.api.moonbase.moonbeam.network" + } + ], + "blockExplorers": [ + { + "name": "MoonScan", + "url": "https://moonbase.moonscan.io", + "apiUrl": "https://api-moonbase.moonscan.io/api", + "family": "etherscan" + } + ], + "blocks": { + "confirmations": 1, + "reorgPeriod": 1, + "estimateBlockTime": 12 + }, + "isTestnet": true + }, + "optimismgoerli": { + "chainId": 420, + "domainId": 420, + "name": "optimismgoerli", + "protocol": "ethereum", + "displayName": "Optimism Goerli", + "displayNameShort": "Opt. Goerli", + "nativeToken": { + "name": "Ether", + "symbol": "ETH", + "decimals": 18 + }, + "rpcUrls": [ + { + "http": "https://goerli.optimism.io" + } + ], + "blockExplorers": [ + { + "name": "Etherscan", + "url": "https://goerli-optimism.etherscan.io", + "apiUrl": "https://api-goerli-optimism.etherscan.io/api", + "family": "etherscan" + } + ], + "blocks": { + "confirmations": 1, + "reorgPeriod": 1, + "estimateBlockTime": 3 + }, + "isTestnet": true + }, + "arbitrumgoerli": { + "chainId": 421613, + "domainId": 421613, + "name": "arbitrumgoerli", + "protocol": "ethereum", + "displayName": "Arbitrum Goerli", + "displayNameShort": "Arb. Goerli", + "nativeToken": { + "name": "Ether", + "symbol": "ETH", + "decimals": 18 + }, + "rpcUrls": [ + { + "http": "https://goerli-rollup.arbitrum.io/rpc" + } + ], + "blockExplorers": [ + { + "name": "Arbiscan", + "url": "https://goerli.arbiscan.io", + "apiUrl": "https://api-goerli.arbiscan.io/api", + "family": "etherscan" + } + ], + "blocks": { + "confirmations": 1, + "reorgPeriod": 1, + "estimateBlockTime": 3 + }, + "isTestnet": true + }, + "sepolia": { + "chainId": 11155111, + "domainId": 11155111, + "name": "sepolia", + "protocol": "ethereum", + "displayName": "Sepolia", + "nativeToken": { + "name": "Ether", + "symbol": "ETH", + "decimals": 18 + }, + "rpcUrls": [ + { + "http": "https://endpoints.omniatech.io/v1/eth/sepolia/public" + }, + { + "http": "https://rpc.sepolia.org" + } + ], + "blockExplorers": [ + { + "name": "Etherscan", + "url": "https://sepolia.etherscan.io", + "apiUrl": "https://api-sepolia.etherscan.io/api", + "family": "etherscan" + } + ], + "blocks": { + "confirmations": 1, + "reorgPeriod": 2, + "estimateBlockTime": 13 + }, + "isTestnet": true + }, + "solanadevnet": { + "chainId": 1399811151, + "name": "solanadevnet", + "rpcUrls": [ + { + "http": "https://api.devnet.solana.com" + } + ] + }, + "proteustestnet": { + "chainId": 88002, + "domainId": 88002, + "name": "proteustestnet", + "protocol": "ethereum", + "displayName": "Proteus Testnet", + "nativeToken": { + "name": "Zebec", + "symbol": "ZBC", + "decimals": 18 + }, + "rpcUrls": [ + { + "http": "https://api.proteus.nautchain.xyz/solana" + } + ] + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/helloworld/hyperlane/helloworld-config.json b/rust/sealevel/environments/testnet3/helloworld/hyperlane/helloworld-config.json new file mode 100644 index 000000000..a03eae987 --- /dev/null +++ b/rust/sealevel/environments/testnet3/helloworld/hyperlane/helloworld-config.json @@ -0,0 +1,32 @@ +{ + "solanadevnet": { + "interchainSecurityModule": "64xkGhsZbxgP5rBJfpPcpmzkzTGkpSVHiDLcMKS5gmQw" + }, + "alfajores": { + "foreignDeployment": "0x477D860f8F41bC69dDD32821F2Bf2C2Af0243F16" + }, + "fuji": { + "foreignDeployment": "0x5da3b8d6F73dF6003A490072106730218c475AAd" + }, + "mumbai": { + "foreignDeployment": "0x1A4d8a5eD6C93Af828655e15C44eeE2c2851F0D6" + }, + "bsctestnet": { + "foreignDeployment": "0xE09BF59dCA6e622efC33f6fbd8EF85dE45233388" + }, + "goerli": { + "foreignDeployment": "0x405BFdEcB33230b4Ad93C29ba4499b776CfBa189" + }, + "moonbasealpha": { + "foreignDeployment": "0x89e02C3C7b97bCBa63279E10E2a44e6cEF69E6B2" + }, + "optimismgoerli": { + "foreignDeployment": "0x3582d1238cBC812165981E4fFaB0E8D9a4518910" + }, + "arbitrumgoerli": { + "foreignDeployment": "0x339B46496D60b1b6B42e9715DeD8B3D2154dA0Bb" + }, + "sepolia": { + "foreignDeployment": "0x5d56B8a669F50193b54319442c6EEE5edD662381" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/helloworld/hyperlane/keys/hyperlane_sealevel_hello_world-solanadevnet.json b/rust/sealevel/environments/testnet3/helloworld/hyperlane/keys/hyperlane_sealevel_hello_world-solanadevnet.json new file mode 100644 index 000000000..db75227a7 --- /dev/null +++ b/rust/sealevel/environments/testnet3/helloworld/hyperlane/keys/hyperlane_sealevel_hello_world-solanadevnet.json @@ -0,0 +1 @@ +[42,226,42,33,87,42,251,0,57,248,173,166,139,84,91,50,218,150,183,254,74,195,88,116,92,195,145,231,63,39,9,98,171,58,146,166,209,139,158,82,151,114,58,235,5,25,129,244,219,192,239,35,53,229,191,115,243,59,174,210,94,26,161,101] \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/helloworld/hyperlane/program-ids.json b/rust/sealevel/environments/testnet3/helloworld/hyperlane/program-ids.json new file mode 100644 index 000000000..1eca03712 --- /dev/null +++ b/rust/sealevel/environments/testnet3/helloworld/hyperlane/program-ids.json @@ -0,0 +1,42 @@ +{ + "bsctestnet": { + "hex": "0x000000000000000000000000e09bf59dca6e622efc33f6fbd8ef85de45233388", + "base58": "11111111111148VaL9DFuVc9DbDjRR7c3qyCEjyy" + }, + "optimismgoerli": { + "hex": "0x0000000000000000000000003582d1238cbc812165981e4ffab0e8d9a4518910", + "base58": "111111111111kEreeMSXc3Nh2JoYtisyZj8pb6X" + }, + "fuji": { + "hex": "0x0000000000000000000000005da3b8d6f73df6003a490072106730218c475aad", + "base58": "1111111111112JfXZf7EYaEMM1st6wFZbcLN2uwA" + }, + "solanadevnet": { + "hex": "0xab3a92a6d18b9e5297723aeb051981f4dbc0ef2335e5bf73f33baed25e1aa165", + "base58": "CXQX54kdkU5GqdRJjCmHpwHfEMgFb5SeBmMWntP2Ds7J" + }, + "mumbai": { + "hex": "0x0000000000000000000000001a4d8a5ed6c93af828655e15c44eee2c2851f0d6", + "base58": "111111111111NFiXSaSzEqfGUNtwSd5dSDgCymP" + }, + "alfajores": { + "hex": "0x000000000000000000000000477d860f8f41bc69ddd32821f2bf2c2af0243f16", + "base58": "111111111111zmUjMVNXAe5bcqPR8cvaPz5SrQu" + }, + "goerli": { + "hex": "0x000000000000000000000000405bfdecb33230b4ad93c29ba4499b776cfba189", + "base58": "111111111111u1H27LrKRuu1G7bDpPWUXKphQSt" + }, + "sepolia": { + "hex": "0x0000000000000000000000005d56b8a669f50193b54319442c6eee5edd662381", + "base58": "1111111111112JRRxgtLh6eyMDsTHUehn6bJcPJ8" + }, + "arbitrumgoerli": { + "hex": "0x000000000000000000000000339b46496d60b1b6b42e9715ded8b3d2154da0bb", + "base58": "111111111111ihbsGG5PRTKTSYSGewGtDFs2vfc" + }, + "moonbasealpha": { + "hex": "0x00000000000000000000000089e02c3c7b97bcba63279e10e2a44e6cef69e6b2", + "base58": "1111111111112vQhuwgKwhQ7SM1HZEm6yXQkzCau" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/helloworld/rc/helloworld-config.json b/rust/sealevel/environments/testnet3/helloworld/rc/helloworld-config.json new file mode 100644 index 000000000..e2a072b56 --- /dev/null +++ b/rust/sealevel/environments/testnet3/helloworld/rc/helloworld-config.json @@ -0,0 +1,32 @@ +{ + "solanadevnet": { + "interchainSecurityModule": "2NE6Y1rXp1Kpp6vBNqDHYL7HNk7iqh8BKmvCoZtUcZLn" + }, + "alfajores": { + "foreignDeployment": "0x40Adcb03F3C58170b4751c4140636FC6085Ff475" + }, + "fuji": { + "foreignDeployment": "0xAc003FcDD0EE223664F2A000B5A59D082745700b" + }, + "mumbai": { + "foreignDeployment": "0xaB0892029C3E7dD4c0235590dc296E618A7b4d03" + }, + "bsctestnet": { + "foreignDeployment": "0xd259b0e793535325786675542aB296c451535c27" + }, + "goerli": { + "foreignDeployment": "0x03e9531ae74e8F0f96DE26788a22d35bdaD24185" + }, + "moonbasealpha": { + "foreignDeployment": "0xE9D6317a10860340f035f3d09052D9d376855bE8" + }, + "optimismgoerli": { + "foreignDeployment": "0x057d38d184d74192B96840D8FbB37e584dDb569A" + }, + "arbitrumgoerli": { + "foreignDeployment": "0xaAF1BF6f2BfaE290ea8615066fd167e396a2f578" + }, + "sepolia": { + "foreignDeployment": "0x6AD4DEBA8A147d000C09de6465267a9047d1c217" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/helloworld/rc/keys/hyperlane_sealevel_hello_world-solanadevnet.json b/rust/sealevel/environments/testnet3/helloworld/rc/keys/hyperlane_sealevel_hello_world-solanadevnet.json new file mode 100644 index 000000000..341933a08 --- /dev/null +++ b/rust/sealevel/environments/testnet3/helloworld/rc/keys/hyperlane_sealevel_hello_world-solanadevnet.json @@ -0,0 +1 @@ +[158,232,241,234,223,84,236,122,65,31,146,220,11,236,43,97,184,113,181,80,237,157,204,188,166,199,112,171,77,38,68,13,187,162,244,131,230,66,68,157,10,57,239,229,249,96,63,124,85,148,35,172,235,211,200,84,208,117,96,204,208,67,146,40] \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/helloworld/rc/program-ids.json b/rust/sealevel/environments/testnet3/helloworld/rc/program-ids.json new file mode 100644 index 000000000..99f4db546 --- /dev/null +++ b/rust/sealevel/environments/testnet3/helloworld/rc/program-ids.json @@ -0,0 +1,42 @@ +{ + "sepolia": { + "hex": "0x0000000000000000000000006ad4deba8a147d000c09de6465267a9047d1c217", + "base58": "1111111111112VKnX2KMsqSTDw9YoXRsZJTwTcUW" + }, + "goerli": { + "hex": "0x00000000000000000000000003e9531ae74e8f0f96de26788a22d35bdad24185", + "base58": "1111111111114AKBbRbDjAP93LQgmXJPvfVU7SC" + }, + "solanadevnet": { + "hex": "0xbba2f483e642449d0a39efe5f9603f7c559423acebd3c854d07560ccd0439228", + "base58": "DdTMkk9nuqH5LnD56HLkPiKMV3yB3BNEYSQfgmJHa5i7" + }, + "optimismgoerli": { + "hex": "0x000000000000000000000000057d38d184d74192b96840d8fbb37e584ddb569a", + "base58": "1111111111115SFp65pWdvPTRK5fmHa3sc4Eq6Z" + }, + "fuji": { + "hex": "0x000000000000000000000000ac003fcdd0ee223664f2a000b5a59d082745700b", + "base58": "1111111111113Pz2bmxxVNgkKkZPpxgouHiZAjTx" + }, + "moonbasealpha": { + "hex": "0x000000000000000000000000e9d6317a10860340f035f3d09052d9d376855be8", + "base58": "1111111111114Fx2onL6wvVgGmyjgzGhy48HzCZM" + }, + "arbitrumgoerli": { + "hex": "0x000000000000000000000000aaf1bf6f2bfae290ea8615066fd167e396a2f578", + "base58": "1111111111113P8WPEsejkHP1Zysy1xXafVFFnaT" + }, + "bsctestnet": { + "hex": "0x000000000000000000000000d259b0e793535325786675542ab296c451535c27", + "base58": "1111111111113vyKMMTb6aSQDhDLqEvqcPBcTtRC" + }, + "alfajores": { + "hex": "0x00000000000000000000000040adcb03f3c58170b4751c4140636fc6085ff475", + "base58": "111111111111uGFbQYrmpk8K5cfeu9x438LAGiQ" + }, + "mumbai": { + "hex": "0x000000000000000000000000ab0892029c3e7dd4c0235590dc296e618a7b4d03", + "base58": "1111111111113PCgiXuWFu2FmvhykJp51x5y5jyC" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/hyperlane/multisig-config.json b/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/hyperlane/multisig-config.json new file mode 100644 index 000000000..c758f40bc --- /dev/null +++ b/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/hyperlane/multisig-config.json @@ -0,0 +1,92 @@ +{ + "alfajores": { + "type": 3, + "threshold": 2, + "validators": [ + "0xe6072396568e73ce6803b12b7e04164e839f1e54", + "0x9f177f51289b22515f41f95872e1511391b8e105", + "0x15f77400845eb1c971ad08de050861d5508cad6c" + ] + }, + "fuji": { + "type": 3, + "threshold": 2, + "validators": [ + "0x9fa19ead5ec76e437948b35e227511b106293c40", + "0x227e7d6507762ece0c94678f8c103eff9d682476", + "0x2379e43740e4aa4fde48cf4f00a3106df1d8420d" + ] + }, + "mumbai": { + "type": 3, + "threshold": 2, + "validators": [ + "0x0a664ea799447da6b15645cf8b9e82072a68343f", + "0x6ae6f12929a960aba24ba74ea310e3d37d0ac045", + "0x51f70c047cd73bc7873273707501568857a619c4" + ] + }, + "bsctestnet": { + "type": 3, + "threshold": 2, + "validators": [ + "0x23338c8714976dd4a57eaeff17cbd26d7e275c08", + "0x85a618d7450ebc37e0d682371f08dac94eec7a76", + "0x95b76562e4ba1791a27ba4236801271c9115b141" + ] + }, + "goerli": { + "type": 3, + "threshold": 2, + "validators": [ + "0xf43fbd072fd38e1121d4b3b0b8a35116bbb01ea9", + "0xa33020552a21f35e75bd385c6ab95c3dfa82d930", + "0x0bba4043ff242f8bf3f39bafa8930a84d644d947" + ] + }, + "sepolia": { + "type": 3, + "threshold": 2, + "validators": [ + "0xbc748ee311f5f2d1975d61cdf531755ce8ce3066", + "0xc4233b2bfe5aec08964a94b403052abb3eafcf07", + "0x6b36286c19f5c10bdc139ea9ee7f82287303f61d" + ] + }, + "moonbasealpha": { + "type": 3, + "threshold": 2, + "validators": [ + "0x890c2aeac157c3f067f3e42b8afc797939c59a32", + "0x1b06d6fe69b972ed7420c83599d5a5c0fc185904", + "0xe70b85206a968a99a597581f0fa09c99e7681093" + ] + }, + "optimismgoerli": { + "type": 3, + "threshold": 2, + "validators": [ + "0xbb8d77eefbecc55db6e5a19b0fc3dc290776f189", + "0x69792508b4ddaa3ca52241ccfcd1e0b119a1ee65", + "0x11ddb46c6b653e0cdd7ad5bee32ae316e18f8453" + ] + }, + "arbitrumgoerli": { + "type": 3, + "threshold": 2, + "validators": [ + "0xce798fa21e323f6b24d9838a10ffecdefdfc4f30", + "0xa792d39dca4426927e0f00c1618d61c9cb41779d", + "0xdf181fcc11dfac5d01467e4547101a856dd5aa04" + ] + }, + "proteustestnet": { + "type": 3, + "threshold": 2, + "validators": [ + "0x79fc73656abb9eeaa5ee853c4569124f5bdaf9d8", + "0x72840388d5ab57323bc4f6e6d3ddedfd5cc911f0", + "0xd4b2a50c53fc6614bb3cd3198e0fdc03f5da973f" + ] + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/rc/keys/hyperlane_sealevel_multisig_ism_message_id-keypair.json b/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/rc/keys/hyperlane_sealevel_multisig_ism_message_id-keypair.json new file mode 100644 index 000000000..8542855f6 --- /dev/null +++ b/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/rc/keys/hyperlane_sealevel_multisig_ism_message_id-keypair.json @@ -0,0 +1 @@ +[187,239,78,162,24,178,190,184,243,9,66,169,19,139,40,129,55,222,218,2,184,14,122,68,163,6,144,157,76,14,169,237,20,75,176,226,241,81,96,106,31,68,222,130,94,67,105,175,112,84,241,60,117,11,107,135,95,48,20,213,115,123,100,3] \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/rc/multisig-config.json b/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/rc/multisig-config.json new file mode 100644 index 000000000..870829e4a --- /dev/null +++ b/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/rc/multisig-config.json @@ -0,0 +1,65 @@ +{ + "alfajores": { + "type": 3, + "threshold": 1, + "validators": [ + "0x45e5c228b38e1cf09e9a3423ed0cf4862c4bf3de" + ] + }, + "fuji": { + "type": 3, + "threshold": 1, + "validators": [ + "0xd81ba169170a9b582812cf0e152d2c168572e21f" + ] + }, + "mumbai": { + "type": 3, + "threshold": 1, + "validators": [ + "0xb537c4ce34e1cad718be52aa30b095e416eae46a" + ] + }, + "bsctestnet": { + "type": 3, + "threshold": 1, + "validators": [ + "0x77f80ef5b18977e15d81aea8dd3a88e7df4bc0eb" + ] + }, + "goerli": { + "type": 3, + "threshold": 1, + "validators": [ + "0x9597ddb4ad2af237665559574b820596bb77ae7a" + ] + }, + "sepolia": { + "type": 3, + "threshold": 1, + "validators": [ + "0x183f15924f3a464c54c9393e8d268eb44d2b208c" + ] + }, + "moonbasealpha": { + "type": 3, + "threshold": 1, + "validators": [ + "0xbeaf158f85d7b64ced36b8aea0bbc4cd0f2d1a5d" + ] + }, + "optimismgoerli": { + "type": 3, + "threshold": 1, + "validators": [ + "0x1d6798671ac532f2bf30c3a5230697a4695705e4" + ] + }, + "arbitrumgoerli": { + "type": 3, + "threshold": 1, + "validators": [ + "0x6d13367c7cd713a4ea79a2552adf824bf1ecdd5e" + ] + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/rc/program-ids.json b/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/rc/program-ids.json new file mode 100644 index 000000000..e615b252e --- /dev/null +++ b/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/rc/program-ids.json @@ -0,0 +1,3 @@ +{ + "program_id": "2NE6Y1rXp1Kpp6vBNqDHYL7HNk7iqh8BKmvCoZtUcZLn" +} \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/solanadevnet/core/keys/hyperlane_sealevel_multisig_ism_message_id-keypair.json b/rust/sealevel/environments/testnet3/solanadevnet/core/keys/hyperlane_sealevel_multisig_ism_message_id-keypair.json index 69fd3baad..d86f1638c 100644 --- a/rust/sealevel/environments/testnet3/solanadevnet/core/keys/hyperlane_sealevel_multisig_ism_message_id-keypair.json +++ b/rust/sealevel/environments/testnet3/solanadevnet/core/keys/hyperlane_sealevel_multisig_ism_message_id-keypair.json @@ -1 +1 @@ -[63,21,4,198,48,27,204,153,114,92,118,116,234,163,49,14,128,10,0,19,56,226,121,151,6,205,21,108,169,125,212,113,29,16,150,112,133,212,123,146,110,230,188,148,124,117,183,159,93,85,69,97,122,78,86,187,44,166,129,154,160,73,41,186] \ No newline at end of file +[247,149,169,2,196,128,74,124,111,206,244,112,63,16,180,19,219,212,45,229,21,114,33,11,202,148,12,47,22,26,192,78,75,78,53,149,190,51,57,253,29,141,136,215,159,45,181,164,239,148,140,163,30,108,158,76,94,113,11,4,142,0,192,20] \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/solanadevnet/core/program-ids.json b/rust/sealevel/environments/testnet3/solanadevnet/core/program-ids.json index 324e0f3d4..ab3f1f593 100644 --- a/rust/sealevel/environments/testnet3/solanadevnet/core/program-ids.json +++ b/rust/sealevel/environments/testnet3/solanadevnet/core/program-ids.json @@ -1,7 +1,7 @@ { "mailbox": "4v25Dz9RccqUrTzmfHzJMsjd1iVoNrWzeJ4o6GYuJrVn", "validator_announce": "CMHKvdq4CopDf7qXnDCaTybS15QekQeRt4oUB219yxsp", - "multisig_ism_message_id": "2xTVcwDWZgBu69aawCdYHXqH7xQP36iBQ7rN2px1g7ms", + "multisig_ism_message_id": "64xkGhsZbxgP5rBJfpPcpmzkzTGkpSVHiDLcMKS5gmQw", "igp_program_id": "HyPQPLfGXDTAQTxzGp7r1uy18KxS89GKgreSHpjeuYDn", "overhead_igp_account": "AR4hjWPqXEobLvzmv8MTh5k4Se49iTDzbvNX4DpdQGJZ", "igp_account": "7hMPEGdgBQFsjEz3aaNwZp8WMFHs615zAM3erXBDJuJR" diff --git a/rust/sealevel/environments/testnet3/warp-routes/chain-config.json b/rust/sealevel/environments/testnet3/warp-routes/chain-config.json deleted file mode 100644 index 06517c08d..000000000 --- a/rust/sealevel/environments/testnet3/warp-routes/chain-config.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "solanadevnet": { - "chainId": 1399811151, - "name": "solanadevnet", - "publicRpcUrls": [ - { - "http": "https://api.devnet.solana.com" - } - ] - }, - "bsctestnet": { - "chainId": 97, - "name": "bsctestnet", - "publicRpcUrls": [ - { - "http": "https://rpc.ankr.com/bsc_testnet_chapel" - } - ] - }, - "proteustestnet": { - "chainId": 88002, - "name": "proteustestnet", - "publicRpcUrls": [ - { - "http": "https://api.proteus.nautchain.xyz/solana" - } - ] - } -} \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/warp-routes/proteustest/program-ids.json b/rust/sealevel/environments/testnet3/warp-routes/proteustest/program-ids.json index 316560afb..f2372bc90 100644 --- a/rust/sealevel/environments/testnet3/warp-routes/proteustest/program-ids.json +++ b/rust/sealevel/environments/testnet3/warp-routes/proteustest/program-ids.json @@ -1,14 +1,14 @@ { - "bsctestnet": { - "hex": "0x00000000000000000000000031b5234a896fbc4b3e2f7237592d054716762131", - "base58": "111111111111hAc1aTgvQGRBFHrYpXpfUqGyqgk" + "solanadevnet": { + "hex": "0x05b6502b1d91c60ca0c0d0ab20a16ec40c66f2559becc7888a4fc3c0cefff9a5", + "base58": "PJH5QAbxAqrrnSXfH3GHR8icua8CDFZmo97z91xmpvx" }, "proteustestnet": { "hex": "0x00000000000000000000000034a9af13c5555bad0783c220911b9ef59cfdbcef", "base58": "111111111111jZ775N1rpEpJ2M8RAzLNNr9Lh7U" }, - "solanadevnet": { - "hex": "0x05b6502b1d91c60ca0c0d0ab20a16ec40c66f2559becc7888a4fc3c0cefff9a5", - "base58": "PJH5QAbxAqrrnSXfH3GHR8icua8CDFZmo97z91xmpvx" + "bsctestnet": { + "hex": "0x00000000000000000000000031b5234a896fbc4b3e2f7237592d054716762131", + "base58": "111111111111hAc1aTgvQGRBFHrYpXpfUqGyqgk" } } \ No newline at end of file diff --git a/rust/sealevel/libraries/account-utils/src/lib.rs b/rust/sealevel/libraries/account-utils/src/lib.rs index 6301c66f4..aa7fa6974 100644 --- a/rust/sealevel/libraries/account-utils/src/lib.rs +++ b/rust/sealevel/libraries/account-utils/src/lib.rs @@ -154,11 +154,15 @@ where { /// Stores the account data in the given account, reallocing the account /// if necessary, and ensuring it is rent exempt. + /// Requires `_system_program_info` to be passed in despite not being directly + /// used to ensure that a CPI to the system program in the case of a realloc + /// that requires additional lamports for rent exemption will be successful. pub fn store_with_rent_exempt_realloc<'a, 'b>( &self, account_info: &'a AccountInfo<'b>, rent: &Rent, payer_info: &'a AccountInfo<'b>, + _system_program_info: &'a AccountInfo<'b>, ) -> Result<(), ProgramError> { let required_size = self.size(); diff --git a/rust/sealevel/libraries/hyperlane-sealevel-token/src/instruction.rs b/rust/sealevel/libraries/hyperlane-sealevel-token/src/instruction.rs index 35a01f4c6..ae1276a22 100644 --- a/rust/sealevel/libraries/hyperlane-sealevel-token/src/instruction.rs +++ b/rust/sealevel/libraries/hyperlane-sealevel-token/src/instruction.rs @@ -200,3 +200,61 @@ pub fn transfer_ownership_instruction( Ok(instruction) } + +/// Gets an instruction to set the ISM. +pub fn set_interchain_security_module_instruction( + program_id: Pubkey, + owner_payer: Pubkey, + new_interchain_security_module: Option, +) -> Result { + let (token_key, _token_bump) = + Pubkey::try_find_program_address(hyperlane_token_pda_seeds!(), &program_id) + .ok_or(ProgramError::InvalidSeeds)?; + + let ixn = Instruction::SetInterchainSecurityModule(new_interchain_security_module); + + // Accounts: + // 0. [writeable] The token PDA account. + // 1. [signer] The current owner. + let accounts = vec![ + AccountMeta::new(token_key, false), + AccountMeta::new_readonly(owner_payer, true), + ]; + + let instruction = SolanaInstruction { + program_id, + data: ixn.encode()?, + accounts, + }; + + Ok(instruction) +} + +/// Sets the igp for a warp route +pub fn set_igp_instruction( + program_id: Pubkey, + owner_payer: Pubkey, + igp_program_and_account: Option<(Pubkey, InterchainGasPaymasterType)>, +) -> Result { + let (token_key, _token_bump) = + Pubkey::try_find_program_address(hyperlane_token_pda_seeds!(), &program_id) + .ok_or(ProgramError::InvalidSeeds)?; + + let ixn = Instruction::SetInterchainGasPaymaster(igp_program_and_account); + + // Accounts: + // 0. [writeable] The token PDA account. + // 1. [signer] The current owner. + let accounts = vec![ + AccountMeta::new(token_key, false), + AccountMeta::new_readonly(owner_payer, true), + ]; + + let instruction = SolanaInstruction { + program_id, + data: ixn.encode()?, + accounts, + }; + + Ok(instruction) +} diff --git a/rust/sealevel/libraries/hyperlane-sealevel-token/src/processor.rs b/rust/sealevel/libraries/hyperlane-sealevel-token/src/processor.rs index 04587274b..c10482857 100644 --- a/rust/sealevel/libraries/hyperlane-sealevel-token/src/processor.rs +++ b/rust/sealevel/libraries/hyperlane-sealevel-token/src/processor.rs @@ -661,6 +661,7 @@ where token_account, &Rent::get()?, owner_account, + system_program, )?; Ok(()) @@ -709,6 +710,7 @@ where token_account, &Rent::get()?, owner_account, + system_program, )?; Ok(()) @@ -849,6 +851,7 @@ where token_account, &Rent::get()?, owner_account, + system_program, )?; Ok(()) diff --git a/rust/sealevel/programs/helloworld/Cargo.toml b/rust/sealevel/programs/helloworld/Cargo.toml new file mode 100644 index 000000000..b0cbf2eaf --- /dev/null +++ b/rust/sealevel/programs/helloworld/Cargo.toml @@ -0,0 +1,30 @@ +cargo-features = ["workspace-inheritance"] + +[package] +name = "hyperlane-sealevel-hello-world" +version = "0.1.0" +edition = "2021" + +[features] +no-entrypoint = [] +test-client = ["dep:solana-program-test", "dep:solana-sdk", "dep:hyperlane-test-utils", "dep:spl-noop"] + +[dependencies] +borsh.workspace = true +solana-program-test = { workspace = true, optional = true } +solana-program.workspace = true +solana-sdk = { workspace = true, optional = true } +spl-noop = { workspace = true, optional = true } + +access-control = { path = "../../libraries/access-control" } +account-utils = { path = "../../libraries/account-utils" } +hyperlane-core = { path = "../../../hyperlane-core" } +hyperlane-sealevel-mailbox = { path = "../mailbox", features = ["no-entrypoint"] } +hyperlane-sealevel-igp = { path = "../hyperlane-sealevel-igp", features = ["no-entrypoint"] } +hyperlane-sealevel-connection-client = { path = "../../libraries/hyperlane-sealevel-connection-client" } +hyperlane-sealevel-message-recipient-interface = { path = "../../libraries/message-recipient-interface" } +hyperlane-test-utils = { path = "../../libraries/test-utils", optional = true } +serializable-account-meta = { path = "../../libraries/serializable-account-meta" } + +[lib] +crate-type = ["cdylib", "lib"] diff --git a/rust/sealevel/programs/helloworld/src/accounts.rs b/rust/sealevel/programs/helloworld/src/accounts.rs new file mode 100644 index 000000000..14e68c3b4 --- /dev/null +++ b/rust/sealevel/programs/helloworld/src/accounts.rs @@ -0,0 +1,104 @@ +//! HelloWorld accounts. +use std::collections::HashMap; + +use access_control::AccessControl; +use account_utils::{AccountData, SizedData}; +use borsh::{BorshDeserialize, BorshSerialize}; +use hyperlane_core::H256; +use hyperlane_sealevel_connection_client::{ + router::{HyperlaneRouter, RemoteRouterConfig}, + HyperlaneConnectionClient, +}; +use hyperlane_sealevel_igp::accounts::InterchainGasPaymasterType; + +use solana_program::{program_error::ProgramError, pubkey::Pubkey}; + +/// The storage account. +pub type HelloWorldStorageAccount = AccountData; + +/// The storage account's data. +#[derive(BorshSerialize, BorshDeserialize, Debug, Default)] +pub struct HelloWorldStorage { + /// The local domain. + pub local_domain: u32, + /// The mailbox. + pub mailbox: Pubkey, + /// The ISM. + pub ism: Option, + /// The IGP. + pub igp: Option<(Pubkey, InterchainGasPaymasterType)>, + /// The owner. + pub owner: Option, + /// A counter of how many messages have been sent from this contract. + pub sent: u64, + /// A counter of how many messages have been received by this contract. + pub received: u64, + /// Keyed by domain, a counter of how many messages that have been sent + /// from this contract to the domain. + pub sent_to: HashMap, + /// Keyed by domain, a counter of how many messages that have been received + /// by this contract from the domain. + pub received_from: HashMap, + /// Keyed by domain, the router for the remote domain. + pub routers: HashMap, +} + +impl SizedData for HelloWorldStorage { + fn size(&self) -> usize { + // local domain + std::mem::size_of::() + + // mailbox + 32 + + // ism + 1 + 32 + + // igp + 1 + 32 + 1 + 32 + + // owner + 1 + 32 + + // sent + std::mem::size_of::() + + // received + std::mem::size_of::() + + // sent_to + (self.sent_to.len() * (std::mem::size_of::() + std::mem::size_of::())) + + // received_from + (self.received_from.len() * (std::mem::size_of::() + std::mem::size_of::())) + + // routers + (self.routers.len() * (std::mem::size_of::() + 32)) + } +} + +impl AccessControl for HelloWorldStorage { + fn owner(&self) -> Option<&Pubkey> { + self.owner.as_ref() + } + + fn set_owner(&mut self, new_owner: Option) -> Result<(), ProgramError> { + self.owner = new_owner; + Ok(()) + } +} + +impl HyperlaneRouter for HelloWorldStorage { + fn router(&self, origin: u32) -> Option<&H256> { + self.routers.get(&origin) + } + + fn enroll_remote_router(&mut self, config: RemoteRouterConfig) { + self.routers.insert(config.domain, config.router.unwrap()); + } +} + +impl HyperlaneConnectionClient for HelloWorldStorage { + fn mailbox(&self) -> &Pubkey { + &self.mailbox + } + + fn interchain_gas_paymaster(&self) -> Option<&(Pubkey, InterchainGasPaymasterType)> { + self.igp.as_ref() + } + + fn interchain_security_module(&self) -> Option<&Pubkey> { + self.ism.as_ref() + } +} diff --git a/rust/sealevel/programs/helloworld/src/instruction.rs b/rust/sealevel/programs/helloworld/src/instruction.rs new file mode 100644 index 000000000..5c0a42eda --- /dev/null +++ b/rust/sealevel/programs/helloworld/src/instruction.rs @@ -0,0 +1,146 @@ +//! HelloWorld instructions. + +use borsh::{BorshDeserialize, BorshSerialize}; +use hyperlane_sealevel_connection_client::router::RemoteRouterConfig; +use hyperlane_sealevel_igp::accounts::InterchainGasPaymasterType; +use solana_program::{ + instruction::{AccountMeta, Instruction}, + program_error::ProgramError, + pubkey::Pubkey, +}; + +use crate::program_storage_pda_seeds; + +/// Init instruction data. +#[derive(BorshSerialize, BorshDeserialize, Debug)] +pub struct Init { + /// The local domain. + pub local_domain: u32, + /// The mailbox. + pub mailbox: Pubkey, + /// The ISM. + pub ism: Option, + /// The IGP. + pub igp: Option<(Pubkey, InterchainGasPaymasterType)>, + /// The owner. + pub owner: Option, +} + +/// A HelloWorld message. +#[derive(BorshSerialize, BorshDeserialize, Debug)] +pub struct HelloWorldMessage { + /// The destination domain. + pub destination: u32, + /// The message. + pub message: String, +} + +/// Instructions for the program. +#[derive(BorshSerialize, BorshDeserialize, Debug)] +pub enum HelloWorldInstruction { + /// Initializes the program. + Init(Init), + /// Dispatches a message using the dispatch authority. + SendHelloWorld(HelloWorldMessage), + /// Sets the ISM. + SetInterchainSecurityModule(Option), + /// Enrolls remote routers + EnrollRemoteRouters(Vec), +} + +/// Gets an instruction to initialize the program. +pub fn init_instruction( + program_id: Pubkey, + payer: Pubkey, + local_domain: u32, + mailbox: Pubkey, + ism: Option, + igp: Option<(Pubkey, InterchainGasPaymasterType)>, + owner: Option, +) -> Result { + let (program_storage_account, _program_storage_bump) = + Pubkey::try_find_program_address(program_storage_pda_seeds!(), &program_id) + .ok_or(ProgramError::InvalidSeeds)?; + + let init = Init { + local_domain, + mailbox, + ism, + igp, + owner, + }; + + // Accounts: + // 0. [executable] System program. + // 1. [signer] Payer. + // 2. [writeable] Storage PDA. + let accounts = vec![ + AccountMeta::new_readonly(solana_program::system_program::id(), false), + AccountMeta::new_readonly(payer, true), + AccountMeta::new(program_storage_account, false), + ]; + + let instruction = Instruction { + program_id, + data: HelloWorldInstruction::Init(init).try_to_vec()?, + accounts, + }; + + Ok(instruction) +} + +/// Gets an instruction to enroll remote routers. +pub fn enroll_remote_routers_instruction( + program_id: Pubkey, + owner: Pubkey, + configs: Vec, +) -> Result { + let (program_storage_account, _program_storage_bump) = + Pubkey::try_find_program_address(program_storage_pda_seeds!(), &program_id) + .ok_or(ProgramError::InvalidSeeds)?; + + // Accounts: + // 0. [executable] System program. + // 1. [signer] Payer. + // 2. [writeable] Storage PDA. + let accounts = vec![ + AccountMeta::new_readonly(solana_program::system_program::id(), false), + AccountMeta::new(program_storage_account, false), + AccountMeta::new(owner, true), + ]; + + let instruction = Instruction { + program_id, + data: HelloWorldInstruction::EnrollRemoteRouters(configs).try_to_vec()?, + accounts, + }; + + Ok(instruction) +} + +/// Gets an instruction to set the interchain security module. +pub fn set_interchain_security_module_instruction( + program_id: Pubkey, + owner: Pubkey, + ism: Option, +) -> Result { + let (program_storage_account, _program_storage_bump) = + Pubkey::try_find_program_address(program_storage_pda_seeds!(), &program_id) + .ok_or(ProgramError::InvalidSeeds)?; + + // Accounts: + // 0. [writeable] Storage PDA account. + // 1. [signer] Owner. + let accounts = vec![ + AccountMeta::new(program_storage_account, false), + AccountMeta::new(owner, true), + ]; + + let instruction = Instruction { + program_id, + data: HelloWorldInstruction::SetInterchainSecurityModule(ism).try_to_vec()?, + accounts, + }; + + Ok(instruction) +} diff --git a/rust/sealevel/programs/helloworld/src/lib.rs b/rust/sealevel/programs/helloworld/src/lib.rs new file mode 100644 index 000000000..7900575e4 --- /dev/null +++ b/rust/sealevel/programs/helloworld/src/lib.rs @@ -0,0 +1,9 @@ +//! A HelloWorld program that sends and receives messages to & from other routers. + +#![deny(warnings)] +#![deny(missing_docs)] +#![deny(unsafe_code)] + +pub mod accounts; +pub mod instruction; +pub mod processor; diff --git a/rust/sealevel/programs/helloworld/src/processor.rs b/rust/sealevel/programs/helloworld/src/processor.rs new file mode 100644 index 000000000..4e1fdcd92 --- /dev/null +++ b/rust/sealevel/programs/helloworld/src/processor.rs @@ -0,0 +1,542 @@ +//! HelloWorld program. + +use access_control::AccessControl; +use account_utils::{create_pda_account, SizedData}; +use borsh::{BorshDeserialize, BorshSerialize}; + +use hyperlane_sealevel_connection_client::{ + router::{HyperlaneRouterAccessControl, HyperlaneRouterDispatch, RemoteRouterConfig}, + HyperlaneConnectionClient, +}; +use hyperlane_sealevel_igp::accounts::InterchainGasPaymasterType; +use hyperlane_sealevel_mailbox::{ + mailbox_message_dispatch_authority_pda_seeds, mailbox_process_authority_pda_seeds, +}; +use hyperlane_sealevel_message_recipient_interface::{ + HandleInstruction, MessageRecipientInstruction, +}; +use serializable_account_meta::{SerializableAccountMeta, SimulationReturnData}; +use solana_program::{ + account_info::{next_account_info, AccountInfo}, + entrypoint::ProgramResult, + instruction::AccountMeta, + msg, + program::set_return_data, + program_error::ProgramError, + pubkey::Pubkey, + rent::Rent, + system_program, + sysvar::Sysvar, +}; + +use crate::{ + accounts::{HelloWorldStorage, HelloWorldStorageAccount}, + instruction::{HelloWorldInstruction, HelloWorldMessage, Init}, +}; + +/// The amount of gas to pay for. +/// TODO: when we actually enforce gas amounts for messages to Solana, +/// we'll need to revisit this and change HelloWorld to use GasRouter. +pub const HANDLE_GAS_AMOUNT: u64 = 50000; + +/// Seeds relating to the PDA account with program data. +#[macro_export] +macro_rules! program_storage_pda_seeds { + () => {{ + &[b"hello_world", b"-", b"handle", b"-", b"storage"] + }}; + + ($bump_seed:expr) => {{ + &[ + b"hello_world", + b"-", + b"handle", + b"-", + b"storage", + &[$bump_seed], + ] + }}; +} + +#[cfg(not(feature = "no-entrypoint"))] +solana_program::entrypoint!(process_instruction); + +/// The program's entrypoint. +pub fn process_instruction( + program_id: &Pubkey, + accounts: &[AccountInfo], + instruction_data: &[u8], +) -> ProgramResult { + if let Ok(recipient_instruction) = MessageRecipientInstruction::decode(instruction_data) { + return match recipient_instruction { + MessageRecipientInstruction::InterchainSecurityModule => { + get_interchain_security_module(program_id, accounts) + } + MessageRecipientInstruction::InterchainSecurityModuleAccountMetas => { + set_account_meta_return_data(program_id) + } + MessageRecipientInstruction::Handle(instruction) => { + handle(program_id, accounts, instruction) + } + MessageRecipientInstruction::HandleAccountMetas(_) => { + set_account_meta_return_data(program_id) + } + }; + } + + let instruction = HelloWorldInstruction::try_from_slice(instruction_data) + .map_err(|_| ProgramError::InvalidInstructionData)?; + match instruction { + HelloWorldInstruction::Init(mailbox) => init(program_id, accounts, mailbox), + HelloWorldInstruction::SendHelloWorld(hello_world) => { + send_hello_world(program_id, accounts, hello_world) + } + HelloWorldInstruction::SetInterchainSecurityModule(ism) => { + set_interchain_security_module(program_id, accounts, ism) + } + HelloWorldInstruction::EnrollRemoteRouters(configs) => { + enroll_remote_routers(program_id, accounts, configs) + } + } +} + +/// Creates the storage PDA. +/// +/// Accounts: +/// 0. [executable] System program. +/// 1. [signer] Payer. +/// 2. [writeable] Storage PDA. +fn init(program_id: &Pubkey, accounts: &[AccountInfo], init: Init) -> ProgramResult { + let accounts_iter = &mut accounts.iter(); + + // Account 0: System program. + let system_program_info = next_account_info(accounts_iter)?; + if system_program_info.key != &system_program::id() { + return Err(ProgramError::InvalidArgument); + } + + // Account 1: Payer. + let payer_info = next_account_info(accounts_iter)?; + if !payer_info.is_signer { + return Err(ProgramError::MissingRequiredSignature); + } + + // Account 2: Storage PDA. + let storage_info = next_account_info(accounts_iter)?; + let (storage_pda_key, storage_pda_bump_seed) = + Pubkey::find_program_address(program_storage_pda_seeds!(), program_id); + if storage_info.key != &storage_pda_key { + return Err(ProgramError::InvalidArgument); + } + + let storage_account = HelloWorldStorageAccount::from(HelloWorldStorage { + local_domain: init.local_domain, + mailbox: init.mailbox, + ism: init.ism, + igp: init.igp, + owner: init.owner, + ..Default::default() + }); + create_pda_account( + payer_info, + &Rent::get()?, + storage_account.size(), + program_id, + system_program_info, + storage_info, + program_storage_pda_seeds!(storage_pda_bump_seed), + )?; + // Store it + storage_account.store(storage_info, false)?; + + Ok(()) +} + +/// Dispatches a message using the dispatch authority. +/// +/// Accounts: +/// 0. [writeable] Program storage. +/// 1. [executable] The Mailbox program. +/// 2. [writeable] Outbox PDA. +/// 3. [] This program's dispatch authority. +/// 4. [executable] System program. +/// 5. [executable] SPL Noop program. +/// 6. [signer] Payer. +/// 7. [signer] Unique message account. +/// 8. [writeable] Dispatched message PDA. An empty message PDA relating to the seeds +/// `mailbox_dispatched_message_pda_seeds` where the message contents will be stored. +/// ---- if an IGP is configured ---- +/// 9. [executable] The IGP program. +/// 10. [writeable] The IGP program data. +/// 11. [writeable] The gas payment PDA. +/// 12. [] OPTIONAL - The Overhead IGP program, if the configured IGP is an Overhead IGP. +/// 13. [writeable] The IGP account. +/// ---- end if an IGP is configured ---- +fn send_hello_world( + program_id: &Pubkey, + accounts: &[AccountInfo], + hello_world: HelloWorldMessage, +) -> ProgramResult { + let accounts_iter = &mut accounts.iter(); + + // Account 0: Program storage. + let storage_info = next_account_info(accounts_iter)?; + let (storage_pda_key, _storage_pda_bump) = + Pubkey::find_program_address(program_storage_pda_seeds!(), program_id); + if storage_info.key != &storage_pda_key { + return Err(ProgramError::InvalidArgument); + } + let mut storage = + HelloWorldStorageAccount::fetch(&mut &storage_info.data.borrow()[..])?.into_inner(); + + // Account 1: Mailbox program. + let _mailbox_info = next_account_info(accounts_iter)?; + + // Account 2: Outbox PDA. + let mailbox_outbox_info = next_account_info(accounts_iter)?; + + // Account 3: Dispatch authority. + let dispatch_authority_info = next_account_info(accounts_iter)?; + let (expected_dispatch_authority_key, expected_dispatch_authority_bump) = + Pubkey::find_program_address(mailbox_message_dispatch_authority_pda_seeds!(), program_id); + if dispatch_authority_info.key != &expected_dispatch_authority_key { + return Err(ProgramError::InvalidArgument); + } + + // Account 4: System program. + let system_program_info = next_account_info(accounts_iter)?; + + // Account 5: SPL Noop program. + let spl_noop_info = next_account_info(accounts_iter)?; + + // Account 6: Payer. + let payer_info = next_account_info(accounts_iter)?; + + // Account 7: Unique message account. + let unique_message_account_info = next_account_info(accounts_iter)?; + + // Account 8: Dispatched message PDA. + let dispatched_message_info = next_account_info(accounts_iter)?; + + let dispatch_account_metas = vec![ + AccountMeta::new(*mailbox_outbox_info.key, false), + AccountMeta::new_readonly(*dispatch_authority_info.key, true), + AccountMeta::new_readonly(*system_program_info.key, false), + AccountMeta::new_readonly(*spl_noop_info.key, false), + AccountMeta::new(*payer_info.key, true), + AccountMeta::new_readonly(*unique_message_account_info.key, true), + AccountMeta::new(*dispatched_message_info.key, false), + ]; + let dispatch_account_infos = &[ + mailbox_outbox_info.clone(), + dispatch_authority_info.clone(), + system_program_info.clone(), + spl_noop_info.clone(), + payer_info.clone(), + unique_message_account_info.clone(), + dispatched_message_info.clone(), + ]; + + let igp_payment_accounts = + if let Some((igp_program_id, igp_account_type)) = storage.interchain_gas_paymaster() { + // Account 9: The IGP program + let igp_program_account_info = next_account_info(accounts_iter)?; + if igp_program_account_info.key != igp_program_id { + return Err(ProgramError::InvalidArgument); + } + + // Account 10: The IGP program data. + // No verification is performed here, the IGP will do that. + let igp_program_data_account_info = next_account_info(accounts_iter)?; + + // Account 11: The gas payment PDA. + // No verification is performed here, the IGP will do that. + let igp_payment_pda_account_info = next_account_info(accounts_iter)?; + + // Account 12: The configured IGP account. + let configured_igp_account_info = next_account_info(accounts_iter)?; + if configured_igp_account_info.key != igp_account_type.key() { + return Err(ProgramError::InvalidArgument); + } + + // Accounts expected by the IGP's `PayForGas` instruction: + // + // 0. [executable] The system program. + // 1. [signer] The payer. + // 2. [writeable] The IGP program data. + // 3. [signer] Unique gas payment account. + // 4. [writeable] Gas payment PDA. + // 5. [writeable] The IGP account. + // 6. [] Overhead IGP account (optional). + + let mut igp_payment_account_metas = vec![ + AccountMeta::new_readonly(solana_program::system_program::id(), false), + AccountMeta::new(*payer_info.key, true), + AccountMeta::new(*igp_program_data_account_info.key, false), + AccountMeta::new_readonly(*unique_message_account_info.key, true), + AccountMeta::new(*igp_payment_pda_account_info.key, false), + ]; + let mut igp_payment_account_infos = vec![ + system_program_info.clone(), + payer_info.clone(), + igp_program_data_account_info.clone(), + unique_message_account_info.clone(), + igp_payment_pda_account_info.clone(), + ]; + + match igp_account_type { + InterchainGasPaymasterType::Igp(_) => { + igp_payment_account_metas + .push(AccountMeta::new(*configured_igp_account_info.key, false)); + igp_payment_account_infos.push(configured_igp_account_info.clone()); + } + InterchainGasPaymasterType::OverheadIgp(_) => { + // Account 13: The inner IGP account. + let inner_igp_account_info = next_account_info(accounts_iter)?; + + // The inner IGP is expected first, then the overhead IGP. + igp_payment_account_metas.extend([ + AccountMeta::new(*inner_igp_account_info.key, false), + AccountMeta::new_readonly(*configured_igp_account_info.key, false), + ]); + igp_payment_account_infos.extend([ + inner_igp_account_info.clone(), + configured_igp_account_info.clone(), + ]); + } + }; + + Some((igp_payment_account_metas, igp_payment_account_infos)) + } else { + None + }; + + let dispatch_authority_seeds: &[&[u8]] = + mailbox_message_dispatch_authority_pda_seeds!(expected_dispatch_authority_bump); + + if let Some((igp_payment_account_metas, igp_payment_account_infos)) = igp_payment_accounts { + // Dispatch the message and pay for gas. + storage.dispatch_with_gas( + program_id, + dispatch_authority_seeds, + hello_world.destination, + hello_world.message.into(), + HANDLE_GAS_AMOUNT, + dispatch_account_metas, + dispatch_account_infos, + igp_payment_account_metas, + &igp_payment_account_infos, + )?; + } else { + // Dispatch the message. + storage.dispatch( + program_id, + dispatch_authority_seeds, + hello_world.destination, + hello_world.message.into(), + dispatch_account_metas, + dispatch_account_infos, + )?; + } + + storage.sent += 1; + storage + .sent_to + .entry(hello_world.destination) + .and_modify(|c| *c += 1) + .or_insert(1); + + // Store it + HelloWorldStorageAccount::from(storage).store(storage_info, false)?; + + Ok(()) +} + +/// Handles a message. +/// +/// Accounts: +/// 0. [writeable] Process authority specific to this program. +/// 1. [] Storage PDA account. +pub fn handle( + program_id: &Pubkey, + accounts: &[AccountInfo], + handle: HandleInstruction, +) -> ProgramResult { + let accounts_iter = &mut accounts.iter(); + + // Account 0: Process authority specific to this program. + let process_authority = next_account_info(accounts_iter)?; + + // Account 1: Storage PDA account. + let storage_info = next_account_info(accounts_iter)?; + let mut storage = + HelloWorldStorageAccount::fetch(&mut &storage_info.data.borrow()[..])?.into_inner(); + + // Verify the process authority + let (expected_process_authority_key, _expected_process_authority_bump) = + Pubkey::find_program_address( + mailbox_process_authority_pda_seeds!(program_id), + &storage.mailbox, + ); + if process_authority.key != &expected_process_authority_key { + return Err(ProgramError::InvalidArgument); + } + if !process_authority.is_signer { + return Err(ProgramError::MissingRequiredSignature); + } + + // Increment counters + storage.received += 1; + storage + .received_from + .entry(handle.origin) + .and_modify(|c| *c += 1) + .or_insert(1); + + let local_domain = storage.local_domain; + + // Store it. + // We don't expect the size of the storage account to change because this is accounted for + // when a remote router is enrolled. + HelloWorldStorageAccount::from(storage).store(storage_info, false)?; + + msg!( + "Received hello world: origin {}, local domain {}, sender {}, message {}", + handle.origin, + local_domain, + handle.sender, + std::str::from_utf8(&handle.message).unwrap() + ); + + Ok(()) +} + +/// Accounts: +/// 0. [writeable] Storage PDA account. +/// 1. [signer] Owner. +fn set_interchain_security_module( + program_id: &Pubkey, + accounts: &[AccountInfo], + ism: Option, +) -> ProgramResult { + let accounts_iter = &mut accounts.iter(); + + // Account 0: Storage PDA account. + // Not bothering with validity checks because this is a test program + let storage_info = next_account_info(accounts_iter)?; + let (expected_storage_pda_key, _expected_storage_pda_bump) = + Pubkey::find_program_address(program_storage_pda_seeds!(), program_id); + if storage_info.key != &expected_storage_pda_key { + return Err(ProgramError::InvalidArgument); + } + let mut storage = + HelloWorldStorageAccount::fetch(&mut &storage_info.data.borrow()[..])?.into_inner(); + + // Account 1: Owner. + let owner_info = next_account_info(accounts_iter)?; + storage.ensure_owner_signer(owner_info)?; + + storage.ism = ism; + + // Store it + HelloWorldStorageAccount::from(storage).store(storage_info, false)?; + + Ok(()) +} + +/// Accounts: +/// 0. [] Storage PDA account. +fn get_interchain_security_module(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { + let accounts_iter = &mut accounts.iter(); + + // Account 0: Storage PDA account. + let storage_info = next_account_info(accounts_iter)?; + let (expected_storage_pda_key, _expected_storage_pda_bump) = + Pubkey::find_program_address(program_storage_pda_seeds!(), program_id); + if storage_info.key != &expected_storage_pda_key { + return Err(ProgramError::InvalidArgument); + } + let storage = + HelloWorldStorageAccount::fetch(&mut &storage_info.data.borrow()[..])?.into_inner(); + + set_return_data( + &storage + .ism + .try_to_vec() + .map_err(|err| ProgramError::BorshIoError(err.to_string()))?[..], + ); + + Ok(()) +} + +fn set_account_meta_return_data(program_id: &Pubkey) -> ProgramResult { + let (storage_pda_key, _storage_pda_bump) = + Pubkey::find_program_address(program_storage_pda_seeds!(), program_id); + + let account_metas: Vec = + vec![AccountMeta::new(storage_pda_key, false).into()]; + + // Wrap it in the SimulationReturnData because serialized account_metas + // may end with zero byte(s), which are incorrectly truncated as + // simulated transaction return data. + // See `SimulationReturnData` for details. + let bytes = SimulationReturnData::new(account_metas) + .try_to_vec() + .map_err(|err| ProgramError::BorshIoError(err.to_string()))?; + set_return_data(&bytes[..]); + + Ok(()) +} + +/// Enrolls remote routers. +/// +/// Accounts: +/// 0. [executable] System program. +/// 1. [writeable] Storage PDA account. +/// 2. [signer] Owner. +fn enroll_remote_routers( + program_id: &Pubkey, + accounts: &[AccountInfo], + configs: Vec, +) -> ProgramResult { + let accounts_iter = &mut accounts.iter(); + + // Account 0: System program. + let system_program_info = next_account_info(accounts_iter)?; + if system_program_info.key != &system_program::id() { + return Err(ProgramError::InvalidArgument); + } + + // Account 1: Storage PDA account. + let storage_info = next_account_info(accounts_iter)?; + let (expected_storage_pda_key, _expected_storage_pda_bump) = + Pubkey::find_program_address(program_storage_pda_seeds!(), program_id); + if storage_info.key != &expected_storage_pda_key { + return Err(ProgramError::InvalidArgument); + } + let mut storage = + HelloWorldStorageAccount::fetch(&mut &storage_info.data.borrow()[..])?.into_inner(); + + // Account 2: Owner. + let owner_info = next_account_info(accounts_iter)?; + storage.ensure_owner_signer(owner_info)?; + + for config in &configs { + // If the sent_to or received_from map doesn't have an entry for this domain yet, + // init it to 0. This is important so that we realloc here if necessary. + storage.sent_to.entry(config.domain).or_insert(0); + storage.received_from.entry(config.domain).or_insert(0); + } + + storage.enroll_remote_routers_only_owner(owner_info, configs)?; + + // Store it, & realloc if needed + HelloWorldStorageAccount::from(storage).store_with_rent_exempt_realloc( + storage_info, + &Rent::get()?, + owner_info, + system_program_info, + )?; + + Ok(()) +} diff --git a/rust/sealevel/programs/hyperlane-sealevel-igp/src/processor.rs b/rust/sealevel/programs/hyperlane-sealevel-igp/src/processor.rs index 5ee90985c..044ce42fc 100644 --- a/rust/sealevel/programs/hyperlane-sealevel-igp/src/processor.rs +++ b/rust/sealevel/programs/hyperlane-sealevel-igp/src/processor.rs @@ -594,7 +594,6 @@ fn set_destination_gas_overheads( let accounts_iter = &mut accounts.iter(); // Account 0: System program. - // Required to invoke `system_instruction::transfer` in `store_with_rent_exempt_realloc`. let system_program_info = next_account_info(accounts_iter)?; if system_program_info.key != &solana_program::system_program::id() { return Err(ProgramError::IncorrectProgramId); @@ -621,6 +620,7 @@ fn set_destination_gas_overheads( overhead_igp_info, &Rent::get()?, owner_info, + system_program_info, )?; Ok(()) @@ -659,7 +659,12 @@ fn set_gas_oracle_configs( let igp_account = IgpAccount::new(igp.into()); - igp_account.store_with_rent_exempt_realloc(igp_info, &Rent::get()?, owner_info)?; + igp_account.store_with_rent_exempt_realloc( + igp_info, + &Rent::get()?, + owner_info, + system_program_info, + )?; Ok(()) } diff --git a/rust/sealevel/programs/ism/multisig-ism-message-id/src/instruction.rs b/rust/sealevel/programs/ism/multisig-ism-message-id/src/instruction.rs index 30c605a97..0732c705f 100644 --- a/rust/sealevel/programs/ism/multisig-ism-message-id/src/instruction.rs +++ b/rust/sealevel/programs/ism/multisig-ism-message-id/src/instruction.rs @@ -5,11 +5,12 @@ use solana_program::{ instruction::{AccountMeta, Instruction as SolanaInstruction}, program_error::ProgramError, pubkey::Pubkey, + system_program, }; use std::collections::HashSet; -use crate::{access_control_pda_seeds, error::Error}; +use crate::{access_control_pda_seeds, domain_data_pda_seeds, error::Error}; #[derive(BorshDeserialize, BorshSerialize, Debug, PartialEq)] pub enum Instruction { @@ -143,6 +144,44 @@ pub fn transfer_ownership_instruction( Ok(instruction) } +/// Greats a SetValidatorsAndThreshold instruction. +pub fn set_validators_and_threshold_instruction( + program_id: Pubkey, + owner_payer: Pubkey, + domain: u32, + validators_and_threshold: ValidatorsAndThreshold, +) -> Result { + let (access_control_pda_key, _access_control_pda_bump) = + Pubkey::find_program_address(access_control_pda_seeds!(), &program_id); + + let (domain_data_pda_key, _domain_data_pda_bump) = + Pubkey::find_program_address(domain_data_pda_seeds!(domain), &program_id); + + let ixn = Instruction::SetValidatorsAndThreshold(Domained { + domain, + data: validators_and_threshold.clone(), + }); + + // Accounts: + // 0. `[signer]` The access control owner and payer of the domain PDA. + // 1. `[]` The access control PDA account. + // 2. `[writable]` The PDA relating to the provided domain. + // 3. `[executable]` OPTIONAL - The system program account. Required if creating the domain PDA. + let accounts = vec![ + AccountMeta::new(owner_payer, true), + AccountMeta::new_readonly(access_control_pda_key, false), + AccountMeta::new(domain_data_pda_key, false), + AccountMeta::new_readonly(system_program::id(), false), + ]; + + let instruction = SolanaInstruction { + program_id, + data: ixn.encode().unwrap(), + accounts, + }; + Ok(instruction) +} + #[cfg(test)] mod test { use super::*; diff --git a/rust/sealevel/programs/mailbox-test/src/functional.rs b/rust/sealevel/programs/mailbox-test/src/functional.rs index 3f46d6cfa..c826b6ed0 100644 --- a/rust/sealevel/programs/mailbox-test/src/functional.rs +++ b/rust/sealevel/programs/mailbox-test/src/functional.rs @@ -2,39 +2,35 @@ use borsh::BorshDeserialize; use hyperlane_core::{ accumulator::incremental::IncrementalMerkle as MerkleTree, HyperlaneMessage, H256, }; - -use solana_program::{ - instruction::{AccountMeta, Instruction}, - pubkey::Pubkey, - system_program, -}; -use solana_program_test::*; -use solana_sdk::{ - instruction::InstructionError, - message::Message, - signature::Signer, - signer::keypair::Keypair, - transaction::{Transaction, TransactionError}, -}; - use hyperlane_sealevel_mailbox::{ accounts::{Inbox, InboxAccount, Outbox}, error::Error as MailboxError, instruction::{Instruction as MailboxInstruction, OutboxDispatch}, mailbox_dispatched_message_pda_seeds, }; - use hyperlane_sealevel_test_ism::{program::TestIsmError, test_client::TestIsmTestClient}; use hyperlane_sealevel_test_send_receiver::{ program::{HandleMode, IsmReturnDataMode, TestSendReceiverError}, test_client::TestSendReceiverTestClient, }; - use hyperlane_test_utils::{ assert_transaction_error, clone_keypair, get_process_account_metas, get_recipient_ism, initialize_mailbox, mailbox_id, new_funded_keypair, process, process_instruction, process_with_accounts, }; +use solana_program::{ + instruction::{AccountMeta, Instruction}, + pubkey::Pubkey, + system_program, +}; +use solana_program_test::*; +use solana_sdk::{ + instruction::InstructionError, + message::Message, + signature::Signer, + signer::keypair::Keypair, + transaction::{Transaction, TransactionError}, +}; use crate::utils::{ assert_dispatched_message, assert_inbox, assert_message_not_processed, assert_outbox, @@ -463,10 +459,10 @@ async fn test_get_recipient_ism_when_specified() { let recipient_id = test_send_receiver.id(); - let ism = Some(Pubkey::new_unique()); + let ism = Pubkey::new_unique(); test_send_receiver - .set_ism(ism, IsmReturnDataMode::EncodeOption) + .set_ism(Some(ism), IsmReturnDataMode::EncodeOption) .await .unwrap(); @@ -474,7 +470,7 @@ async fn test_get_recipient_ism_when_specified() { get_recipient_ism(&mut banks_client, &payer, &mailbox_accounts, recipient_id) .await .unwrap(); - assert_eq!(recipient_ism, ism.unwrap()); + assert_eq!(recipient_ism, ism); } #[tokio::test] diff --git a/rust/sealevel/programs/mailbox/src/instruction.rs b/rust/sealevel/programs/mailbox/src/instruction.rs index 9f130b502..78a553f92 100644 --- a/rust/sealevel/programs/mailbox/src/instruction.rs +++ b/rust/sealevel/programs/mailbox/src/instruction.rs @@ -142,3 +142,31 @@ pub fn transfer_ownership_instruction( }; Ok(instruction) } + +/// Creates an InboxSetDefaultIsm instruction. +pub fn set_default_ism_instruction( + program_id: Pubkey, + owner_payer: Pubkey, + default_ism: Pubkey, +) -> Result { + let (inbox_account, _inbox_bump) = + Pubkey::try_find_program_address(mailbox_inbox_pda_seeds!(), &program_id) + .ok_or(ProgramError::InvalidSeeds)?; + let (outbox_account, _outbox_bump) = + Pubkey::try_find_program_address(mailbox_outbox_pda_seeds!(), &program_id) + .ok_or(ProgramError::InvalidSeeds)?; + + // 0. [writeable] - The Inbox PDA account. + // 1. [] - The Outbox PDA account. + // 2. [signer] - The owner of the Mailbox. + let instruction = SolanaInstruction { + program_id, + data: Instruction::InboxSetDefaultIsm(default_ism).into_instruction_data()?, + accounts: vec![ + AccountMeta::new(inbox_account, false), + AccountMeta::new_readonly(outbox_account, false), + AccountMeta::new(owner_payer, true), + ], + }; + Ok(instruction) +} diff --git a/rust/sealevel/programs/test-send-receiver/src/program.rs b/rust/sealevel/programs/test-send-receiver/src/program.rs index 66e7c86c6..0f2bbc37a 100644 --- a/rust/sealevel/programs/test-send-receiver/src/program.rs +++ b/rust/sealevel/programs/test-send-receiver/src/program.rs @@ -5,9 +5,7 @@ use account_utils::{create_pda_account, AccountData, SizedData}; use borsh::{BorshDeserialize, BorshSerialize}; use hyperlane_sealevel_mailbox::{ instruction::{InboxProcess, Instruction as MailboxInstruction, OutboxDispatch}, - mailbox_message_dispatch_authority_pda_seeds, - mailbox_process_authority_pda_seeds, - // mailbox_inbox_pda_seeds + mailbox_message_dispatch_authority_pda_seeds, mailbox_process_authority_pda_seeds, }; use hyperlane_sealevel_message_recipient_interface::{ HandleInstruction, MessageRecipientInstruction, diff --git a/rust/utils/run-locally/src/solana.rs b/rust/utils/run-locally/src/solana.rs index d35f2bf60..db3b8fb82 100644 --- a/rust/utils/run-locally/src/solana.rs +++ b/rust/utils/run-locally/src/solana.rs @@ -51,8 +51,7 @@ const SOLANA_KEYPAIR: &str = "config/test-sealevel-keys/test_deployer-keypair.js const SOLANA_DEPLOYER_ACCOUNT: &str = "config/test-sealevel-keys/test_deployer-account.json"; const SOLANA_WARPROUTE_TOKEN_CONFIG_FILE: &str = "sealevel/environments/local-e2e/warp-routes/testwarproute/token-config.json"; -const SOLANA_CHAIN_CONFIG_FILE: &str = - "sealevel/environments/local-e2e/warp-routes/chain-config.json"; +const SOLANA_CHAIN_CONFIG_FILE: &str = "sealevel/environments/local-e2e/chain-config.json"; const SOLANA_ENVS_DIR: &str = "sealevel/environments"; const SOLANA_ENV_NAME: &str = "local-e2e"; @@ -314,8 +313,10 @@ pub fn _initiate_solana_hyperlane_transfer( sealevel_client(&solana_cli_tools_path, &solana_config_path) .cmd("igp") .cmd("pay-for-gas") - .cmd("GwHaw8ewMyzZn9vvrZEnTEAAYpLdkGYs195XWcLDCN4U") - .cmd(message_id) + .arg("program-id", "GwHaw8ewMyzZn9vvrZEnTEAAYpLdkGYs195XWcLDCN4U") + .arg("message-id", message_id) + .arg("destination-domain", SOLANA_REMOTE_CHAIN_ID) + .arg("gas", "100000") .run() .join(); } diff --git a/solidity/contracts/token/HypERC20.sol b/solidity/contracts/token/HypERC20.sol index 1dac128a8..fe1c2a070 100644 --- a/solidity/contracts/token/HypERC20.sol +++ b/solidity/contracts/token/HypERC20.sol @@ -40,6 +40,7 @@ contract HypERC20 is ERC20Upgradeable, TokenRouter { function balanceOf(address _account) public view + virtual override(TokenRouter, ERC20Upgradeable) returns (uint256) { @@ -67,7 +68,7 @@ contract HypERC20 is ERC20Upgradeable, TokenRouter { address _recipient, uint256 _amount, bytes calldata // no metadata - ) internal override { + ) internal virtual override { _mint(_recipient, _amount); } } diff --git a/solidity/contracts/token/HypERC20Collateral.sol b/solidity/contracts/token/HypERC20Collateral.sol index 6bde836c0..12ea51375 100644 --- a/solidity/contracts/token/HypERC20Collateral.sol +++ b/solidity/contracts/token/HypERC20Collateral.sol @@ -55,7 +55,7 @@ contract HypERC20Collateral is TokenRouter { address _recipient, uint256 _amount, bytes calldata // no metadata - ) internal override { + ) internal virtual override { wrappedToken.safeTransfer(_recipient, _amount); } } diff --git a/solidity/contracts/token/extensions/FastHypERC20.sol b/solidity/contracts/token/extensions/FastHypERC20.sol new file mode 100644 index 000000000..5d37af809 --- /dev/null +++ b/solidity/contracts/token/extensions/FastHypERC20.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.8.0; + +import {HypERC20} from "../HypERC20.sol"; +import {FastTokenRouter} from "../libs/FastTokenRouter.sol"; +import {TokenRouter} from "../libs/TokenRouter.sol"; + +import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; + +/** + * @title Hyperlane ERC20 Token Router that extends ERC20 with remote transfer functionality. + * @author Abacus Works + * @dev Supply on each chain is not constant but the aggregate supply across all chains is. + */ +contract FastHypERC20 is FastTokenRouter, HypERC20 { + constructor(uint8 __decimals, address _mailbox) + HypERC20(__decimals, _mailbox) + {} + + /** + * @dev delegates transfer logic to `_transferTo`. + * @inheritdoc TokenRouter + */ + function _handle( + uint32 _origin, + bytes32 _sender, + bytes calldata _message + ) internal virtual override(FastTokenRouter, TokenRouter) { + FastTokenRouter._handle(_origin, _sender, _message); + } + + /** + * @dev Mints `_amount` of tokens to `_recipient`. + * @inheritdoc FastTokenRouter + */ + function _fastTransferTo(address _recipient, uint256 _amount) + internal + override + { + _mint(_recipient, _amount); + } + + /** + * @dev Burns `_amount` of tokens from `_recipient`. + * @inheritdoc FastTokenRouter + */ + function _fastRecieveFrom(address _sender, uint256 _amount) + internal + override + { + _burn(_sender, _amount); + } + + function balanceOf(address _account) + public + view + virtual + override(HypERC20, TokenRouter) + returns (uint256) + { + return ERC20Upgradeable.balanceOf(_account); + } +} diff --git a/solidity/contracts/token/extensions/FastHypERC20Collateral.sol b/solidity/contracts/token/extensions/FastHypERC20Collateral.sol new file mode 100644 index 000000000..98152e144 --- /dev/null +++ b/solidity/contracts/token/extensions/FastHypERC20Collateral.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.8.0; + +import {HypERC20Collateral} from "../HypERC20Collateral.sol"; +import {FastTokenRouter} from "../libs/FastTokenRouter.sol"; +import {TokenRouter} from "../libs/TokenRouter.sol"; + +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +/** + * @title Hyperlane ERC20 Token Collateral that wraps an existing ERC20 with remote transfer functionality. + * @author Abacus Works + */ +contract FastHypERC20Collateral is FastTokenRouter, HypERC20Collateral { + using SafeERC20 for IERC20; + + /** + * @notice Constructor + * @param erc20 Address of the token to keep as collateral + */ + constructor(address erc20, address _mailbox) + HypERC20Collateral(erc20, _mailbox) + {} + + /** + * @dev delegates transfer logic to `_transferTo`. + * @inheritdoc FastTokenRouter + */ + function _handle( + uint32 _origin, + bytes32 _sender, + bytes calldata _message + ) internal virtual override(FastTokenRouter, TokenRouter) { + FastTokenRouter._handle(_origin, _sender, _message); + } + + /** + * @dev Transfers `_amount` of `wrappedToken` to `_recipient`. + * @inheritdoc FastTokenRouter + */ + function _fastTransferTo(address _recipient, uint256 _amount) + internal + override + { + wrappedToken.safeTransfer(_recipient, _amount); + } + + /** + * @dev Transfers in `_amount` of `wrappedToken` from `_recipient`. + * @inheritdoc FastTokenRouter + */ + function _fastRecieveFrom(address _sender, uint256 _amount) + internal + override + { + wrappedToken.safeTransferFrom(_sender, address(this), _amount); + } +} diff --git a/solidity/contracts/token/libs/FastTokenRouter.sol b/solidity/contracts/token/libs/FastTokenRouter.sol new file mode 100644 index 000000000..bba44d49d --- /dev/null +++ b/solidity/contracts/token/libs/FastTokenRouter.sol @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.8.0; + +import {TypeCasts} from "../../libs/TypeCasts.sol"; + +import {TokenMessage} from "./TokenMessage.sol"; +import {TokenRouter} from "./TokenRouter.sol"; + +/** + * @title Common FastTokenRouter functionality for ERC20 Tokens with remote transfer support. + * @author Abacus Works + */ +abstract contract FastTokenRouter is TokenRouter { + using TypeCasts for bytes32; + using TokenMessage for bytes; + + uint256 public fastTransferId; + // maps `fastTransferId` to the filler address. + mapping(bytes32 => address) filledFastTransfers; + + /** + * @dev delegates transfer logic to `_transferTo`. + * @inheritdoc TokenRouter + */ + function _handle( + uint32 _origin, + bytes32, + bytes calldata _message + ) internal virtual override { + bytes32 recipient = _message.recipient(); + uint256 amount = _message.amount(); + bytes calldata metadata = _message.metadata(); + _transferTo(recipient.bytes32ToAddress(), amount, _origin, metadata); + emit ReceivedTransferRemote(_origin, recipient, amount); + } + + /** + * @dev Transfers `_amount` of token to `_recipient`/`fastFiller` who provided LP. + * @dev Called by `handle` after message decoding. + */ + function _transferTo( + address _recipient, + uint256 _amount, + uint32 _origin, + bytes calldata _metadata + ) internal virtual { + address _tokenRecipient = _getTokenRecipient( + _recipient, + _amount, + _origin, + _metadata + ); + + _fastTransferTo(_tokenRecipient, _amount); + } + + /** + * @dev allows an external user to full an unfilled fast transfer order. + * @param _recipient The recepient of the wrapped token on base chain. + * @param _amount The amount of wrapped tokens that is being bridged. + * @param _fastFee The fee the bridging entity will pay. + * @param _fastTransferId Id assigned on the remote chain to uniquely identify the transfer. + */ + function fillFastTransfer( + address _recipient, + uint256 _amount, + uint256 _fastFee, + uint32 _origin, + uint256 _fastTransferId + ) external virtual { + bytes32 filledFastTransfersKey = _getFastTransfersKey( + _origin, + _fastTransferId, + _amount, + _fastFee, + _recipient + ); + require( + filledFastTransfers[filledFastTransfersKey] == address(0), + "request already filled" + ); + + filledFastTransfers[filledFastTransfersKey] = msg.sender; + + _fastRecieveFrom(msg.sender, _amount - _fastFee); + _fastTransferTo(_recipient, _amount - _fastFee); + } + + /** + * @dev Transfers `_amountOrId` token to `_recipient` on `_destination` domain. + * @dev Delegates transfer logic to `_fastTransferFromSender` implementation. + * @dev Emits `SentTransferRemote` event on the origin chain. + * @param _destination The identifier of the destination chain. + * @param _recipient The address of the recipient on the destination chain. + * @param _amountOrId The amount or identifier of tokens to be sent to the remote recipient. + * @return messageId The identifier of the dispatched message. + */ + function fastTransferRemote( + uint32 _destination, + bytes32 _recipient, + uint256 _amountOrId, + uint256 _fastFee + ) public payable virtual returns (bytes32 messageId) { + uint256 _fastTransferId = fastTransferId + 1; + fastTransferId = _fastTransferId; + bytes memory metadata = _fastTransferFromSender( + _amountOrId, + _fastFee, + _fastTransferId + ); + + messageId = _dispatch( + _destination, + TokenMessage.format(_recipient, _amountOrId, metadata) + ); + emit SentTransferRemote(_destination, _recipient, _amountOrId); + } + + /** + * @dev Burns `_amount` of token from `msg.sender` balance. + * @dev Pays `_fastFee` of tokens to LP on source chain. + * @dev Returns `fastFee` as bytes in the form of metadata. + */ + function _fastTransferFromSender( + uint256 _amount, + uint256 _fastFee, + uint256 _fastTransferId + ) internal virtual returns (bytes memory) { + _fastRecieveFrom(msg.sender, _amount); + return abi.encode(_fastFee, _fastTransferId); + } + + /** + * @dev returns an address that indicates who should recieve the bridged tokens. + * @dev if _fastFees was inlcuded and someone filled the order before the mailbox made the contract call, the filler gets the funds. + */ + function _getTokenRecipient( + address _recipient, + uint256 _amount, + uint32 _origin, + bytes calldata _metadata + ) internal view returns (address) { + if (_metadata.length == 0) { + return _recipient; + } + + // decode metadata to extract `_fastFee` and `_fastTransferId`. + (uint256 _fastFee, uint256 _fastTransferId) = abi.decode( + _metadata, + (uint256, uint256) + ); + + address _fillerAddress = filledFastTransfers[ + _getFastTransfersKey( + _origin, + _fastTransferId, + _amount, + _fastFee, + _recipient + ) + ]; + if (_fillerAddress != address(0)) { + return _fillerAddress; + } + + return _recipient; + } + + /** + * @dev generates the key for storing the filler address of fast transfers. + */ + function _getFastTransfersKey( + uint32 _origin, + uint256 _fastTransferId, + uint256 _amount, + uint256 _fastFee, + address _recipient + ) internal pure returns (bytes32) { + return + keccak256( + abi.encodePacked( + _origin, + _fastTransferId, + _amount, + _fastFee, + _recipient + ) + ); + } + + /** + * @dev Should transfer `_amount` of tokens to `_recipient`. + * @dev The implementation is delegated. + */ + function _fastTransferTo(address _recipient, uint256 _amount) + internal + virtual; + + /** + * @dev Should collect `amount` of tokens from `_sender`. + * @dev The implementation is delegated. + */ + function _fastRecieveFrom(address _sender, uint256 _amount) + internal + virtual; +} diff --git a/solidity/contracts/token/libs/TokenRouter.sol b/solidity/contracts/token/libs/TokenRouter.sol index 76feee927..40b965812 100644 --- a/solidity/contracts/token/libs/TokenRouter.sol +++ b/solidity/contracts/token/libs/TokenRouter.sol @@ -111,7 +111,7 @@ abstract contract TokenRouter is GasRouter { uint32 _origin, bytes32, bytes calldata _message - ) internal override { + ) internal virtual override { bytes32 recipient = _message.recipient(); uint256 amount = _message.amount(); bytes calldata metadata = _message.metadata(); diff --git a/typescript/helloworld/src/app/app.ts b/typescript/helloworld/src/app/app.ts index 7ecf5badf..44c1fccee 100644 --- a/typescript/helloworld/src/app/app.ts +++ b/typescript/helloworld/src/app/app.ts @@ -9,7 +9,7 @@ import { MultiProvider, RouterApp, } from '@hyperlane-xyz/sdk'; -import { debug } from '@hyperlane-xyz/utils'; +import { Address, debug } from '@hyperlane-xyz/utils'; import { HelloWorld } from '../types'; @@ -21,8 +21,9 @@ export class HelloWorldApp extends RouterApp { public readonly core: HyperlaneCore, contractsMap: HyperlaneContractsMap, multiProvider: MultiProvider, + foreignDeployments: ChainMap
= {}, ) { - super(contractsMap, multiProvider); + super(contractsMap, multiProvider, undefined, foreignDeployments); } router(contracts: HyperlaneContracts): HelloWorld { diff --git a/typescript/helloworld/src/app/types.ts b/typescript/helloworld/src/app/types.ts index 52483d4cd..8b9800ada 100644 --- a/typescript/helloworld/src/app/types.ts +++ b/typescript/helloworld/src/app/types.ts @@ -1,4 +1,4 @@ export type StatCounts = { - sent: number | bigint; - received: number | bigint; + sent: number; + received: number; }; diff --git a/typescript/helloworld/src/multiProtocolApp/evmAdapter.ts b/typescript/helloworld/src/multiProtocolApp/evmAdapter.ts index e2f903029..481c1eb34 100644 --- a/typescript/helloworld/src/multiProtocolApp/evmAdapter.ts +++ b/typescript/helloworld/src/multiProtocolApp/evmAdapter.ts @@ -9,7 +9,6 @@ import { } from '@hyperlane-xyz/sdk'; import { Address } from '@hyperlane-xyz/utils'; -import { StatCounts } from '../app/types'; import { HelloWorld, HelloWorld__factory } from '../types'; import { IHelloWorldAdapter } from './types'; @@ -61,22 +60,11 @@ export class EvmHelloWorldAdapter return { transaction: tx, type: ProviderType.EthersV5 }; } - async channelStats( - destination: ChainName, - destinationMailbox: Address, - ): Promise { - const originDomain = this.multiProvider.getDomainId(this.chainName); + async sentStat(destination: ChainName): Promise { const destinationDomain = this.multiProvider.getDomainId(destination); const originContract = this.getConnectedContract(); const sent = await originContract.sentTo(destinationDomain); - const destinationProvider = - this.multiProvider.getEthersV5Provider(destination); - const destinationContract = HelloWorld__factory.connect( - destinationMailbox, - destinationProvider, - ); - const received = await destinationContract.sentTo(originDomain); - return { sent: sent.toNumber(), received: received.toNumber() }; + return sent.toNumber(); } override getConnectedContract(): HelloWorld { diff --git a/typescript/helloworld/src/multiProtocolApp/multiProtocolApp.ts b/typescript/helloworld/src/multiProtocolApp/multiProtocolApp.ts index f969acbcd..39a0964e7 100644 --- a/typescript/helloworld/src/multiProtocolApp/multiProtocolApp.ts +++ b/typescript/helloworld/src/multiProtocolApp/multiProtocolApp.ts @@ -38,11 +38,15 @@ export class HelloMultiProtocolApp extends MultiProtocolRouterApp< ); } - channelStats(origin: ChainName, destination: ChainName): Promise { - return this.adapter(origin).channelStats( - destination, - this.addresses[destination].mailbox, - ); + async channelStats( + origin: ChainName, + destination: ChainName, + ): Promise { + const [sent, received] = await Promise.all([ + this.adapter(origin).sentStat(destination), + this.adapter(destination).sentStat(origin), + ]); + return { sent, received }; } async stats(): Promise>> { diff --git a/typescript/helloworld/src/multiProtocolApp/sealevelAdapter.ts b/typescript/helloworld/src/multiProtocolApp/sealevelAdapter.ts index 22bea23e5..c3b3fcb2f 100644 --- a/typescript/helloworld/src/multiProtocolApp/sealevelAdapter.ts +++ b/typescript/helloworld/src/multiProtocolApp/sealevelAdapter.ts @@ -24,8 +24,6 @@ import { } from '@hyperlane-xyz/sdk'; import { Address, Domain } from '@hyperlane-xyz/utils'; -import { StatCounts } from '../app/types'; - import { IHelloWorldAdapter } from './types'; export class SealevelHelloWorldAdapter @@ -164,21 +162,20 @@ export class SealevelHelloWorldAdapter ); } - async channelStats(_destination: ChainName): Promise { + async sentStat(destination: ChainName): Promise { + const destinationDomain = this.multiProvider.getDomainId(destination); const data = await this.getAccountInfo(); - return { sent: data.sent, received: data.received }; + return Number(data.sent_to.get(destinationDomain) || 0); } async getAccountInfo(): Promise { const address = this.addresses.router; const connection = this.getProvider(); - const msgRecipientPda = this.deriveMessageRecipientPda(address); - const accountInfo = await connection.getAccountInfo(msgRecipientPda); + const pda = this.deriveProgramStoragePDA(address); + const accountInfo = await connection.getAccountInfo(pda); if (!accountInfo) - throw new Error( - `No account info found for ${msgRecipientPda.toBase58()}}`, - ); + throw new Error(`No account info found for ${pda.toBase58()}}`); const accountData = deserializeUnchecked( HelloWorldDataSchema, diff --git a/typescript/helloworld/src/multiProtocolApp/types.ts b/typescript/helloworld/src/multiProtocolApp/types.ts index 2f9eb5e5c..06d775768 100644 --- a/typescript/helloworld/src/multiProtocolApp/types.ts +++ b/typescript/helloworld/src/multiProtocolApp/types.ts @@ -5,8 +5,6 @@ import { } from '@hyperlane-xyz/sdk'; import { Address } from '@hyperlane-xyz/utils'; -import { StatCounts } from '../app/types'; - export interface IHelloWorldAdapter extends IRouterAdapter { populateSendHelloTx: ( destination: ChainName, @@ -15,10 +13,5 @@ export interface IHelloWorldAdapter extends IRouterAdapter { sender: Address, ) => Promise; - // TODO break apart into separate origin + destination methods to - // handle case where origin/dest protocols differ - channelStats: ( - destination: ChainName, - destinationMailbox: Address, - ) => Promise; + sentStat: (destination: ChainName) => Promise; } diff --git a/typescript/infra/config/aggregationIsm.ts b/typescript/infra/config/aggregationIsm.ts index 8d084eb04..6393806ca 100644 --- a/typescript/infra/config/aggregationIsm.ts +++ b/typescript/infra/config/aggregationIsm.ts @@ -1,103 +1,24 @@ import { AggregationIsmConfig, ChainName, - Chains, - IsmConfig, ModuleType, - MultisigIsmConfig, - RoutingIsmConfig, } from '@hyperlane-xyz/sdk'; -import { Address } from '@hyperlane-xyz/utils'; - -import { DeployEnvironment } from '../src/config'; import { Contexts } from './contexts'; -import { owners as mainnet2Owners } from './environments/mainnet2/owners'; -import { owners as testOwners } from './environments/test/owners'; -import { owners as testnet4Owners } from './environments/testnet4/owners'; -import { multisigIsms } from './multisigIsm'; - -const owners = { - testnet4: testnet4Owners, - mainnet2: mainnet2Owners, - test: testOwners, -}; - -/// Routing => Multisig ISM type -export const routingIsm = ( - environment: DeployEnvironment, - local: ChainName, - type: MultisigIsmConfig['type'], - context: Contexts, -): RoutingIsmConfig => { - const defaultMultisigIsmConfigs = multisigIsms( - environment, - local, - type, - context, - ); - return { - type: ModuleType.ROUTING, - domains: defaultMultisigIsmConfigs, - owner: owners[environment][local], - }; -}; +import { multisigIsm } from './multisigIsm'; -// mainnet cache -const aggregationIsmAddresses: Record = { - [Chains.arbitrum]: '0x7995D00bdDb146334d6568b627bcd2a7DdA3B005', - [Chains.avalanche]: '0xF6bF41939ebA2363A6e311E886Ed4a5ab3dc1F5D', - [Chains.bsc]: '0x294F19d5fe29646f8E2cA4A71b6B18b78db10F9f', - [Chains.celo]: '0x656bF500F0E2EE55F26dF3bc69b44c6eA84dd065', - [Chains.ethereum]: '0xe39eA548F36d1c3DA9b871Badd11345f836a290A', - [Chains.gnosis]: '0xD0Ec4de35069520CD17522281D36DD299525d85f', - [Chains.moonbeam]: '0x04100049AC8e279C85E895d48aab1E188152e939', - [Chains.optimism]: '0x99663d142576204284b91e96d39771db94eD5188', - [Chains.polygon]: '0x0673cc1cc5eb80816E0d0E2dA5FE10053Da97943', -}; - -/// 1/2 Aggregation => Routing => Multisig ISM +// Merkle Root Message ID export const aggregationIsm = ( - environment: DeployEnvironment, - local: ChainName, + remote: ChainName, context: Contexts, -): AggregationIsmConfig | Address => { - if (environment === 'mainnet2') { - return aggregationIsmAddresses[local]; - } - - const config: AggregationIsmConfig = { +): AggregationIsmConfig => { + return { type: ModuleType.AGGREGATION, modules: [ - // ORDERING MATTERS - routingIsm(environment, local, ModuleType.MERKLE_ROOT_MULTISIG, context), - routingIsm(environment, local, ModuleType.MESSAGE_ID_MULTISIG, context), + // Ordering matters to preserve determinism + multisigIsm(remote, ModuleType.MERKLE_ROOT_MULTISIG, context), + multisigIsm(remote, ModuleType.MESSAGE_ID_MULTISIG, context), ], threshold: 1, }; - return config; -}; - -const replacerEnum = (key: string, value: any) => { - if (key === 'type') { - switch (value) { - case ModuleType.AGGREGATION: - return 'AGGREGATION'; - case ModuleType.ROUTING: - return 'ROUTING'; - case ModuleType.MERKLE_ROOT_MULTISIG: - return 'MERKLE_ROOT_MULTISIG'; - case ModuleType.LEGACY_MULTISIG: - return 'LEGACY_MULTISIG'; - case ModuleType.MESSAGE_ID_MULTISIG: - return 'MESSAGE_ID_MULTISIG'; - default: - return value; - } - } - return value; -}; - -export const printIsmConfig = (ism: IsmConfig): string => { - return JSON.stringify(ism, replacerEnum, 2); }; diff --git a/typescript/infra/config/environments/mainnet2/agent.ts b/typescript/infra/config/environments/mainnet2/agent.ts index c7941a48f..369ad0389 100644 --- a/typescript/infra/config/environments/mainnet2/agent.ts +++ b/typescript/infra/config/environments/mainnet2/agent.ts @@ -2,9 +2,10 @@ import { GasPaymentEnforcementPolicyType, RpcConsensusType, chainMetadata, + getDomainId, hyperlaneEnvironments, } from '@hyperlane-xyz/sdk'; -import { objMap } from '@hyperlane-xyz/utils'; +import { ProtocolType, objFilter, objMap } from '@hyperlane-xyz/utils'; import { RootAgentConfig, @@ -23,13 +24,14 @@ const releaseCandidateHelloworldMatchingList = routerMatchingList( helloWorld[Contexts.ReleaseCandidate].addresses, ); -const interchainQueryRouters = objMap( - hyperlaneEnvironments.mainnet, - (_, addresses) => { +const interchainQueryRouters = objFilter( + objMap(hyperlaneEnvironments.mainnet, (_, addresses) => { return { router: addresses.interchainQueryRouter, }; - }, + }), + (chain, _addresses): _addresses is { router: string } => + chainMetadata[chain].protocol === ProtocolType.Ethereum, ); const interchainQueriesMatchingList = routerMatchingList( @@ -48,6 +50,33 @@ const contextBase = { }, } as const; +const bscNautilusWarpRoutes: Array<{ router: string }> = [ + // ZBC + { + router: '0xC27980812E2E66491FD457D488509b7E04144b98', + }, + // ETH + { + router: '0x2a6822dc5639b3fe70de6b65b9ff872e554162fa', + }, + // USDC + { + router: '0x6937a62f93a56D2AE9392Fa1649b830ca37F3ea4', + }, + // BTC + { + router: '0xB3545006A532E8C23ebC4e33d5ab2232Cafc35Ad', + }, + // USDT + { + router: '0xb7d36720a16A1F9Cfc1f7910Ac49f03965401a36', + }, + // POSE + { + router: '0x97a2D58d30A2c838946194494207F7Cf50c25815', + }, +]; + const gasPaymentEnforcement: GasPaymentEnforcementConfig[] = [ { type: GasPaymentEnforcementPolicyType.None, @@ -55,26 +84,41 @@ const gasPaymentEnforcement: GasPaymentEnforcementConfig[] = [ // all messages between interchain query routers. // This whitelist will become more strict with // https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/1605 - matchingList: interchainQueriesMatchingList, + matchingList: [ + ...interchainQueriesMatchingList, + { + originDomain: [getDomainId(chainMetadata.bsc)], + senderAddress: bscNautilusWarpRoutes.map((r) => r.router), + destinationDomain: '*', + recipientAddress: '*', + }, + // Temporarily don't charge gas for the Solana -> Nautilus ZBC warp route, + // as IGP indexing in the agents is currently incompatible with the deployed IGP. + { + originDomain: [getDomainId(chainMetadata.solana)], + senderAddress: ['EJqwFjvVJSAxH8Ur2PYuMfdvoJeutjmH6GkoEFQ4MdSa'], + destinationDomain: [getDomainId(chainMetadata.nautilus)], + recipientAddress: '*', + }, + // Similarly, temporarily not charging gas for Helloworld from Solana + { + originDomain: [getDomainId(chainMetadata.solana)], + senderAddress: [ + // Hyperlane context + '4k1gruSdH1r57V9QQK4aunzfMYzLFfF83jdYkkEwyem6', + // Rc context + '3pPDp16iVTJFge2sm85Q61hW61UN5xNqeG24gqFhzLFV', + ], + destinationDomain: '*', + recipientAddress: '*', + }, + ], }, { type: GasPaymentEnforcementPolicyType.OnChainFeeQuoting, }, ]; -const nautilusZbcWarpRouteMatchingList = routerMatchingList({ - bsc: { - router: '0xC27980812E2E66491FD457D488509b7E04144b98', - }, - nautilus: { - router: '0x4501bBE6e731A4bC5c60C03A77435b2f6d5e9Fe7', - }, - solana: { - router: - '0xc5ba229fa2822fe65ac2bd0a93d8371d75292c3415dd381923c1088a3308528b', - }, -}); - const hyperlane: RootAgentConfig = { ...contextBase, context: Contexts.Hyperlane, @@ -83,7 +127,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '3b0685f-20230815-110725', + tag: '35fdc74-20230913-104940', }, blacklist: [ ...releaseCandidateHelloworldMatchingList, @@ -92,17 +136,7 @@ const hyperlane: RootAgentConfig = { recipientAddress: '0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE', }, ], - gasPaymentEnforcement: [ - // Don't require gas payments for ZBC bridging. - // In practice, gas payments are forced to still occur due to on-chain fee quoting. - // We need this because the IGP that's paid on BSC isn't the "canonical" one (it's from a PI deployment), - // and because the Solana warp route does not yet have an IGP configured. - { - type: GasPaymentEnforcementPolicyType.None, - matchingList: nautilusZbcWarpRouteMatchingList, - }, - ...gasPaymentEnforcement, - ], + gasPaymentEnforcement, }, validators: { docker: { @@ -137,7 +171,7 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '3b0685f-20230815-110725', + tag: '35fdc74-20230913-104940', }, whitelist: releaseCandidateHelloworldMatchingList, gasPaymentEnforcement, diff --git a/typescript/infra/config/environments/mainnet2/chains.ts b/typescript/infra/config/environments/mainnet2/chains.ts index bbd719489..4a38456d8 100644 --- a/typescript/infra/config/environments/mainnet2/chains.ts +++ b/typescript/infra/config/environments/mainnet2/chains.ts @@ -2,7 +2,7 @@ import { ChainMap, ChainMetadata, chainMetadata } from '@hyperlane-xyz/sdk'; import { AgentChainNames, Role } from '../../../src/roles'; -export const mainnetConfigs: ChainMap = { +export const ethereumMainnetConfigs: ChainMap = { bsc: { ...chainMetadata.bsc, transactionOverrides: { @@ -40,12 +40,26 @@ export const mainnetConfigs: ChainMap = { gnosis: chainMetadata.gnosis, }; +// Blessed non-Ethereum chains. +export const nonEthereumMainnetConfigs: ChainMap = { + solana: chainMetadata.solana, +}; + +export const mainnetConfigs: ChainMap = { + ...ethereumMainnetConfigs, + ...nonEthereumMainnetConfigs, +}; + export type MainnetChains = keyof typeof mainnetConfigs; export const supportedChainNames = Object.keys( mainnetConfigs, ) as MainnetChains[]; export const environment = 'mainnet2'; +export const ethereumChainNames = Object.keys( + ethereumMainnetConfigs, +) as MainnetChains[]; + const validatorChainNames = [ ...supportedChainNames, chainMetadata.solana.name, @@ -57,5 +71,5 @@ const relayerChainNames = validatorChainNames; export const agentChainNames: AgentChainNames = { [Role.Validator]: validatorChainNames, [Role.Relayer]: relayerChainNames, - [Role.Scraper]: supportedChainNames, + [Role.Scraper]: ethereumChainNames, }; diff --git a/typescript/infra/config/environments/mainnet2/core.ts b/typescript/infra/config/environments/mainnet2/core.ts index ca40b0530..0ae945b56 100644 --- a/typescript/infra/config/environments/mainnet2/core.ts +++ b/typescript/infra/config/environments/mainnet2/core.ts @@ -11,14 +11,14 @@ import { } from '@hyperlane-xyz/sdk'; import { objMap } from '@hyperlane-xyz/utils'; -import { aggregationIsm } from '../../aggregationIsm'; import { Contexts } from '../../contexts'; +import { routingIsm } from '../../routingIsm'; import { igp } from './igp'; import { owners } from './owners'; export const core: ChainMap = objMap(owners, (local, owner) => { - const defaultIsm = aggregationIsm('mainnet2', local, Contexts.Hyperlane); + const defaultIsm = routingIsm('mainnet2', local, Contexts.Hyperlane); let upgrade: CoreConfig['upgrade']; if (local === 'arbitrum') { diff --git a/typescript/infra/config/environments/mainnet2/gas-oracle.ts b/typescript/infra/config/environments/mainnet2/gas-oracle.ts index a5b9344bb..7e98342a3 100644 --- a/typescript/infra/config/environments/mainnet2/gas-oracle.ts +++ b/typescript/infra/config/environments/mainnet2/gas-oracle.ts @@ -6,26 +6,23 @@ import { AllStorageGasOracleConfigs, getAllStorageGasOracleConfigs, } from '../../../src/config'; -import { TOKEN_EXCHANGE_RATE_DECIMALS } from '../../../src/config/gas-oracle'; +import { + TOKEN_EXCHANGE_RATE_DECIMALS, + getTokenExchangeRateFromValues, +} from '../../../src/config/gas-oracle'; import { supportedChainNames } from './chains'; -// Overcharge by 30% to account for market making risk -const TOKEN_EXCHANGE_RATE_MULTIPLIER = ethers.utils.parseUnits( - '1.30', - TOKEN_EXCHANGE_RATE_DECIMALS, -); - // Taken by looking at each network's gas history and overestimating // Last updated Mar 9, 2023. const gasPrices: ChainMap = { - // https://dune.com/hicrypto/BNBChain-Gas + // https://bscscan.com/chart/gasprice bsc: ethers.utils.parseUnits('7', 'gwei'), // https://snowtrace.io/chart/gasprice - avalanche: ethers.utils.parseUnits('45', 'gwei'), - // https://dune.com/sealaunch/Polygon-Gas-Prices + avalanche: ethers.utils.parseUnits('35', 'gwei'), + // https://polygonscan.com/chart/gasprice polygon: ethers.utils.parseUnits('200', 'gwei'), - // https://explorer.bitquery.io/celo_rc1/gas + // https://celoscan.io/chart/gasprice // This one is interesting - the average is high (~20 gwei) // but the median is low (< 10). This is likely because a popular wallet is // overpaying, but all our txs tend to be < 10 gwei. @@ -45,6 +42,9 @@ const gasPrices: ChainMap = { // https://gnosisscan.io/chart/gasprice // People also seem to be overpaying here gnosis: ethers.utils.parseUnits('2', 'gwei'), + // Arbitrarily chosen as gas prices aren't really a thing + // in Solana. + solana: ethers.BigNumber.from('28'), }; // Accurate from coingecko as of Mar 9, 2023. @@ -52,23 +52,25 @@ const gasPrices: ChainMap = { // tokens are what matters. These generally have high beta const tokenUsdPrices: ChainMap = { // https://www.coingecko.com/en/coins/bnb - bsc: ethers.utils.parseUnits('330.60', TOKEN_EXCHANGE_RATE_DECIMALS), + bsc: ethers.utils.parseUnits('211.55', TOKEN_EXCHANGE_RATE_DECIMALS), // https://www.coingecko.com/en/coins/avalanche - avalanche: ethers.utils.parseUnits('18.39', TOKEN_EXCHANGE_RATE_DECIMALS), + avalanche: ethers.utils.parseUnits('9.25', TOKEN_EXCHANGE_RATE_DECIMALS), // https://www.coingecko.com/en/coins/polygon - polygon: ethers.utils.parseUnits('1.13', TOKEN_EXCHANGE_RATE_DECIMALS), + polygon: ethers.utils.parseUnits('0.518', TOKEN_EXCHANGE_RATE_DECIMALS), // https://www.coingecko.com/en/coins/celo - celo: ethers.utils.parseUnits('0.69', TOKEN_EXCHANGE_RATE_DECIMALS), + celo: ethers.utils.parseUnits('0.42', TOKEN_EXCHANGE_RATE_DECIMALS), // https://www.coingecko.com/en/coins/ethereum - arbitrum: ethers.utils.parseUnits('1922.00', TOKEN_EXCHANGE_RATE_DECIMALS), + arbitrum: ethers.utils.parseUnits('1619.00', TOKEN_EXCHANGE_RATE_DECIMALS), // https://www.coingecko.com/en/coins/ethereum - optimism: ethers.utils.parseUnits('1922.00', TOKEN_EXCHANGE_RATE_DECIMALS), + optimism: ethers.utils.parseUnits('1619.00', TOKEN_EXCHANGE_RATE_DECIMALS), // https://www.coingecko.com/en/coins/ethereum - ethereum: ethers.utils.parseUnits('1922.00', TOKEN_EXCHANGE_RATE_DECIMALS), + ethereum: ethers.utils.parseUnits('1619.00', TOKEN_EXCHANGE_RATE_DECIMALS), // https://www.coingecko.com/en/coins/moonbeam - moonbeam: ethers.utils.parseUnits('0.38', TOKEN_EXCHANGE_RATE_DECIMALS), + moonbeam: ethers.utils.parseUnits('0.166', TOKEN_EXCHANGE_RATE_DECIMALS), // xDAI gnosis: ethers.utils.parseUnits('1.00', TOKEN_EXCHANGE_RATE_DECIMALS), + // https://www.coingecko.com/en/coins/solana + solana: ethers.utils.parseUnits('18.85', TOKEN_EXCHANGE_RATE_DECIMALS), }; // Gets the exchange rate of the remote quoted in local tokens @@ -76,8 +78,7 @@ function getTokenExchangeRate(local: ChainName, remote: ChainName): BigNumber { const localValue = tokenUsdPrices[local]; const remoteValue = tokenUsdPrices[remote]; - // Apply multiplier to overcharge - return remoteValue.mul(TOKEN_EXCHANGE_RATE_MULTIPLIER).div(localValue); + return getTokenExchangeRateFromValues(local, localValue, remote, remoteValue); } export const storageGasOracleConfig: AllStorageGasOracleConfigs = diff --git a/typescript/infra/config/environments/mainnet2/helloworld.ts b/typescript/infra/config/environments/mainnet2/helloworld.ts index 1e1fc9b3c..467004e47 100644 --- a/typescript/infra/config/environments/mainnet2/helloworld.ts +++ b/typescript/infra/config/environments/mainnet2/helloworld.ts @@ -1,7 +1,7 @@ import { RpcConsensusType } from '@hyperlane-xyz/sdk'; import { HelloWorldConfig } from '../../../src/config'; -import { HelloWorldKathyRunMode } from '../../../src/config/helloworld'; +import { HelloWorldKathyRunMode } from '../../../src/config/helloworld/types'; import { Contexts } from '../../contexts'; import { environment } from './chains'; diff --git a/typescript/infra/config/environments/mainnet2/helloworld/hyperlane/addresses.json b/typescript/infra/config/environments/mainnet2/helloworld/hyperlane/addresses.json index b567822e8..eee1f4b7c 100644 --- a/typescript/infra/config/environments/mainnet2/helloworld/hyperlane/addresses.json +++ b/typescript/infra/config/environments/mainnet2/helloworld/hyperlane/addresses.json @@ -25,5 +25,8 @@ }, "gnosis": { "router": "0x26f32245fCF5Ad53159E875d5Cae62aEcf19c2d4" + }, + "solana": { + "router": "4k1gruSdH1r57V9QQK4aunzfMYzLFfF83jdYkkEwyem6" } } diff --git a/typescript/infra/config/environments/mainnet2/helloworld/rc/addresses.json b/typescript/infra/config/environments/mainnet2/helloworld/rc/addresses.json index ace3e7c1e..fc05200b7 100644 --- a/typescript/infra/config/environments/mainnet2/helloworld/rc/addresses.json +++ b/typescript/infra/config/environments/mainnet2/helloworld/rc/addresses.json @@ -25,5 +25,8 @@ }, "moonbeam": { "router": "0x3eB9eE2CFC8DCB6F58B5869D33336CFcBf1dC354" + }, + "solana": { + "router": "3pPDp16iVTJFge2sm85Q61hW61UN5xNqeG24gqFhzLFV" } } diff --git a/typescript/infra/config/environments/mainnet2/igp.ts b/typescript/infra/config/environments/mainnet2/igp.ts index 59b1ab7e5..64f498126 100644 --- a/typescript/infra/config/environments/mainnet2/igp.ts +++ b/typescript/infra/config/environments/mainnet2/igp.ts @@ -7,7 +7,11 @@ import { } from '@hyperlane-xyz/sdk'; import { exclude, objMap } from '@hyperlane-xyz/utils'; -import { MainnetChains, supportedChainNames } from './chains'; +import { + MainnetChains, + ethereumChainNames, + supportedChainNames, +} from './chains'; import { owners } from './owners'; // TODO: make this generic @@ -23,22 +27,19 @@ function getGasOracles(local: MainnetChains) { ); } -export const igp: ChainMap = objMap(owners, (chain, owner) => { - const overhead = Object.fromEntries( - exclude(chain, supportedChainNames).map((remote) => [ +export const igp: ChainMap = objMap(owners, (chain, owner) => ({ + owner, + oracleKey: DEPLOYER_ADDRESS, + beneficiary: KEY_FUNDER_ADDRESS, + gasOracleType: getGasOracles(chain), + overhead: Object.fromEntries( + // Not setting overhead for non-Ethereum destination chains + exclude(chain, ethereumChainNames).map((remote) => [ remote, multisigIsmVerificationCost( defaultMultisigIsmConfigs[remote].threshold, defaultMultisigIsmConfigs[remote].validators.length, ), ]), - ); - - return { - owner, - oracleKey: DEPLOYER_ADDRESS, - beneficiary: KEY_FUNDER_ADDRESS, - gasOracleType: getGasOracles(chain), - overhead, - }; -}); + ), +})); diff --git a/typescript/infra/config/environments/mainnet2/ism/verification.json b/typescript/infra/config/environments/mainnet2/ism/verification.json index cad366185..199b97842 100644 --- a/typescript/infra/config/environments/mainnet2/ism/verification.json +++ b/typescript/infra/config/environments/mainnet2/ism/verification.json @@ -29,6 +29,12 @@ "address": "0x1079056da3EC7D55521F27e1E094015C0d39Cc65", "constructorArguments": "", "isProxy": false + }, + { + "name": "DomainRoutingIsm", + "address": "0x870aeae147917c0b87ba55613d977026d3019018", + "constructorArguments": "", + "isProxy": false } ], "ethereum": [ @@ -61,6 +67,12 @@ "address": "0x60305F347C369E31782B3bc42D08dC2c7F218807", "constructorArguments": "", "isProxy": false + }, + { + "name": "DomainRoutingIsm", + "address": "0x2f14526ec4a7ec292e792ebf836fea303ce038fa", + "constructorArguments": "", + "isProxy": false } ], "avalanche": [ @@ -93,6 +105,12 @@ "address": "0x6f82eC078317aAbE2ae5200f09d4550987A1bF28", "constructorArguments": "", "isProxy": false + }, + { + "name": "DomainRoutingIsm", + "address": "0x5d6945e61a2d67f5a8e93873eaf60c9ee1313314", + "constructorArguments": "", + "isProxy": false } ], "polygon": [ @@ -125,6 +143,12 @@ "address": "0x720663C310D3Db506F28a8F65c39Af37BFBe0248", "constructorArguments": "", "isProxy": false + }, + { + "name": "DomainRoutingIsm", + "address": "0x3c8d4e08ea1ad7644dcfc23e0fe708eb6a1f7ad7", + "constructorArguments": "", + "isProxy": false } ], "bsc": [ @@ -157,6 +181,12 @@ "address": "0xcf14426dF1D973A656e20c3AcD2B9E18C3C05793", "constructorArguments": "", "isProxy": false + }, + { + "name": "DomainRoutingIsm", + "address": "0xae16de9c4e403c2ed47c8be6e32e58dbcc2551e2", + "constructorArguments": "", + "isProxy": false } ], "arbitrum": [ @@ -189,6 +219,12 @@ "address": "0x9D7B7245C6dAEBD5543080C91523e0702fC27024", "constructorArguments": "", "isProxy": false + }, + { + "name": "DomainRoutingIsm", + "address": "0x0bf2518aADbAb4E16b16Ba21231CE8B17E3c8843", + "constructorArguments": "", + "isProxy": false } ], "optimism": [ @@ -221,6 +257,12 @@ "address": "0x3A7Ff7b700681A2655377cFDc88eE2031cf270E1", "constructorArguments": "", "isProxy": false + }, + { + "name": "DomainRoutingIsm", + "address": "0x0ca4d54b91f2d8b728117ef3fb1ce7b52017d335", + "constructorArguments": "", + "isProxy": false } ], "moonbeam": [ @@ -253,6 +295,12 @@ "address": "0x147F8C69f0FCF571c03093dE4180718FaecbA31E", "constructorArguments": "", "isProxy": false + }, + { + "name": "DomainRoutingIsm", + "address": "0x96b595b97bb5106518ac830c59b2d7cd32f2a150", + "constructorArguments": "", + "isProxy": false } ], "gnosis": [ @@ -285,6 +333,12 @@ "address": "0xC4275763D7b621eb732847957012F1fb35C90BB8", "constructorArguments": "", "isProxy": false + }, + { + "name": "DomainRoutingIsm", + "address": "0x2f134de027e88142aaad486572cc91c1f81214bf", + "constructorArguments": "", + "isProxy": false } ] } diff --git a/typescript/infra/config/environments/mainnet2/owners.ts b/typescript/infra/config/environments/mainnet2/owners.ts index 2899e3ada..b900b7507 100644 --- a/typescript/infra/config/environments/mainnet2/owners.ts +++ b/typescript/infra/config/environments/mainnet2/owners.ts @@ -11,4 +11,5 @@ export const owners: ChainMap
= { optimism: '0xb523CFAf45AACF472859f8B793CB0BFDB16bD257', moonbeam: '0xF0cb1f968Df01fc789762fddBfA704AE0F952197', gnosis: '0x36b0AA0e7d04e7b825D7E409FEa3c9A3d57E4C22', + solana: 'EzppBFV2taxWw8kEjxNYvby6q7W1biJEqwP3iC7YgRe3', }; diff --git a/typescript/infra/config/environments/testnet4/agent.ts b/typescript/infra/config/environments/testnet4/agent.ts index e2526b874..bea891f92 100644 --- a/typescript/infra/config/environments/testnet4/agent.ts +++ b/typescript/infra/config/environments/testnet4/agent.ts @@ -2,10 +2,7 @@ import { GasPaymentEnforcementPolicyType, RpcConsensusType, chainMetadata, - getDomainId, - hyperlaneEnvironments, } from '@hyperlane-xyz/sdk'; -import { objMap } from '@hyperlane-xyz/utils'; import { RootAgentConfig, @@ -24,20 +21,6 @@ const releaseCandidateHelloworldMatchingList = routerMatchingList( helloWorld[Contexts.ReleaseCandidate].addresses, ); -const interchainQueryRouters = objMap( - hyperlaneEnvironments.testnet, - (_, addresses) => { - return { - // @ts-ignore moonbasealpha has no interchain query router - router: addresses.interchainQueryRouter, - }; - }, -); - -const interchainQueriesMatchingList = routerMatchingList( - interchainQueryRouters, -); - const repo = 'gcr.io/abacus-labs-dev/hyperlane-agent'; const contextBase = { @@ -51,14 +34,6 @@ const contextBase = { } as const; const gasPaymentEnforcement: GasPaymentEnforcementConfig[] = [ - { - type: GasPaymentEnforcementPolicyType.None, - // To continue relaying interchain query callbacks, we whitelist - // all messages between interchain query routers. - // This whitelist will become more strict with - // https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/1605 - matchingList: interchainQueriesMatchingList, - }, // Default policy is OnChainFeeQuoting { type: GasPaymentEnforcementPolicyType.OnChainFeeQuoting, @@ -93,14 +68,6 @@ const hyperlane: RootAgentConfig = { repo, tag: 'cfaf553-20231009-174623', }, - chainDockerOverrides: { - [chainMetadata.solanadevnet.name]: { - tag: '79bad9d-20230706-190752', - }, - [chainMetadata.proteustestnet.name]: { - tag: 'c7c44b2-20230811-133851', - }, - }, chains: validatorChainConfig(Contexts.Hyperlane), }, scraper: { @@ -122,56 +89,8 @@ const releaseCandidate: RootAgentConfig = { repo, tag: 'cfaf553-20231009-174623', }, - whitelist: [ - ...releaseCandidateHelloworldMatchingList, - // Whitelist all traffic to solanadevnet - { - originDomain: '*', - senderAddress: '*', - destinationDomain: [ - getDomainId(chainMetadata.solanadevnet), - getDomainId(chainMetadata.proteustestnet), - ], - recipientAddress: '*', - }, - // Whitelist all traffic from solanadevnet to fuji - { - originDomain: [ - getDomainId(chainMetadata.solanadevnet), - getDomainId(chainMetadata.proteustestnet), - ], - senderAddress: '*', - destinationDomain: [getDomainId(chainMetadata.bsctestnet)], - recipientAddress: '*', - }, - ], - gasPaymentEnforcement: [ - // Don't require gas payments from solanadevnet - { - type: GasPaymentEnforcementPolicyType.None, - matchingList: [ - { - originDomain: [getDomainId(chainMetadata.solanadevnet)], - senderAddress: '*', - destinationDomain: [ - getDomainId(chainMetadata.bsctestnet), - getDomainId(chainMetadata.proteustestnet), - ], - recipientAddress: '*', - }, - { - originDomain: [getDomainId(chainMetadata.bsctestnet)], - senderAddress: '*', - destinationDomain: [ - getDomainId(chainMetadata.solanadevnet), - getDomainId(chainMetadata.proteustestnet), - ], - recipientAddress: '*', - }, - ], - }, - ...gasPaymentEnforcement, - ], + whitelist: [...releaseCandidateHelloworldMatchingList], + gasPaymentEnforcement, transactionGasLimit: 750000, // Skipping arbitrum because the gas price estimates are inclusive of L1 // fees which leads to wildly off predictions. diff --git a/typescript/infra/config/environments/testnet4/chains.ts b/typescript/infra/config/environments/testnet4/chains.ts index b93b5eade..64ad786e0 100644 --- a/typescript/infra/config/environments/testnet4/chains.ts +++ b/typescript/infra/config/environments/testnet4/chains.ts @@ -2,14 +2,15 @@ import { ChainMap, ChainMetadata, chainMetadata } from '@hyperlane-xyz/sdk'; import { AgentChainNames, Role } from '../../../src/roles'; -export const testnetConfigs: ChainMap = { +// Blessed +export const ethereumTestnetConfigs: ChainMap = { alfajores: chainMetadata.alfajores, basegoerli: chainMetadata.basegoerli, fuji: chainMetadata.fuji, mumbai: { ...chainMetadata.mumbai, transactionOverrides: { - maxFeePerGas: 70 * 10 ** 9, // 70 gwei + maxFeePerGas: 150 * 10 ** 9, // 70 gwei maxPriorityFeePerGas: 40 * 10 ** 9, // 40 gwei }, }, @@ -23,23 +24,34 @@ export const testnetConfigs: ChainMap = { polygonzkevmtestnet: chainMetadata.polygonzkevmtestnet, }; -// "Blessed" chains that we want core contracts for. +// Blessed non-Ethereum chains. +// export const nonEthereumTestnetConfigs: ChainMap = { +// solanadevnet: chainMetadata.solanadevnet, +// }; + +export const testnetConfigs: ChainMap = { + ...ethereumTestnetConfigs, + // ...nonEthereumTestnetConfigs, +}; + export type TestnetChains = keyof typeof testnetConfigs; export const supportedChainNames = Object.keys( testnetConfigs, ) as TestnetChains[]; export const environment = 'testnet4'; +export const ethereumChainNames = Object.keys( + ethereumTestnetConfigs, +) as TestnetChains[]; const validatorChainNames = [ ...supportedChainNames, // chainMetadata.solanadevnet.name, // chainMetadata.proteustestnet.name, ]; - const relayerChainNames = validatorChainNames; export const agentChainNames: AgentChainNames = { [Role.Validator]: validatorChainNames, [Role.Relayer]: relayerChainNames, - [Role.Scraper]: supportedChainNames, + [Role.Scraper]: ethereumChainNames, }; diff --git a/typescript/infra/config/environments/testnet4/core.ts b/typescript/infra/config/environments/testnet4/core.ts index 6339a2653..3b1a31cbb 100644 --- a/typescript/infra/config/environments/testnet4/core.ts +++ b/typescript/infra/config/environments/testnet4/core.ts @@ -11,14 +11,14 @@ import { } from '@hyperlane-xyz/sdk'; import { objMap } from '@hyperlane-xyz/utils'; -import { aggregationIsm } from '../../aggregationIsm'; import { Contexts } from '../../contexts'; +import { routingIsm } from '../../routingIsm'; import { igp } from './igp'; import { owners } from './owners'; export const core: ChainMap = objMap(owners, (local, owner) => { - const defaultIsm = aggregationIsm('testnet4', local, Contexts.Hyperlane); + const defaultIsm = routingIsm('testnet4', local, Contexts.Hyperlane); const merkleHook: MerkleTreeHookConfig = { type: HookType.MERKLE_TREE, diff --git a/typescript/infra/config/environments/testnet4/gas-oracle.ts b/typescript/infra/config/environments/testnet4/gas-oracle.ts index b1d1fbc1f..b173f4d3a 100644 --- a/typescript/infra/config/environments/testnet4/gas-oracle.ts +++ b/typescript/infra/config/environments/testnet4/gas-oracle.ts @@ -6,16 +6,13 @@ import { AllStorageGasOracleConfigs, getAllStorageGasOracleConfigs, } from '../../../src/config'; -import { TOKEN_EXCHANGE_RATE_DECIMALS } from '../../../src/config/gas-oracle'; +import { + TOKEN_EXCHANGE_RATE_DECIMALS, + getTokenExchangeRateFromValues, +} from '../../../src/config/gas-oracle'; import { TestnetChains, supportedChainNames } from './chains'; -// Overcharge by 30% to account for market making risk -const TOKEN_EXCHANGE_RATE_MULTIPLIER = ethers.utils.parseUnits( - '1.30', - TOKEN_EXCHANGE_RATE_DECIMALS, -); - // Taken by looking at each testnet and overestimating gas prices const gasPrices: ChainMap = { alfajores: ethers.utils.parseUnits('10', 'gwei'), @@ -32,6 +29,7 @@ const gasPrices: ChainMap = { lineagoerli: ethers.utils.parseUnits('1', 'gwei'), polygonzkevmtestnet: ethers.utils.parseUnits('1', 'gwei'), chiado: ethers.utils.parseUnits('2', 'gwei'), + // solanadevnet: ethers.BigNumber.from('28'), }; // Used to categorize rarity of testnet tokens & approximate exchange rates. @@ -65,6 +63,7 @@ const chainTokenRarity: ChainMap = { lineagoerli: Rarity.Rare, polygonzkevmtestnet: Rarity.Common, chiado: Rarity.Common, + // solanadevnet: Rarity.Common, }; // Gets the "value" of a testnet chain @@ -78,8 +77,7 @@ function getTokenExchangeRate(local: ChainName, remote: ChainName): BigNumber { const localValue = getApproximateValue(local); const remoteValue = getApproximateValue(remote); - // Apply multiplier to overcharge - return remoteValue.mul(TOKEN_EXCHANGE_RATE_MULTIPLIER).div(localValue); + return getTokenExchangeRateFromValues(local, localValue, remote, remoteValue); } export const storageGasOracleConfig: AllStorageGasOracleConfigs = diff --git a/typescript/infra/config/environments/testnet4/helloworld.ts b/typescript/infra/config/environments/testnet4/helloworld.ts index a83c49383..2cb293020 100644 --- a/typescript/infra/config/environments/testnet4/helloworld.ts +++ b/typescript/infra/config/environments/testnet4/helloworld.ts @@ -1,7 +1,7 @@ import { RpcConsensusType } from '@hyperlane-xyz/sdk'; import { HelloWorldConfig } from '../../../src/config'; -import { HelloWorldKathyRunMode } from '../../../src/config/helloworld'; +import { HelloWorldKathyRunMode } from '../../../src/config/helloworld/types'; import { Contexts } from '../../contexts'; import { environment } from './chains'; diff --git a/typescript/infra/config/environments/testnet4/owners.ts b/typescript/infra/config/environments/testnet4/owners.ts index a618c83b5..2522ea389 100644 --- a/typescript/infra/config/environments/testnet4/owners.ts +++ b/typescript/infra/config/environments/testnet4/owners.ts @@ -1,10 +1,14 @@ import { ChainMap } from '@hyperlane-xyz/sdk'; import { Address } from '@hyperlane-xyz/utils'; -import { supportedChainNames } from './chains'; +import { ethereumChainNames } from './chains'; -const DEPLOYER_ADDRESS = '0xfaD1C94469700833717Fa8a3017278BC1cA8031C'; +const ETHEREUM_DEPLOYER_ADDRESS = '0xfaD1C94469700833717Fa8a3017278BC1cA8031C'; +// const SEALEVEL_DEPLOYER_ADDRESS = '6DjHX6Ezjpq3zZMZ8KsqyoFYo1zPSDoiZmLLkxD4xKXS'; -export const owners: ChainMap
= Object.fromEntries( - supportedChainNames.map((chain) => [chain, DEPLOYER_ADDRESS]), -); +export const owners: ChainMap
= { + ...Object.fromEntries( + ethereumChainNames.map((chain) => [chain, ETHEREUM_DEPLOYER_ADDRESS]), + ), + // [chainMetadata.solanadevnet.name]: SEALEVEL_DEPLOYER_ADDRESS, +}; diff --git a/typescript/infra/config/multisigIsm.ts b/typescript/infra/config/multisigIsm.ts index f584519aa..8c7d5ea75 100644 --- a/typescript/infra/config/multisigIsm.ts +++ b/typescript/infra/config/multisigIsm.ts @@ -39,3 +39,19 @@ export const multisigIsms = ( type, }), ); + +export const multisigIsm = ( + remote: ChainName, + type: MultisigIsmConfig['type'], + context: Contexts, +): MultisigIsmConfig => { + const configs = + context === Contexts.ReleaseCandidate + ? rcMultisigIsmConfigs + : defaultMultisigIsmConfigs; + + return { + ...configs[remote], + type, + }; +}; diff --git a/typescript/infra/config/routingIsm.ts b/typescript/infra/config/routingIsm.ts new file mode 100644 index 000000000..d8fdb3a26 --- /dev/null +++ b/typescript/infra/config/routingIsm.ts @@ -0,0 +1,101 @@ +import { + AggregationIsmConfig, + ChainMap, + ChainName, + IsmConfig, + ModuleType, + RoutingIsmConfig, +} from '@hyperlane-xyz/sdk'; +import { Address } from '@hyperlane-xyz/utils'; + +import { DeployEnvironment } from '../src/config'; + +import { aggregationIsm } from './aggregationIsm'; +import { Contexts } from './contexts'; +import { supportedChainNames as mainnet2Chains } from './environments/mainnet2/chains'; +import { owners as mainnet2Owners } from './environments/mainnet2/owners'; +import { chainNames as testChains } from './environments/test/chains'; +import { owners as testOwners } from './environments/test/owners'; +import { supportedChainNames as testnet4Chains } from './environments/testnet4/chains'; +import { owners as testnet4Owners } from './environments/testnet4/owners'; + +const chains = { + mainnet2: mainnet2Chains, + testnet4: testnet4Chains, + test: testChains, +}; + +const owners = { + testnet4: testnet4Owners, + mainnet2: mainnet2Owners, + test: testOwners, +}; + +export const mainnetHyperlaneDefaultIsmCache: ChainMap
= { + ethereum: '0x3Ef03aEf1392E5e0C16fd4D22C3c3b4f81C8AF0C', + optimism: '0xA7a0f9CB7d3bc3520A82ECF009F8f3949a926237', + arbitrum: '0xD629aB5353D6B11f52eD80EFb26a28e5E347B52F', + avalanche: '0x143A34E3Eaf1E77a8c994EcADb5268d717183150', + polygon: '0xE1403b9d64185f715414A4a7BEcec124Bd9198A7', + bsc: '0x307c66E1E2E9f20b6ED3c4561398215CF9b633c4', + celo: '0xAC0246a09f1FEaF4CEe32e43792eE12d6B277332', + moonbeam: '0xB32d147334AF9C15A65716Ab254a2460307648D1', + gnosis: '0xF6c174AcC399eD8407663387857f30f92B0db958', + solana: '6pHP4EeX2Xek24Be7PPTWCqcpmNEPENW1m9RnZSFSmA1', +}; + +// Intended to be the "entrypoint" ISM. +// Routing ISM => Aggregation (1/2) +// | | +// | | +// v v +// Merkle Root Message ID +export const routingIsm = ( + environment: DeployEnvironment, + local: ChainName, + context: Contexts, +): RoutingIsmConfig | string => { + if (environment === 'mainnet2' && context === Contexts.Hyperlane) { + return mainnetHyperlaneDefaultIsmCache[local]; + } + + const aggregationIsms: ChainMap = chains[ + environment + ].reduce( + (acc, chain) => ({ + ...acc, + [chain]: aggregationIsm(chain, context), + }), + {}, + ); + + return { + type: ModuleType.ROUTING, + domains: aggregationIsms, + owner: owners[environment][local], + }; +}; + +const replacerEnum = (key: string, value: any) => { + if (key === 'type') { + switch (value) { + case ModuleType.AGGREGATION: + return 'AGGREGATION'; + case ModuleType.ROUTING: + return 'ROUTING'; + case ModuleType.MERKLE_ROOT_MULTISIG: + return 'MERKLE_ROOT_MULTISIG'; + case ModuleType.LEGACY_MULTISIG: + return 'LEGACY_MULTISIG'; + case ModuleType.MESSAGE_ID_MULTISIG: + return 'MESSAGE_ID_MULTISIG'; + default: + return value; + } + } + return value; +}; + +export const printIsmConfig = (ism: IsmConfig): string => { + return JSON.stringify(ism, replacerEnum, 2); +}; diff --git a/typescript/infra/helm/helloworld-kathy/templates/external-secret.yaml b/typescript/infra/helm/helloworld-kathy/templates/external-secret.yaml index f3d1ed045..dff0d4797 100644 --- a/typescript/infra/helm/helloworld-kathy/templates/external-secret.yaml +++ b/typescript/infra/helm/helloworld-kathy/templates/external-secret.yaml @@ -22,13 +22,17 @@ spec: data: GCP_SECRET_OVERRIDES_ENABLED: "true" GCP_SECRET_OVERRIDE_HYPERLANE_{{ .Values.hyperlane.runEnv | upper }}_KEY_DEPLOYER: {{ print "'{{ .deployer_key | toString }}'" }} -{{/* + {{/* + * Always get the GCP-based key, which is used for non-EVM chains. + */}} + GCP_SECRET_OVERRIDE_{{ .Values.hyperlane.context | upper }}_{{ .Values.hyperlane.runEnv | upper }}_KEY_KATHY: {{ print "'{{ .gcp_private_key | toString }}'" }} + {{/* * For each network, create an environment variable with the RPC endpoint. * The templating of external-secrets will use the data section below to know how * to replace the correct value in the created secret. */}} - {{- range .Values.hyperlane.chains }} - {{- if or (eq $.Values.hyperlane.connectionType "quorum") (eq $.Values.hyperlane.connectionType "fallback") }} + {{- range .Values.hyperlane.chains.relayer }} + {{- if or (eq $.Values.hyperlane.connectionType "httpQuorum") (eq $.Values.hyperlane.connectionType "httpFallback") }} GCP_SECRET_OVERRIDE_{{ $.Values.hyperlane.runEnv | upper }}_RPC_ENDPOINTS_{{ . | upper }}: {{ printf "'{{ .%s_rpcs | toString }}'" . }} {{- else }} GCP_SECRET_OVERRIDE_{{ $.Values.hyperlane.runEnv | upper }}_RPC_ENDPOINT_{{ . | upper }}: {{ printf "'{{ .%s_rpc | toString }}'" . }} @@ -37,8 +41,6 @@ spec: {{- if .Values.hyperlane.aws }} AWS_ACCESS_KEY_ID: {{ print "'{{ .aws_access_key_id | toString }}'" }} AWS_SECRET_ACCESS_KEY: {{ print "'{{ .aws_secret_access_key | toString }}'" }} - {{- else }} - GCP_SECRET_OVERRIDE_{{ .Values.hyperlane.context | upper }}_{{ .Values.hyperlane.runEnv | upper }}_KEY_KATHY: {{ print "'{{ .gcp_private_key | toString }}'" }} {{- end }} data: - secretKey: deployer_key @@ -48,8 +50,8 @@ spec: * For each network, load the secret in GCP secret manager with the form: environment-rpc-endpoint-network, * and associate it with the secret key networkname_rpc. */}} - {{- range .Values.hyperlane.chains }} - {{- if or (eq $.Values.hyperlane.connectionType "quorum") (eq $.Values.hyperlane.connectionType "fallback") }} + {{- range .Values.hyperlane.chains.relayer }} + {{- if or (eq $.Values.hyperlane.connectionType "httpQuorum") (eq $.Values.hyperlane.connectionType "httpFallback") }} - secretKey: {{ printf "%s_rpcs" . }} remoteRef: key: {{ printf "%s-rpc-endpoints-%s" $.Values.hyperlane.runEnv . }} @@ -66,9 +68,7 @@ spec: - secretKey: aws_secret_access_key remoteRef: key: {{ printf "%s-%s-kathy-aws-secret-access-key" .Values.hyperlane.context .Values.hyperlane.runEnv }} - {{- else }} + {{- end }} - secretKey: gcp_private_key remoteRef: key: {{ printf "%s-%s-key-kathy" $.Values.hyperlane.context $.Values.hyperlane.runEnv }} - property: privateKey - {{- end }} diff --git a/typescript/infra/package.json b/typescript/infra/package.json index 963f11e0b..fcd9e5589 100644 --- a/typescript/infra/package.json +++ b/typescript/infra/package.json @@ -1,7 +1,7 @@ { "name": "@hyperlane-xyz/infra", "description": "Infrastructure utilities for the Hyperlane Network", - "version": "1.5.0", + "version": "1.5.1", "dependencies": { "@arbitrum/sdk": "^3.0.0", "@aws-sdk/client-iam": "^3.74.0", diff --git a/typescript/infra/scripts/check-deploy.ts b/typescript/infra/scripts/check-deploy.ts index 4393cca27..2b570c477 100644 --- a/typescript/infra/scripts/check-deploy.ts +++ b/typescript/infra/scripts/check-deploy.ts @@ -1,3 +1,4 @@ +import { HelloWorldChecker } from '@hyperlane-xyz/helloworld'; import { HyperlaneCore, HyperlaneCoreChecker, @@ -8,32 +9,39 @@ import { InterchainAccountChecker, InterchainQuery, InterchainQueryChecker, + filterChainMapToProtocol, } from '@hyperlane-xyz/sdk'; +import { ProtocolType } from '@hyperlane-xyz/utils'; +import { Contexts } from '../config/contexts'; import { deployEnvToSdkEnv } from '../src/config/environment'; +import { helloWorldRouterConfig } from '../src/config/helloworld/config'; import { HyperlaneAppGovernor } from '../src/govern/HyperlaneAppGovernor'; import { HyperlaneCoreGovernor } from '../src/govern/HyperlaneCoreGovernor'; import { HyperlaneIgpGovernor } from '../src/govern/HyperlaneIgpGovernor'; import { ProxiedRouterGovernor } from '../src/govern/ProxiedRouterGovernor'; +import { Role } from '../src/roles'; import { impersonateAccount, useLocalProvider } from '../src/utils/fork'; +import { getHelloWorldApp } from './helloworld/utils'; import { Modules, getEnvironmentConfig, getProxiedRouterConfig, getArgs as getRootArgs, + withContext, withModuleAndFork, } from './utils'; function getArgs() { - return withModuleAndFork(getRootArgs()) + return withModuleAndFork(withContext(getRootArgs())) .boolean('govern') .default('govern', false) .alias('g', 'govern').argv; } async function check() { - const { fork, govern, module, environment } = await getArgs(); + const { fork, govern, module, environment, context } = await getArgs(); const config = getEnvironmentConfig(environment); const multiProvider = await config.getMultiProvider(); @@ -64,8 +72,9 @@ async function check() { const checker = new HyperlaneIgpChecker(multiProvider, igp, config.igp); governor = new HyperlaneIgpGovernor(checker, config.owners); } else if (module === Modules.INTERCHAIN_ACCOUNTS) { - const routerConfig = await getProxiedRouterConfig( - environment, + const routerConfig = filterChainMapToProtocol( + await getProxiedRouterConfig(environment, multiProvider), + ProtocolType.Ethereum, multiProvider, ); const ica = InterchainAccount.fromEnvironment(env, multiProvider); @@ -87,6 +96,29 @@ async function check() { routerConfig, ); governor = new ProxiedRouterGovernor(checker, config.owners); + } else if (module === Modules.HELLO_WORLD) { + const app = await getHelloWorldApp( + config, + context, + Role.Deployer, + Contexts.Hyperlane, // Owner should always be from the hyperlane context + ); + const hwConfig = await helloWorldRouterConfig( + environment, + context, + multiProvider, + ); + const ismFactory = HyperlaneIsmFactory.fromEnvironment( + deployEnvToSdkEnv[environment], + multiProvider, + ); + const checker = new HelloWorldChecker( + multiProvider, + app, + hwConfig, + ismFactory, + ); + governor = new ProxiedRouterGovernor(checker, config.owners); } else { console.log(`Skipping ${module}, checker or governor unimplemented`); return; diff --git a/typescript/infra/scripts/gas-oracle/update-storage-gas-oracle.ts b/typescript/infra/scripts/gas-oracle/update-storage-gas-oracle.ts index af3e4c4f4..a3b36a581 100644 --- a/typescript/infra/scripts/gas-oracle/update-storage-gas-oracle.ts +++ b/typescript/infra/scripts/gas-oracle/update-storage-gas-oracle.ts @@ -1,4 +1,5 @@ -import { ChainName, HyperlaneIgp } from '@hyperlane-xyz/sdk'; +import { ChainName, HyperlaneIgp, MultiProvider } from '@hyperlane-xyz/sdk'; +import { ProtocolType } from '@hyperlane-xyz/utils'; import { RemoteGasData, StorageGasOracleConfig } from '../../src/config'; import { deployEnvToSdkEnv } from '../../src/config/environment'; @@ -38,8 +39,16 @@ async function main() { ); for (const chain of igp.chains()) { + if ( + multiProvider.getChainMetadata(chain).protocol !== ProtocolType.Ethereum + ) { + console.log(`Skipping ${chain} because it is not an Ethereum chain`); + continue; + } + await setStorageGasOracleValues( igp, + multiProvider, storageGasOracleConfig[chain], chain, args.dryRun, @@ -50,6 +59,9 @@ async function main() { async function setStorageGasOracleValues( igp: HyperlaneIgp, + // This multiProvider is used instead of the one on the IGP because the IGP's + // multiprovider will have filtered out non-Ethereum chains. + multiProvider: MultiProvider, localStorageGasOracleConfig: StorageGasOracleConfig, local: ChainName, dryRun: boolean, @@ -61,7 +73,7 @@ async function setStorageGasOracleValues( for (const remote in localStorageGasOracleConfig) { const desiredGasData = localStorageGasOracleConfig[remote]!; - const remoteId = igp.multiProvider.getDomainId(remote); + const remoteId = multiProvider.getDomainId(remote); const existingGasData: RemoteGasData = await storageGasOracle.remoteGasData( remoteId, @@ -92,7 +104,7 @@ async function setStorageGasOracleValues( console.log(`Updating ${configsToSet.length} configs on local ${local}:`); console.log( configsToSet - .map((config) => prettyRemoteGasDataConfig(igp.multiProvider, config)) + .map((config) => prettyRemoteGasDataConfig(multiProvider, config)) .join('\n\t--\n'), ); diff --git a/typescript/infra/scripts/helloworld/check.ts b/typescript/infra/scripts/helloworld/check.ts deleted file mode 100644 index 4be8f0911..000000000 --- a/typescript/infra/scripts/helloworld/check.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { HelloWorldChecker } from '@hyperlane-xyz/helloworld'; - -import { Contexts } from '../../config/contexts'; -import { Role } from '../../src/roles'; -import { - getArgs, - getEnvironmentConfig, - getRouterConfig, - withContext, -} from '../utils'; - -import { getHelloWorldApp } from './utils'; - -async function main() { - const { environment, context } = await withContext(getArgs()).argv; - const coreConfig = getEnvironmentConfig(environment); - const multiProvider = await coreConfig.getMultiProvider(); - const app = await getHelloWorldApp( - coreConfig, - context, - Role.Deployer, - Contexts.Hyperlane, // Owner should always be from the hyperlane context - ); - const configMap = await getRouterConfig(environment, multiProvider, true); - const checker = new HelloWorldChecker(multiProvider, app, configMap); - await checker.check(); - checker.expectEmpty(); -} - -main() - .then(() => console.info('HelloWorld check complete')) - .catch(console.error); diff --git a/typescript/infra/scripts/helloworld/deploy.ts b/typescript/infra/scripts/helloworld/deploy.ts index e39a61207..9d4fc317e 100644 --- a/typescript/infra/scripts/helloworld/deploy.ts +++ b/typescript/infra/scripts/helloworld/deploy.ts @@ -1,29 +1,55 @@ import path from 'path'; import { HelloWorldDeployer } from '@hyperlane-xyz/helloworld'; -import { serializeContractsMap } from '@hyperlane-xyz/sdk'; +import { + HyperlaneIsmFactory, + filterChainMapExcludeProtocol, + serializeContractsMap, +} from '@hyperlane-xyz/sdk'; +import { + ProtocolType, + hexOrBase58ToHex, + objMap, + objMerge, +} from '@hyperlane-xyz/utils'; import { Contexts } from '../../config/contexts'; +import { deployEnvToSdkEnv } from '../../src/config/environment'; +import { helloWorldRouterConfig } from '../../src/config/helloworld/config'; import { Role } from '../../src/roles'; import { readJSON, writeJSON } from '../../src/utils/utils'; import { - getArgs, getEnvironmentConfig, getEnvironmentDirectory, - getRouterConfig, + getArgs as getRootArgs, withContext, } from '../utils'; +function getArgs() { + return withContext(getRootArgs()) + .boolean('govern') + .default('govern', false) + .alias('g', 'govern').argv; +} + async function main() { - const { environment, context } = await withContext(getArgs()).argv; + const { environment, context } = await getArgs(); const coreConfig = getEnvironmentConfig(environment); // Always deploy from the hyperlane deployer const multiProvider = await coreConfig.getMultiProvider( Contexts.Hyperlane, Role.Deployer, ); - const configMap = await getRouterConfig(environment, multiProvider, true); - const deployer = new HelloWorldDeployer(multiProvider); + const configMap = await helloWorldRouterConfig( + environment, + context, + multiProvider, + ); + const ismFactory = HyperlaneIsmFactory.fromEnvironment( + deployEnvToSdkEnv[environment], + multiProvider, + ); + const deployer = new HelloWorldDeployer(multiProvider, ismFactory); const dir = path.join( getEnvironmentDirectory(environment), 'helloworld', @@ -39,18 +65,32 @@ async function main() { console.info(`Could not load previous deployment, file may not exist`); } + const configMapWithForeignDeployments = objMerge( + configMap, + objMap( + filterChainMapExcludeProtocol( + deployer.cachedAddresses, + ProtocolType.Ethereum, + multiProvider, + ), + (_chain, addresses) => ({ + foreignDeployment: hexOrBase58ToHex(addresses.router), + }), + ), + ); + try { - await deployer.deploy(configMap); + await deployer.deploy(configMapWithForeignDeployments); } catch (e) { console.error(`Encountered error during deploy`); console.error(e); } - writeJSON( - dir, - 'addresses.json', - serializeContractsMap(deployer.deployedContracts), - ); + writeJSON(dir, 'addresses.json', { + // To include foreign deployments that may be present in the addresses.json already + ...deployer.cachedAddresses, + ...serializeContractsMap(deployer.deployedContracts), + }); writeJSON( dir, 'verification.json', diff --git a/typescript/infra/scripts/helloworld/kathy.ts b/typescript/infra/scripts/helloworld/kathy.ts index 76025ce5c..2599b895f 100644 --- a/typescript/infra/scripts/helloworld/kathy.ts +++ b/typescript/infra/scripts/helloworld/kathy.ts @@ -23,6 +23,7 @@ import { log, objMap, retryAsync, + strip0x, timeout, warn, } from '@hyperlane-xyz/utils'; @@ -39,7 +40,7 @@ import { DeployEnvironment } from '../../src/config/environment'; import { Role } from '../../src/roles'; import { startMetricsServer } from '../../src/utils/metrics'; import { assertChain, diagonalize, sleep } from '../../src/utils/utils'; -import { getAddressesForKey, getArgs, withContext } from '../utils'; +import { getArgs, getEnvironmentConfig, withContext } from '../utils'; import { getHelloWorldMultiProtocolApp } from './utils'; @@ -162,9 +163,8 @@ async function main(): Promise { startMetricsServer(metricsRegister); debug('Starting up', { environment }); - // TODO (Rossy) remove getCoreConfigStub and re-enable getEnvironmentConfig - // const coreConfig = getEnvironmentConfig(environment); - const coreConfig = getCoreConfigStub(environment); + const coreConfig = getEnvironmentConfig(environment); + // const coreConfig = getCoreConfigStub(environment); const { app, core, igp, multiProvider, keys } = await getHelloWorldMultiProtocolApp( @@ -423,7 +423,13 @@ async function sendMessage( }); const sendAndConfirmMsg = async () => { - const sender = getAddressesForKey(keys, origin, multiProvider); + const originProtocol = app.metadata(origin).protocol; + const sender = keys[origin].addressForProtocol(originProtocol); + if (!sender) { + throw new Error( + `No sender address found for chain ${origin} and protocol ${originProtocol}`, + ); + } const tx = await app.populateHelloWorldTx( origin, destination, @@ -449,7 +455,7 @@ async function sendMessage( // that have not yet been ported over const connection = app.multiProvider.getSolanaWeb3Provider(origin); const payer = Keypair.fromSeed( - Buffer.from(keys[origin].privateKey, 'hex'), + Buffer.from(strip0x(keys[origin].privateKey), 'hex'), ); tx.transaction.partialSign(payer); // Note, tx signature essentially tx means hash on sealevel @@ -491,7 +497,8 @@ async function sendMessage( }); await timeout( - core.waitForMessagesProcessed(origin, destination, receipt, 5000, 36), + // Will check for up to 12 minutes + core.waitForMessagesProcessed(origin, destination, receipt, 5000, 144), messageReceiptTimeout, 'Timeout waiting for message to be received', ); diff --git a/typescript/infra/scripts/helloworld/utils.ts b/typescript/infra/scripts/helloworld/utils.ts index 489e22e99..ac42023d0 100644 --- a/typescript/infra/scripts/helloworld/utils.ts +++ b/typescript/infra/scripts/helloworld/utils.ts @@ -11,19 +11,20 @@ import { MultiProvider, RpcConsensusType, attachContractsMap, + attachContractsMapAndGetForeignDeployments, chainMetadata, - filterAddressesToProtocol, + filterChainMapToProtocol, hyperlaneEnvironments, - hyperlaneEnvironmentsWithSealevel, igpFactories, } from '@hyperlane-xyz/sdk'; -import { ProtocolType, objMerge } from '@hyperlane-xyz/utils'; +import { ProtocolType, objMap } from '@hyperlane-xyz/utils'; import { Contexts } from '../../config/contexts'; import { EnvironmentConfig } from '../../src/config'; import { deployEnvToSdkEnv } from '../../src/config/environment'; -import { HelloWorldConfig } from '../../src/config/helloworld'; +import { HelloWorldConfig } from '../../src/config/helloworld/types'; import { Role } from '../../src/roles'; +import { getKeyForRole } from '../utils'; export async function getHelloWorldApp( coreConfig: EnvironmentConfig, @@ -38,15 +39,24 @@ export async function getHelloWorldApp( connectionType, ); const helloworldConfig = getHelloWorldConfig(coreConfig, context); - const contracts = attachContractsMap( - helloworldConfig.addresses, - helloWorldFactories, - ); + + const { contractsMap, foreignDeployments } = + attachContractsMapAndGetForeignDeployments( + helloworldConfig.addresses, + helloWorldFactories, + multiProvider, + ); + const core = HyperlaneCore.fromEnvironment( deployEnvToSdkEnv[coreConfig.environment], multiProvider, ); - return new HelloWorldApp(core, contracts, multiProvider); + return new HelloWorldApp( + core, + contractsMap, + multiProvider, + foreignDeployments, + ); } export async function getHelloWorldMultiProtocolApp( @@ -62,8 +72,13 @@ export async function getHelloWorldMultiProtocolApp( connectionType, ); const sdkEnvName = deployEnvToSdkEnv[coreConfig.environment]; + const envAddresses = hyperlaneEnvironments[sdkEnvName]; const keys = await coreConfig.getKeys(keyContext, keyRole); + // Fetch all the keys, which is required to get the address for + // certain cloud keys + await Promise.all(Object.values(keys).map((key) => key.fetch())); + const helloworldConfig = getHelloWorldConfig(coreConfig, context); const multiProtocolProvider = @@ -71,18 +86,48 @@ export async function getHelloWorldMultiProtocolApp( // Hacking around infra code limitations, we may need to add solana manually // because the it's not in typescript/infra/config/environments/testnet4/chains.ts // Adding it there breaks many things - if (!multiProtocolProvider.getKnownChainNames().includes('solanadevnet')) { - multiProtocolProvider.addChain(chainMetadata.solanadevnet); + // if ( + // coreConfig.environment === 'testnet3' && + // !multiProtocolProvider.getKnownChainNames().includes('solanadevnet') + // ) { + // multiProvider.addChain(chainMetadata.solanadevnet); + // multiProtocolProvider.addChain(chainMetadata.solanadevnet); + // keys['solanadevnet'] = getKeyForRole( + // coreConfig.environment, + // context, + // 'solanadevnet', + // keyRole, + // ); + // await keys['solanadevnet'].fetch(); + // } else + + if ( + coreConfig.environment === 'mainnet2' && + !multiProtocolProvider.getKnownChainNames().includes('solana') + ) { + multiProvider.addChain(chainMetadata.solana); + multiProtocolProvider.addChain(chainMetadata.solana); + keys['solana'] = getKeyForRole( + coreConfig.environment, + context, + 'solana', + keyRole, + ); + await keys['solana'].fetch(); } const core = MultiProtocolCore.fromAddressesMap( - hyperlaneEnvironmentsWithSealevel[sdkEnvName], + envAddresses, multiProtocolProvider, ); - const routersAndMailboxes = objMerge( - core.chainMap, + const routersAndMailboxes = objMap( helloworldConfig.addresses, + (chain, addresses) => ({ + router: addresses.router, + // @ts-ignore allow loosely typed chain name to index env addresses + mailbox: envAddresses[chain].mailbox, + }), ); const app = new HelloMultiProtocolApp( multiProtocolProvider, @@ -92,8 +137,7 @@ export async function getHelloWorldMultiProtocolApp( // TODO we need a MultiProtocolIgp // Using an standard IGP for just evm chains for now // Unfortunately this requires hacking surgically around certain addresses - const envAddresses = hyperlaneEnvironments[sdkEnvName]; - const filteredAddresses = filterAddressesToProtocol( + const filteredAddresses = filterChainMapToProtocol( envAddresses, ProtocolType.Ethereum, multiProtocolProvider, diff --git a/typescript/infra/scripts/module-can-verify.ts b/typescript/infra/scripts/module-can-verify.ts new file mode 100644 index 000000000..25eacfe20 --- /dev/null +++ b/typescript/infra/scripts/module-can-verify.ts @@ -0,0 +1,63 @@ +import { HyperlaneCore, moduleCanCertainlyVerify } from '@hyperlane-xyz/sdk'; +import { ProtocolType } from '@hyperlane-xyz/utils'; + +import { mainnetHyperlaneDefaultIsmCache } from '../config/routingIsm'; +import { deployEnvToSdkEnv } from '../src/config/environment'; + +import { getArgs, getEnvironmentConfig } from './utils'; + +// Hacky temporary script just to make sure that default ISMs are correct. +// Testnet3 has already been updated, mainnet2 hasn't, so the above cache +// is used for mainnet2. + +async function main() { + const args = await getArgs().argv; + + const { environment } = args; + + const config = getEnvironmentConfig(environment); + const multiProvider = await config.getMultiProvider(); + + const core = HyperlaneCore.fromEnvironment( + deployEnvToSdkEnv[environment], + multiProvider, + ); + + for (const local of core.chains()) { + if ( + multiProvider.getChainMetadata(local).protocol !== ProtocolType.Ethereum + ) { + continue; + } + + let ismToCheck = ''; + if (environment === 'testnet4') { + ismToCheck = await core.getContracts(local).mailbox.defaultIsm(); + } else if (environment === 'mainnet2') { + ismToCheck = mainnetHyperlaneDefaultIsmCache[local]!; + } else { + throw new Error(`Unsupported environment ${environment}`); + } + + const remotes = multiProvider.getRemoteChains(local); + for (const remote of remotes) { + console.log(`Checking chain ${local} can receive from ${remote}...`); + const canVerify = await moduleCanCertainlyVerify( + ismToCheck, + multiProvider, + remote, + local, + ); + if (canVerify) { + console.log('All good!'); + } else { + console.error(`Chain ${local} cannot receive from ${remote}!!!!`); + } + } + } +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/typescript/infra/scripts/print-chain-metadatas.ts b/typescript/infra/scripts/print-chain-metadatas.ts new file mode 100644 index 000000000..7b96c3580 --- /dev/null +++ b/typescript/infra/scripts/print-chain-metadatas.ts @@ -0,0 +1,17 @@ +import { getArgs, getEnvironmentConfig } from './utils'; + +// This script exists to print the chain metadata configs for a given environment +// so they can easily be copied into the Sealevel tooling. :'( + +async function main() { + const args = await getArgs().argv; + + const environmentConfig = getEnvironmentConfig(args.environment); + + console.log(JSON.stringify(environmentConfig.chainMetadataConfigs, null, 2)); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/typescript/infra/scripts/print-multisig-ism-config.ts b/typescript/infra/scripts/print-multisig-ism-config.ts new file mode 100644 index 000000000..580afbf0f --- /dev/null +++ b/typescript/infra/scripts/print-multisig-ism-config.ts @@ -0,0 +1,29 @@ +import { AllChains, ModuleType } from '@hyperlane-xyz/sdk'; + +import { multisigIsms } from '../config/multisigIsm'; + +import { getArgs, withContext } from './utils'; + +// This script exists to print the default multisig ISM validator sets for a given environment +// so they can easily be copied into the Sealevel tooling. :'( + +async function main() { + const args = await withContext(getArgs()) + .describe('local', 'local chain') + .choices('local', AllChains) + .demandOption('local').argv; + + const config = multisigIsms( + args.environment, + args.local, + ModuleType.MESSAGE_ID_MULTISIG, + args.context, + ); + + console.log(JSON.stringify(config, null, 2)); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/typescript/infra/scripts/utils.ts b/typescript/infra/scripts/utils.ts index ea3ed6a6b..db0fd5ab7 100644 --- a/typescript/infra/scripts/utils.ts +++ b/typescript/infra/scripts/utils.ts @@ -1,5 +1,3 @@ -import { Keypair } from '@solana/web3.js'; -import { Wallet } from 'ethers'; import path from 'path'; import yargs from 'yargs'; @@ -7,7 +5,6 @@ import { AllChains, ChainMap, ChainMetadata, - ChainMetadataManager, ChainName, Chains, CoreConfig, @@ -19,12 +16,7 @@ import { RpcConsensusType, collectValidators, } from '@hyperlane-xyz/sdk'; -import { - ProtocolType, - objMap, - promiseObjAll, - strip0x, -} from '@hyperlane-xyz/utils'; +import { ProtocolType, objMap, promiseObjAll } from '@hyperlane-xyz/utils'; import { Contexts } from '../config/contexts'; import { environments } from '../config/environments'; @@ -167,7 +159,7 @@ export function getAgentConfig( return agentConfig; } -function getKeyForRole( +export function getKeyForRole( environment: DeployEnvironment, context: Contexts, chain: ChainName, @@ -191,7 +183,6 @@ export async function getMultiProviderForRole( if (process.env.CI === 'true') { return new MultiProvider(); // use default RPCs } - const multiProvider = new MultiProvider(txConfigs); await promiseObjAll( objMap(txConfigs, async (chain, _) => { @@ -202,6 +193,7 @@ export async function getMultiProviderForRole( multiProvider.setSigner(chain, signer); }), ); + return multiProvider; } @@ -219,34 +211,13 @@ export async function getKeysForRole( } const keys = await promiseObjAll( - objMap(txConfigs, async (chain, _) => { - const key = getKeyForRole(environment, context, chain, role, index); - if (!key.privateKey) - throw new Error(`Key for ${chain} does not have private key`); - return key; - }), + objMap(txConfigs, async (chain, _) => + getKeyForRole(environment, context, chain, role, index), + ), ); return keys; } -// Note: this will only work for keystores that allow key's to be extracted. -export function getAddressesForKey( - keys: ChainMap, - chain: ChainName, - manager: ChainMetadataManager, -) { - const protocol = manager.getChainMetadata(chain).protocol; - if (protocol === ProtocolType.Ethereum) { - return new Wallet(keys[chain]).address; - } else if (protocol === ProtocolType.Sealevel) { - return Keypair.fromSeed( - Buffer.from(strip0x(keys[chain].privateKey), 'hex'), - ).publicKey.toBase58(); - } else { - throw Error(`Protocol ${protocol} not supported`); - } -} - export function getContractAddressesSdkFilepath() { return path.join('../sdk/src/consts/environments'); } @@ -337,10 +308,29 @@ export async function getRouterConfig( ).intersection; for (const chain of knownChains) { + // CI will not have signers for all known chains. To avoid failing, we + // default to the owner configured in the environment if we cannot get a + // signer address. + const getSignerAddress = (chain: ChainName) => { + const signer = multiProvider.tryGetSigner(chain); + if (!signer) { + const owner = owners[chain]; + console.warn( + `Unable to get signer for chain, ${chain}, defaulting to configured owner ${owner}`, + ); + return owner; + } + return signer.getAddress(); + }; + + // MultiProvider signers are only used for Ethereum chains. + const owner = + useMultiProviderOwners && + multiProvider.getChainMetadata(chain).protocol === ProtocolType.Ethereum + ? await getSignerAddress(chain) + : owners[chain]; config[chain] = { - owner: useMultiProviderOwners - ? await multiProvider.getSignerAddress(chain) - : owners[chain], + owner: owner, mailbox: core.getContracts(chain).mailbox.address, // hook: igp.getContracts(chain).interchainGasPaymaster.address, }; diff --git a/typescript/infra/src/agents/key-utils.ts b/typescript/infra/src/agents/key-utils.ts index 547b2f015..e7afc68ba 100644 --- a/typescript/infra/src/agents/key-utils.ts +++ b/typescript/infra/src/agents/key-utils.ts @@ -31,11 +31,11 @@ export function getRelayerCloudAgentKeys( const keys = []; keys.push(new AgentAwsKey(agentConfig, Role.Relayer)); - const nonEthereumChains = agentConfig.contextChainNames[Role.Relayer].find( - (chainName) => chainMetadata[chainName].protocol !== ProtocolType.Ethereum, - ); + const hasNonEthereumChains = !!agentConfig.contextChainNames[ + Role.Relayer + ].find(isNotEthereumProtocolChain); // If there are any non-ethereum chains, we also want hex keys. - if (nonEthereumChains) { + if (hasNonEthereumChains) { keys.push( new AgentGCPKey(agentConfig.runEnv, agentConfig.context, Role.Relayer), ); @@ -43,6 +43,23 @@ export function getRelayerCloudAgentKeys( return keys; } +export function getKathyCloudAgentKeys( + agentConfig: AgentContextConfig, +): Array { + const gcpKey = new AgentGCPKey( + agentConfig.runEnv, + agentConfig.context, + Role.Kathy, + ); + if (!agentConfig.aws) { + return [gcpKey]; + } + + // Return both GCP and AWS keys for Kathy even if the agentConfig is configured + // to use AWS. Non-Ethereum chains require GCP keys. + return [gcpKey, new AgentAwsKey(agentConfig, Role.Kathy)]; +} + // If getting all keys for relayers or validators, it's recommended to use // `getRelayerCloudAgentKeys` or `getValidatorCloudAgentKeys` instead. export function getCloudAgentKey( @@ -51,10 +68,19 @@ export function getCloudAgentKey( chainName?: ChainName, index?: number, ): CloudAgentKey { - // The deployer is always GCP-based - if (!!agentConfig.aws && role !== Role.Deployer) { + // Non-evm Kathy is always GCP-based but does not index by chain + if ( + role === Role.Kathy && + chainName && + isNotEthereumProtocolChain(chainName) + ) { + return new AgentGCPKey(agentConfig.runEnv, agentConfig.context, role); + } + // Otherwise use an AWS key except for the deployer + else if (!!agentConfig.aws && role !== Role.Deployer) { return new AgentAwsKey(agentConfig, role, chainName, index); } else { + // Fallback to GCP return new AgentGCPKey( agentConfig.runEnv, agentConfig.context, @@ -88,8 +114,12 @@ export function getAllCloudAgentKeys( keys.push(...getRelayerCloudAgentKeys(agentConfig)); if ((agentConfig.rolesWithKeys ?? []).includes(Role.Validator)) keys.push(...getValidatorCloudAgentKeys(agentConfig)); + if ((agentConfig.rolesWithKeys ?? []).includes(Role.Kathy)) + keys.push(...getKathyCloudAgentKeys(agentConfig)); + for (const role of agentConfig.rolesWithKeys) { - if (role == Role.Relayer || role == Role.Validator) continue; + if (role == Role.Relayer || role == Role.Validator || role == Role.Kathy) + continue; keys.push(getCloudAgentKey(agentConfig, role)); } return keys; @@ -203,3 +233,8 @@ function addressesIdentifier( ) { return `${context}-${environment}-key-addresses`; } + +function isNotEthereumProtocolChain(chainName: ChainName) { + if (!chainMetadata[chainName]) throw new Error(`Unknown chain ${chainName}`); + return chainMetadata[chainName].protocol !== ProtocolType.Ethereum; +} diff --git a/typescript/infra/src/config/chain.ts b/typescript/infra/src/config/chain.ts index fee1568ce..9d8197fc0 100644 --- a/typescript/infra/src/config/chain.ts +++ b/typescript/infra/src/config/chain.ts @@ -53,7 +53,6 @@ export async function fetchProvider( // and are ordered randomly for each RPC. priority: index, }; - console.log('fallbackProviderConfig', fallbackProviderConfig); return fallbackProviderConfig; }), 1, // a single provider is "quorum", but failure will cause failover to the next provider diff --git a/typescript/infra/src/config/environment.ts b/typescript/infra/src/config/environment.ts index 8be815ed8..f2eb0b40e 100644 --- a/typescript/infra/src/config/environment.ts +++ b/typescript/infra/src/config/environment.ts @@ -19,7 +19,7 @@ import { Role } from '../roles'; import { RootAgentConfig } from './agent'; import { KeyFunderConfig } from './funding'; import { AllStorageGasOracleConfigs } from './gas-oracle'; -import { HelloWorldConfig } from './helloworld'; +import { HelloWorldConfig } from './helloworld/types'; import { InfrastructureConfig } from './infrastructure'; import { LiquidityLayerRelayerConfig } from './middleware'; diff --git a/typescript/infra/src/config/gas-oracle.ts b/typescript/infra/src/config/gas-oracle.ts index b28661e86..41bf8ccac 100644 --- a/typescript/infra/src/config/gas-oracle.ts +++ b/typescript/infra/src/config/gas-oracle.ts @@ -1,6 +1,9 @@ import { BigNumber, ethers } from 'ethers'; import { ChainMap, ChainName } from '@hyperlane-xyz/sdk'; +import { convertDecimalsEthersBigNumber } from '@hyperlane-xyz/utils'; + +import { mustGetChainNativeTokenDecimals } from '../utils/utils'; export type RemoteGasData = { tokenExchangeRate: BigNumber; @@ -24,6 +27,12 @@ export const TOKEN_EXCHANGE_RATE_SCALE = ethers.utils.parseUnits( TOKEN_EXCHANGE_RATE_DECIMALS, ); +// Overcharge by 30% to account for market making risk +const TOKEN_EXCHANGE_RATE_MULTIPLIER = ethers.utils.parseUnits( + '1.30', + TOKEN_EXCHANGE_RATE_DECIMALS, +); + // Gets the StorageGasOracleConfig for a particular local chain function getLocalStorageGasOracleConfig( local: ChainName, @@ -62,3 +71,21 @@ export function getAllStorageGasOracleConfigs( }; }, {}) as AllStorageGasOracleConfigs; } + +export function getTokenExchangeRateFromValues( + local: ChainName, + localValue: BigNumber, + remote: ChainName, + remoteValue: BigNumber, +) { + // This does not yet account for decimals! + const exchangeRate = remoteValue + .mul(TOKEN_EXCHANGE_RATE_MULTIPLIER) + .div(localValue); + + return convertDecimalsEthersBigNumber( + mustGetChainNativeTokenDecimals(remote), + mustGetChainNativeTokenDecimals(local), + exchangeRate, + ); +} diff --git a/typescript/infra/src/config/helloworld/config.ts b/typescript/infra/src/config/helloworld/config.ts new file mode 100644 index 000000000..ea1ca7128 --- /dev/null +++ b/typescript/infra/src/config/helloworld/config.ts @@ -0,0 +1,27 @@ +import { ChainMap } from '@hyperlane-xyz/sdk'; +import { MultiProvider, RouterConfig } from '@hyperlane-xyz/sdk'; +import { objMap } from '@hyperlane-xyz/utils'; + +import { Contexts } from '../../../config/contexts'; +import { + mainnetHyperlaneDefaultIsmCache, + routingIsm, +} from '../../../config/routingIsm'; +import { getRouterConfig } from '../../../scripts/utils'; +import { DeployEnvironment } from '../environment'; + +export async function helloWorldRouterConfig( + environment: DeployEnvironment, + context: Contexts, + multiProvider: MultiProvider, +): Promise> { + const routerConfig = await getRouterConfig(environment, multiProvider, true); + return objMap(routerConfig, (chain, config) => ({ + ...config, + interchainSecurityModule: + context === Contexts.Hyperlane + ? // TODO move back to `undefined` after these are verified and made the default ISMs + mainnetHyperlaneDefaultIsmCache[chain] + : routingIsm(environment, chain, context), + })); +} diff --git a/typescript/infra/src/config/helloworld.ts b/typescript/infra/src/config/helloworld/types.ts similarity index 96% rename from typescript/infra/src/config/helloworld.ts rename to typescript/infra/src/config/helloworld/types.ts index 3f9d97700..89c729bc3 100644 --- a/typescript/infra/src/config/helloworld.ts +++ b/typescript/infra/src/config/helloworld/types.ts @@ -1,6 +1,6 @@ import { ChainMap, ChainName, RpcConsensusType } from '@hyperlane-xyz/sdk'; -import { DockerConfig } from './agent'; +import { DockerConfig } from '../agent'; export enum HelloWorldKathyRunMode { // Sends messages between all pairwise chains diff --git a/typescript/infra/src/config/index.ts b/typescript/infra/src/config/index.ts index 262d276de..f98fa4b3b 100644 --- a/typescript/infra/src/config/index.ts +++ b/typescript/infra/src/config/index.ts @@ -5,6 +5,6 @@ export { StorageGasOracleConfig, getAllStorageGasOracleConfigs, } from './gas-oracle'; -export { HelloWorldConfig } from './helloworld'; +export { HelloWorldConfig } from './helloworld/types'; export { InfrastructureConfig } from './infrastructure'; export * from './agent'; diff --git a/typescript/infra/src/govern/HyperlaneCoreGovernor.ts b/typescript/infra/src/govern/HyperlaneCoreGovernor.ts index 3b98153d8..019c7321a 100644 --- a/typescript/infra/src/govern/HyperlaneCoreGovernor.ts +++ b/typescript/infra/src/govern/HyperlaneCoreGovernor.ts @@ -25,7 +25,7 @@ export class HyperlaneCoreGovernor extends HyperlaneAppGovernor< } protected async handleMailboxViolation(violation: MailboxViolation) { - switch (violation.mailboxType) { + switch (violation.subType) { case MailboxViolationType.DefaultIsm: { let ismAddress: string; if (typeof violation.expected === 'object') { diff --git a/typescript/infra/src/govern/ProxiedRouterGovernor.ts b/typescript/infra/src/govern/ProxiedRouterGovernor.ts index c30018ca6..b2e5eccb9 100644 --- a/typescript/infra/src/govern/ProxiedRouterGovernor.ts +++ b/typescript/infra/src/govern/ProxiedRouterGovernor.ts @@ -3,8 +3,12 @@ import { ConnectionClientViolation, ConnectionClientViolationType, HyperlaneAppChecker, + OwnerViolation, RouterApp, RouterConfig, + RouterViolation, + RouterViolationType, + ViolationType, } from '@hyperlane-xyz/sdk'; import { Address } from '@hyperlane-xyz/utils'; @@ -23,13 +27,18 @@ export class ProxiedRouterGovernor< protected async mapViolationsToCalls() { for (const violation of this.checker.violations) { - if ( - violation.type === - ConnectionClientViolationType.InterchainSecurityModule - ) { - this.handleIsmViolation(violation as ConnectionClientViolation); - } else { - throw new Error(`Unsupported violation type ${violation.type}`); + switch (violation.type) { + case ConnectionClientViolationType.InterchainSecurityModule: + this.handleIsmViolation(violation as ConnectionClientViolation); + break; + case RouterViolationType.EnrolledRouter: + this.handleEnrolledRouterViolation(violation as RouterViolation); + break; + case ViolationType.Owner: + this.handleOwnerViolation(violation as OwnerViolation); + break; + default: + throw new Error(`Unsupported violation type ${violation.type}`); } } } @@ -44,4 +53,18 @@ export class ProxiedRouterGovernor< description: `Set ISM of ${violation.contract.address} to ${violation.expected}`, }); } + + protected handleEnrolledRouterViolation(violation: RouterViolation) { + const remoteDomain = this.checker.multiProvider.getDomainId( + violation.remoteChain, + ); + this.pushCall(violation.chain, { + to: violation.contract.address, + data: violation.contract.interface.encodeFunctionData( + 'enrollRemoteRouter', + [remoteDomain, violation.expected], + ), + description: `Enroll router for remote chain ${violation.remoteChain} (${remoteDomain}) ${violation.expected} in ${violation.contract.address}`, + }); + } } diff --git a/typescript/infra/src/helloworld/kathy.ts b/typescript/infra/src/helloworld/kathy.ts index 0b823250a..56881fadf 100644 --- a/typescript/infra/src/helloworld/kathy.ts +++ b/typescript/infra/src/helloworld/kathy.ts @@ -1,10 +1,11 @@ import { Contexts } from '../../config/contexts'; import { AgentAwsUser } from '../agents/aws'; +import { AgentGCPKey } from '../agents/gcp'; import { AgentContextConfig } from '../config'; import { HelloWorldKathyConfig, HelloWorldKathyRunMode, -} from '../config/helloworld'; +} from '../config/helloworld/types'; import { Role } from '../roles'; import { HelmCommand, helmifyValues } from '../utils/helm'; import { execCmd } from '../utils/utils'; @@ -26,6 +27,15 @@ export async function runHelloworldKathyHelmCommand( await awsUser.createKeyIfNotExists(agentConfig); } + // Also ensure a GCP key exists, which is used for non-EVM chains even if + // the agent config is AWS-based + const kathyKey = new AgentGCPKey( + agentConfig.runEnv, + agentConfig.context, + Role.Kathy, + ); + await kathyKey.createIfNotExists(); + const values = getHelloworldKathyHelmValues(agentConfig, kathyConfig); return execCmd( diff --git a/typescript/infra/src/utils/utils.ts b/typescript/infra/src/utils/utils.ts index 783748708..1a8a79f8e 100644 --- a/typescript/infra/src/utils/utils.ts +++ b/typescript/infra/src/utils/utils.ts @@ -5,7 +5,12 @@ import { ethers } from 'ethers'; import fs from 'fs'; import path from 'path'; -import { AllChains, ChainName, CoreChainName } from '@hyperlane-xyz/sdk'; +import { + AllChains, + ChainName, + CoreChainName, + chainMetadata, +} from '@hyperlane-xyz/sdk'; import { objMerge } from '@hyperlane-xyz/utils'; import { Contexts } from '../../config/contexts'; @@ -260,3 +265,11 @@ export function diagonalize(array: Array>): Array { } return diagonalized; } + +export function mustGetChainNativeTokenDecimals(chain: ChainName): number { + const metadata = chainMetadata[chain]; + if (!metadata.nativeToken) { + throw new Error(`No native token for chain ${chain}`); + } + return metadata.nativeToken.decimals; +} diff --git a/typescript/sdk/src/consts/chainMetadata.ts b/typescript/sdk/src/consts/chainMetadata.ts index 859054873..779c1de0a 100644 --- a/typescript/sdk/src/consts/chainMetadata.ts +++ b/typescript/sdk/src/consts/chainMetadata.ts @@ -270,7 +270,10 @@ export const celo: ChainMetadata = { reorgPeriod: 0, estimateBlockTime: 5, }, - gnosisSafeTransactionServiceUrl: 'https://safe-transaction-celo.safe.global/', + // The official Gnosis safe URL `https://safe-transaction-celo.safe.global` doesn't work well + // with delegates on a multisig created with the old unofficial Celo tooling. + gnosisSafeTransactionServiceUrl: + 'https://mainnet-tx-svc.celo-safe-prod.celo-networks-dev.org/', }; export const ethereum: ChainMetadata = { diff --git a/typescript/sdk/src/consts/chains.ts b/typescript/sdk/src/consts/chains.ts index 5f9085aa6..f7b07cc6e 100644 --- a/typescript/sdk/src/consts/chains.ts +++ b/typescript/sdk/src/consts/chains.ts @@ -58,6 +58,7 @@ export const Mainnets: Array = [ Chains.optimism, Chains.polygon, Chains.gnosis, + Chains.solana, ]; export const Testnets: Array = [ @@ -75,6 +76,7 @@ export const Testnets: Array = [ Chains.polygonzkevmtestnet, Chains.scrollsepolia, Chains.sepolia, + Chains.solanadevnet, ]; export const TestChains: Array = [ diff --git a/typescript/sdk/src/consts/environments/index.ts b/typescript/sdk/src/consts/environments/index.ts index 66c9e2983..16f52ec93 100644 --- a/typescript/sdk/src/consts/environments/index.ts +++ b/typescript/sdk/src/consts/environments/index.ts @@ -4,14 +4,9 @@ import { ChainName } from '../../types'; import { CoreChainName } from '../chains'; import mainnet from './mainnet.json'; -import testnetSealevel from './testnet-sealevel.json'; import testnet from './testnet.json'; export const hyperlaneEnvironments = { testnet, mainnet }; -export const hyperlaneEnvironmentsWithSealevel = { - ...hyperlaneEnvironments, - testnet: { ...testnet, ...testnetSealevel }, -}; export type HyperlaneEnvironment = keyof typeof hyperlaneEnvironments; export type HyperlaneEnvironmentChain = Extract< diff --git a/typescript/sdk/src/consts/environments/mainnet.json b/typescript/sdk/src/consts/environments/mainnet.json index 7523050da..b5c4f927a 100644 --- a/typescript/sdk/src/consts/environments/mainnet.json +++ b/typescript/sdk/src/consts/environments/mainnet.json @@ -153,5 +153,22 @@ "interchainAccountRouter": "0xe9E3444DDD80c50276c0Fcf316026f6d7fEc2c47", "merkleRootMultisigIsmFactory": "0x784b9D0f4eF9fb8444DfB5d24AB221C9D1A85395", "messageIdMultisigIsmFactory": "0xC4275763D7b621eb732847957012F1fb35C90BB8" + }, + "solana": { + "storageGasOracle": "11111111111111111111111111111111", + "validatorAnnounce": "C88Lk5GR6cPxYoJxPbNDDEwsx5Kxn1wZEomvQ2So333g", + "proxyAdmin": "11111111111111111111111111111111", + "mailbox": "Ge9atjAc3Ltu91VTbNpJDCjZ9CFxFyck4h3YBcTF9XPq", + "interchainGasPaymaster": "FCNfmLSZLo5x7oNYmkYU8WdPUu7pj636P9CaMxkmaCp7", + "defaultIsmInterchainGasPaymaster": "GTj6WzNxLNFydq5zJrV9p13fyqotRoo1MQykNCWuVpbS", + "multisigIsm": "11111111111111111111111111111111", + "testRecipient": "11111111111111111111111111111111", + "interchainAccountIsm": "11111111111111111111111111111111", + "aggregationIsmFactory": "11111111111111111111111111111111", + "routingIsmFactory": "11111111111111111111111111111111", + "interchainQueryRouter": "11111111111111111111111111111111", + "interchainAccountRouter": "11111111111111111111111111111111", + "merkleRootMultisigIsmFactory": "11111111111111111111111111111111", + "messageIdMultisigIsmFactory": "11111111111111111111111111111111" } } diff --git a/typescript/sdk/src/consts/environments/testnet-sealevel.json b/typescript/sdk/src/consts/environments/testnet-sealevel.json deleted file mode 100644 index 214c32ece..000000000 --- a/typescript/sdk/src/consts/environments/testnet-sealevel.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "solanadevnet": { - "storageGasOracle": "", - "validatorAnnounce": "", - "proxyAdmin": "", - "mailbox": "4v25Dz9RccqUrTzmfHzJMsjd1iVoNrWzeJ4o6GYuJrVn", - "interchainGasPaymaster": "", - "defaultIsmInterchainGasPaymaster": "", - "multisigIsm": "", - "testRecipient": "", - "interchainAccountIsm": "", - "aggregationIsmFactory": "", - "routingIsmFactory": "", - "interchainQueryRouter": "", - "interchainAccountRouter": "", - "merkleRootMultisigIsmFactory": "", - "messageIdMultisigIsmFactory": "" - } -} diff --git a/typescript/sdk/src/consts/environments/testnet.json b/typescript/sdk/src/consts/environments/testnet.json index 920740777..d764955ed 100644 --- a/typescript/sdk/src/consts/environments/testnet.json +++ b/typescript/sdk/src/consts/environments/testnet.json @@ -190,5 +190,22 @@ "protocolFee": "0x244d1F7e30Be144A87602905baBF86630e8f39DC", "mailbox": "0x2d1889fe5B092CD988972261434F7E5f26041115", "validatorAnnounce": "0x99303EFF09332cDd93E8BC8b2F07b2416e4501e5" + }, + "solanadevnet": { + "storageGasOracle": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x0000000000000000000000000000000000000000", + "proxyAdmin": "0x0000000000000000000000000000000000000000", + "mailbox": "4v25Dz9RccqUrTzmfHzJMsjd1iVoNrWzeJ4o6GYuJrVn", + "interchainGasPaymaster": "7hMPEGdgBQFsjEz3aaNwZp8WMFHs615zAM3erXBDJuJR", + "defaultIsmInterchainGasPaymaster": "0x0000000000000000000000000000000000000000", + "multisigIsm": "0x0000000000000000000000000000000000000000", + "testRecipient": "0x0000000000000000000000000000000000000000", + "interchainAccountIsm": "0x0000000000000000000000000000000000000000", + "aggregationIsmFactory": "0x0000000000000000000000000000000000000000", + "routingIsmFactory": "0x0000000000000000000000000000000000000000", + "interchainQueryRouter": "0x0000000000000000000000000000000000000000", + "interchainAccountRouter": "0x0000000000000000000000000000000000000000", + "merkleRootMultisigIsmFactory": "0x0000000000000000000000000000000000000000", + "messageIdMultisigIsmFactory": "0x0000000000000000000000000000000000000000" } } diff --git a/typescript/sdk/src/consts/multisigIsm.ts b/typescript/sdk/src/consts/multisigIsm.ts index 1bdf0ea00..3bcda6a5c 100644 --- a/typescript/sdk/src/consts/multisigIsm.ts +++ b/typescript/sdk/src/consts/multisigIsm.ts @@ -98,6 +98,14 @@ export const defaultMultisigIsmConfigs: ChainMap = { '0xa18580444eaeb1c5957e7b66a6bf84b6519f904d', // staked ], }, + solana: { + threshold: 2, + validators: [ + '0x3cd1a081f38874bbb075bf10b62adcb858db864c', // abacus + '0x2b0c45f6111ae1c1684d4287792e3bd6ebd1abcc', // ZKV + '0x7b9ec253a8ba38994457eb9dbe386938d545351a', // everstake + ], + }, // ----------------- Testnets ----------------- alfajores: { threshold: 2, @@ -212,4 +220,12 @@ export const defaultMultisigIsmConfigs: ChainMap = { '0xd476548222f43206d0abaa30e46e28670aa7859c', ], }, + solanadevnet: { + threshold: 2, + validators: [ + '0xec0f73dbc5b1962a20f7dcbe07c98414025b0c43', + '0x9c20a149dfa09ea9f77f5a7ca09ed44f9c025133', + '0x967c5ecdf2625ae86580bd203b630abaaf85cd62', + ], + }, }; diff --git a/typescript/sdk/src/contracts/contracts.ts b/typescript/sdk/src/contracts/contracts.ts index be0934209..e52fe1cad 100644 --- a/typescript/sdk/src/contracts/contracts.ts +++ b/typescript/sdk/src/contracts/contracts.ts @@ -1,10 +1,11 @@ -import { Contract } from 'ethers'; +import { Contract, ethers } from 'ethers'; import { Ownable } from '@hyperlane-xyz/core'; import { Address, ProtocolType, ValueOf, + hexOrBase58ToHex, objFilter, objMap, pick, @@ -65,19 +66,30 @@ export function filterAddressesMap( ); } -export function filterAddressesToProtocol( - addresses: HyperlaneAddressesMap, +export function filterChainMapToProtocol( + contractsMap: ChainMap, protocolType: ProtocolType, - // Note, MultiProviders are passable here metadataManager: ChainMetadataManager, -): HyperlaneAddressesMap { +): ChainMap { return objFilter( - addresses, + contractsMap, (c, _addrs): _addrs is any => metadataManager.tryGetChainMetadata(c)?.protocol === protocolType, ); } +export function filterChainMapExcludeProtocol( + contractsMap: ChainMap, + protocolType: ProtocolType, + metadataManager: ChainMetadataManager, +): ChainMap { + return objFilter( + contractsMap, + (c, _addrs): _addrs is any => + metadataManager.tryGetChainMetadata(c)?.protocol !== protocolType, + ); +} + export function attachContracts( addresses: HyperlaneAddresses, factories: F, @@ -98,6 +110,40 @@ export function attachContractsMap( ) as HyperlaneContractsMap; } +export function attachContractsMapAndGetForeignDeployments< + F extends HyperlaneFactories, +>( + addressesMap: HyperlaneAddressesMap, + factories: F, + metadataManager: ChainMetadataManager, +): { + contractsMap: HyperlaneContractsMap; + foreignDeployments: ChainMap
; +} { + const contractsMap = attachContractsMap( + filterChainMapToProtocol( + addressesMap, + ProtocolType.Ethereum, + metadataManager, + ), + factories, + ); + + const foreignDeployments = objMap( + filterChainMapExcludeProtocol( + addressesMap, + ProtocolType.Ethereum, + metadataManager, + ), + (_, addresses) => hexOrBase58ToHex(addresses.router), + ); + + return { + contractsMap, + foreignDeployments, + }; +} + export function connectContracts( contracts: HyperlaneContracts, connection: Connection, @@ -143,9 +189,31 @@ export function appFromAddressesMapHelper( contractsMap: HyperlaneContractsMap; multiProvider: MultiProvider; } { + // Hack to accommodate non-Ethereum artifacts, while still retaining their + // presence in the addressesMap so that they are included in the list of chains + // on the MultiProvider (needed for getting metadata). A non-Ethereum-style address + // from another execution environment will cause Ethers to throw if we try to attach + // it, so we just replace it with the zero address. + const addressesMapWithEthereumizedAddresses = objMap( + addressesMap, + (chain, addresses) => { + const metadata = multiProvider.getChainMetadata(chain); + if (metadata.protocol === ProtocolType.Ethereum) { + return addresses; + } + return objMap( + addresses, + (_key, _address) => ethers.constants.AddressZero, + ); + }, + ); + // Attaches contracts for each chain for which we have a complete set of // addresses - const contractsMap = attachContractsMap(addressesMap, factories); + const contractsMap = attachContractsMap( + addressesMapWithEthereumizedAddresses, + factories, + ); // Filters out providers for chains for which we don't have a complete set // of addresses diff --git a/typescript/sdk/src/core/HyperlaneCoreChecker.ts b/typescript/sdk/src/core/HyperlaneCoreChecker.ts index b16e92cca..1ed96c75e 100644 --- a/typescript/sdk/src/core/HyperlaneCoreChecker.ts +++ b/typescript/sdk/src/core/HyperlaneCoreChecker.ts @@ -84,7 +84,7 @@ export class HyperlaneCoreChecker extends HyperlaneAppChecker< if (!matches) { const violation: MailboxViolation = { type: CoreViolationType.Mailbox, - mailboxType: MailboxViolationType.DefaultIsm, + subType: MailboxViolationType.DefaultIsm, contract: mailbox, chain, actual: actualIsm, diff --git a/typescript/sdk/src/core/MultiProtocolCore.test.ts b/typescript/sdk/src/core/MultiProtocolCore.test.ts index ddb2a07b1..5c21e842d 100644 --- a/typescript/sdk/src/core/MultiProtocolCore.test.ts +++ b/typescript/sdk/src/core/MultiProtocolCore.test.ts @@ -4,13 +4,14 @@ import { ethers } from 'ethers'; import { ethereum } from '../consts/chainMetadata'; import { Chains } from '../consts/chains'; import { MultiProtocolProvider } from '../providers/MultiProtocolProvider'; +import { ProviderType } from '../providers/ProviderType'; import { MultiProtocolCore } from './MultiProtocolCore'; import { EvmCoreAdapter } from './adapters/EvmCoreAdapter'; describe('MultiProtocolCore', () => { describe('constructs', () => { - it('with constructor', async () => { + it('with constructor', () => { const multiProvider = new MultiProtocolProvider({ ethereum: { ...ethereum, @@ -27,7 +28,7 @@ describe('MultiProtocolCore', () => { const ethAdapter = core.adapter(Chains.ethereum); expect(ethAdapter).to.be.instanceOf(EvmCoreAdapter); }); - it('from environment', async () => { + it('from environment', () => { const multiProvider = new MultiProtocolProvider(); const core = MultiProtocolCore.fromEnvironment('mainnet', multiProvider); expect(core).to.be.instanceOf(MultiProtocolCore); @@ -35,4 +36,58 @@ describe('MultiProtocolCore', () => { expect(ethAdapter).to.be.instanceOf(EvmCoreAdapter); }); }); + + describe('checks delivery', () => { + it('to EVM', async () => { + const multiProvider = new MultiProtocolProvider(); + const core = MultiProtocolCore.fromEnvironment('mainnet', multiProvider); + // https://arbiscan.io//tx/0x9da03376486327fc9b1e8069538e0fef91641055cb3a2ff89460c7955ab68264#eventlog + const receipt = { + transactionHash: + '0x9da03376486327fc9b1e8069538e0fef91641055cb3a2ff89460c7955ab68264', + logs: [ + { + data: '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000005300000013670000a4b100000000000000000000000096271ca0ab9eefb3ca481749c0ca4c705fd4f523000000890000000000000000000000006c0ac8cea75232aa7bed8cbe9c4f820e7a77a9c348656c6c6f2100000000000000000000000000', + topics: [ + '0x769f711d20c679153d382254f59892613b58a97cc876b249134ac25c80f9c814', + '0x00000000000000000000000096271ca0ab9eefb3ca481749c0ca4c705fd4f523', + '0x0000000000000000000000000000000000000000000000000000000000000089', + '0x0000000000000000000000006c0ac8cea75232aa7bed8cbe9c4f820e7a77a9c3', + ], + }, + ], + } as ethers.providers.TransactionReceipt; + // Should return immediately + await core.waitForMessagesProcessed(Chains.arbitrum, Chains.polygon, { + type: ProviderType.EthersV5, + receipt, + }); + }); + + it('to Sealevel', async () => { + const multiProvider = new MultiProtocolProvider(); + const core = MultiProtocolCore.fromEnvironment('mainnet', multiProvider); + // https://arbiscan.io//tx/0x9da03376486327fc9b1e8069538e0fef91641055cb3a2ff89460c7955ab68264#eventlog + const receipt = { + transactionHash: + '0x9da03376486327fc9b1e8069538e0fef91641055cb3a2ff89460c7955ab68264', + logs: [ + { + data: '0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000053000000136d0000a4b100000000000000000000000096271ca0ab9eefb3ca481749c0ca4c705fd4f523536f6c4d3797d0096b18b5b645c346a66d7f18c6c5738782c6bce24da57a3462bdef82b148656c6c6f2100000000000000000000000000', + topics: [ + '0x769f711d20c679153d382254f59892613b58a97cc876b249134ac25c80f9c814', + '0x00000000000000000000000096271ca0ab9eefb3ca481749c0ca4c705fd4f523', + '0x00000000000000000000000000000000000000000000000000000000536f6c4d', + '0x3797d0096b18b5b645c346a66d7f18c6c5738782c6bce24da57a3462bdef82b1', + ], + }, + ], + } as ethers.providers.TransactionReceipt; + // Should return immediately + await core.waitForMessagesProcessed(Chains.arbitrum, Chains.solana, { + type: ProviderType.EthersV5, + receipt, + }); + }); + }); }); diff --git a/typescript/sdk/src/core/adapters/SealevelCoreAdapter.ts b/typescript/sdk/src/core/adapters/SealevelCoreAdapter.ts index 027774d90..1709d9f0f 100644 --- a/typescript/sdk/src/core/adapters/SealevelCoreAdapter.ts +++ b/typescript/sdk/src/core/adapters/SealevelCoreAdapter.ts @@ -1,6 +1,12 @@ import { PublicKey } from '@solana/web3.js'; -import { Address, HexString, pollAsync } from '@hyperlane-xyz/utils'; +import { + Address, + HexString, + ensure0x, + pollAsync, + strip0x, +} from '@hyperlane-xyz/utils'; import { BaseSealevelAdapter } from '../../app/MultiProtocolApp'; import { MultiProtocolProvider } from '../../providers/MultiProtocolProvider'; @@ -35,14 +41,13 @@ export class SealevelCoreAdapter `Unsupported provider type for SealevelCoreAdapter ${sourceTx.type}`, ); } - const receipt = sourceTx.receipt; - const logs = receipt.meta?.logMessages; + const logs = sourceTx.receipt.meta?.logMessages; if (!logs) throw new Error('Transaction logs required to check message delivery'); const parsedLogs = SealevelCoreAdapter.parseMessageDispatchLogs(logs); if (!parsedLogs.length) throw new Error('Message dispatch log not found'); return parsedLogs.map(({ destination, messageId }) => ({ - messageId: Buffer.from(messageId, 'hex').toString('hex'), + messageId: ensure0x(messageId), destination: this.multiProvider.getChainName(destination), })); } @@ -144,10 +149,16 @@ export class SealevelCoreAdapter static deriveMailboxMessageProcessedPda( mailboxProgramId: string | PublicKey, - messageId: string, + messageId: HexString, ): PublicKey { return super.derivePda( - ['hyperlane', '-', 'processed_message', Buffer.from(messageId, 'hex')], + [ + 'hyperlane', + '-', + 'processed_message', + '-', + Buffer.from(strip0x(messageId), 'hex'), + ], mailboxProgramId, ); } diff --git a/typescript/sdk/src/core/types.ts b/typescript/sdk/src/core/types.ts index aacefe25b..2d15a2ae1 100644 --- a/typescript/sdk/src/core/types.ts +++ b/typescript/sdk/src/core/types.ts @@ -28,8 +28,8 @@ export enum MailboxViolationType { export interface MailboxViolation extends CheckerViolation { type: CoreViolationType.Mailbox; + subType: MailboxViolationType; contract: Mailbox; - mailboxType: MailboxViolationType; } export interface MailboxMultisigIsmViolation extends MailboxViolation { diff --git a/typescript/sdk/src/deploy/HyperlaneAppChecker.ts b/typescript/sdk/src/deploy/HyperlaneAppChecker.ts index a840cdbec..f8d5418e0 100644 --- a/typescript/sdk/src/deploy/HyperlaneAppChecker.ts +++ b/typescript/sdk/src/deploy/HyperlaneAppChecker.ts @@ -3,6 +3,7 @@ import { keccak256 } from 'ethers/lib/utils'; import { Ownable, TimelockController__factory } from '@hyperlane-xyz/core'; import { Address, + ProtocolType, assert, eqAddress, objMap, @@ -49,7 +50,11 @@ export abstract class HyperlaneAppChecker< async check(): Promise { Object.keys(this.configMap) - .filter((_) => !this.app.chains().includes(_)) + .filter( + (chain) => + this.multiProvider.getChainMetadata(chain).protocol === + ProtocolType.Ethereum && !this.app.chains().includes(chain), + ) .forEach((chain: string) => this.addViolation({ type: ViolationType.NotDeployed, @@ -60,6 +65,7 @@ export abstract class HyperlaneAppChecker< ); return Promise.all( + // this.app.chains() will only return Ethereum chains that can be interacted with. this.app.chains().map((chain) => this.checkChain(chain)), ); } @@ -228,4 +234,24 @@ export abstract class HyperlaneAppChecker< const count = this.violations.length; assert(count === 0, `Found ${count} violations`); } + + logViolationsTable(): void { + const violations = this.violations; + if (violations.length > 0) { + // eslint-disable-next-line no-console + console.table(violations, [ + 'chain', + 'remote', + 'name', + 'type', + 'subType', + 'actual', + 'expected', + 'description', + ]); + } else { + // eslint-disable-next-line no-console + console.info(`${module} Checker found no violations`); + } + } } diff --git a/typescript/sdk/src/deploy/HyperlaneDeployer.ts b/typescript/sdk/src/deploy/HyperlaneDeployer.ts index e7b0971c4..87d24cd8c 100644 --- a/typescript/sdk/src/deploy/HyperlaneDeployer.ts +++ b/typescript/sdk/src/deploy/HyperlaneDeployer.ts @@ -13,7 +13,12 @@ import { TransparentUpgradeableProxy, TransparentUpgradeableProxy__factory, } from '@hyperlane-xyz/core'; -import { Address, eqAddress, runWithTimeout } from '@hyperlane-xyz/utils'; +import { + Address, + ProtocolType, + eqAddress, + runWithTimeout, +} from '@hyperlane-xyz/utils'; import { HyperlaneAddressesMap, @@ -77,8 +82,14 @@ export abstract class HyperlaneDeployer< configMap: ChainMap, ): Promise> { const configChains = Object.keys(configMap); + const ethereumConfigChains = configChains.filter( + (chain) => + this.multiProvider.getChainMetadata(chain).protocol === + ProtocolType.Ethereum, + ); + const targetChains = this.multiProvider.intersect( - configChains, + ethereumConfigChains, true, ).intersection; diff --git a/typescript/sdk/src/gas/HyperlaneIgpChecker.ts b/typescript/sdk/src/gas/HyperlaneIgpChecker.ts index 2e7f5e1e2..e654bc609 100644 --- a/typescript/sdk/src/gas/HyperlaneIgpChecker.ts +++ b/typescript/sdk/src/gas/HyperlaneIgpChecker.ts @@ -1,4 +1,4 @@ -import { BigNumber, utils as ethersUtils } from 'ethers'; +import { BigNumber, ethers, utils as ethersUtils } from 'ethers'; import { Address, eqAddress } from '@hyperlane-xyz/utils'; @@ -95,7 +95,13 @@ export class HyperlaneIgpChecker extends HyperlaneAppChecker< const remotes = this.app.remoteChains(local); for (const remote of remotes) { - const expectedOverhead = this.configMap[local].overhead[remote]; + let expectedOverhead = this.configMap[local].overhead[remote]; + if (!expectedOverhead) { + this.app.logger( + `No overhead configured for ${local} -> ${remote}, defaulting to 0`, + ); + expectedOverhead = 0; + } const remoteId = this.multiProvider.getDomainId(remote); const existingOverhead = await defaultIsmIgp.destinationGasLimit( @@ -132,14 +138,19 @@ export class HyperlaneIgpChecker extends HyperlaneAppChecker< expected: {}, }; - const remotes = this.app.remoteChains(local); + // In addition to all remote chains on the app, which are just Ethereum chains, + // also consider what the config says about non-Ethereum chains. + const remotes = new Set([ + ...this.app.remoteChains(local), + ...Object.keys(this.configMap[local].gasOracleType), + ]); for (const remote of remotes) { const remoteId = this.multiProvider.getDomainId(remote); const destinationGasConfigs = await igp.destinationGasConfigs(remoteId); const actualGasOracle = destinationGasConfigs.gasOracle; const expectedGasOracle = this.getGasOracleAddress(local, remote); - if (eqAddress(actualGasOracle, expectedGasOracle)) { + if (!eqAddress(actualGasOracle, expectedGasOracle)) { const remoteChain = remote as ChainName; gasOraclesViolation.actual[remoteChain] = actualGasOracle; gasOraclesViolation.expected[remoteChain] = expectedGasOracle; @@ -152,7 +163,7 @@ export class HyperlaneIgpChecker extends HyperlaneAppChecker< const actualBeneficiary = await igp.beneficiary(); const expectedBeneficiary = this.configMap[local].beneficiary; - if (eqAddress(actualBeneficiary, expectedBeneficiary)) { + if (!eqAddress(actualBeneficiary, expectedBeneficiary)) { const violation: IgpBeneficiaryViolation = { type: 'InterchainGasPaymaster', subType: IgpViolationType.Beneficiary, @@ -169,9 +180,10 @@ export class HyperlaneIgpChecker extends HyperlaneAppChecker< const config = this.configMap[local]; const gasOracleType = config.gasOracleType[remote]; if (!gasOracleType) { - throw Error( - `Expected gas oracle type for local ${local} and remote ${remote}`, + this.app.logger( + `No gas oracle for local ${local} and remote ${remote}, defaulting to zero address`, ); + return ethers.constants.AddressZero; } const coreContracts = this.app.getContracts(local); switch (gasOracleType) { diff --git a/typescript/sdk/src/index.ts b/typescript/sdk/src/index.ts index 5202e0371..95da574d2 100644 --- a/typescript/sdk/src/index.ts +++ b/typescript/sdk/src/index.ts @@ -28,17 +28,18 @@ export { HyperlaneEnvironmentChain, hyperlaneContractAddresses, hyperlaneEnvironments, - hyperlaneEnvironmentsWithSealevel, } from './consts/environments'; export { defaultMultisigIsmConfigs } from './consts/multisigIsm'; export { SEALEVEL_SPL_NOOP_ADDRESS } from './consts/sealevel'; export { attachContracts, attachContractsMap, + attachContractsMapAndGetForeignDeployments, connectContracts, connectContractsMap, filterAddressesMap, - filterAddressesToProtocol, + filterChainMapExcludeProtocol, + filterChainMapToProtocol, filterOwnableContracts, serializeContracts, serializeContractsMap, @@ -120,6 +121,7 @@ export { export { HyperlaneIsmFactory, collectValidators, + moduleCanCertainlyVerify, } from './ism/HyperlaneIsmFactory'; export { AggregationIsmConfig, @@ -253,6 +255,7 @@ export { } from './router/adapters/SealevelRouterAdapter'; export { IGasRouterAdapter, IRouterAdapter } from './router/adapters/types'; export { + MailboxClientConfig as ConnectionClientConfig, ClientViolation as ConnectionClientViolation, ClientViolationType as ConnectionClientViolationType, ForeignDeploymentConfig, @@ -264,6 +267,8 @@ export { ProxiedRouterConfig, RouterAddress, RouterConfig, + RouterViolation, + RouterViolationType, proxiedFactories, } from './router/types'; export { diff --git a/typescript/sdk/src/ism/HyperlaneIsmFactory.ts b/typescript/sdk/src/ism/HyperlaneIsmFactory.ts index b44a2d621..a8e5ed21a 100644 --- a/typescript/sdk/src/ism/HyperlaneIsmFactory.ts +++ b/typescript/sdk/src/ism/HyperlaneIsmFactory.ts @@ -346,6 +346,12 @@ export async function moduleMatchesConfig( return eqAddress(moduleAddress, config); } + // If the module address is zero, it can't match any object-based config. + // The subsequent check of what moduleType it is will throw, so we fail here. + if (eqAddress(moduleAddress, ethers.constants.AddressZero)) { + return false; + } + const provider = multiProvider.getProvider(chain); const module = IInterchainSecurityModule__factory.connect( moduleAddress, diff --git a/typescript/sdk/src/metadata/matchingList.ts b/typescript/sdk/src/metadata/matchingList.ts index 336c37cde..d09c23c15 100644 --- a/typescript/sdk/src/metadata/matchingList.ts +++ b/typescript/sdk/src/metadata/matchingList.ts @@ -12,11 +12,7 @@ const DomainSchema = z.union([ z.array(ZNzUint).nonempty(), ]); -const AddressSchema = z.union([ - z.literal('*'), - ZHash, - z.array(ZHash).nonempty(), -]); +const AddressSchema = z.union([z.literal('*'), ZHash, z.array(ZHash)]); const MatchingListElementSchema = z.object({ originDomain: DomainSchema.optional(), diff --git a/typescript/sdk/src/middleware/account/InterchainAccount.ts b/typescript/sdk/src/middleware/account/InterchainAccount.ts index 0c4a1d068..bfa2a061f 100644 --- a/typescript/sdk/src/middleware/account/InterchainAccount.ts +++ b/typescript/sdk/src/middleware/account/InterchainAccount.ts @@ -1,10 +1,14 @@ import { InterchainAccountRouter } from '@hyperlane-xyz/core'; +import { ProtocolType } from '@hyperlane-xyz/utils'; import { HyperlaneEnvironment, hyperlaneEnvironments, } from '../../consts/environments'; -import { appFromAddressesMapHelper } from '../../contracts/contracts'; +import { + appFromAddressesMapHelper, + filterChainMapToProtocol, +} from '../../contracts/contracts'; import { HyperlaneAddressesMap, HyperlaneContracts, @@ -32,7 +36,13 @@ export class InterchainAccount extends RouterApp { if (!envAddresses) { throw new Error(`No addresses found for ${env}`); } - return InterchainAccount.fromAddressesMap(envAddresses, multiProvider); + // Filter out non-EVM chains, as interchain accounts are EVM only at the moment. + const ethAddresses = filterChainMapToProtocol( + envAddresses, + ProtocolType.Ethereum, + multiProvider, + ); + return InterchainAccount.fromAddressesMap(ethAddresses, multiProvider); } static fromAddressesMap( diff --git a/typescript/sdk/src/router/HyperlaneRouterChecker.ts b/typescript/sdk/src/router/HyperlaneRouterChecker.ts index 512d46890..e47291c87 100644 --- a/typescript/sdk/src/router/HyperlaneRouterChecker.ts +++ b/typescript/sdk/src/router/HyperlaneRouterChecker.ts @@ -1,10 +1,16 @@ +import { ConnectionClientViolation } from '..'; import { ethers } from 'ethers'; -import { addressToBytes32, assert, eqAddress } from '@hyperlane-xyz/utils'; +import { addressToBytes32, eqAddress } from '@hyperlane-xyz/utils'; import { HyperlaneFactories } from '../contracts/types'; import { HyperlaneAppChecker } from '../deploy/HyperlaneAppChecker'; -import { ChainName } from '../types'; +import { + HyperlaneIsmFactory, + moduleMatchesConfig, +} from '../ism/HyperlaneIsmFactory'; +import { MultiProvider } from '../providers/MultiProvider'; +import { ChainMap, ChainName } from '../types'; import { RouterApp } from './RouterApps'; import { @@ -13,6 +19,8 @@ import { MailboxClientConfig, OwnableConfig, RouterConfig, + RouterViolation, + RouterViolationType, } from './types'; export class HyperlaneRouterChecker< @@ -20,6 +28,15 @@ export class HyperlaneRouterChecker< App extends RouterApp, Config extends RouterConfig, > extends HyperlaneAppChecker { + constructor( + multiProvider: MultiProvider, + app: App, + configMap: ChainMap, + readonly ismFactory?: HyperlaneIsmFactory, + ) { + super(multiProvider, app, configMap); + } + async checkChain(chain: ChainName): Promise { await this.checkMailboxClient(chain); await this.checkEnrolledRouters(chain); @@ -34,10 +51,44 @@ export class HyperlaneRouterChecker< violationType: ClientViolationType, ) => { const actual = await router[property](); - // TODO: check for IsmConfig const value = this.configMap[chain][property]; - if (value && typeof value === 'object') - throw new Error('object config unimplemented'); + + // If the value is an object, it's an ISM config + // and we should make sure it matches the actual ISM config + if (value && typeof value === 'object') { + if (!this.ismFactory) { + throw Error( + 'ISM factory not provided to HyperlaneRouterChecker, cannot check object-based ISM config', + ); + } + + const matches = await moduleMatchesConfig( + chain, + actual, + value, + this.multiProvider, + this.ismFactory!.chainMap[chain], + ); + + if (!matches) { + this.app.logger( + `Deploying ISM; ISM config of actual ${actual} does not match expected config ${JSON.stringify( + value, + )}`, + ); + const deployedIsm = await this.ismFactory.deploy(chain, value); + const violation: ConnectionClientViolation = { + chain, + type: violationType, + contract: router, + actual, + expected: deployedIsm.address, + description: `ISM config does not match deployed ISM at ${deployedIsm.address}`, + }; + this.addViolation(violation); + } + return; + } const expected = value && typeof value === 'string' ? value @@ -67,12 +118,21 @@ export class HyperlaneRouterChecker< await Promise.all( this.app.remoteChains(chain).map(async (remoteChain) => { - const remoteRouter = this.app.router( - this.app.getContracts(remoteChain), - ); + const remoteRouterAddress = this.app.routerAddress(remoteChain); const remoteDomainId = this.multiProvider.getDomainId(remoteChain); - const address = await router.routers(remoteDomainId); - assert(address === addressToBytes32(remoteRouter.address)); + const actualRouter = await router.routers(remoteDomainId); + const expectedRouter = addressToBytes32(remoteRouterAddress); + if (actualRouter !== expectedRouter) { + const violation: RouterViolation = { + chain, + remoteChain, + type: RouterViolationType.EnrolledRouter, + contract: router, + actual: actualRouter, + expected: expectedRouter, + }; + this.addViolation(violation); + } }), ); } diff --git a/typescript/sdk/src/router/RouterApps.ts b/typescript/sdk/src/router/RouterApps.ts index d805338d5..fc1221801 100644 --- a/typescript/sdk/src/router/RouterApps.ts +++ b/typescript/sdk/src/router/RouterApps.ts @@ -1,10 +1,21 @@ +import debug from 'debug'; import type { BigNumber } from 'ethers'; import { GasRouter, Router } from '@hyperlane-xyz/core'; -import { Address, objMap, promiseObjAll } from '@hyperlane-xyz/utils'; +import { + Address, + ProtocolType, + objMap, + promiseObjAll, +} from '@hyperlane-xyz/utils'; import { HyperlaneApp } from '../app/HyperlaneApp'; -import { HyperlaneContracts, HyperlaneFactories } from '../contracts/types'; +import { + HyperlaneContracts, + HyperlaneContractsMap, + HyperlaneFactories, +} from '../contracts/types'; +import { MultiProvider } from '../providers/MultiProvider'; import { ChainMap, ChainName } from '../types'; export { Router } from '@hyperlane-xyz/core'; @@ -12,8 +23,34 @@ export { Router } from '@hyperlane-xyz/core'; export abstract class RouterApp< Factories extends HyperlaneFactories, > extends HyperlaneApp { + constructor( + contractsMap: HyperlaneContractsMap, + multiProvider: MultiProvider, + logger?: debug.Debugger, + readonly foreignDeployments: ChainMap
= {}, + ) { + super(contractsMap, multiProvider, logger); + } + abstract router(contracts: HyperlaneContracts): Router; + routerAddress(chainName: string): Address { + if ( + this.multiProvider.getChainMetadata(chainName).protocol === + ProtocolType.Ethereum + ) { + return this.router(this.contractsMap[chainName]).address; + } + return this.foreignDeployments[chainName]; + } + + override remoteChains(chainName: string): string[] { + return [ + ...super.remoteChains(chainName), + ...Object.keys(this.foreignDeployments), + ].filter((chain) => chain !== chainName); + } + getSecurityModules(): Promise> { return promiseObjAll( objMap(this.chainMap, (_, contracts) => diff --git a/typescript/sdk/src/router/types.ts b/typescript/sdk/src/router/types.ts index 5a6cfaf88..a990503c7 100644 --- a/typescript/sdk/src/router/types.ts +++ b/typescript/sdk/src/router/types.ts @@ -1,6 +1,7 @@ import { MailboxClient, ProxyAdmin__factory, + Router, TimelockController__factory, } from '@hyperlane-xyz/core'; import type { Address } from '@hyperlane-xyz/utils'; @@ -64,4 +65,17 @@ export interface ClientViolation extends CheckerViolation { contract: MailboxClient; actual: string; expected: string; + description?: string; +} + +export enum RouterViolationType { + EnrolledRouter = 'EnrolledRouter', +} + +export interface RouterViolation extends CheckerViolation { + type: RouterViolationType.EnrolledRouter; + remoteChain: string; + contract: Router; + actual: string; + expected: string; } diff --git a/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts b/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts index 91b3b1745..b6d3b0024 100644 --- a/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts +++ b/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts @@ -6,6 +6,7 @@ import { } from '@solana/spl-token'; import { AccountMeta, + ComputeBudgetProgram, Keypair, PublicKey, SystemProgram, @@ -153,6 +154,14 @@ export class SealevelTokenAdapter } } +// The compute limit to set for the transfer remote instruction. +// This is typically around ~160k, but can be higher depending on +// the index in the merkle tree, which can result in more moderately +// more expensive merkle tree insertion. +// Because a higher compute limit doesn't increase the fee for a transaction, +// we generously request 1M units. +const TRANSFER_REMOTE_COMPUTE_LIMIT = 1_000_000; + export abstract class SealevelHypTokenAdapter extends SealevelTokenAdapter implements IHypTokenAdapter @@ -271,15 +280,24 @@ export abstract class SealevelHypTokenAdapter ]), }); + const setComputeLimitInstruction = ComputeBudgetProgram.setComputeUnitLimit( + { + units: TRANSFER_REMOTE_COMPUTE_LIMIT, + }, + ); + const recentBlockhash = ( await this.getProvider().getLatestBlockhash('finalized') ).blockhash; + // @ts-ignore Workaround for bug in the web3 lib, sometimes uses recentBlockhash and sometimes uses blockhash const tx = new Transaction({ feePayer: fromWalletPubKey, blockhash: recentBlockhash, recentBlockhash, - }).add(transferRemoteInstruction); + }) + .add(setComputeLimitInstruction) + .add(transferRemoteInstruction); tx.partialSign(randomWallet); return tx; } diff --git a/typescript/sdk/src/token/config.ts b/typescript/sdk/src/token/config.ts index 6772cda27..271b8a896 100644 --- a/typescript/sdk/src/token/config.ts +++ b/typescript/sdk/src/token/config.ts @@ -4,8 +4,10 @@ import { GasRouterConfig } from '../router/types'; export enum TokenType { synthetic = 'synthetic', + fastSynthetic = 'fastSynthetic', syntheticUri = 'syntheticUri', collateral = 'collateral', + fastCollateral = 'fastCollateral', collateralUri = 'collateralUri', native = 'native', } @@ -30,11 +32,14 @@ export const isTokenMetadata = (metadata: any): metadata is TokenMetadata => export const isErc20Metadata = (metadata: any): metadata is ERC20Metadata => metadata.decimals && isTokenMetadata(metadata); -export type SyntheticConfig = { - type: TokenType.synthetic | TokenType.syntheticUri; +export type SyntheticConfig = TokenMetadata & { + type: TokenType.synthetic | TokenType.syntheticUri | TokenType.fastSynthetic; } & TokenMetadata; export type CollateralConfig = { - type: TokenType.collateral | TokenType.collateralUri; + type: + | TokenType.collateral + | TokenType.collateralUri + | TokenType.fastCollateral; token: string; } & Partial; export type NativeConfig = { @@ -47,12 +52,15 @@ export const isCollateralConfig = ( config: TokenConfig, ): config is CollateralConfig => config.type === TokenType.collateral || - config.type === TokenType.collateralUri; + config.type === TokenType.collateralUri || + config.type === TokenType.fastCollateral; export const isSyntheticConfig = ( config: TokenConfig, ): config is SyntheticConfig => - config.type === TokenType.synthetic || config.type === TokenType.syntheticUri; + config.type === TokenType.synthetic || + config.type === TokenType.syntheticUri || + config.type === TokenType.fastSynthetic; export const isNativeConfig = (config: TokenConfig): config is NativeConfig => config.type === TokenType.native; @@ -61,6 +69,10 @@ export const isUriConfig = (config: TokenConfig) => config.type === TokenType.syntheticUri || config.type === TokenType.collateralUri; +export const isFastConfig = (config: TokenConfig) => + config.type === TokenType.fastSynthetic || + config.type === TokenType.fastCollateral; + export type HypERC20Config = GasRouterConfig & SyntheticConfig & ERC20Metadata; export type HypERC20CollateralConfig = GasRouterConfig & CollateralConfig & diff --git a/typescript/sdk/src/token/contracts.ts b/typescript/sdk/src/token/contracts.ts index 9cbf36614..fe4d389f6 100644 --- a/typescript/sdk/src/token/contracts.ts +++ b/typescript/sdk/src/token/contracts.ts @@ -1,4 +1,6 @@ import { + FastHypERC20Collateral__factory, + FastHypERC20__factory, HypERC20Collateral__factory, HypERC20__factory, HypERC721Collateral__factory, @@ -8,7 +10,12 @@ import { } from '@hyperlane-xyz/core'; export type HypERC20Factories = { - router: HypERC20__factory | HypERC20Collateral__factory | HypNative__factory; + router: + | HypERC20__factory + | HypERC20Collateral__factory + | HypNative__factory + | FastHypERC20__factory + | FastHypERC20Collateral__factory; }; export type HypERC721Factories = { router: diff --git a/typescript/sdk/src/token/deploy.ts b/typescript/sdk/src/token/deploy.ts index 4e05305d4..32140e116 100644 --- a/typescript/sdk/src/token/deploy.ts +++ b/typescript/sdk/src/token/deploy.ts @@ -3,6 +3,8 @@ import { providers } from 'ethers'; import { ERC20__factory, ERC721EnumerableUpgradeable__factory, + FastHypERC20Collateral__factory, + FastHypERC20__factory, HypERC20, HypERC20Collateral, HypERC20Collateral__factory, @@ -39,6 +41,7 @@ import { TokenMetadata, isCollateralConfig, isErc20Metadata, + isFastConfig, isNativeConfig, isSyntheticConfig, isTokenMetadata, @@ -72,11 +75,14 @@ export class HypERC20Deployer extends GasRouterDeployer< static gasOverheadDefault(config: TokenConfig): number { switch (config.type) { + case 'fastSynthetic': + return 64_000; case 'synthetic': return 64_000; case 'native': return 44_000; case 'collateral': + case 'fastCollateral': default: return 68_000; } @@ -121,12 +127,16 @@ export class HypERC20Deployer extends GasRouterDeployer< chain: ChainName, config: HypERC20CollateralConfig, ): Promise { - return this.deployContractFromFactory( - chain, - new HypERC20Collateral__factory(), - 'HypERC20Collateral', - [config.token, config.mailbox], - ); + const isFast = isFastConfig(config); + const factory = isFast + ? new FastHypERC20Collateral__factory() + : new HypERC20Collateral__factory(); + const name = isFast ? 'FastHypERC20Collateral' : 'HypERC20Collateral'; + + return this.deployContractFromFactory(chain, factory, name, [ + config.token, + config.mailbox, + ]); } protected async deployNative( @@ -156,12 +166,17 @@ export class HypERC20Deployer extends GasRouterDeployer< chain: ChainName, config: HypERC20Config, ): Promise { - const router = await this.deployContractFromFactory( - chain, - new HypERC20__factory(), - 'HypERC20', - [config.decimals, config.mailbox], - ); + const isFast = isFastConfig(config); + const factory = isFast + ? new FastHypERC20__factory() + : new HypERC20__factory(); + const name = isFast ? 'FastHypERC20' : 'HypERC20'; + + const router = await this.deployContractFromFactory(chain, factory, name, [ + config.decimals, + config.mailbox, + ]); + await this.multiProvider.handleTx( chain, router.initialize(config.totalSupply, config.name, config.symbol), diff --git a/typescript/utils/index.ts b/typescript/utils/index.ts index e0b66e099..a60d68b8d 100644 --- a/typescript/utils/index.ts +++ b/typescript/utils/index.ts @@ -30,6 +30,7 @@ export { } from './src/addresses'; export { convertDecimals, + convertDecimalsEthersBigNumber, eqAmountApproximate, fromWei, fromWeiRounded, @@ -44,7 +45,7 @@ export { sleep, timeout, } from './src/async'; -export { base58ToBuffer, bufferToBase58 } from './src/base58'; +export { base58ToBuffer, bufferToBase58, hexOrBase58ToHex } from './src/base58'; export { fromBase64, toBase64 } from './src/base64'; export { BigNumberMax, diff --git a/typescript/utils/src/addresses.ts b/typescript/utils/src/addresses.ts index f41567727..4ad170803 100644 --- a/typescript/utils/src/addresses.ts +++ b/typescript/utils/src/addresses.ts @@ -135,14 +135,14 @@ export function isValidTransactionHashSealevel(input: string) { return SEALEVEL_TX_HASH_REGEX.test(input); } -export function isValidTransactionHash(input: string, protocol?: ProtocolType) { - return routeAddressUtil( - isValidTransactionHashEvm, - isValidTransactionHashSealevel, - false, - input, - protocol, - ); +export function isValidTransactionHash(input: string, protocol: ProtocolType) { + if (protocol === ProtocolType.Ethereum) { + return isValidTransactionHashEvm(input); + } else if (protocol === ProtocolType.Sealevel) { + return isValidTransactionHashSealevel(input); + } else { + return false; + } } export function isZeroishAddress(address: Address) { diff --git a/typescript/utils/src/amount.ts b/typescript/utils/src/amount.ts index b4c115b86..9aae2cad6 100644 --- a/typescript/utils/src/amount.ts +++ b/typescript/utils/src/amount.ts @@ -1,5 +1,6 @@ import { formatUnits, parseUnits } from '@ethersproject/units'; import BigNumber from 'bignumber.js'; +import { ethers } from 'ethers'; const DEFAULT_MIN_ROUNDED_VALUE = 0.00001; const DEFAULT_DISPLAY_DECIMALS = 4; @@ -117,3 +118,28 @@ export function convertDecimals( return amount.times(new BigNumber(10).pow(difference)); } } + +/** + * Converts a value with `fromDecimals` decimals to a value with `toDecimals` decimals. + * Incurs a loss of precision when `fromDecimals` > `toDecimals`. + * @param fromDecimals The number of decimals `value` has. + * @param toDecimals The number of decimals to convert `value` to. + * @param value The value to convert. + * @returns `value` represented with `toDecimals` decimals. + */ +export function convertDecimalsEthersBigNumber( + fromDecimals: number, + toDecimals: number, + value: ethers.BigNumber, +) { + if (fromDecimals === toDecimals) return value; + else if (fromDecimals > toDecimals) { + const difference = fromDecimals - toDecimals; + return value.div(ethers.BigNumber.from('10').pow(difference)); + } + // fromDecimals < toDecimals + else { + const difference = toDecimals - fromDecimals; + return value.mul(ethers.BigNumber.from('10').pow(difference)); + } +} diff --git a/typescript/utils/src/base58.ts b/typescript/utils/src/base58.ts index 9a591f0ae..b30bbcb4b 100644 --- a/typescript/utils/src/base58.ts +++ b/typescript/utils/src/base58.ts @@ -7,3 +7,11 @@ export function base58ToBuffer(value: string) { export function bufferToBase58(value: Buffer) { return utils.base58.encode(value); } + +// If the value is already hex (checked by 0x prefix), return it as is. +// Otherwise, treat it as base58 and convert it to hex. +export function hexOrBase58ToHex(value: string) { + if (value.startsWith('0x')) return value; + + return utils.hexlify(base58ToBuffer(value)); +} diff --git a/typescript/utils/src/objects.ts b/typescript/utils/src/objects.ts index 4ceb780b2..a99e7ff1d 100644 --- a/typescript/utils/src/objects.ts +++ b/typescript/utils/src/objects.ts @@ -95,8 +95,8 @@ export function invertKeysAndValues(data: any) { } // Returns an object with the keys as values from an array and value set to true -export function arrayToObject(keys: Array, val = true) { - return keys.reduce>((result, k) => { +export function arrayToObject(keys: Array, val = true) { + return keys.reduce>((result, k) => { result[k] = val; return result; }, {}); diff --git a/vectors/signedCheckpoint.json b/vectors/signedCheckpoint.json index fff9f5b18..566fc2b26 100644 --- a/vectors/signedCheckpoint.json +++ b/vectors/signedCheckpoint.json @@ -1 +1 @@ -[{"domain":1000,"index":1,"mailbox":"0x0000000000000000000000002222222222222222222222222222222222222222","root":"0x0202020202020202020202020202020202020202020202020202020202020202","signature":{"r":"0xa5769d4ad3041f82a95c1c8b26fd3fcdac5a95560d336b46b83d585ba91a8f9","s":"0x4e1464c784e2836b8c22a2b2c76fdfbdff57f173b20641544ecc0d465af6ed05","v":28},"signer":"0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a"},{"domain":1000,"index":2,"mailbox":"0x0000000000000000000000002222222222222222222222222222222222222222","root":"0x0303030303030303030303030303030303030303030303030303030303030303","signature":{"r":"0x555b204a20caa709685df249c0f4e5d96532483a94cad7afb2aff6c3b72eabff","s":"0x50e19964d11bbcc3ac9ec4b3aaaf365aa9254b1d824509c63aa2470c140f30ea","v":27},"signer":"0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a"},{"domain":1000,"index":3,"mailbox":"0x0000000000000000000000002222222222222222222222222222222222222222","root":"0x0404040404040404040404040404040404040404040404040404040404040404","signature":{"r":"0x7aaf6ca4c12c1ec82bc91eca5c04ac599ce9a169d2f59f6d38e6dfc37a696194","s":"0x4b42a96d83f3fd1b5e844f8b34f284bc33154cf3a8fbfc6fad93de1d3fe71230","v":27},"signer":"0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a"}] \ No newline at end of file +[{"domain":1000,"index":1,"merkle_tree_hook":"0x0000000000000000000000002222222222222222222222222222222222222222","root":"0x0202020202020202020202020202020202020202020202020202020202020202","signature":{"r":"0xa5769d4ad3041f82a95c1c8b26fd3fcdac5a95560d336b46b83d585ba91a8f9","s":"0x4e1464c784e2836b8c22a2b2c76fdfbdff57f173b20641544ecc0d465af6ed05","v":28},"signer":"0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a"},{"domain":1000,"index":2,"merkle_tree_hook":"0x0000000000000000000000002222222222222222222222222222222222222222","root":"0x0303030303030303030303030303030303030303030303030303030303030303","signature":{"r":"0x555b204a20caa709685df249c0f4e5d96532483a94cad7afb2aff6c3b72eabff","s":"0x50e19964d11bbcc3ac9ec4b3aaaf365aa9254b1d824509c63aa2470c140f30ea","v":27},"signer":"0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a"},{"domain":1000,"index":3,"merkle_tree_hook":"0x0000000000000000000000002222222222222222222222222222222222222222","root":"0x0404040404040404040404040404040404040404040404040404040404040404","signature":{"r":"0x7aaf6ca4c12c1ec82bc91eca5c04ac599ce9a169d2f59f6d38e6dfc37a696194","s":"0x4b42a96d83f3fd1b5e844f8b34f284bc33154cf3a8fbfc6fad93de1d3fe71230","v":27},"signer":"0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a"}] \ No newline at end of file