From 9349ef73ed6847040cb1adf9cff5d5141f08e1b3 Mon Sep 17 00:00:00 2001 From: Yorke Rhodes Date: Wed, 18 Dec 2024 16:03:53 -0500 Subject: [PATCH 01/37] fix: strategy flag propagation (#5033) ### Description `--strategy` flag was not being propagated through context middleware properly ### Drive-by changes ### Related issues ### Backward compatibility ### Testing warp apply e2e tests --- .changeset/polite-bulldogs-sit.md | 5 ++++ typescript/cli/src/config/strategy.ts | 39 +++------------------------ typescript/cli/src/context/context.ts | 11 ++++---- 3 files changed, 15 insertions(+), 40 deletions(-) create mode 100644 .changeset/polite-bulldogs-sit.md diff --git a/.changeset/polite-bulldogs-sit.md b/.changeset/polite-bulldogs-sit.md new file mode 100644 index 0000000000..8acf555fe5 --- /dev/null +++ b/.changeset/polite-bulldogs-sit.md @@ -0,0 +1,5 @@ +--- +"@hyperlane-xyz/cli": patch +--- + +Fix strategy flag propagation diff --git a/typescript/cli/src/config/strategy.ts b/typescript/cli/src/config/strategy.ts index f57c7d3378..cb2abaf437 100644 --- a/typescript/cli/src/config/strategy.ts +++ b/typescript/cli/src/config/strategy.ts @@ -10,17 +10,15 @@ import { import { ProtocolType, assert, - errorToString, isAddress, isPrivateKeyEvm, } from '@hyperlane-xyz/utils'; import { CommandContext } from '../context/types.js'; -import { errorRed, log, logBlue, logGreen, logRed } from '../logger.js'; +import { errorRed, log, logBlue, logGreen } from '../logger.js'; import { runSingleChainSelectionStep } from '../utils/chains.js'; import { indentYamlOrJson, - isFile, readYamlOrJson, writeYamlOrJson, } from '../utils/files.js'; @@ -33,38 +31,9 @@ export async function readChainSubmissionStrategyConfig( filePath: string, ): Promise { log(`Reading submission strategy in ${filePath}`); - try { - const strategyConfig = readYamlOrJson(filePath); - - const parseResult = ChainSubmissionStrategySchema.parse(strategyConfig); - - return parseResult; - } catch (error) { - logRed(`⛔️ Error reading strategy config:`, errorToString(error)); - throw error; // Re-throw to let caller handle the error - } -} - -/** - * Safely reads chain submission strategy config, returns empty object if any errors occur - */ -export async function safeReadChainSubmissionStrategyConfig( - filePath: string, -): Promise { - try { - const trimmedFilePath = filePath.trim(); - if (!isFile(trimmedFilePath)) { - logBlue(`File ${trimmedFilePath} does not exist, returning empty config`); - return {}; - } - return await readChainSubmissionStrategyConfig(trimmedFilePath); - } catch (error) { - logRed( - `Failed to read strategy config, defaulting to empty config:`, - errorToString(error), - ); - return {}; - } + const strategyConfig = readYamlOrJson(filePath); + const parseResult = ChainSubmissionStrategySchema.parse(strategyConfig); + return parseResult; } export async function createStrategyConfig({ diff --git a/typescript/cli/src/context/context.ts b/typescript/cli/src/context/context.ts index 570b233cde..d3bb47d3a9 100644 --- a/typescript/cli/src/context/context.ts +++ b/typescript/cli/src/context/context.ts @@ -16,9 +16,8 @@ import { } from '@hyperlane-xyz/sdk'; import { isHttpsUrl, isNullish, rootLogger } from '@hyperlane-xyz/utils'; -import { DEFAULT_STRATEGY_CONFIG_PATH } from '../commands/options.js'; import { isSignCommand } from '../commands/signCommands.js'; -import { safeReadChainSubmissionStrategyConfig } from '../config/strategy.js'; +import { readChainSubmissionStrategyConfig } from '../config/strategy.js'; import { PROXY_DEPLOYED_URL } from '../consts.js'; import { forkNetworkToMultiProvider, verifyAnvil } from '../deploy/dry-run.js'; import { logBlue } from '../logger.js'; @@ -62,9 +61,9 @@ export async function signerMiddleware(argv: Record) { if (!requiresKey) return argv; - const strategyConfig = await safeReadChainSubmissionStrategyConfig( - strategyPath ?? DEFAULT_STRATEGY_CONFIG_PATH, - ); + const strategyConfig = strategyPath + ? await readChainSubmissionStrategyConfig(strategyPath) + : {}; /** * Intercepts Hyperlane command to determine chains. @@ -106,6 +105,7 @@ export async function getContext({ requiresKey, skipConfirmation, disableProxy = false, + strategyPath, }: ContextSettings): Promise { const registry = getRegistry(registryUri, registryOverrideUri, !disableProxy); @@ -127,6 +127,7 @@ export async function getContext({ key, skipConfirmation: !!skipConfirmation, signerAddress, + strategyPath, } as CommandContext; } From 9f6b8c514b3b60ccac1536efc68b9726480b0721 Mon Sep 17 00:00:00 2001 From: Yorke Rhodes Date: Thu, 19 Dec 2024 06:09:04 -0500 Subject: [PATCH 02/37] feat: implement multi-message relay (#4812) ### Description - Allow self-relaying of all messages if there are multiple in a given dispatch transaction. - Adds `relayAll` method to HyperlaneRelayer in SDK. - Finds and attempts to relay all undelivered messages in the given dispatch tx. ### Drive-by changes - use `getContracts` in HyperlaneCore - add assertions in HyperlaneCore for expected number of process events ### Related issues ### Backward compatibility ### Testing Manual with ICA governance tx ``` $ yarn workspace @hyperlane-xyz/cli hyperlane status --dispatchTx 0x96377bd88332ac86b10bc87ab3c97c89a9edef1046d0179ac8bec215d8964611 --origin ethereum --relay -r /Users/pbio/work/hyperlane-registry/ --overrides '' ``` ``` Hyperlane CLI File /Users/pbio/.hyperlane/strategies/default-strategy.yaml does not exist, returning empty config Checking status of message 0x2f02bc5d50e6e5e3722d1cfe1f44c12f726ddffa0d0702358fca23653b1a1ead on lisk Message 0x2f02bc5d50e6e5e3722d1cfe1f44c12f726ddffa0d0702358fca23653b1a1ead was delivered Checking status of message 0x83a95fe23bdf0d0f5e6d04c9fc2b5af8259c06d7a69e1a3d549eea27f7c68f0c on flowmainnet Message 0x83a95fe23bdf0d0f5e6d04c9fc2b5af8259c06d7a69e1a3d549eea27f7c68f0c was delivered Checking status of message 0xb65fab794926ed121076adb4d590000ac629f036bd1aba7638faa59bc3514efa on cheesechain Message 0xb65fab794926ed121076adb4d590000ac629f036bd1aba7638faa59bc3514efa was delivered Checking status of message 0x75e5f9a914879f74b68c6e557cca0394ecae85d08d48030285235f6904b10756 on superpositionmainnet Message 0x75e5f9a914879f74b68c6e557cca0394ecae85d08d48030285235f6904b10756 was delivered Checking status of message 0xe11da92d16dacc82e5e7ddb9f5b0e4fbbab7250e0980f304b8b79c808e62aad3 on flame Message 0xe11da92d16dacc82e5e7ddb9f5b0e4fbbab7250e0980f304b8b79c808e62aad3 was delivered Checking status of message 0x6207cec7be3f192ccd18850bcb56564d1750f3fdf763fd21136fed8a4a47c209 on kroma Message 0x6207cec7be3f192ccd18850bcb56564d1750f3fdf763fd21136fed8a4a47c209 was delivered Checking status of message 0xd9b7bde4c1195ec474b1d75d1e3c6bb3fe2c991aa84685deaf6d3090ae43c59d on molten Message 0xd9b7bde4c1195ec474b1d75d1e3c6bb3fe2c991aa84685deaf6d3090ae43c59d was delivered Checking status of message 0xa88533a9d8cdbdd6ab1cb656707ed60b18077f45867b29e0099560dafab691f9 on gravity Message 0xa88533a9d8cdbdd6ab1cb656707ed60b18077f45867b29e0099560dafab691f9 was delivered Checking status of message 0xb942a6663af5f35fbd3efec75200c8909d9542428167267a9bbf7878c9ba02a5 on viction Message 0xb942a6663af5f35fbd3efec75200c8909d9542428167267a9bbf7878c9ba02a5 was delivered Checking status of message 0x36e7cb4857bab78d04d04e3c7c43a9f06150c533d51a8157641f8a99090c763d on bitlayer Message 0x36e7cb4857bab78d04d04e3c7c43a9f06150c533d51a8157641f8a99090c763d was delivered Checking status of message 0x11bf61480a63bcb3a77131d9bd5eecf756579675c95a32c7049f3585ed6b6a15 on harmony Message 0x11bf61480a63bcb3a77131d9bd5eecf756579675c95a32c7049f3585ed6b6a15 was delivered Checking status of message 0x146906d5315af3eaa2db01d4b999426df5dfae1d5ea99ef68a274d5a8e351bdc on shibarium Message 0x146906d5315af3eaa2db01d4b999426df5dfae1d5ea99ef68a274d5a8e351bdc was delivered Checking status of message 0x49451d6040aef68faff3163b99d01601f807e3c6f743de523e3c7df227b47314 on rootstockmainnet Message 0x49451d6040aef68faff3163b99d01601f807e3c6f743de523e3c7df227b47314 was delivered Checking status of message 0x716f57169a0faa0a4d895b67a95a82447afae19dd3d62121660cb701b1dd8f0d on xlayer Message 0x716f57169a0faa0a4d895b67a95a82447afae19dd3d62121660cb701b1dd8f0d was delivered Checking status of message 0x170ba3d0e049a1f2585308d7114c05726994b394ab0cf2a4c1ef6a13d005e429 on chilizmainnet Message 0x170ba3d0e049a1f2585308d7114c05726994b394ab0cf2a4c1ef6a13d005e429 was delivered Checking status of message 0x42197cf0eb431ca1b289e48f4b2f8eaf9ccf7b0a16c425b3bb52cd7e9ebb3ffa on cyber Message 0x42197cf0eb431ca1b289e48f4b2f8eaf9ccf7b0a16c425b3bb52cd7e9ebb3ffa was delivered Checking status of message 0x215f8dba14a1fa0f4931abacf0ba59a46691c99b805ea350b6e5d165eb4b5f7d on lukso Message 0x215f8dba14a1fa0f4931abacf0ba59a46691c99b805ea350b6e5d165eb4b5f7d was delivered Checking status of message 0x408d4bb711692901b0c61532c614eb442da7b45f230307705143137a7ce4cb52 on merlin Message 0x408d4bb711692901b0c61532c614eb442da7b45f230307705143137a7ce4cb52 was delivered Checking status of message 0x5b896efa1c70cd321f2867cedaf4be1934929c9877a175d0095e61f4f2d2a2d1 on apechain Message 0x5b896efa1c70cd321f2867cedaf4be1934929c9877a175d0095e61f4f2d2a2d1 was delivered Checking status of message 0xb759a9c039dedd54a0ddab0e970ed7725424a1de42e436c07d28bc67daee609e on mint Message 0xb759a9c039dedd54a0ddab0e970ed7725424a1de42e436c07d28bc67daee609e was delivered Checking status of message 0x00e64e88520ee03e5d88cb61ebc2e7d53524dcd84743433716bad70f94e4b897 on arbitrumnova Message 0x00e64e88520ee03e5d88cb61ebc2e7d53524dcd84743433716bad70f94e4b897 was delivered Checking status of message 0xf204eea06d7d7c64c145e5a62a5922fdc254d1ffe9390c6883e74ce57e809df9 on prom Message 0xf204eea06d7d7c64c145e5a62a5922fdc254d1ffe9390c6883e74ce57e809df9 was delivered Checking status of message 0x125a752d042355bfe6182d80b2553bb2f7c74df3c188376c58b5463d6fb9f370 on astar Message 0x125a752d042355bfe6182d80b2553bb2f7c74df3c188376c58b5463d6fb9f370 was delivered Checking status of message 0x5130d380008fc94b04c9b81dca4ed0a445d189e8b6a82b5dc7cc412e56cd4a9b on astarzkevm Message 0x5130d380008fc94b04c9b81dca4ed0a445d189e8b6a82b5dc7cc412e56cd4a9b was delivered Checking status of message 0x13e53bdd365bb582e9d8ae5169f6a7cc6fb673c60824552c1e1cf44f3836dc3a on morph Message 0x13e53bdd365bb582e9d8ae5169f6a7cc6fb673c60824552c1e1cf44f3836dc3a was delivered Checking status of message 0x811bd7834b02ade059deaad3aea6f2bfb652019ee0b9f73ebc558686d58e7a8b on dogechain Message 0x811bd7834b02ade059deaad3aea6f2bfb652019ee0b9f73ebc558686d58e7a8b was delivered Checking status of message 0x61d0d7a5952aff88d962e0f6891f9e87e841982fffd27f595d9c9b8d88ab60aa on superseed Message 0x61d0d7a5952aff88d962e0f6891f9e87e841982fffd27f595d9c9b8d88ab60aa was delivered Checking status of message 0x23f2afc9ba360a2e11b90582904af09f5f56f2963ffede3118e6e10458c83e50 on moonbeam Message 0x23f2afc9ba360a2e11b90582904af09f5f56f2963ffede3118e6e10458c83e50 was delivered Checking status of message 0x698ae1a2ae198c5d96db7162d8373ad638de647f45a16aeb7512d2b0a9a6c6f7 on sanko Message 0x698ae1a2ae198c5d96db7162d8373ad638de647f45a16aeb7512d2b0a9a6c6f7 was delivered Checking status of message 0xe8740981b523da2806556eb39c9c4050b31e9ab80607ab40ae8bfdd10b130e7e on real Message 0xe8740981b523da2806556eb39c9c4050b31e9ab80607ab40ae8bfdd10b130e7e was delivered Checking status of message 0x6b6bd8a4c6a243a3405df69b08a16438a066ad9397c3f9d7dcb07b46de2c4284 on alephzeroevmmainnet Message 0x6b6bd8a4c6a243a3405df69b08a16438a066ad9397c3f9d7dcb07b46de2c4284 was delivered Checking status of message 0x8d5536254e7df327d13cd0ac1d347ea816c693409f05946933f85120d2453488 on everclear Message 0x8d5536254e7df327d13cd0ac1d347ea816c693409f05946933f85120d2453488 was delivered Checking status of message 0x004e26517383e3a90c8df3528e57208ad0cb091207feee14cb5b5ba654ccfa22 on flare Message 0x004e26517383e3a90c8df3528e57208ad0cb091207feee14cb5b5ba654ccfa22 was delivered Checking status of message 0xea0ffd9380f1c8e4340b7fb7479701472213557d1b647b90c782a7541cd261fa on immutablezkevmmainnet Message 0xea0ffd9380f1c8e4340b7fb7479701472213557d1b647b90c782a7541cd261fa was delivered Checking status of message 0xa907f67a44244da93b9d9cb0bbe270df8044a10be90c4798707a0727e4cae659 on coredao Message 0xa907f67a44244da93b9d9cb0bbe270df8044a10be90c4798707a0727e4cae659 was delivered Checking status of message 0x0649707b3ef5a04d4b22a13402fddb6fded89b8616105ae3e1da541ea617f5ca on worldchain Message 0x0649707b3ef5a04d4b22a13402fddb6fded89b8616105ae3e1da541ea617f5ca was delivered Checking status of message 0xd847f86f99db8d59fa8990ac8a41558f49c758ce4c8074bf2c23c37e81a4d08e on xai Message 0xd847f86f99db8d59fa8990ac8a41558f49c758ce4c8074bf2c23c37e81a4d08e was delivered Checking status of message 0xdd9d0252a9f4b8d571834db046311ad2b76385ef33e14a381d8fdcdb57ed0dd3 on unichain Message 0xdd9d0252a9f4b8d571834db046311ad2b76385ef33e14a381d8fdcdb57ed0dd3 was delivered Checking status of message 0xf72776b71109674e3aec514e4ffea1058c4f2b88224f12068cf328a99f99856a on b3 Message 0xf72776b71109674e3aec514e4ffea1058c4f2b88224f12068cf328a99f99856a was delivered Checking status of message 0xfa889d5fc81bded784b0dc97228372a4947e1320fe04fe442286aa0c94b71bbf on vana Message 0xfa889d5fc81bded784b0dc97228372a4947e1320fe04fe442286aa0c94b71bbf was delivered Checking status of message 0xe197b6e1116dbe8f8bbe7676033143f6a1f82fbfd88f39e2adb4142a379a98bd on endurance Message 0xe197b6e1116dbe8f8bbe7676033143f6a1f82fbfd88f39e2adb4142a379a98bd was delivered Checking status of message 0x67546be1d30a2154c150411a7d5e3cd12d277ad4f55e1bd50277af12f4465ce3 on inevm Message 0x67546be1d30a2154c150411a7d5e3cd12d277ad4f55e1bd50277af12f4465ce3 was delivered Checking status of message 0xd3af75c6f2688b0fa8a68fef0c7bea1bd096ca201ff2443b8ca9b6b33516a8e8 on tangle Message 0xd3af75c6f2688b0fa8a68fef0c7bea1bd096ca201ff2443b8ca9b6b33516a8e8 was delivered Checking status of message 0x180897afcd9ea6b7ea7e73cacca55bd223b6357f201e99657cd23e2b781f3916 on boba Message 0x180897afcd9ea6b7ea7e73cacca55bd223b6357f201e99657cd23e2b781f3916 was delivered Checking status of message 0x9663665c6579d4607e569ff7ed0b9df4e3d2cac4ad5790aa9340356ccec4ddbb on orderly Message 0x9663665c6579d4607e569ff7ed0b9df4e3d2cac4ad5790aa9340356ccec4ddbb was delivered Checking status of message 0x71582853e19ffcd85004928ed2f125b10db015cfeb1f180b06d222628ae356c7 on degenchain Message 0x71582853e19ffcd85004928ed2f125b10db015cfeb1f180b06d222628ae356c7 was delivered Checking status of message 0xbd96b2fb0b938831b20e30414533956e21e0b24df5c55971b1bf143915fd24c6 on oortmainnet Message 0xbd96b2fb0b938831b20e30414533956e21e0b24df5c55971b1bf143915fd24c6 was delivered Checking status of message 0xa0b1a56f4c47c939d47d7c64401b736f0a5797a7e2cdb83e8cb360c61d8b1ec9 on fantom Message 0xa0b1a56f4c47c939d47d7c64401b736f0a5797a7e2cdb83e8cb360c61d8b1ec9 was delivered Checking status of message 0xae2ee30ced87d19f5761e80c564ffff4f6ba98ef87bc0a16c69e5dfd4aafd74b on mantle Message 0xae2ee30ced87d19f5761e80c564ffff4f6ba98ef87bc0a16c69e5dfd4aafd74b was delivered Checking status of message 0x43c48ee511ee24019bb938ecb5ac9159d4b973b52bffc3c9765894f5d118b022 on polynomialfi Message 0x43c48ee511ee24019bb938ecb5ac9159d4b973b52bffc3c9765894f5d118b022 was delivered Checking status of message 0xee54dde1a75c8219f307bbce84fb3eb16fdcc5b05b3609b5adf2b8a32d022232 on bob Message 0xee54dde1a75c8219f307bbce84fb3eb16fdcc5b05b3609b5adf2b8a32d022232 was delivered Checking status of message 0x3ccc0fd020089e3fcb51e5ee057aee6666bd06096ae9c04cff889f5cfbf4b62b on fusemainnet Message 0x3ccc0fd020089e3fcb51e5ee057aee6666bd06096ae9c04cff889f5cfbf4b62b was delivered Checking status of message 0xeb16ee6f398d3a2c4a60edb8a797b5df56b7f8b1b43f336bded666536c038508 on kaia Message 0xeb16ee6f398d3a2c4a60edb8a797b5df56b7f8b1b43f336bded666536c038508 was delivered Checking status of message 0xcf23f9de2c9db41136fb9c2fdfd3f9c47378a7e10296cdc1b2d02440effa1f20 on metal Message 0xcf23f9de2c9db41136fb9c2fdfd3f9c47378a7e10296cdc1b2d02440effa1f20 was delivered Checking status of message 0x172604a47d0b6de9bc40c3ad9eff75294711533646836e12d4c4238be8c07faf on metis Message 0x172604a47d0b6de9bc40c3ad9eff75294711533646836e12d4c4238be8c07faf was delivered Checking status of message 0xc9cf61e11586a8578f2cfc637919954f24bdf011787b751ca9dd107a11a1b5af on rarichain Message 0xc9cf61e11586a8578f2cfc637919954f24bdf011787b751ca9dd107a11a1b5af was delivered Checking status of message 0x01cf143ee8da8d82d9c72f152e25658665d0d2f52a94cf2ebab0d433f9432a69 on snaxchain Message 0x01cf143ee8da8d82d9c72f152e25658665d0d2f52a94cf2ebab0d433f9432a69 was delivered Checking status of message 0x1574e00fffb8737c5a3ab3dd2efbdaec13dfaba98be3cbd9c49fdbb1c34b0275 on polygon Message 0x1574e00fffb8737c5a3ab3dd2efbdaec13dfaba98be3cbd9c49fdbb1c34b0275 was delivered Checking status of message 0x7e2ca4f70b77a70333bc736ba75bc0e70482aacc3d5c1022bf4c74352e14a849 on bsquared Message 0x7e2ca4f70b77a70333bc736ba75bc0e70482aacc3d5c1022bf4c74352e14a849 was delivered Checking status of message 0x84b20bbbdb401d3e42f99b8bb18a78e07812906a2992ec16baa3dc1779ce1dc0 on scroll Message 0x84b20bbbdb401d3e42f99b8bb18a78e07812906a2992ec16baa3dc1779ce1dc0 was delivered Checking status of message 0x082ef17aee560b3497cd61621b7233b919fda83dd729b3c99c97423485891317 on redstone Message 0x082ef17aee560b3497cd61621b7233b919fda83dd729b3c99c97423485891317 was delivered Checking status of message 0xb81a893a09ac7432f678d19d6072da17094a7530ecbeb99d132ce535ee92583b on proofofplay Message 0xb81a893a09ac7432f678d19d6072da17094a7530ecbeb99d132ce535ee92583b was delivered Checking status of message 0x7e2013853d26a64346de2dfe7cfa324e7f497db3865453cddb9c511b2398a9b9 on duckchain Message 0x7e2013853d26a64346de2dfe7cfa324e7f497db3865453cddb9c511b2398a9b9 was delivered Checking status of message 0xe355415b1d57853ac7479d9db40c2c060e80834cb5360e6ff7868bd2d455875b on polygonzkevm Message 0xe355415b1d57853ac7479d9db40c2c060e80834cb5360e6ff7868bd2d455875b was delivered Checking status of message 0xb0391835a971c5936f8407b58bea5cef1f4dd761f2cf875b06107c8cff7fce75 on ancient8 Message 0xb0391835a971c5936f8407b58bea5cef1f4dd761f2cf875b06107c8cff7fce75 was delivered Checking status of message 0xf8a9f9e6468d609711e2596d905f8e3c642f912de06efe79312e88b0d7b2957e on swell Message 0xf8a9f9e6468d609711e2596d905f8e3c642f912de06efe79312e88b0d7b2957e was delivered Checking status of message 0x0fd455f8b27982e6d4cb0f6e082baa8ee42d461073335ab0cefe6c65f9e0c018 on avalanche Message 0x0fd455f8b27982e6d4cb0f6e082baa8ee42d461073335ab0cefe6c65f9e0c018 was delivered Checking status of message 0xaefe33e5517790e3644a28c841cd45ea352e2afb34753fb5268a4778ca52ce66 on gnosis Message 0xaefe33e5517790e3644a28c841cd45ea352e2afb34753fb5268a4778ca52ce66 was delivered Checking status of message 0xbbb2293291a684292e3c6bdaa4bfc9ffd38c7e2b5fcc6edc7ec470f6331f7931 on celo Message 0xbbb2293291a684292e3c6bdaa4bfc9ffd38c7e2b5fcc6edc7ec470f6331f7931 was delivered Checking status of message 0x50aa9ebcbf4ce4ed8d0e21580bf2ae56cf9c2983e0c373463261dfca065e9606 on zoramainnet Message 0x50aa9ebcbf4ce4ed8d0e21580bf2ae56cf9c2983e0c373463261dfca065e9606 was delivered Checking status of message 0xe7d2bd88c7ee856bda482e8db56c49bc35b2212bbf8b74a108e6e4ec146f00a8 on zetachain Message 0xe7d2bd88c7ee856bda482e8db56c49bc35b2212bbf8b74a108e6e4ec146f00a8 was delivered ``` --------- Signed-off-by: pbio <10051819+paulbalaji@users.noreply.github.com> Co-authored-by: pbio <10051819+paulbalaji@users.noreply.github.com> --- .changeset/lovely-planes-end.md | 6 ++ typescript/cli/src/commands/send.ts | 19 +++-- typescript/cli/src/commands/status.ts | 3 +- typescript/cli/src/commands/warp.ts | 4 +- typescript/cli/src/status/message.ts | 77 +++++++-------------- typescript/sdk/src/core/HyperlaneCore.ts | 10 ++- typescript/sdk/src/core/HyperlaneRelayer.ts | 32 +++++++++ 7 files changed, 89 insertions(+), 62 deletions(-) create mode 100644 .changeset/lovely-planes-end.md diff --git a/.changeset/lovely-planes-end.md b/.changeset/lovely-planes-end.md new file mode 100644 index 0000000000..81bd6a8598 --- /dev/null +++ b/.changeset/lovely-planes-end.md @@ -0,0 +1,6 @@ +--- +'@hyperlane-xyz/cli': minor +'@hyperlane-xyz/sdk': minor +--- + +Allow self-relaying of all messages if there are multiple in a given dispatch transaction. diff --git a/typescript/cli/src/commands/send.ts b/typescript/cli/src/commands/send.ts index 1167b3b559..7879523a19 100644 --- a/typescript/cli/src/commands/send.ts +++ b/typescript/cli/src/commands/send.ts @@ -17,17 +17,13 @@ export const sendCommand: CommandModule = { }; /** - * Message command + * Base options for all message/warp send/status commands */ export const messageOptions: { [k: string]: Options } = { origin: { type: 'string', description: 'Origin chain to send message from', }, - destination: { - type: 'string', - description: 'Destination chain to send message to', - }, timeout: { type: 'number', description: 'Timeout in seconds', @@ -45,6 +41,17 @@ export const messageOptions: { [k: string]: Options } = { }, }; +/** + * Options for message/warp send command with destination chain specified + */ +export const messageSendOptions: { [k: string]: Options } = { + ...messageOptions, + destination: { + type: 'string', + description: 'Destination chain to send message to', + }, +}; + export interface MessageOptionsArgTypes { origin?: string; destination?: string; @@ -59,7 +66,7 @@ const messageCommand: CommandModuleWithWriteContext< command: 'message', describe: 'Send a test message to a remote chain', builder: { - ...messageOptions, + ...messageSendOptions, body: { type: 'string', description: 'Optional Message body', diff --git a/typescript/cli/src/commands/status.ts b/typescript/cli/src/commands/status.ts index 2b4c204880..0875b64675 100644 --- a/typescript/cli/src/commands/status.ts +++ b/typescript/cli/src/commands/status.ts @@ -19,12 +19,11 @@ export const statusCommand: CommandModuleWithWriteContext< description: 'Dispatch transaction hash', }, }, - handler: async ({ context, origin, destination, id, relay, dispatchTx }) => { + handler: async ({ context, origin, id, relay, dispatchTx }) => { await checkMessageStatus({ context, dispatchTx, messageId: id, - destination, origin, selfRelay: relay, }); diff --git a/typescript/cli/src/commands/warp.ts b/typescript/cli/src/commands/warp.ts index 3cd99fe6a0..5d09047617 100644 --- a/typescript/cli/src/commands/warp.ts +++ b/typescript/cli/src/commands/warp.ts @@ -40,7 +40,7 @@ import { warpCoreConfigCommandOption, warpDeploymentConfigCommandOption, } from './options.js'; -import { MessageOptionsArgTypes, messageOptions } from './send.js'; +import { MessageOptionsArgTypes, messageSendOptions } from './send.js'; /** * Parent command @@ -245,7 +245,7 @@ const send: CommandModuleWithWriteContext< command: 'send', describe: 'Send a test token transfer on a warp route', builder: { - ...messageOptions, + ...messageSendOptions, symbol: { ...symbolCommandOption, demandOption: false, diff --git a/typescript/cli/src/status/message.ts b/typescript/cli/src/status/message.ts index 5e22aec7b0..b5051b4c92 100644 --- a/typescript/cli/src/status/message.ts +++ b/typescript/cli/src/status/message.ts @@ -2,17 +2,15 @@ import type { TransactionReceipt } from '@ethersproject/providers'; import { input } from '@inquirer/prompts'; import { ChainName, HyperlaneCore, HyperlaneRelayer } from '@hyperlane-xyz/sdk'; -import { assert, parseWarpRouteMessage } from '@hyperlane-xyz/utils'; import { WriteCommandContext } from '../context/types.js'; -import { log, logBlue, logGray, logGreen, logRed } from '../logger.js'; +import { log, logBlue, logGreen, logRed } from '../logger.js'; import { runSingleChainSelectionStep } from '../utils/chains.js'; import { stubMerkleTreeConfig } from '../utils/relay.js'; export async function checkMessageStatus({ context, messageId, - destination, origin, selfRelay, dispatchTx, @@ -20,7 +18,6 @@ export async function checkMessageStatus({ context: WriteCommandContext; dispatchTx?: string; messageId?: string; - destination?: ChainName; origin?: ChainName; selfRelay?: boolean; }) { @@ -31,15 +28,9 @@ export async function checkMessageStatus({ ); } - if (!messageId) { - messageId = await input({ - message: 'Please specify the message id', - }); - } - - const chainAddresses = await context.registry.getAddresses(); + const coreAddresses = await context.registry.getAddresses(); const core = HyperlaneCore.fromAddressesMap( - chainAddresses, + coreAddresses, context.multiProvider, ); @@ -50,6 +41,9 @@ export async function checkMessageStatus({ .getProvider(origin) .getTransactionReceipt(dispatchTx); } else { + messageId ??= await input({ + message: 'Please specify the message id', + }); try { dispatchedReceipt = await core.getDispatchTx(origin, messageId); } catch { @@ -64,48 +58,29 @@ export async function checkMessageStatus({ } } - const messages = core.getDispatchedMessages(dispatchedReceipt!); - const match = messages.find((m) => m.id === messageId); - assert(match, `Message ${messageId} not found in dispatch tx ${dispatchTx}`); - const message = match; - try { - const { amount, recipient } = parseWarpRouteMessage(message.parsed.body); - logGray(`Warping ${amount} to ${recipient}`); - // eslint-disable-next-line no-empty - } catch {} - - let deliveredTx: TransactionReceipt; - - log(`Checking status of message ${messageId} on ${destination}`); - const delivered = await core.isDelivered(message); - if (delivered) { - logGreen(`Message ${messageId} was delivered`); - deliveredTx = await core.getProcessedReceipt(message); - } else { - logBlue(`Message ${messageId} was not yet delivered`); + const messages = core.getDispatchedMessages(dispatchedReceipt); - if (!selfRelay) { - return; + const undelivered = []; + for (const message of messages) { + log( + `Checking status of message ${message.id} on ${message.parsed.destinationChain}`, + ); + const delivered = await core.isDelivered(message); + if (delivered) { + logGreen(`Message ${message.id} was delivered`); + } else { + logBlue(`Message ${message.id} was not yet delivered`); + undelivered.push(message); } + } + if (selfRelay) { const relayer = new HyperlaneRelayer({ core }); - - const hookAddress = await core.getSenderHookAddress(message); - const merkleAddress = chainAddresses[origin].merkleTreeHook; - stubMerkleTreeConfig(relayer, origin, hookAddress, merkleAddress); - - deliveredTx = await relayer.relayMessage( - dispatchedReceipt, - undefined, - message, - ); + for (const message of undelivered) { + const hookAddress = await core.getSenderHookAddress(message); + const merkleAddress = coreAddresses[origin].merkleTreeHook; + stubMerkleTreeConfig(relayer, origin, hookAddress, merkleAddress); + } + await relayer.relayAll(dispatchedReceipt, undelivered); } - - logGreen( - `Message ${messageId} delivered in ${ - context.multiProvider.tryGetExplorerTxUrl(message.parsed.destination, { - hash: deliveredTx.transactionHash, - }) ?? deliveredTx.transactionHash - }`, - ); } diff --git a/typescript/sdk/src/core/HyperlaneCore.ts b/typescript/sdk/src/core/HyperlaneCore.ts index a3130ecd1a..10d4415ee7 100644 --- a/typescript/sdk/src/core/HyperlaneCore.ts +++ b/typescript/sdk/src/core/HyperlaneCore.ts @@ -14,6 +14,7 @@ import { ProtocolType, addBufferToGasLimit, addressToBytes32, + assert, bytes32ToAddress, isZeroishAddress, messageId, @@ -313,7 +314,7 @@ export class HyperlaneCore extends HyperlaneApp { message: DispatchedMessage, ): Promise { const destinationChain = this.getDestination(message); - const mailbox = this.contractsMap[destinationChain].mailbox; + const mailbox = this.getContracts(destinationChain).mailbox; const processedBlock = await mailbox.processedAt(message.id); const events = await mailbox.queryFilter( @@ -321,6 +322,11 @@ export class HyperlaneCore extends HyperlaneApp { processedBlock, processedBlock, ); + + assert( + events.length === 1, + `Expected exactly one process event, got ${events.length}`, + ); const processedEvent = events[0]; return processedEvent.getTransactionReceipt(); } @@ -428,6 +434,8 @@ export class HyperlaneCore extends HyperlaneApp { if (matching.length === 0) { throw new Error(`No dispatch event found for message ${messageId}`); } + + assert(matching.length === 1, 'Multiple dispatch events found'); const event = matching[0]; // only 1 event per message ID return event.getTransactionReceipt(); } diff --git a/typescript/sdk/src/core/HyperlaneRelayer.ts b/typescript/sdk/src/core/HyperlaneRelayer.ts index 9b82f90cbb..c36a37aef3 100644 --- a/typescript/sdk/src/core/HyperlaneRelayer.ts +++ b/typescript/sdk/src/core/HyperlaneRelayer.ts @@ -207,6 +207,38 @@ export class HyperlaneRelayer { return this.getIsmConfig(destinationChain, ism, message); } + async relayAll( + dispatchTx: providers.TransactionReceipt, + messages = HyperlaneCore.getDispatchedMessages(dispatchTx), + ): Promise> { + const destinationMap: ChainMap = {}; + messages.forEach((message) => { + destinationMap[message.parsed.destination] ??= []; + destinationMap[message.parsed.destination].push(message); + }); + + // parallelize relaying to different destinations + return promiseObjAll( + objMap(destinationMap, async (_destination, messages) => { + const receipts: ethers.ContractReceipt[] = []; + // serially relay messages to the same destination + for (const message of messages) { + try { + const receipt = await this.relayMessage( + dispatchTx, + undefined, + message, + ); + receipts.push(receipt); + } catch (e) { + this.logger.error(`Failed to relay message ${message.id}, ${e}`); + } + } + return receipts; + }), + ); + } + async relayMessage( dispatchTx: providers.TransactionReceipt, messageIndex = 0, From 1e187aecadd89fbc9058b651f9bb9dde4ad600c9 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Thu, 19 Dec 2024 11:12:11 +0000 Subject: [PATCH 03/37] chore: looser gas payment enforcement policy on Treasure (#5048) ### Description A temporary measure, seems like Treasure likes to overestimate gas limits. One offending message has been flagged as having insufficient payments, see https://discord.com/channels/935678348330434570/1264883412188135555/1319115367167033399 for details ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- typescript/infra/config/environments/mainnet3/agent.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index 29366ebcf0..416fe1d008 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -412,6 +412,8 @@ const gasPaymentEnforcement: GasPaymentEnforcement[] = [ matchingList: [ // Temporary workaround due to funky Mantle gas amounts. { destinationDomain: getDomainId('mantle') }, + // Temporary workaround for some high gas amount estimates on Treasure + ...warpRouteMatchingList(WarpRouteIds.ArbitrumTreasureMAGIC), ], }, { From 0e83758f4bdf121dbb69c87762a3564ba46722c2 Mon Sep 17 00:00:00 2001 From: xeno097 Date: Thu, 19 Dec 2024 10:15:03 -0400 Subject: [PATCH 04/37] feat(infra): usdc appchain base + ubtc boba (#5028) ### Description uBTC warp route extension + USDC route that connects AppChain to Base ### Drive-by changes - No ### Backward compatibility - YES ### Testing Manual --------- Co-authored-by: nambrot --- .changeset/neat-apples-marry.md | 5 ++ .registryrc | 2 +- .../getAppchainBaseUSDCWarpConfig.ts | 45 ++++++++++++++++ .../getBobaBsquaredSwellUBTCWarpConfig.ts | 54 +++++++++++++++++++ .../environments/mainnet3/warp/warpIds.ts | 2 + typescript/infra/config/warp.ts | 10 +++- typescript/infra/src/config/warp.ts | 3 ++ 7 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 .changeset/neat-apples-marry.md create mode 100644 typescript/infra/config/environments/mainnet3/warp/configGetters/getAppchainBaseUSDCWarpConfig.ts create mode 100644 typescript/infra/config/environments/mainnet3/warp/configGetters/getBobaBsquaredSwellUBTCWarpConfig.ts diff --git a/.changeset/neat-apples-marry.md b/.changeset/neat-apples-marry.md new file mode 100644 index 0000000000..514ac94ce8 --- /dev/null +++ b/.changeset/neat-apples-marry.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/infra': minor +--- + +added ubtc route extension config + usdc from appchain to base diff --git a/.registryrc b/.registryrc index 088ae6645a..db55765651 100644 --- a/.registryrc +++ b/.registryrc @@ -1 +1 @@ -d3e1e71965d7d06a8f8761c8255e718699c78f11 +32b4ab3b3df2bedd0d905c6745bcf1c673a60a01 diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getAppchainBaseUSDCWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getAppchainBaseUSDCWarpConfig.ts new file mode 100644 index 0000000000..fce2c5c6ae --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getAppchainBaseUSDCWarpConfig.ts @@ -0,0 +1,45 @@ +import { ethers } from 'ethers'; + +import { + ChainMap, + HypTokenRouterConfig, + IsmConfig, + TokenType, +} from '@hyperlane-xyz/sdk'; +import { Address } from '@hyperlane-xyz/utils'; + +import { + RouterConfigWithoutOwner, + tokens, +} from '../../../../../src/config/warp.js'; + +const safeOwners: ChainMap
= { + appchain: '0xe3436b3335fa6d4f1b58153079FB360c6Aa83Fd9', + base: '0xE3b50a565fbcdb6CC67B30bEB112f9e7FC855359', +}; + +export const getAppChainBaseUSDCWarpConfig = async ( + routerConfig: ChainMap, +): Promise> => { + const ISM_CONFIG: IsmConfig = ethers.constants.AddressZero; // Use the default ISM + + const appchain: HypTokenRouterConfig = { + mailbox: routerConfig.appchain.mailbox, + owner: safeOwners.appchain, + type: TokenType.synthetic, + interchainSecurityModule: ISM_CONFIG, + }; + + const base: HypTokenRouterConfig = { + mailbox: routerConfig.base.mailbox, + owner: safeOwners.base, + type: TokenType.collateral, + token: tokens.base.USDC, + interchainSecurityModule: ISM_CONFIG, + }; + + return { + appchain, + base, + }; +}; diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getBobaBsquaredSwellUBTCWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getBobaBsquaredSwellUBTCWarpConfig.ts new file mode 100644 index 0000000000..fcd48c686c --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getBobaBsquaredSwellUBTCWarpConfig.ts @@ -0,0 +1,54 @@ +import { ethers } from 'ethers'; + +import { + ChainMap, + HypTokenRouterConfig, + IsmConfig, + TokenType, +} from '@hyperlane-xyz/sdk'; +import { Address } from '@hyperlane-xyz/utils'; + +import { + RouterConfigWithoutOwner, + tokens, +} from '../../../../../src/config/warp.js'; + +const safeOwners: ChainMap
= { + bsquared: '0x7A363efD42305BeDBA307d25351F8ea157b69A1A', + swell: '0xC11e22A31787394950B31e2DEb1d2b5546689B65', + boba: '0x207FfFa7325fC5d0362aB01605D84B268b61888f', +}; + +export const getBobaBsquaredSwellUBTCWarpConfig = async ( + routerConfig: ChainMap, +): Promise> => { + const ISM_CONFIG: IsmConfig = ethers.constants.AddressZero; // Use the default ISM + + const boba: HypTokenRouterConfig = { + mailbox: routerConfig.boba.mailbox, + owner: safeOwners.boba, + type: TokenType.synthetic, + interchainSecurityModule: ISM_CONFIG, + }; + + const bsquared: HypTokenRouterConfig = { + mailbox: routerConfig.bsquared.mailbox, + owner: safeOwners.bsquared, + type: TokenType.collateral, + token: tokens.bsquared.uBTC, + interchainSecurityModule: ISM_CONFIG, + }; + + const swell: HypTokenRouterConfig = { + mailbox: routerConfig.swell.mailbox, + owner: safeOwners.swell, + type: TokenType.synthetic, + interchainSecurityModule: ISM_CONFIG, + }; + + return { + boba, + bsquared, + swell, + }; +}; diff --git a/typescript/infra/config/environments/mainnet3/warp/warpIds.ts b/typescript/infra/config/environments/mainnet3/warp/warpIds.ts index 2dc43bbae2..d9bf87760d 100644 --- a/typescript/infra/config/environments/mainnet3/warp/warpIds.ts +++ b/typescript/infra/config/environments/mainnet3/warp/warpIds.ts @@ -36,4 +36,6 @@ export enum WarpRouteIds { ArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDT = 'USDT/arbitrum-ethereum-mantle-mode-polygon-scroll-zeronetwork', ArbitrumBaseEthereumLiskOptimismPolygonZeroNetworkUSDC = 'USDC/arbitrum-base-ethereum-lisk-optimism-polygon-zeronetwork', ArbitrumBaseBlastBscEthereumGnosisLiskMantleModeOptimismPolygonScrollZeroNetworkZoraMainnet = 'ETH/arbitrum-base-blast-bsc-ethereum-gnosis-lisk-mantle-mode-optimism-polygon-scroll-zeronetwork-zoramainnet', + AppchainBaseUSDC = 'USDC/appchain-base', + BobaBsquaredSwellUBTC = 'UBTC/boba-bsquared-swell', } diff --git a/typescript/infra/config/warp.ts b/typescript/infra/config/warp.ts index 9f01e26466..c68e2221d9 100644 --- a/typescript/infra/config/warp.ts +++ b/typescript/infra/config/warp.ts @@ -13,6 +13,7 @@ import { import { RouterConfigWithoutOwner } from '../src/config/warp.js'; import { getAncient8EthereumUSDCWarpConfig } from './environments/mainnet3/warp/configGetters/getAncient8EthereumUSDCWarpConfig.js'; +import { getAppChainBaseUSDCWarpConfig } from './environments/mainnet3/warp/configGetters/getAppchainBaseUSDCWarpConfig.js'; import { getArbitrumBaseBlastBscEthereumGnosisMantleModeOptimismPolygonScrollZeroNetworkZoraMainnetETHWarpConfig } from './environments/mainnet3/warp/configGetters/getArbitrumBaseBlastBscEthereumGnosisMantleModeOptimismPolygonScrollZeroNetworkZoraMainnetETHWarpConfig.js'; import { getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDC } from './environments/mainnet3/warp/configGetters/getArbitrumBaseEthereumOptimismPolygonZeroNetworkUSDCWarpConfig.js'; import { getArbitrumEthereumMantleModePolygonScrollZeroNetworkUSDTWarpConfig } from './environments/mainnet3/warp/configGetters/getArbitrumBscEthereumMantleModePolygonScrollZeronetworkUSDTWarpConfig.js'; @@ -20,6 +21,7 @@ import { getArbitrumEthereumZircuitAmphrETHWarpConfig } from './environments/mai import { getArbitrumNeutronEclipWarpConfig } from './environments/mainnet3/warp/configGetters/getArbitrumNeutronEclipWarpConfig.js'; import { getArbitrumNeutronTiaWarpConfig } from './environments/mainnet3/warp/configGetters/getArbitrumNeutronTiaWarpConfig.js'; import { getBaseZeroNetworkCBBTCWarpConfig } from './environments/mainnet3/warp/configGetters/getBaseZeroNetworkCBBTCWarpConfig.js'; +import { getBobaBsquaredSwellUBTCWarpConfig } from './environments/mainnet3/warp/configGetters/getBobaBsquaredSwellUBTCWarpConfig.js'; import { getEclipseEthereumApxEthWarpConfig } from './environments/mainnet3/warp/configGetters/getEclipseEthereumApxETHWarpConfig.js'; import { getEclipseEthereumSolanaUSDTWarpConfig } from './environments/mainnet3/warp/configGetters/getEclipseEthereumSolanaUSDTWarpConfig.js'; import { getEclipseEthereumWBTCWarpConfig } from './environments/mainnet3/warp/configGetters/getEclipseEthereumWBTCWarpConfig.js'; @@ -79,6 +81,8 @@ export const warpConfigGetterMap: Record = { getArbitrumBaseBlastBscEthereumGnosisMantleModeOptimismPolygonScrollZeroNetworkZoraMainnetETHWarpConfig, [WarpRouteIds.EclipseStrideTIA]: getEclipseStrideTiaWarpConfig, [WarpRouteIds.EclipseStrideSTTIA]: getEclipseStrideStTiaWarpConfig, + [WarpRouteIds.AppchainBaseUSDC]: getAppChainBaseUSDCWarpConfig, + [WarpRouteIds.BobaBsquaredSwellUBTC]: getBobaBsquaredSwellUBTCWarpConfig, [WarpRouteIds.EthereumZircuitRe7LRT]: getEthereumZircuitRe7LRTWarpConfig, }; @@ -93,7 +97,11 @@ export async function getWarpConfig( ); // Strip the owners from the router config const routerConfigWithoutOwner = objMap(routerConfig, (_chain, config) => { - const { owner, ownerOverrides, ...configWithoutOwner } = config; + const { + owner: _owner, + ownerOverrides: _ownerOverrides, + ...configWithoutOwner + } = config; return configWithoutOwner; }); // Isolate the owners from the router config diff --git a/typescript/infra/src/config/warp.ts b/typescript/infra/src/config/warp.ts index f07a5e8fb6..2ceb1c8cb1 100644 --- a/typescript/infra/src/config/warp.ts +++ b/typescript/infra/src/config/warp.ts @@ -21,6 +21,9 @@ export const tokens: ChainMap> = { cbBTC: '0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf', USDC: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', }, + bsquared: { + uBTC: '0x796e4D53067FF374B89b2Ac101ce0c1f72ccaAc2', + }, arbitrum: { USDT: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9', USDC: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', From a51b50c3c5fe8f2659ae9180539a922f578189f8 Mon Sep 17 00:00:00 2001 From: Mohammed Hussan Date: Thu, 19 Dec 2024 17:46:46 +0000 Subject: [PATCH 05/37] feat(funding): Update desiredBalancePerChain for mainnet chains (#5051) ### Description - update desiredBalancePerChain inline with adjusted high urgency alerts that account for at least 2 days worth of transactions at current usage --- typescript/infra/config/environments/mainnet3/funding.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/typescript/infra/config/environments/mainnet3/funding.ts b/typescript/infra/config/environments/mainnet3/funding.ts index 81d1251723..e16bb07c90 100644 --- a/typescript/infra/config/environments/mainnet3/funding.ts +++ b/typescript/infra/config/environments/mainnet3/funding.ts @@ -46,7 +46,7 @@ export const keyFunderConfig: KeyFunderConfig< boba: '0.05', bsc: '5', bsquared: '0.002', - celo: '3', + celo: '4', cheesechain: '50', chilizmainnet: '200', conflux: '100', @@ -97,7 +97,7 @@ export const keyFunderConfig: KeyFunderConfig< polygon: '40', polygonzkevm: '0.5', polynomialfi: '0.05', - prom: '5', + prom: '18', proofofplay: '0.05', rarichain: '0.05', real: '0.1', @@ -119,7 +119,7 @@ export const keyFunderConfig: KeyFunderConfig< taiko: '0.2', tangle: '2', telos: '100', - treasure: '800', + treasure: '900', unichain: '0.05', // temporarily low until we're able to fund more vana: '0.001', From 70d9526b3e540f9e95feecd6ccec4af9fe48eb2e Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Fri, 20 Dec 2024 12:44:18 +0000 Subject: [PATCH 06/37] chore: update registryrc + agent config (#5054) ### Description chore: update registryrc + agent config fixes red agent config test ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --------- Signed-off-by: pbio <10051819+paulbalaji@users.noreply.github.com> --- .registryrc | 2 +- rust/main/config/mainnet_config.json | 214 ++++++++++++++------------- 2 files changed, 114 insertions(+), 102 deletions(-) diff --git a/.registryrc b/.registryrc index db55765651..7c11b8020a 100644 --- a/.registryrc +++ b/.registryrc @@ -1 +1 @@ -32b4ab3b3df2bedd0d905c6745bcf1c673a60a01 +207a4ffe96b0f33701ca6f92d8ca4905a8a0233a diff --git a/rust/main/config/mainnet_config.json b/rust/main/config/mainnet_config.json index 05599c97e3..5b465b90cf 100644 --- a/rust/main/config/mainnet_config.json +++ b/rust/main/config/mainnet_config.json @@ -34,7 +34,7 @@ "interchainAccountIsm": "0xd766e7C7517f2d0D92754b2fe4aE7AdEf7bDEC3e", "interchainAccountRouter": "0x25C87e735021F72d8728438C2130b02E3141f2cb", "interchainGasPaymaster": "0x8F1E22d309baa69D398a03cc88E9b46037e988AA", - "interchainSecurityModule": "0x88BD2166Ed70a1Cae544037e16582e38bd74C6e6", + "interchainSecurityModule": "0xd3fA56EDc496f986df5D2464E4C2c6027F8b4cEf", "isTestnet": false, "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0x811808Dd29ba8B0FC6C0ec0b5537035E59745162", @@ -100,7 +100,7 @@ "interchainAccountIsm": "0x2A7574358Ec53522CE2452887661AB4c86F7d400", "interchainAccountRouter": "0x91874Dbed74925dFe6059B90385EEb90DdE0B2E6", "interchainGasPaymaster": "0x3b6044acd6767f017e99318AA6Ef93b7B06A5a22", - "interchainSecurityModule": "0xd53Ea5FD4C735cabc468CC676E76d2A97ef03eF3", + "interchainSecurityModule": "0xe99C998dd9823B9b713A100fB885dd192C0d86DA", "mailbox": "0x979Ca5202784112f4738403dBec5D0F3B9daabB9", "merkleTreeHook": "0x748040afB89B8FdBb992799808215419d36A0930", "name": "arbitrum", @@ -172,7 +172,7 @@ "interchainAccountIsm": "0x27a3233c05C1Df7c163123301D14bE9349E3Cb48", "interchainAccountRouter": "0xa82a0227e6d6db53AF4B264A852bfF91C6504a51", "interchainGasPaymaster": "0x95519ba800BBd0d34eeAE026fEc620AD978176C0", - "interchainSecurityModule": "0x5958A0062297AE89276E6b28D31899310B4CB873", + "interchainSecurityModule": "0xFf2139af114d1D43C7348CA7471e08c7B31213A3", "mailbox": "0xFf06aFcaABaDDd1fb08371f9ccA15D73D51FeBD6", "merkleTreeHook": "0x84eea61D679F42D92145fA052C89900CBAccE95A", "name": "avalanche", @@ -245,7 +245,7 @@ "interchainAccountIsm": "0x223F7D3f27E6272266AE4B5B91Fd5C7A2d798cD8", "interchainAccountRouter": "0x4767D22117bBeeb295413000B620B93FD8522d53", "interchainGasPaymaster": "0xc3F23848Ed2e04C0c6d41bd7804fa8f89F940B94", - "interchainSecurityModule": "0x67a2F6B3AAf57A36f78e230ef92B3C3781bEb42C", + "interchainSecurityModule": "0x22c3D036C518A3953c57Ec3101b404a78158b7e4", "mailbox": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", "merkleTreeHook": "0x19dc38aeae620380430C200a6E990D5Af5480117", "name": "base", @@ -316,7 +316,7 @@ "interchainAccountIsm": "0xe93f2f409ad8B5000431D234472973fe848dcBEC", "interchainAccountRouter": "0x2f4Eb04189e11Af642237Da62d163Ab714614498", "interchainGasPaymaster": "0xB3fCcD379ad66CED0c91028520C64226611A48c9", - "interchainSecurityModule": "0x3FB5E5eCC49fbe363573622C8bE677984d25AE21", + "interchainSecurityModule": "0xff226D43F30B95Fac99D225E6aD36f308ff03d7A", "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", "merkleTreeHook": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "name": "blast", @@ -384,7 +384,7 @@ "interchainAccountIsm": "0x451dF8AB0936D85526D816f0b4dCaDD934A034A4", "interchainAccountRouter": "0x5C02157068a52cEcfc98EDb6115DE6134EcB4764", "interchainGasPaymaster": "0x62B7592C1B6D1E43f4630B8e37f4377097840C05", - "interchainSecurityModule": "0xB50d23647410d02Ff0C3F1C763E501EDAbb75783", + "interchainSecurityModule": "0xD94FA19BaC0A4f8Aa14D4ccB4B7A7Efb777F0A1e", "mailbox": "0x8358D8291e3bEDb04804975eEa0fe9fe0fAfB147", "merkleTreeHook": "0x781bE492F1232E66990d83a9D3AC3Ec26f56DAfB", "name": "bob", @@ -450,7 +450,7 @@ "interchainAccountIsm": "0x9e22945bE593946618383B108CC5bce09eBA4C26", "interchainAccountRouter": "0x32A07c1B7a7fe8D4A0e44B0181873aB9d64C16c1", "interchainGasPaymaster": "0x78E25e7f84416e69b9339B0A6336EB6EFfF6b451", - "interchainSecurityModule": "0x41B0dDBa97657551325f4579E93D756701d46fD6", + "interchainSecurityModule": "0x8ca30D8d1406B820b411dD1799fAa2A5E15a9D93", "mailbox": "0x2971b9Aec44bE4eb673DF1B88cDB57b96eefe8a4", "merkleTreeHook": "0xFDb9Cd5f9daAA2E4474019405A328a88E7484f26", "name": "bsc", @@ -531,7 +531,7 @@ "interchainAccountIsm": "0xB732c83aeE29596E3163Da2260710eAB67Bc0B29", "interchainAccountRouter": "0x27a6cAe33378bB6A6663b382070427A01fc9cB37", "interchainGasPaymaster": "0x571f1435613381208477ac5d6974310d88AC7cB7", - "interchainSecurityModule": "0x89ee5969142a040653912D500d561A9F6b03Ad72", + "interchainSecurityModule": "0x0df8056AB4fd95D7c8f848BcF95d63b76F6bFA04", "mailbox": "0x50da3B3907A08a24fe4999F4Dcf337E8dC7954bb", "merkleTreeHook": "0x04dB778f05854f26E67e0a66b740BBbE9070D366", "name": "celo", @@ -596,7 +596,7 @@ "interchainAccountIsm": "0x4Eb82Ee35b0a1c1d776E3a3B547f9A9bA6FCC9f2", "interchainAccountRouter": "0xEF9A332Ec1fD233Bf9344A58be56ff9E104B4f60", "interchainGasPaymaster": "0x7E27456a839BFF31CA642c060a2b68414Cb6e503", - "interchainSecurityModule": "0x3dE3032C39Df41CC9b986584A69f7839781Dd16a", + "interchainSecurityModule": "0xB8508EecB14dd8d3762782eefC1ead0E92D89Ea5", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0x0054D19613f20dD72721A146ED408971a2CCA9BD", "name": "cheesechain", @@ -659,7 +659,7 @@ "from": 4842212 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0xD9267b8C7D7f23D257717BC45E401b07Cd635DA2", + "interchainSecurityModule": "0x18250E686438602607205154288f160065d5F600", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "cyber", @@ -726,7 +726,7 @@ "from": 23783929 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0x1811eabA3aaF0CCc197BE558D700C17C9F57756f", + "interchainSecurityModule": "0x7fc28AE86f109B1F2f2aeCb9aDFaFF10A5440E02", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "degenchain", @@ -839,7 +839,7 @@ "interchainAccountIsm": "0xCeafc098e5c3c7768b9229Be2FEC275862A81Abd", "interchainAccountRouter": "0xed9a722c543883FB7e07E78F3879762DE09eA7D5", "interchainGasPaymaster": "0xB30EAB08aa87138D57168D0e236850A530f49921", - "interchainSecurityModule": "0x6c4C65E1559EEB81B1041810795405c182b021F2", + "interchainSecurityModule": "0x99d4146DC4910e3881b63C3471198F3Cb6aBAc52", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xC831271c1fB212012811a91Dd43e5926C1020563", "name": "endurance", @@ -910,7 +910,7 @@ "interchainAccountIsm": "0x292C614ED53DaaDBf971521bc2C652d1ca51cB47", "interchainAccountRouter": "0x5E532F7B610618eE73C2B462978e94CB1F7995Ce", "interchainGasPaymaster": "0x9e6B1022bE9BBF5aFd152483DAD9b88911bC8611", - "interchainSecurityModule": "0xdcB6cB7477d19fB13C7168061FE034FE4BA255E3", + "interchainSecurityModule": "0x8C3c9e95091A504b28902dF319F4cE8Bd77D2f59", "mailbox": "0xc005dc82818d67AF737725bD4bf75435d065D239", "merkleTreeHook": "0x48e6c30B97748d1e2e03bf3e9FbE3890ca5f8CCA", "name": "ethereum", @@ -982,7 +982,7 @@ "interchainAccountIsm": "0x7C012DCA02C42cfA3Fd7Da3B0ED7234B52AE68eF", "interchainAccountRouter": "0xbed53B5C5BCE9433f25A2A702e6df13E22d84Ae9", "interchainGasPaymaster": "0x2Fca7f6eC3d4A0408900f2BB30004d4616eE985E", - "interchainSecurityModule": "0xC15c21430Dfa1e0184FE57BdD6bf31d9607D6705", + "interchainSecurityModule": "0xd0C1FC10c922434452DAc6DA518Fe91582CF2407", "mailbox": "0x2f9DB5616fa3fAd1aB06cB2C906830BA63d135e3", "merkleTreeHook": "0x8358D8291e3bEDb04804975eEa0fe9fe0fAfB147", "name": "fraxtal", @@ -1050,7 +1050,7 @@ "interchainAccountIsm": "0x9629c28990F11c31735765A6FD59E1E1bC197DbD", "interchainAccountRouter": "0x2351FBe24C1212F253b7a300ff0cBCFd97952a19", "interchainGasPaymaster": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", - "interchainSecurityModule": "0xd097D848a044F6F044ae63f6B8d35B70A7334997", + "interchainSecurityModule": "0xE62f55C6760CE12688Ae4C4FDBf2Cb7f08C468A6", "mailbox": "0x3071D4DA6020C956Fe15Bfd0a9Ca8D4574f16696", "merkleTreeHook": "0xfBc08389224d23b79cb21cDc16c5d42F0ad0F57f", "name": "fusemainnet", @@ -1124,7 +1124,7 @@ "interchainAccountIsm": "0x07E2062A1bC66a2C1d05cb5C3870a4AF86e0056E", "interchainAccountRouter": "0xBE70Ab882D1F7E37e04a70CDd9Ec23b37a234064", "interchainGasPaymaster": "0xDd260B99d302f0A3fF885728c086f729c06f227f", - "interchainSecurityModule": "0x4e3D805431E5658Ba753D04BBbD50D9930CaE38F", + "interchainSecurityModule": "0x93BC454D1E8049a286EC84E75e628AB23e5E61f8", "mailbox": "0xaD09d78f4c6b9dA2Ae82b1D34107802d380Bb74f", "merkleTreeHook": "0x2684C6F89E901987E1FdB7649dC5Be0c57C61645", "name": "gnosis", @@ -1195,7 +1195,7 @@ "interchainAccountIsm": "0x708E002637792FDC031E6B62f23DD60014AC976a", "interchainAccountRouter": "0xfB8cea1c7F45608Da30655b50bbF355D123A4358", "interchainGasPaymaster": "0x19dc38aeae620380430C200a6E990D5Af5480117", - "interchainSecurityModule": "0x65b590874726F780e11Aee7E68932c196ac5Cfef", + "interchainSecurityModule": "0xe6a1cA2c655FD86E5bD0307b1577f11e031E4691", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0x0972954923a1e2b2aAb04Fa0c4a0797e5989Cd65", "name": "inevm", @@ -1323,7 +1323,7 @@ "from": 14616307 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0xF2270f4b8a3274924c3e048F25c92fB7A5B091D4", + "interchainSecurityModule": "0xc11B492a516F839F3f05D0CE8f79E51Bf65a66E9", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "kroma", @@ -1396,7 +1396,7 @@ "interchainAccountIsm": "0xdcA646C56E7768DD11654956adE24bfFf9Ba4893", "interchainAccountRouter": "0xD59dA396F162Ed93a41252Cebb8d5DD4F093238C", "interchainGasPaymaster": "0x8105a095368f1a184CceA86cCe21318B5Ee5BE28", - "interchainSecurityModule": "0x1a669B1f14C6C62B2d56f1eBb5C016904E17F831", + "interchainSecurityModule": "0x73171e4CAAe12ae90EFA6f5D33818021b2C8F80F", "mailbox": "0x02d16BC51af6BfD153d67CA61754cF912E82C4d9", "merkleTreeHook": "0xC077A0Cc408173349b1c9870C667B40FE3C01dd7", "name": "linea", @@ -1467,7 +1467,7 @@ "from": 4195553 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0x0c3b196dbA07dfaCcb5Ea4D2809712A2114EC37b", + "interchainSecurityModule": "0x629c7Bf8586256624f5D925360F4382defa04eBf", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "lisk", @@ -1531,7 +1531,7 @@ "from": 3088760 }, "interchainGasPaymaster": "0x441a01Fca2eD731C0Fc4633998332f9FEDB17575", - "interchainSecurityModule": "0x1Df3B3fc76F1E76Ce2D87a921F4436D888E79038", + "interchainSecurityModule": "0x6f3e2043611307bE33016a9aaDf1943cCd10d7db", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0x062200d92dF6bB7bA89Ce4D6800110450f94784e", "name": "lukso", @@ -1605,7 +1605,7 @@ "interchainAccountIsm": "0x8Ea50255C282F89d1A14ad3F159437EE5EF0507f", "interchainAccountRouter": "0x693A4cE39d99e46B04cb562329e3F0141cA17331", "interchainGasPaymaster": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", - "interchainSecurityModule": "0x22BdaC15c02353D331C5787858117F1aC1183831", + "interchainSecurityModule": "0x809253E828b9F94781Ee58089bA696883D05dDca", "isTestnet": false, "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x149db7afD694722747035d5AEC7007ccb6F8f112", @@ -1675,7 +1675,7 @@ "interchainAccountIsm": "0xe039DA3A0071BEd087A12660D7b03cf669c7776E", "interchainAccountRouter": "0x45285463352c53a481e882cD5E2AF2E25BBdAd0D", "interchainGasPaymaster": "0x8105a095368f1a184CceA86cCe21318B5Ee5BE28", - "interchainSecurityModule": "0x3bb99404072f895B0F10bB27855Ede36E15c43E2", + "interchainSecurityModule": "0xA9dfB22e46133f23bB635818aeB04Ae31645D2a8", "mailbox": "0x398633D19f4371e1DB5a8EFE90468eB70B1176AA", "merkleTreeHook": "0x5332D1AC0A626D265298c14ff681c0A8D28dB86d", "name": "mantle", @@ -1737,7 +1737,7 @@ "from": 13523607 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0x1f2c988EE1b80434d89B52A16585595A558fAa48", + "interchainSecurityModule": "0x8Dc8e217071F55C772Ffa21CCCCf3B70FCec40c7", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "merlin", @@ -1804,7 +1804,7 @@ "from": 17966274 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0x90b5E844395e3539CE3Ab008C50E1D1c9648F632", + "interchainSecurityModule": "0x67F16B702A2b8AC3d8fc1ffAEfBAB7Fe6fe762AD", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "metis", @@ -1869,7 +1869,7 @@ "from": 3752032 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0x26918172f13F5E481564fb3Ed14b2cA4Dd4b8AC7", + "interchainSecurityModule": "0xFE82E2d4278124122e9771D7A525846e506BbAD3", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "mint", @@ -1936,7 +1936,7 @@ "interchainAccountIsm": "0xa377b8269e0A47cdd2fD5AAeAe860b45623c6d82", "interchainAccountRouter": "0x6e1B9f776bd415d7cC3C7458A5f0d801016918f8", "interchainGasPaymaster": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "interchainSecurityModule": "0x2151dBB20aaa63E644276bb59A024a81f4b9BC9A", + "interchainSecurityModule": "0xA54Cca261C76B09663Ae4bCe042d644e6F822e2E", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xE2ee936bEa8e42671c400aC96dE198E06F2bA2A6", "name": "mode", @@ -2004,7 +2004,7 @@ "interchainAccountIsm": "0x79b3730CE3685f65802aF1771319992bA960EB9D", "interchainAccountRouter": "0xc4482f66191754a8629D35289043C4EB0285F10E", "interchainGasPaymaster": "0x14760E32C0746094cF14D97124865BC7F0F7368F", - "interchainSecurityModule": "0x194A56Bf67bF4821290A0F3C56573eCed842d8D2", + "interchainSecurityModule": "0xe12cd1ddf6AA5f71FBee45CE9A819a0B3B2fb67F", "mailbox": "0x094d03E751f49908080EFf000Dd6FD177fd44CC3", "merkleTreeHook": "0x87403b85f6f316e7ba91ba1fa6C3Fb7dD4095547", "name": "moonbeam", @@ -2146,7 +2146,7 @@ "interchainAccountIsm": "0x2c46BF14641d00549ECa4779BF5CBf91602C1DEd", "interchainAccountRouter": "0x03D6cC17d45E9EA27ED757A8214d1F07F7D901aD", "interchainGasPaymaster": "0xD8A76C4D91fCbB7Cc8eA795DFDF870E48368995C", - "interchainSecurityModule": "0x8A04d9237BE52200F7363cB82BF099990d4390b1", + "interchainSecurityModule": "0x10b4d96f8D4F095f0B708195B502FDeC165b739b", "mailbox": "0xd4C1905BB1D26BC93DAC913e13CaCC278CdCC80D", "merkleTreeHook": "0x68eE9bec9B4dbB61f69D9D293Ae26a5AACb2e28f", "name": "optimism", @@ -2281,7 +2281,7 @@ "interchainAccountIsm": "0xBAC4529cdfE7CCe9E858BF706e41F8Ed096C1BAd", "interchainAccountRouter": "0xF163949AD9F88977ebF649D0461398Ca752E64B9", "interchainGasPaymaster": "0x0071740Bf129b05C4684abfbBeD248D80971cce2", - "interchainSecurityModule": "0x7dFd4006e9AB433d3f999830F611B2188631B33f", + "interchainSecurityModule": "0x47E840c5537547E0Fff9aB9cCd05eDe70A6D9139", "mailbox": "0x5d934f4e2f797775e53561bB72aca21ba36B96BB", "merkleTreeHook": "0x73FbD25c3e817DC4B4Cd9d00eff6D83dcde2DfF6", "name": "polygon", @@ -2358,7 +2358,7 @@ "interchainAccountIsm": "0xc1198e241DAe48BF5AEDE5DCE49Fe4A6064cF7a7", "interchainAccountRouter": "0x20a0A32a110362920597F72974E1E0d7e25cA20a", "interchainGasPaymaster": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", - "interchainSecurityModule": "0x3e1D7F5c1208A0eb48bc30717F9Ca2614257bFF5", + "interchainSecurityModule": "0x008cF37Cf63dE3179A9a93F558537daB1A73a46C", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x149db7afD694722747035d5AEC7007ccb6F8f112", "name": "polygonzkevm", @@ -2426,7 +2426,7 @@ "from": 32018468 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0xC453a7f24540B90aB43681A5Bc07c9E03acC1a48", + "interchainSecurityModule": "0x56c8e2Ca77EA631D049e9AdbCC81902c4A420AF8", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "proofofplay", @@ -2490,7 +2490,7 @@ "from": 363159 }, "interchainGasPaymaster": "0x3071D4DA6020C956Fe15Bfd0a9Ca8D4574f16696", - "interchainSecurityModule": "0xc43577973b012058Fb96dbF0A4b3246019b54016", + "interchainSecurityModule": "0x16a780d2f0d39B0e0e7B3d05a7E9d0BA772D2e2D", "mailbox": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", "merkleTreeHook": "0x55E4F0bc6b7Bb493D50839A8592e7ad8d5e93cf7", "name": "real", @@ -2557,7 +2557,7 @@ "interchainAccountIsm": "0x5DA60220C5dDe35b7aE91c042ff5979047FA0785", "interchainAccountRouter": "0x7a4d31a686A36285d68e14EDD53631417eB19603", "interchainGasPaymaster": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", - "interchainSecurityModule": "0x32A4A7aB028eb349b20D6370e8B56ba82CbB45c7", + "interchainSecurityModule": "0xF11b43b26e1Df7A7fc9E2fB7982630Ca9E78Db17", "mailbox": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", "merkleTreeHook": "0x8F1E22d309baa69D398a03cc88E9b46037e988AA", "name": "redstone", @@ -2619,7 +2619,7 @@ "from": 937117 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0x72ee68825c1Ad788E4F2414972f11CE92d141Fc1", + "interchainSecurityModule": "0xa8dBD60A10bC4f2B807Abc040c620453b1Ad9fb0", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "sanko", @@ -2687,7 +2687,7 @@ "interchainAccountIsm": "0x32af5Df81fEd5E26119F6640FBB13f3d63a94CDe", "interchainAccountRouter": "0x0B48a744698ba8dFa514742dFEB6728f52fD66f7", "interchainGasPaymaster": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", - "interchainSecurityModule": "0x2811146829429c551285889134C0014c35b38c24", + "interchainSecurityModule": "0x86e427d91d26eCD2542846D4C9b57186e9339a37", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0x6119E37Bd66406A1Db74920aC79C15fB8411Ba76", "name": "scroll", @@ -2703,7 +2703,19 @@ "proxyAdmin": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", "rpcUrls": [ { - "http": "https://scroll.blockpi.network/v1/rpc/public" + "http": "https://rpc.scroll.io" + }, + { + "http": "https://rpc.ankr.com/scroll" + }, + { + "http": "https://scroll-mainnet.chainstacklabs.com" + }, + { + "http": "https://scroll.drpc.org" + }, + { + "http": "https://1rpc.io/scroll" } ], "staticAggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", @@ -2755,7 +2767,7 @@ "interchainAccountIsm": "0xf35dc7B9eE4Ebf0cd3546Bd6EE3b403dE2b9F5D6", "interchainAccountRouter": "0xBcaedE97a98573A88242B3b0CB0A255F3f90d4d5", "interchainGasPaymaster": "0xFC62DeF1f08793aBf0E67f69257c6be258194F72", - "interchainSecurityModule": "0xBD1AF09F9dEC9E3b7a63C9c0B65C514E035238ff", + "interchainSecurityModule": "0xFDd69b3E4c13c6D65f856F99A12BFA0F0cc529D7", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xca1b69fA4c4a7c7fD839bC50867c589592bcfe49", "name": "sei", @@ -2870,7 +2882,7 @@ "interchainAccountIsm": "0xAE557e108b3336130370aC74836f1356B4b30Cf2", "interchainAccountRouter": "0x1F8CF09F060A2AE962c0Bb1F92e209a1E7b0E10B", "interchainGasPaymaster": "0x273Bc6b01D9E88c064b6E5e409BdF998246AEF42", - "interchainSecurityModule": "0x21008aF197f3Ae07fA485d0b34A3b4Dd26FC7633", + "interchainSecurityModule": "0xd663Fb2EE0F8E351869e3E4CdbDeA3b0e440Eb98", "mailbox": "0x28EFBCadA00A7ed6772b3666F3898d276e88CAe3", "merkleTreeHook": "0x6A55822cf11f9fcBc4c75BC2638AfE8Eb942cAdd", "name": "taiko", @@ -2932,7 +2944,7 @@ "from": 1678063 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0xC453a7f24540B90aB43681A5Bc07c9E03acC1a48", + "interchainSecurityModule": "0x56c8e2Ca77EA631D049e9AdbCC81902c4A420AF8", "isTestnet": false, "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", @@ -3000,7 +3012,7 @@ "interchainAccountIsm": "0x551BbEc45FD665a8C95ca8731CbC32b7653Bc59B", "interchainAccountRouter": "0xc11f8Cf2343d3788405582F65B8af6A4F7a6FfC8", "interchainGasPaymaster": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", - "interchainSecurityModule": "0xDbfc19C0F633126a8e276ec884297B49CF5E13E2", + "interchainSecurityModule": "0x83D407ebFa89d1D64833F314f6ad6bBc57728D66", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0x149db7afD694722747035d5AEC7007ccb6F8f112", "name": "viction", @@ -3068,7 +3080,7 @@ "interchainAccountIsm": "0xCB9f90EE5d83Ea52ABd922BD70898f0155D54798", "interchainAccountRouter": "0x473884010F0C1742DA8Ad01E7E295624B931076b", "interchainGasPaymaster": "0x7E27456a839BFF31CA642c060a2b68414Cb6e503", - "interchainSecurityModule": "0xFFd81050Be09ee3D565C3a930dA0692e1D9d634F", + "interchainSecurityModule": "0x85bD389452743323afF638a3486A8cC451a33329", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0x0054D19613f20dD72721A146ED408971a2CCA9BD", "name": "worldchain", @@ -3130,7 +3142,7 @@ "from": 24395308 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0xE18ED85F5fb475B3BB24972c0EdeD1ea7642EB8b", + "interchainSecurityModule": "0x1E6fdCA7fda50eD32c2c54b28a0E0557e6065fba", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "xai", @@ -3198,7 +3210,7 @@ "interchainAccountIsm": "0x29B37088724B745C0ABcE591449Cf042772160C2", "interchainAccountRouter": "0x03cF708E42C89623bd83B281A56935cB562b9258", "interchainGasPaymaster": "0x7E27456a839BFF31CA642c060a2b68414Cb6e503", - "interchainSecurityModule": "0xB80876e19Ce1beF4Db7F52B07155C9d57A3c5135", + "interchainSecurityModule": "0x0F95bC504568913673CB7C1e6BeFc605a31bD945", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0x0054D19613f20dD72721A146ED408971a2CCA9BD", "name": "xlayer", @@ -3266,7 +3278,7 @@ "interchainAccountIsm": "0x2b6d3F7d28B5EC8C3C028fBCAdcf774D9709Dd29", "interchainAccountRouter": "0x3AdCBc94ab8C48EC52D06dc65Bb787fD1981E3d5", "interchainGasPaymaster": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "interchainSecurityModule": "0x75834EC3d39e0D1307715355dd7Dd2b2E8f3fC0A", + "interchainSecurityModule": "0x0c979c68c88459F6b4cCfEc73B6d6Ac262f7159e", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xE2ee936bEa8e42671c400aC96dE198E06F2bA2A6", "name": "zetachain", @@ -3332,7 +3344,7 @@ "from": 1511458 }, "interchainGasPaymaster": "0x03cF708E42C89623bd83B281A56935cB562b9258", - "interchainSecurityModule": "0x52E661244b9c2123EeF1D4cb85D489bba9422D73", + "interchainSecurityModule": "0x3abC1e4D81c96C7cF6E80638dD8dBcc9FF1BD4C3", "mailbox": "0xc2FbB9411186AB3b1a6AFCCA702D1a80B48b197c", "merkleTreeHook": "0x4C97D35c668EE5194a13c8DE8Afc18cce40C9F28", "name": "zircuit", @@ -3405,7 +3417,7 @@ "interchainAccountIsm": "0xb2674E213019972f937CCFc5e23BF963D915809e", "interchainAccountRouter": "0x11b76D93a9D39Eb51F54eBf5566308640cDe882b", "interchainGasPaymaster": "0x18B0688990720103dB63559a3563f7E8d0f63EDb", - "interchainSecurityModule": "0xc5354e5540D9Ae61Ee7ee7D177F4d9B58A1883d1", + "interchainSecurityModule": "0x0393C0ba78562BCC043c0C7c76Ed09230b04A35f", "mailbox": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "merkleTreeHook": "0x886BB0f329781b98f98FDeb1ce7a8957F2d43B9F", "name": "zoramainnet", @@ -3476,7 +3488,7 @@ "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "fallbackRoutingHook": "0xc401e251CCa7A364114504A994D6fC7cb1c243AB", "interchainGasPaymaster": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", - "interchainSecurityModule": "0x7d38C592002bB0994fB5Dc2F557AF32a1aF94605", + "interchainSecurityModule": "0xa5a09C88941410515A5Fc6D42FFb4Db3B6722274", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x441a01Fca2eD731C0Fc4633998332f9FEDB17575", "pausableHook": "0x5Ed813B8b41f25c8002B01A72bbDBe6A0232Fe27", @@ -3543,7 +3555,7 @@ "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "fallbackRoutingHook": "0xc401e251CCa7A364114504A994D6fC7cb1c243AB", "interchainGasPaymaster": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", - "interchainSecurityModule": "0x203Ac1908BCd2721BbC36B1E7889be3C3d19858E", + "interchainSecurityModule": "0xa221Fda4b2fd4b586BbE14aB39E79F186de16CA9", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x441a01Fca2eD731C0Fc4633998332f9FEDB17575", "pausableHook": "0x5Ed813B8b41f25c8002B01A72bbDBe6A0232Fe27", @@ -3613,7 +3625,7 @@ "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "fallbackRoutingHook": "0xc401e251CCa7A364114504A994D6fC7cb1c243AB", "interchainGasPaymaster": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", - "interchainSecurityModule": "0xf83ef66fa36643516b82Af671D43C914B7ea7582", + "interchainSecurityModule": "0xdC6E982Ea3091006b1Ef0CB6Acfe018b058eEd19", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x441a01Fca2eD731C0Fc4633998332f9FEDB17575", "pausableHook": "0x5Ed813B8b41f25c8002B01A72bbDBe6A0232Fe27", @@ -3689,7 +3701,7 @@ "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "fallbackRoutingHook": "0xc401e251CCa7A364114504A994D6fC7cb1c243AB", "interchainGasPaymaster": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", - "interchainSecurityModule": "0x6912fb43dE8878bAee4CF38Dd4982c4eb42F81EF", + "interchainSecurityModule": "0x27d3ADAA10646b8787A614D5d1ef257593BEe1Ea", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x441a01Fca2eD731C0Fc4633998332f9FEDB17575", "pausableHook": "0x5Ed813B8b41f25c8002B01A72bbDBe6A0232Fe27", @@ -3753,7 +3765,7 @@ "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "fallbackRoutingHook": "0xc401e251CCa7A364114504A994D6fC7cb1c243AB", "interchainGasPaymaster": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", - "interchainSecurityModule": "0xc324703B7bDD8640B85d6be73888D9673DB5736e", + "interchainSecurityModule": "0xe43d5f9b4da11ef7F775Ba2B40b112A7F706dF83", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x441a01Fca2eD731C0Fc4633998332f9FEDB17575", "pausableHook": "0x5Ed813B8b41f25c8002B01A72bbDBe6A0232Fe27", @@ -3826,7 +3838,7 @@ "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "fallbackRoutingHook": "0xc401e251CCa7A364114504A994D6fC7cb1c243AB", "interchainGasPaymaster": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", - "interchainSecurityModule": "0xEED58BC374386596a848856aDCD07f597B70e2d9", + "interchainSecurityModule": "0xb9B8bD14661DE405F7597CC5b0D859C568700dF6", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x441a01Fca2eD731C0Fc4633998332f9FEDB17575", "pausableHook": "0x5Ed813B8b41f25c8002B01A72bbDBe6A0232Fe27", @@ -3894,7 +3906,7 @@ "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "fallbackRoutingHook": "0xc401e251CCa7A364114504A994D6fC7cb1c243AB", "interchainGasPaymaster": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", - "interchainSecurityModule": "0xC444cA862A91A87fa3DCdbcf1747E4A8B27eAD73", + "interchainSecurityModule": "0x0081c30768c4C91C73d7871E5ae25B0d27276Fc0", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x441a01Fca2eD731C0Fc4633998332f9FEDB17575", "pausableHook": "0x5Ed813B8b41f25c8002B01A72bbDBe6A0232Fe27", @@ -3957,7 +3969,7 @@ "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "fallbackRoutingHook": "0xc401e251CCa7A364114504A994D6fC7cb1c243AB", "interchainGasPaymaster": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", - "interchainSecurityModule": "0x8a2320fD0B7c75DE3Ad99Faf1F01930d6a6D166a", + "interchainSecurityModule": "0xa4D428c24332A7E42a49b47A8c56f489ce43426F", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x441a01Fca2eD731C0Fc4633998332f9FEDB17575", "pausableHook": "0x5Ed813B8b41f25c8002B01A72bbDBe6A0232Fe27", @@ -4027,7 +4039,7 @@ "interchainAccountIsm": "0xcd9D3744512F07AE844c40E27912092d7c503565", "interchainAccountRouter": "0x92cdbF0Ccdf8E93467FA858fb986fa650A02f2A8", "interchainGasPaymaster": "0xb58257cc81E47EC72fD38aE16297048de23163b4", - "interchainSecurityModule": "0x1f586E16bfB622123E9d166bAd2F3CB1F87Dc117", + "interchainSecurityModule": "0x621711bf30D680256460884a626b6B2A3F8Fa2Bd", "mailbox": "0x7f50C5776722630a0024fAE05fDe8b47571D7B39", "merkleTreeHook": "0xCC3D1659D50461d27a2F025dDb2c9B06B584B7e1", "pausableHook": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", @@ -4087,7 +4099,7 @@ "interchainAccountIsm": "0xc23BaF5Eb5848D19701BbE7f139645e6bd58a319", "interchainAccountRouter": "0x7c58Cadcc2b60ACF794eE1843488d6f5703f76BE", "interchainGasPaymaster": "0xb4fc9B5fD57499Ef6FfF3995728a55F7A618ef86", - "interchainSecurityModule": "0x80140b82E5C4c6Ee0Fe755De0A59aEb0fBBFef8B", + "interchainSecurityModule": "0x70a1E8ee35227962f03a767c2b1073D2000c0c3D", "mailbox": "0xb129828B9EDa48192D0B2db35D0E40dCF51B3594", "merkleTreeHook": "0x3E969bA938E6A993eeCD6F65b0dd8712B07dFe59", "pausableHook": "0x6Fb36672365C7c797028C400A61c58c0ECc53cD2", @@ -4218,7 +4230,7 @@ "interchainAccountIsm": "0x6119B76720CcfeB3D256EC1b91218EEfFD6756E1", "interchainAccountRouter": "0x9eaaC366BFD70430cFee6E70265fefFf1CfC9E47", "interchainGasPaymaster": "0x18B0688990720103dB63559a3563f7E8d0f63EDb", - "interchainSecurityModule": "0xab325b387731928dADf80E8E5501B474f85e769C", + "interchainSecurityModule": "0x2011044b3949C6406Cf47BC8c3122e7D70331266", "mailbox": "0x7f50C5776722630a0024fAE05fDe8b47571D7B39", "merkleTreeHook": "0x886BB0f329781b98f98FDeb1ce7a8957F2d43B9F", "pausableHook": "0x2F619Ac5122689180AeBB930ADccdae215d538a9", @@ -4282,7 +4294,7 @@ "interchainAccountIsm": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", "interchainAccountRouter": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", "interchainGasPaymaster": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", - "interchainSecurityModule": "0x01F399cD43B492D536a925CF52BEAB620019a479", + "interchainSecurityModule": "0xb35e7Cc9d45F3A5AD7dab46EB71F0b564B985322", "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", "merkleTreeHook": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", "pausableHook": "0xD0dca420feFda68537695A8D887080eeF4030AF7", @@ -4343,7 +4355,7 @@ "interchainAccountIsm": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", "interchainAccountRouter": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", "interchainGasPaymaster": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", - "interchainSecurityModule": "0x5bc300Fa6c5761019425c2919F73C7df7A5cb6aA", + "interchainSecurityModule": "0x9219ED9F72c64961c7745C61A1a3492b16435d06", "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", "merkleTreeHook": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", "pausableHook": "0xD0dca420feFda68537695A8D887080eeF4030AF7", @@ -4422,7 +4434,7 @@ "interchainAccountIsm": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", "interchainAccountRouter": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", "interchainGasPaymaster": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", - "interchainSecurityModule": "0x77F728DAF925089cF9536F89C32aB0abAa2e294D", + "interchainSecurityModule": "0xe24AdcdFA8B77A69F4134c6A7c8AB571911fDD43", "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", "merkleTreeHook": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", "pausableHook": "0xD0dca420feFda68537695A8D887080eeF4030AF7", @@ -4489,7 +4501,7 @@ "interchainAccountIsm": "0x783EC5e105234a570eB90f314284E5dBe53bdd90", "interchainAccountRouter": "0xc5D6aCaafBCcEC6D7fD7d92F4509befce641c563", "interchainGasPaymaster": "0xf3dFf6747E7FC74B431C943961054B7BF6309d8a", - "interchainSecurityModule": "0x8ca77BeAC75B19052a49Ae6Ef743fe02EC56B41f", + "interchainSecurityModule": "0xD6f9589f8d1Aa8e4f196174Be8046cF94D4364B9", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x5090dF2FBDa7127c7aDa41f60B79F5c55D380Dd8", "pausableHook": "0x886BB0f329781b98f98FDeb1ce7a8957F2d43B9F", @@ -4560,7 +4572,7 @@ "interchainAccountIsm": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", "interchainAccountRouter": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", "interchainGasPaymaster": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", - "interchainSecurityModule": "0x643dC25467048e2e835A44561D913A98f90bD962", + "interchainSecurityModule": "0x9DdF78676e47935f24DbcC6C0FDF9FF6687Fc461", "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", "merkleTreeHook": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", "pausableHook": "0xD0dca420feFda68537695A8D887080eeF4030AF7", @@ -4625,7 +4637,7 @@ "interchainAccountIsm": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", "interchainAccountRouter": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", "interchainGasPaymaster": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", - "interchainSecurityModule": "0xeAe69924A25c0CC57Ff771689fb05d53a7FD29B1", + "interchainSecurityModule": "0x540605591E8C763FA4C1DeE91A674E39b33c768a", "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", "merkleTreeHook": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", "pausableHook": "0xD0dca420feFda68537695A8D887080eeF4030AF7", @@ -4689,7 +4701,7 @@ "interchainAccountIsm": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", "interchainAccountRouter": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", "interchainGasPaymaster": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", - "interchainSecurityModule": "0x790e7dDEf110E6dF8aA51f713400f6ec0e6758b6", + "interchainSecurityModule": "0x132655e619098058D595E871206e7ec1f4a8cb00", "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", "merkleTreeHook": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", "pausableHook": "0xD0dca420feFda68537695A8D887080eeF4030AF7", @@ -4756,7 +4768,7 @@ "interchainAccountIsm": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", "interchainAccountRouter": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", "interchainGasPaymaster": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", - "interchainSecurityModule": "0x57d57F50065F7390Afc13e8917562061998CfaB2", + "interchainSecurityModule": "0x1277E184770eFF57bc54a8317cff379C53Ed307C", "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", "merkleTreeHook": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", "pausableHook": "0xD0dca420feFda68537695A8D887080eeF4030AF7", @@ -4820,7 +4832,7 @@ "interchainAccountIsm": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", "interchainAccountRouter": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", "interchainGasPaymaster": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", - "interchainSecurityModule": "0x01F399cD43B492D536a925CF52BEAB620019a479", + "interchainSecurityModule": "0xb35e7Cc9d45F3A5AD7dab46EB71F0b564B985322", "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", "merkleTreeHook": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", "pausableHook": "0xD0dca420feFda68537695A8D887080eeF4030AF7", @@ -4882,7 +4894,7 @@ "fallbackDomainRoutingHook": "0x671836d35BB15E21ECc92c4936F0e3131efe12B4", "fallbackRoutingHook": "0x671836d35BB15E21ECc92c4936F0e3131efe12B4", "interchainGasPaymaster": "0x318FbdB17d4e743aBF3183658a4730777101B75C", - "interchainSecurityModule": "0xd60690A755C070817AacF88d1FD3F64451D80149", + "interchainSecurityModule": "0x753876a50C354F2EfB35D1D37dA7F25fd5a78250", "mailbox": "0xd7b351D2dE3495eA259DD10ab4b9300A378Afbf3", "merkleTreeHook": "0x55379421409961Ef129738c24261379ef8A547Df", "proxyAdmin": "0x72e2A678442Edc65f14476A0E4c94312C0469f4A", @@ -5006,7 +5018,7 @@ "interchainAccountIsm": "0x4d264424905535E97396Db83bd553D0d73A4EF9d", "interchainAccountRouter": "0x26A29486480BD74f9B830a9B8dB33cb43C40f496", "interchainGasPaymaster": "0x9c2214467Daf9e2e1F45b36d08ce0b9C65BFeA88", - "interchainSecurityModule": "0xd2bFe972583b513302C0d0BD4Ef0Ac146428776C", + "interchainSecurityModule": "0x89Ea8BAEC48ec2EB8F5444aD600a15dABd4426D7", "mailbox": "0x5bdADEAD721Eb4C4038fF7c989E3C7BbBA302435", "merkleTreeHook": "0x2684C6F89E901987E1FdB7649dC5Be0c57C61645", "pausableHook": "0xC8E323036AAFB4B4201e7B640E79C4Db285A3FC8", @@ -5070,7 +5082,7 @@ "interchainAccountIsm": "0x545E289B88c6d97b74eC0B96e308cae46Bf5f832", "interchainAccountRouter": "0x4ef363Da5bb09CC6aeA16973786963d0C8820778", "interchainGasPaymaster": "0x561BcA8D862536CD9C88f332C1A1Da0fC8F96e40", - "interchainSecurityModule": "0x8Ef057A24ad9E3B5e86ba9015e63E1FDf16E5B83", + "interchainSecurityModule": "0x89E35A66511eF1446DaB436FDF9c1eF93EFaa03D", "mailbox": "0x248aDe14C0489E20C9a7Fea5F86DBfC3702208eF", "merkleTreeHook": "0x9c2214467Daf9e2e1F45b36d08ce0b9C65BFeA88", "pausableHook": "0x2f536FB7a37bd817Af644072a904Ddc02Dae429f", @@ -5137,7 +5149,7 @@ "interchainAccountIsm": "0x60bB6D060393D3C206719A7bD61844cC82891cfB", "interchainAccountRouter": "0x9534122Aae7978dB8f5f10dF4432233c53e820A1", "interchainGasPaymaster": "0x61374178e45F65fF9D6252d017Cd580FC60B7654", - "interchainSecurityModule": "0x7A36F64A6CbFd2e6fA82D9616a8557731B283856", + "interchainSecurityModule": "0xF88aFBD5C7D6f804bb525Bc8f3A12906Ded7C715", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xEe08043cf22c80b27BF24d19999231dF4a3fC256", "pausableHook": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", @@ -5202,7 +5214,7 @@ "interchainAccountIsm": "0xcdc31BA959DE8C035A03167ebAE1961208CDf172", "interchainAccountRouter": "0x349831a180eE4265008C5FFB9465Ff97c1CF0028", "interchainGasPaymaster": "0x6AA10748a036a49Cb290C0e12B77319b76792D5E", - "interchainSecurityModule": "0xd03eb8402F1849B9C3317f0ac12Ef8cbB0288ebd", + "interchainSecurityModule": "0xEeEF1a33C123D8a3Ee96C670fC740D4e7A603329", "mailbox": "0xd9Cc2e652A162bb93173d1c44d46cd2c0bbDA59D", "merkleTreeHook": "0x2783D98CC073dbcDa90241C804d16982D3d75821", "pausableHook": "0x3bb2D0a828f7dD91bA786091F421f6d7cF376445", @@ -5272,7 +5284,7 @@ "interchainAccountIsm": "0x545E289B88c6d97b74eC0B96e308cae46Bf5f832", "interchainAccountRouter": "0x4ef363Da5bb09CC6aeA16973786963d0C8820778", "interchainGasPaymaster": "0xc6835e52C1b976F1ebC71Bc8919738E02849FdA9", - "interchainSecurityModule": "0x666519868126919DD68C24E684D2059cc8991833", + "interchainSecurityModule": "0x06B1B5afCFfB8F49Dcea935A5E9E6A8FF96Da029", "mailbox": "0x1c6f404800bA49Ed581af734eA0d25c0c7d017B2", "merkleTreeHook": "0xdAa1B65547fB969c9ff5678956AB2FF9771B883D", "pausableHook": "0xA0e0829DA397CcF55d5B779C31728f21Cb8219DF", @@ -5384,7 +5396,7 @@ "interchainAccountIsm": "0x8c794a781327b819416E7b67908f1D22397f1E67", "interchainAccountRouter": "0x16625230dD6cFe1B2bec3eCaEc7d43bA3A902CD6", "interchainGasPaymaster": "0x2b79328DA089E89A9E9c08732b56dd31F01011Db", - "interchainSecurityModule": "0x6819a646e9Fc12eB98848038314728082F539617", + "interchainSecurityModule": "0x7E81F96355529Cf7Cb768B3CCf49552E727eC7EE", "mailbox": "0x730f8a4128Fa8c53C777B62Baa1abeF94cAd34a9", "merkleTreeHook": "0x9c64f327F0140DeBd430aab3E2F1d6cbcA921227", "pausableHook": "0x2684C6F89E901987E1FdB7649dC5Be0c57C61645", @@ -5448,7 +5460,7 @@ "interchainAccountIsm": "0xE67Dc24970B482579923551Ede52BD35a2858989", "interchainAccountRouter": "0xDDE46032Baf4da13fDD79BF9dfbaA2749615C409", "interchainGasPaymaster": "0x2f536FB7a37bd817Af644072a904Ddc02Dae429f", - "interchainSecurityModule": "0xfBB6D85fafFE439d470652fBE1F86f782807403f", + "interchainSecurityModule": "0xdA07f00d13E908b7c52C02b38AB748d924DdE890", "mailbox": "0x2f0E57527Bb37E5E064EF243fad56CCE6241906c", "merkleTreeHook": "0xC8E323036AAFB4B4201e7B640E79C4Db285A3FC8", "pausableHook": "0xdAa1B65547fB969c9ff5678956AB2FF9771B883D", @@ -5512,7 +5524,7 @@ "interchainAccountIsm": "0x20a0A32a110362920597F72974E1E0d7e25cA20a", "interchainAccountRouter": "0x5b3EeADcc0E2d4284eA6816e2E503c24d30a9E54", "interchainGasPaymaster": "0x282629Af1A2f9b8e2c5Cbc54C35C7989f21950c6", - "interchainSecurityModule": "0xea0A028775a07EF27CfD1363Ce7463b31ad57296", + "interchainSecurityModule": "0x90B3696922e6a8128EEB23F08Cbf5b2545f849C6", "mailbox": "0x5C02157068a52cEcfc98EDb6115DE6134EcB4764", "merkleTreeHook": "0xf147bBD944C610F86DaE6C7668497D22932C1E4A", "pausableHook": "0x872Bd98057931c8809927c6dE2ef39738a80Eb0C", @@ -5579,7 +5591,7 @@ "interchainAccountIsm": "0xf40eE9FF75Fa34910b7C4C8d68d4850B3bD184D3", "interchainAccountRouter": "0xf6fB78dc009C1A4286c0E7d90C10c9E8906a62Ea", "interchainGasPaymaster": "0xDDE46032Baf4da13fDD79BF9dfbaA2749615C409", - "interchainSecurityModule": "0x36Fe416e4415C93352bD33DC77B83A17850d581e", + "interchainSecurityModule": "0x727DaD31073Dcc961B76Cbd625D6d074fA7988c9", "mailbox": "0x65dCf8F6b3f6a0ECEdf3d0bdCB036AEa47A1d615", "merkleTreeHook": "0x8c794a781327b819416E7b67908f1D22397f1E67", "pausableHook": "0x4d264424905535E97396Db83bd553D0d73A4EF9d", @@ -5646,7 +5658,7 @@ "interchainAccountIsm": "0xd9Cc2e652A162bb93173d1c44d46cd2c0bbDA59D", "interchainAccountRouter": "0x7279B1e11142078b8dC9e69620200f4C84FB8aaa", "interchainGasPaymaster": "0x5ae1ECA065aC8ee92Ce98E584fc3CE43070020e7", - "interchainSecurityModule": "0xe93396575310fbfd1815505dA60963f874cb9CC8", + "interchainSecurityModule": "0x8A4C3c1f234e9da8e325902f7105471B078a1e23", "mailbox": "0x96D51cc3f7500d501bAeB1A2a62BB96fa03532F8", "merkleTreeHook": "0x086c3947F71BE98A0bDf4AB7239955e7542b0CbA", "pausableHook": "0x9C6e8d989ea7F212e679191BEb44139d83ac927a", @@ -5716,7 +5728,7 @@ "interchainAccountIsm": "0x8a733038eF4BbC314eE0F7595257D8d3799B6aA9", "interchainAccountRouter": "0xCE8260c1b5cF2fAD15bb4B6542716b050Fdf35c9", "interchainGasPaymaster": "0xa1c3884EbE24Cccb120B2E98a55f85140563aa4C", - "interchainSecurityModule": "0x22CD0d9ad151e694cE92Ce07cB921918ad94d87e", + "interchainSecurityModule": "0xff5306993F410008d1d7581695252e95c55bc458", "mailbox": "0x5e8a0fCc0D1DF583322943e01F02cB243e5300f6", "merkleTreeHook": "0x2f536FB7a37bd817Af644072a904Ddc02Dae429f", "pausableHook": "0xc6835e52C1b976F1ebC71Bc8919738E02849FdA9", @@ -5777,7 +5789,7 @@ "interchainAccountIsm": "0x25EAC2007b0D40E3f0AF112FD346412321038719", "interchainAccountRouter": "0xfF26696DcDb6BbFD27e959b847D4f1399D5BcF64", "interchainGasPaymaster": "0x9534122Aae7978dB8f5f10dF4432233c53e820A1", - "interchainSecurityModule": "0x802173518092b5af3079762a1fbeF8bbAf4593a0", + "interchainSecurityModule": "0x16c7A4C2f4BCDBFC8205Baa9F1dCBcd44B4d8d90", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x9eaaC366BFD70430cFee6E70265fefFf1CfC9E47", "pausableHook": "0x9eb56085DdbDA60aDf7d2B533AFeD90e38fC9666", @@ -5847,7 +5859,7 @@ "interchainAccountIsm": "0x25EAC2007b0D40E3f0AF112FD346412321038719", "interchainAccountRouter": "0xfF26696DcDb6BbFD27e959b847D4f1399D5BcF64", "interchainGasPaymaster": "0x9534122Aae7978dB8f5f10dF4432233c53e820A1", - "interchainSecurityModule": "0xA07D4B3a8da7F513BFD70866fCFfC42D34d91a87", + "interchainSecurityModule": "0x78E12CF0Ee442f7B9E6df137e0c038A4b6e8d8Eb", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x9eaaC366BFD70430cFee6E70265fefFf1CfC9E47", "pausableHook": "0x9eb56085DdbDA60aDf7d2B533AFeD90e38fC9666", @@ -5908,7 +5920,7 @@ "interchainAccountIsm": "0x25EAC2007b0D40E3f0AF112FD346412321038719", "interchainAccountRouter": "0xfF26696DcDb6BbFD27e959b847D4f1399D5BcF64", "interchainGasPaymaster": "0x9534122Aae7978dB8f5f10dF4432233c53e820A1", - "interchainSecurityModule": "0x2d9c837157923B40bdF1e64a2A9fC13E37Dd9246", + "interchainSecurityModule": "0xF2c7E4Ad99b08d1a2CF139cb7b79d645b50BEdEa", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x9eaaC366BFD70430cFee6E70265fefFf1CfC9E47", "pausableHook": "0x9eb56085DdbDA60aDf7d2B533AFeD90e38fC9666", @@ -5972,7 +5984,7 @@ "interchainAccountIsm": "0x25EAC2007b0D40E3f0AF112FD346412321038719", "interchainAccountRouter": "0xfF26696DcDb6BbFD27e959b847D4f1399D5BcF64", "interchainGasPaymaster": "0x9534122Aae7978dB8f5f10dF4432233c53e820A1", - "interchainSecurityModule": "0x8d7912ca475362E055808Bc3Dd67B117e007523d", + "interchainSecurityModule": "0x0D9b693d45690a9FfE8F1e8E0dC41A2C6207E090", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x9eaaC366BFD70430cFee6E70265fefFf1CfC9E47", "pausableHook": "0x9eb56085DdbDA60aDf7d2B533AFeD90e38fC9666", @@ -6036,7 +6048,7 @@ "interchainAccountIsm": "0x25EAC2007b0D40E3f0AF112FD346412321038719", "interchainAccountRouter": "0xfF26696DcDb6BbFD27e959b847D4f1399D5BcF64", "interchainGasPaymaster": "0x9534122Aae7978dB8f5f10dF4432233c53e820A1", - "interchainSecurityModule": "0xdBB9466B300AC49BC2C91bB975643b592A126514", + "interchainSecurityModule": "0x78E12CF0Ee442f7B9E6df137e0c038A4b6e8d8Eb", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x9eaaC366BFD70430cFee6E70265fefFf1CfC9E47", "pausableHook": "0x9eb56085DdbDA60aDf7d2B533AFeD90e38fC9666", @@ -6109,7 +6121,7 @@ "interchainAccountIsm": "0xfF26696DcDb6BbFD27e959b847D4f1399D5BcF64", "interchainAccountRouter": "0x4D50044335dc1d4D26c343AdeDf6E47808475Deb", "interchainGasPaymaster": "0x70EbA87Cd15616f32C736B3f3BdCfaeD0713a82B", - "interchainSecurityModule": "0xcf5B2Fa0cDa42ec6CB947A84384f136416FF8341", + "interchainSecurityModule": "0xE490AbA47A8b6B26dFF22A61aAFd51C52fD386c2", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xbb0AE51BCa526cF313b6a95BfaB020794af6C394", "pausableHook": "0x83475ca5bEB2Eaa59A2FF48a0544ebaa4a32c2de", @@ -6173,7 +6185,7 @@ "interchainAccountIsm": "0x31Bb27f6007C33acD1be83ACEd3164C60f8F7b13", "interchainAccountRouter": "0xEeb5a99a75585fe137c83E7b62b74f87264A5481", "interchainGasPaymaster": "0xb7C9307fE90B9AB093c6D3EdeE3259f5378D5f03", - "interchainSecurityModule": "0x0a698848Dbf4819b22A37E02748588AcF9936F22", + "interchainSecurityModule": "0x56b99cFe785C7e9F61040cdd4081d63Ef48Cf928", "mailbox": "0x0dF25A2d59F03F039b56E90EdC5B89679Ace28Bc", "merkleTreeHook": "0xC88636fFdFAc7cb87b7A76310B7a62AF0A000595", "pausableHook": "0x2AF32cF8e3Cf42d221eDa0c843818fA5ee129E27", @@ -6237,7 +6249,7 @@ "interchainAccountIsm": "0x28291a7062afA569104bEd52F7AcCA3dD2FafD11", "interchainAccountRouter": "0xe9E3444DDD80c50276c0Fcf316026f6d7fEc2c47", "interchainGasPaymaster": "0x25EAC2007b0D40E3f0AF112FD346412321038719", - "interchainSecurityModule": "0xdd8378d5A11ad5B48699dfCF87a06aF595410D49", + "interchainSecurityModule": "0xCb1514f34Dfd366cF74D0F58F3D8BDE49C059533", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x5C02157068a52cEcfc98EDb6115DE6134EcB4764", "pausableHook": "0x99fEFc1119E86Ee0153eb887cF8E8ab2d92A16e8", @@ -6305,7 +6317,7 @@ "interchainAccountIsm": "0x027eFD1695941969435AA640542B690044dF7E06", "interchainAccountRouter": "0x65F1343AC23D4fF48bf6c7E0c55872d245397567", "interchainGasPaymaster": "0x28291a7062afA569104bEd52F7AcCA3dD2FafD11", - "interchainSecurityModule": "0x0a297b209EE1Db131F4624819fa0cc8D119d01e5", + "interchainSecurityModule": "0xF0f29D8b9d16aE092886A38571D5E7Ffdb508EB3", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xcd90D49b046772F710250b9119117169CB2e4D8b", "pausableHook": "0x7CE76f5f0C469bBB4cd7Ea6EbabB54437A093127", @@ -6488,7 +6500,7 @@ "interchainAccountIsm": "0xf9609bB22847e0DB5F6fB8f95b84D25A19b46ac5", "interchainAccountRouter": "0x2b6d3F7d28B5EC8C3C028fBCAdcf774D9709Dd29", "interchainGasPaymaster": "0xFb7D175d6F53800D68D32C3Fe1416807A394cC24", - "interchainSecurityModule": "0x565f3Ad39Acb67D673fb568575DcE6Ba1861d723", + "interchainSecurityModule": "0x882eE9A3938501558823514b70d8bBa945953B80", "mailbox": "0x473884010F0C1742DA8Ad01E7E295624B931076b", "merkleTreeHook": "0xdA629E1B79e3420ECd1e80571aDd6a4a3b13AE79", "pausableHook": "0xe93f2f409ad8B5000431D234472973fe848dcBEC", @@ -6555,7 +6567,7 @@ "interchainAccountIsm": "0xF457D831d9F55e87B2F0b35AD6D033fd6b4181Ed", "interchainAccountRouter": "0x021D2810a758c833080DEc2F1Fa8F571Aae97D45", "interchainGasPaymaster": "0xc0C2dB448fC2c84213394Fcb93a3C467e50ECa9E", - "interchainSecurityModule": "0x4DaeEeC1c7FC2316419261ae3C4220191D6e12A3", + "interchainSecurityModule": "0x597210e915409D2B9D4487Ef21ea115444e419Ed", "mailbox": "0x7f50C5776722630a0024fAE05fDe8b47571D7B39", "merkleTreeHook": "0xA8A311B69f688c1D9928259D872C31ca0d473642", "pausableHook": "0x48C427782Bc1e9ecE406b3e277481b28ABcBdf03", @@ -6625,7 +6637,7 @@ "interchainAccountIsm": "0x93D41E41cA545a35A81d11b08D2eE8b852C768df", "interchainAccountRouter": "0xc2466492C451E1AE49d8C874bB9f89293Aaad59b", "interchainGasPaymaster": "0xDf178647caB5e0222F4B53C57274FD2A03BEaed6", - "interchainSecurityModule": "0xd285467525196946efDa56fc26DAbFe816A5E4E4", + "interchainSecurityModule": "0x7664aAE6C1bCFfe364F742c75f52D3dC70FF412E", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xDc1508844B99C606E16C2Ae87f33c373edD4B0F6", "pausableHook": "0xA8A311B69f688c1D9928259D872C31ca0d473642", @@ -6692,7 +6704,7 @@ "interchainAccountIsm": "0x11b76D93a9D39Eb51F54eBf5566308640cDe882b", "interchainAccountRouter": "0x93D41E41cA545a35A81d11b08D2eE8b852C768df", "interchainGasPaymaster": "0xDf178647caB5e0222F4B53C57274FD2A03BEaed6", - "interchainSecurityModule": "0xf0F40bf7FFc8B05b639D657f02Fc7B75A218e068", + "interchainSecurityModule": "0x44f0875a9a412849B2167c7a4A3ECE4e13ba1ac4", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xDc1508844B99C606E16C2Ae87f33c373edD4B0F6", "pausableHook": "0xA8A311B69f688c1D9928259D872C31ca0d473642", @@ -6762,7 +6774,7 @@ "interchainAccountIsm": "0x11b76D93a9D39Eb51F54eBf5566308640cDe882b", "interchainAccountRouter": "0x93D41E41cA545a35A81d11b08D2eE8b852C768df", "interchainGasPaymaster": "0xDf178647caB5e0222F4B53C57274FD2A03BEaed6", - "interchainSecurityModule": "0xf0F40bf7FFc8B05b639D657f02Fc7B75A218e068", + "interchainSecurityModule": "0x44f0875a9a412849B2167c7a4A3ECE4e13ba1ac4", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xDc1508844B99C606E16C2Ae87f33c373edD4B0F6", "pausableHook": "0xA8A311B69f688c1D9928259D872C31ca0d473642", @@ -6832,7 +6844,7 @@ "interchainAccountIsm": "0x11b76D93a9D39Eb51F54eBf5566308640cDe882b", "interchainAccountRouter": "0x93D41E41cA545a35A81d11b08D2eE8b852C768df", "interchainGasPaymaster": "0xDf178647caB5e0222F4B53C57274FD2A03BEaed6", - "interchainSecurityModule": "0xf0F40bf7FFc8B05b639D657f02Fc7B75A218e068", + "interchainSecurityModule": "0x03Def25faaABF7aa393eC4108267f0b2c0B1F58f", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xDc1508844B99C606E16C2Ae87f33c373edD4B0F6", "pausableHook": "0xA8A311B69f688c1D9928259D872C31ca0d473642", @@ -6896,7 +6908,7 @@ "interchainAccountIsm": "0x11b76D93a9D39Eb51F54eBf5566308640cDe882b", "interchainAccountRouter": "0x93D41E41cA545a35A81d11b08D2eE8b852C768df", "interchainGasPaymaster": "0xDf178647caB5e0222F4B53C57274FD2A03BEaed6", - "interchainSecurityModule": "0xf0F40bf7FFc8B05b639D657f02Fc7B75A218e068", + "interchainSecurityModule": "0x03Def25faaABF7aa393eC4108267f0b2c0B1F58f", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xDc1508844B99C606E16C2Ae87f33c373edD4B0F6", "pausableHook": "0xA8A311B69f688c1D9928259D872C31ca0d473642", @@ -6949,7 +6961,7 @@ "protocol": "ethereum", "rpcUrls": [ { - "http": "https://rpc.inkonchain.com" + "http": "https://rpc-qnd.inkonchain.com" } ], "technicalStack": "opstack", @@ -6960,7 +6972,7 @@ "interchainAccountIsm": "0x60515f328B2c55Df63f456D9D839a0082892dEf8", "interchainAccountRouter": "0xF457D831d9F55e87B2F0b35AD6D033fd6b4181Ed", "interchainGasPaymaster": "0xc0C2dB448fC2c84213394Fcb93a3C467e50ECa9E", - "interchainSecurityModule": "0x7f124ad38fDF2FDdcD8554fd44f72b0A56EA5644", + "interchainSecurityModule": "0x017a26fE04aB95c77BA6DeE4d441A3F2402BBe17", "mailbox": "0x7f50C5776722630a0024fAE05fDe8b47571D7B39", "merkleTreeHook": "0xA8A311B69f688c1D9928259D872C31ca0d473642", "pausableHook": "0x48C427782Bc1e9ecE406b3e277481b28ABcBdf03", @@ -7027,7 +7039,7 @@ "interchainAccountIsm": "0xd64d126941EaC2Cf53e0E4E8146cC70449b60D73", "interchainAccountRouter": "0x1A4F09A615aA4a35E5a146DC2fa19975bebF21A5", "interchainGasPaymaster": "0x3cECBa60A580dE20CC57D87528953a00f4ED99EA", - "interchainSecurityModule": "0x8f1953EFbd2C720223faD02d6CB5CD25f97D7fC9", + "interchainSecurityModule": "0x0fB08d319a9Daaf6e8005873D2BA1980Bd59c614", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x1c6f404800bA49Ed581af734eA0d25c0c7d017B2", "pausableHook": "0x9e8b689e83d929cb8c2d9166E55319a4e6aA83B7", @@ -7088,7 +7100,7 @@ "interchainAccountIsm": "0x11b76D93a9D39Eb51F54eBf5566308640cDe882b", "interchainAccountRouter": "0x93D41E41cA545a35A81d11b08D2eE8b852C768df", "interchainGasPaymaster": "0xDf178647caB5e0222F4B53C57274FD2A03BEaed6", - "interchainSecurityModule": "0xf0F40bf7FFc8B05b639D657f02Fc7B75A218e068", + "interchainSecurityModule": "0x7664aAE6C1bCFfe364F742c75f52D3dC70FF412E", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xDc1508844B99C606E16C2Ae87f33c373edD4B0F6", "pausableHook": "0xA8A311B69f688c1D9928259D872C31ca0d473642", @@ -7152,7 +7164,7 @@ "interchainAccountIsm": "0x11b76D93a9D39Eb51F54eBf5566308640cDe882b", "interchainAccountRouter": "0x93D41E41cA545a35A81d11b08D2eE8b852C768df", "interchainGasPaymaster": "0xDf178647caB5e0222F4B53C57274FD2A03BEaed6", - "interchainSecurityModule": "0xf0F40bf7FFc8B05b639D657f02Fc7B75A218e068", + "interchainSecurityModule": "0x794BF08dE42238809d811193A6189D6d03177DF8", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xDc1508844B99C606E16C2Ae87f33c373edD4B0F6", "pausableHook": "0xA8A311B69f688c1D9928259D872C31ca0d473642", @@ -7219,7 +7231,7 @@ "interchainAccountIsm": "0x11b76D93a9D39Eb51F54eBf5566308640cDe882b", "interchainAccountRouter": "0xF457D831d9F55e87B2F0b35AD6D033fd6b4181Ed", "interchainGasPaymaster": "0xDf178647caB5e0222F4B53C57274FD2A03BEaed6", - "interchainSecurityModule": "0xf0F40bf7FFc8B05b639D657f02Fc7B75A218e068", + "interchainSecurityModule": "0x557862e7ADd75E72779316b119F75358b3445102", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0xDc1508844B99C606E16C2Ae87f33c373edD4B0F6", "pausableHook": "0xA8A311B69f688c1D9928259D872C31ca0d473642", From 1f2945b45555b5394125218cc085873dcfd5cc7a Mon Sep 17 00:00:00 2001 From: Maxime GRIS Date: Fri, 20 Dec 2024 13:48:12 +0100 Subject: [PATCH 07/37] fix: Google Cloud Storage CheckpointSyncer checker implementation (#4971) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Description The current CLI command `hyperlane avs check` only checks when storage location starts with `s3://` This PR allow to retrieve and check the validator state on Google Cloud Storage - when storage location starts with `gs://` ### Drive-by changes * Use the latest location set during Validator registration & retrieve it from ValidatorAnnounce.getAnnouncedStorageLocations ### Related issues * None ### Backward compatibility * Yes ### Testing * Manual using locally compiled `yarn hyperlane avs check` command Before ```bash Operator name: Born to be wild Operator address: 0xd38d980188604c7051dbce2980a0c6e38423fda4 Validator address: 0x58a607f46a481c9e5209ab27f45189411fA83D41 Validator is not validating on any chain ``` After ```bash Operator name: Born to be wild Operator address: 0xd38d980188604c7051dbce2980a0c6e38423fda4 Validator address: 0x58a607f46a481c9e5209ab27f45189411fA83D41 Validating on... base Storage location: https://storage.googleapis.com/hyperlane_2pa2pyhawbcl35nnfr079f50iy7/gcsLatestIndexKey Latest merkle tree checkpoint index: 992526 Latest validator checkpoint index: 992525 ✅ Validator is signing latest checkpoint optimism Storage location: https://storage.googleapis.com/hyperlane_2okiqlhes2ki6lfsgsa0qlzaclx/gcsLatestIndexKey Latest merkle tree checkpoint index: 654920 Latest validator checkpoint index: 654920 ✅ Validator is signing latest checkpoint ``` --- typescript/cli/src/avs/check.ts | 24 ++++++++++++++---------- typescript/cli/src/validator/utils.ts | 17 +++++++++-------- typescript/sdk/src/index.ts | 5 ++++- typescript/sdk/src/utils/validator.ts | 7 +++++++ typescript/utils/src/validator.ts | 12 ++++++++++++ 5 files changed, 46 insertions(+), 19 deletions(-) diff --git a/typescript/cli/src/avs/check.ts b/typescript/cli/src/avs/check.ts index 27055c6848..b2af3f7b73 100644 --- a/typescript/cli/src/avs/check.ts +++ b/typescript/cli/src/avs/check.ts @@ -6,7 +6,12 @@ import { MerkleTreeHook__factory, ValidatorAnnounce__factory, } from '@hyperlane-xyz/core'; -import { ChainMap, ChainName, MultiProvider } from '@hyperlane-xyz/sdk'; +import { + ChainMap, + ChainName, + MultiProvider, + isValidValidatorStorageLocation, +} from '@hyperlane-xyz/sdk'; import { Address, ProtocolType, isObjEmpty } from '@hyperlane-xyz/utils'; import { CommandContext } from '../context/types.js'; @@ -288,19 +293,18 @@ const setValidatorInfo = async ( const storageLocation = validatorStorageLocations[i]; const warnings: string[] = []; - // Skip if no storage location is found, address is not validating on this chain or if storage location string doesn't not start with s3:// - if ( - storageLocation.length === 0 || - !storageLocation[0].startsWith('s3://') - ) { + const lastStorageLocation = + storageLocation.length > 0 ? storageLocation.slice(-1)[0] : ''; + + // Skip if no storage location is found, address is not validating on this chain or if not a valid storage location + if (!isValidValidatorStorageLocation(lastStorageLocation)) { continue; } const [latestValidatorCheckpointIndex, latestCheckpointUrl] = - (await getLatestValidatorCheckpointIndexAndUrl(storageLocation[0])) ?? [ - undefined, - undefined, - ]; + (await getLatestValidatorCheckpointIndexAndUrl( + lastStorageLocation, + )) ?? [undefined, undefined]; if (!latestMerkleTreeCheckpointIndex) { warnings.push( diff --git a/typescript/cli/src/validator/utils.ts b/typescript/cli/src/validator/utils.ts index c96aa66125..f1218c2b1b 100644 --- a/typescript/cli/src/validator/utils.ts +++ b/typescript/cli/src/validator/utils.ts @@ -1,5 +1,6 @@ import { MerkleTreeHook, ValidatorAnnounce } from '@hyperlane-xyz/core'; -import { S3Validator } from '@hyperlane-xyz/sdk'; +import { getValidatorFromStorageLocation } from '@hyperlane-xyz/sdk'; +import { BaseValidator } from '@hyperlane-xyz/utils'; import { logDebug } from '../logger.js'; @@ -36,23 +37,23 @@ export const getValidatorStorageLocations = async ( }; export const getLatestValidatorCheckpointIndexAndUrl = async ( - s3StorageLocation: string, + storageLocation: string, ): Promise<[number, string] | undefined> => { - let s3Validator: S3Validator; + let validator: BaseValidator; try { - s3Validator = await S3Validator.fromStorageLocation(s3StorageLocation); + validator = await getValidatorFromStorageLocation(storageLocation); } catch (err) { logDebug( - `Failed to instantiate S3Validator at location ${s3StorageLocation}: ${err}`, + `Failed to instantiate Validator at location ${storageLocation}: ${err}`, ); return undefined; } try { - const latestCheckpointIndex = await s3Validator.getLatestCheckpointIndex(); - return [latestCheckpointIndex, s3Validator.getLatestCheckpointUrl()]; + const latestCheckpointIndex = await validator.getLatestCheckpointIndex(); + return [latestCheckpointIndex, validator.getLatestCheckpointUrl()]; } catch (err) { logDebug( - `Failed to get latest checkpoint index from S3Validator at location ${s3StorageLocation}: ${err}`, + `Failed to get latest checkpoint index from Validator at location ${storageLocation}: ${err}`, ); return undefined; } diff --git a/typescript/sdk/src/index.ts b/typescript/sdk/src/index.ts index ad8c6c4870..dd94504fcd 100644 --- a/typescript/sdk/src/index.ts +++ b/typescript/sdk/src/index.ts @@ -615,7 +615,10 @@ export { getSealevelAccountDataSchema, } from './utils/sealevelSerialization.js'; export { getChainIdFromTxs } from './utils/transactions.js'; -export { getValidatorFromStorageLocation } from './utils/validator.js'; +export { + getValidatorFromStorageLocation, + isValidValidatorStorageLocation, +} from './utils/validator.js'; export { FeeConstantConfig, RouteBlacklist, diff --git a/typescript/sdk/src/utils/validator.ts b/typescript/sdk/src/utils/validator.ts index c49612f8a7..10f5a0d510 100644 --- a/typescript/sdk/src/utils/validator.ts +++ b/typescript/sdk/src/utils/validator.ts @@ -10,3 +10,10 @@ export async function getValidatorFromStorageLocation(location: string) { throw new Error('Invalid storage location'); } } + +export function isValidValidatorStorageLocation(location: string) { + return ( + location?.startsWith(GCP_LOCATION_PREFIX) || + location?.startsWith(S3_LOCATION_PREFIX) + ); +} diff --git a/typescript/utils/src/validator.ts b/typescript/utils/src/validator.ts index 99cb9772ee..63f135909d 100644 --- a/typescript/utils/src/validator.ts +++ b/typescript/utils/src/validator.ts @@ -93,4 +93,16 @@ export class BaseValidator { ); return eqAddress(address, this.config.address); } + + getLatestCheckpointIndex(): Promise { + throw new Error('Not implemented'); + } + + storageLocation(): string { + throw new Error('Not implemented'); + } + + getLatestCheckpointUrl(): string { + throw new Error('Not implemented'); + } } From 143b46e4705387799e791bf7849486f78cfba169 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Fri, 20 Dec 2024 12:50:59 +0000 Subject: [PATCH 08/37] chore: blacklist txs between unenrolled warp routers (#5058) ### Description ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- typescript/infra/config/environments/mainnet3/agent.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index 416fe1d008..7820908d81 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -532,6 +532,11 @@ const blacklistedMessageIds = [ // MAGIC/ethereum-treasure native funding txs '0x9d51f4123be816cbaeef2e2b34a5760f633a7cb8a019fe16f88a3227cc22451e', '0x663c221137028ceeeb102a98e48b362a7b48d626b93c88c7fdf1871a948b1223', + + // txs between unenrolled routers of + // ETH/arbitrum-base-blast-bsc-ethereum-gnosis-lisk-mantle-mode-optimism-polygon-scroll-zeronetwork-zoramainnet + '0x229a832dfdfa23dfc27eb773e6b34e87f329067393f4f7b616251b3d7d52d294', + '0xcdfd5294e8b1253263908e1919d27675f80a2e9a3bb339b759810efdbb81faa5', ]; // Blacklist matching list intended to be used by all contexts. From 5467c65231cae76ea16d3786b481d2d3ffee90dc Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Fri, 20 Dec 2024 13:58:17 +0000 Subject: [PATCH 09/37] chore: update Tessellated validator version (#5059) ### Description chore: update Tessellated validator version ### Drive-by changes ### Related issues ### Backward compatibility ### Testing Signed-off-by: pbio <10051819+paulbalaji@users.noreply.github.com> --- .../infra/scripts/check/check-validator-version.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/typescript/infra/scripts/check/check-validator-version.ts b/typescript/infra/scripts/check/check-validator-version.ts index ee6992d88a..f87f1d32fd 100644 --- a/typescript/infra/scripts/check/check-validator-version.ts +++ b/typescript/infra/scripts/check/check-validator-version.ts @@ -36,12 +36,9 @@ const acceptableValidatorVersions: Record = { 'a64af8be9a76120d0cfc727bb70660fa07e70cce': '1.0.0-beta', // 1.0.0 'ffbe1dd82e2452dbc111b6fb469a34fb870da8f1': '1.0.0', - // Tessellated's Build from November 2024 - // https://github.com/Tessellated-io/hyperlane-monorepo/commit/9b855686d3e2b3d6b81238ce51a576ff5e0f770f - '9b855686d3e2b3d6b81238ce51a576ff5e0f770f': 'Tesselated - November 2024', - // Tesselated's Build from December 2024 - // https://github.com/Tessellated-io/hyperlane-monorepo/commit/2bd17da1b30725a6d741197914f5c4b2ae25b566 - '2bd17da1b30725a6d741197914f5c4b2ae25b566': 'Tesselated - December 2024', + // Tessellated's Build from December 2024 + // https://github.com/Tessellated-io/hyperlane-monorepo/commit/c0719c7f5387c4801a9dec8edc1227fb33b44ea7 + 'c0719c7f5387c4801a9dec8edc1227fb33b44ea7': 'Tessellated - December 2024', }; type ValidatorInfo = { From e7350efcf1b996cc68f11bc6b0e9432aa84e1629 Mon Sep 17 00:00:00 2001 From: Mohammed Hussan Date: Fri, 20 Dec 2024 15:09:36 +0000 Subject: [PATCH 10/37] feat(warpConfig): Update FastUSD/ethereum-sei collateral router owner (#5053) ### Description - Update FastUSD/ethereum-sei collateral router owner ### Testing Manual --- .../warp/configGetters/getEthereumSeiFastUSDWarpConfig.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumSeiFastUSDWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumSeiFastUSDWarpConfig.ts index 73c3a5655d..cb90ba42a8 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumSeiFastUSDWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumSeiFastUSDWarpConfig.ts @@ -15,6 +15,7 @@ import { // Elixir const owner = '0x00000000F51340906F767C6999Fe512b1275955C'; +const elixirSafe = '0x738744237b7fd97af670d9ddf54390c24263cea8'; const ownerConfig = getOwnerConfigForAddress(owner); export const getEthereumSeiFastUSDWarpConfig = async ( @@ -34,7 +35,10 @@ export const getEthereumSeiFastUSDWarpConfig = async ( const ethereum: HypTokenRouterConfig = { ...routerConfig.ethereum, - ...ownerConfig, + owner: elixirSafe, + ownerOverrides: { + proxyAdmin: owner, + }, type: TokenType.collateral, token: tokens.ethereum.deUSD, interchainSecurityModule: ethers.constants.AddressZero, From 774d54971812abc26c85aee52647dc762f14d65f Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Fri, 20 Dec 2024 16:36:34 +0000 Subject: [PATCH 11/37] chore(retrying_provider): reduce log verbosity (#5038) ### Description 60% of validator logs are `Dispatching request`, which really isn't needed at the info/debug level and we can safe quite significantly ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- .../src/rpc_clients/retrying.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/retrying.rs b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/retrying.rs index 7c57e61702..59baeec762 100644 --- a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/retrying.rs +++ b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/retrying.rs @@ -10,7 +10,7 @@ use serde::{de::DeserializeOwned, Serialize}; use serde_json::Value; use thiserror::Error; use tokio::time::sleep; -use tracing::{debug, error, instrument, trace, warn_span}; +use tracing::{error, instrument, trace, warn, warn_span}; /// An HTTP Provider with a simple naive exponential backoff built-in #[derive(Debug, Clone)] @@ -89,13 +89,16 @@ where { let params = serde_json::to_value(params).expect("valid"); - let mut last_err; + let mut last_err = None; let mut i = 1; loop { let mut rate_limited = false; let backoff_ms = self.base_retry_ms * 2u64.pow(i - 1); - trace!(params = %serde_json::to_string(¶ms).unwrap_or_default(), "Dispatching request with params"); - debug!(attempt = i, "Dispatching request"); + if let Some(ref last_err) = last_err { + // `last_err` is always expected to be `Some` if `i > 1` + warn!(attempt = i, ?last_err, "Dispatching request"); + } + trace!(attempt = i, params = %serde_json::to_string(¶ms).unwrap_or_default(), "Dispatching request"); let fut = match params { Value::Null => self.inner.request(method, ()), @@ -110,10 +113,10 @@ where return Err(RetryingProviderError::JsonRpcClientError(e)); } HandleMethod::Retry(e) => { - last_err = e; + last_err = Some(e); } HandleMethod::RateLimitedRetry(e) => { - last_err = e; + last_err = Some(e); rate_limited = true; } } @@ -128,7 +131,7 @@ where trace!(backoff_ms, rate_limited, "Retrying provider going to sleep"); sleep(Duration::from_millis(backoff_ms)).await; } else { - trace!( + warn!( requests_made = self.max_requests, "Retrying provider reached max requests" ); @@ -150,7 +153,7 @@ where JsonRpcClientError(P::Error), /// Hit max requests #[error("Hit max requests")] - MaxRequests(P::Error), + MaxRequests(Option), } impl

From> for ProviderError From d4cf9687445ab57becf18e283135fcc2e8da48ef Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Sat, 21 Dec 2024 23:24:19 +0000 Subject: [PATCH 12/37] chore: rm Viction env-test (#5067) ### Description - Removing the consistently failing Viction env test, see https://discord.com/channels/935678348330434570/1319783601407262854/1319788686111674418 for context ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 98426a76a6..d2e5f9a40b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -301,7 +301,7 @@ jobs: fail-fast: false matrix: environment: [mainnet3] - chain: [ethereum, arbitrum, optimism, inevm, viction] + chain: [ethereum, arbitrum, optimism, inevm] module: [core, igp] include: - environment: testnet4 From ccba2925299627c8bcd65c918dc89a562e426b3c Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Sun, 22 Dec 2024 05:58:31 +0000 Subject: [PATCH 13/37] chore: add undeliverable message to blacklist (#5066) ### Description ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- typescript/infra/config/environments/mainnet3/agent.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index 7820908d81..3756344809 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -537,6 +537,10 @@ const blacklistedMessageIds = [ // ETH/arbitrum-base-blast-bsc-ethereum-gnosis-lisk-mantle-mode-optimism-polygon-scroll-zeronetwork-zoramainnet '0x229a832dfdfa23dfc27eb773e6b34e87f329067393f4f7b616251b3d7d52d294', '0xcdfd5294e8b1253263908e1919d27675f80a2e9a3bb339b759810efdbb81faa5', + + // txs between unenrolled routers of + // USDT/arbitrum-ethereum-mantle-mode-polygon-scroll-zeronetwork + '0x10159bf1b5b2142b882cb060d1da9f9123d82974ca265ba432138221e52c2a27', ]; // Blacklist matching list intended to be used by all contexts. From 09679f8b04331583a89ce185a340b1cc4e8413ab Mon Sep 17 00:00:00 2001 From: anson-caldera Date: Sun, 22 Dec 2024 22:36:02 -0800 Subject: [PATCH 14/37] fix: correct instructions in Rust readme for building docker image (#5064) ### Description Fix incorrect instructions in Rust readme for building the docker image. The build does not work as the readme dictates. Being in the rust folder gives a `ERROR: failed to solve: failed to compute cache key: failed to calculate checksum of ref y1ezs2da6re8bcvxgo9psu1xk::hydxdsbeaxqx3rvjjzako1134: "/rust/main/config": not found` error. Being at the top level causes a successful build. Also have to edit the build script to target the correct directory dockerfile. --- rust/README.md | 5 ++--- rust/build.sh | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/rust/README.md b/rust/README.md index 35050b1e79..2e4577deac 100644 --- a/rust/README.md +++ b/rust/README.md @@ -119,11 +119,10 @@ cargo test --release --package run-locally --bin run-locally --features cosmos - ### Building Agent Docker Images There exists a docker build for the agent binaries. These docker images are used for deploying the agents in a -production environment. +production environment. You should run this at the top level of the repo. ```bash -cd rust -./build.sh +./rust/build.sh ``` ### Deploy Procedure diff --git a/rust/build.sh b/rust/build.sh index 65214900b5..16af4721ba 100755 --- a/rust/build.sh +++ b/rust/build.sh @@ -19,4 +19,4 @@ if [[ -z $TAG ]]; then fi fi -DOCKER_BUILDKIT=1 docker build $PLATFORM -t gcr.io/abacus-labs-dev/hyperlane-agent:$TAG . +DOCKER_BUILDKIT=1 docker build -f rust/Dockerfile $PLATFORM -t gcr.io/abacus-labs-dev/hyperlane-agent:$TAG . From c914123909c6dc9732d4aea7c19ab0b9206d6466 Mon Sep 17 00:00:00 2001 From: Mohammed Hussan Date: Mon, 23 Dec 2024 17:07:52 +0000 Subject: [PATCH 15/37] fix(warpChecker): Check for token mismatch before decimals check (#5023) ### Description - reorder checks to so that a token mismatch is caught before decimal checks are done. This will avoid us calling `.decimals` on a contract that is not a token contract i.e if the config has been misconfigured ### Testing Manual --- typescript/sdk/src/token/checker.ts | 47 +++++++++++++++-------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/typescript/sdk/src/token/checker.ts b/typescript/sdk/src/token/checker.ts index cf29d28c81..7f18e27b80 100644 --- a/typescript/sdk/src/token/checker.ts +++ b/typescript/sdk/src/token/checker.ts @@ -68,29 +68,7 @@ export class HypERC20Checker extends ProxiedRouterChecker< const expectedConfig = this.configMap[chain]; const hypToken = this.app.router(this.app.getContracts(chain)); - // Check all actual decimals are consistent - const actualChainDecimals = await this.getEvmActualDecimals(); - this.checkDecimalConsistency( - chain, - hypToken, - actualChainDecimals, - 'actual', - true, - ); - - // Check all config decimals are consistent as well - const configDecimals = objMap( - this.configMap, - (_chain, config) => config.decimals, - ); - this.checkDecimalConsistency( - chain, - hypToken, - configDecimals, - 'config', - false, - ); - + // Check if configured token type matches actual token type if (isNativeTokenConfig(expectedConfig)) { try { await this.multiProvider.estimateGas(chain, { @@ -126,6 +104,29 @@ export class HypERC20Checker extends ProxiedRouterChecker< this.addViolation(violation); } } + + // Check all actual decimals are consistent, this should be done after checking the token type to avoid 'decimal()' calls to non collateral token that would fail + const actualChainDecimals = await this.getEvmActualDecimals(); + this.checkDecimalConsistency( + chain, + hypToken, + actualChainDecimals, + 'actual', + true, + ); + + // Check all config decimals are consistent as well + const configDecimals = objMap( + this.configMap, + (_chain, config) => config.decimals, + ); + this.checkDecimalConsistency( + chain, + hypToken, + configDecimals, + 'config', + false, + ); } private cachedAllActualDecimals: Record | undefined = From e9911bb9d2bbe68783c61f7ade166954db55e9fd Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Mon, 23 Dec 2024 21:03:29 +0000 Subject: [PATCH 16/37] feat: SVM priority fee oracles and transaction submitters, more consistent tx submission (#4959) ### Description The fruition of https://www.notion.so/hyperlanexyz/Landing-Solana-Transactions-15a6d35200d680cbaf5eea5f9dbec1b1 Major highlights: - Instead of using the max CUs, estimate CU usage and apply a 10% buffer - Allow for priority fees to be configured. If not specified, defaults to a constant priority fee of 0. Supported options are: - a constant fee - the Helius priority fee API (https://docs.helius.dev/solana-apis/priority-fee-api) - Allow for transaction submission to be configured. If not specified, defaults to the configured RPC url for the chain. Supported options are: - An arbitrary RPC URL, which will use priority fee auctions for fees - Jito, which will use a tip for fees - In our own infra, we have logic to plumb through the Helius URL found at https://console.cloud.google.com/security/secret-manager/secret/mainnet3-rpc-endpoint-helius-solanamainnet/versions?project=abacus-labs-dev - Signs txs with a finalized blockhash, as suggested by [How to Land Transactions on Solana](https://www.helius.dev/blog/how-to-land-transactions-on-solana) - Partially implements `process_estimate_costs` to test that messages are processable. Does not yet actually enforce gas limits, because this will take some rollout effort. This is part of https://www.notion.so/hyperlanexyz/Alerting-on-critical-processing-issues-for-any-and-all-messages-15b6d35200d680a79d40fabc2e4e49da ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --------- Co-authored-by: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> --- .changeset/long-llamas-fly.md | 2 +- .changeset/polite-bulldogs-sit.md | 2 +- .changeset/spotty-guests-dance.md | 5 + .../main/chains/hyperlane-sealevel/src/lib.rs | 2 + .../chains/hyperlane-sealevel/src/mailbox.rs | 392 ++++++------------ .../hyperlane-sealevel/src/priority_fee.rs | 218 ++++++++++ .../hyperlane-sealevel/src/rpc/client.rs | 262 +++++++++++- .../hyperlane-sealevel/src/trait_builder.rs | 110 +++++ .../hyperlane-sealevel/src/tx_submitter.rs | 115 +++++ rust/main/config/mainnet_config.json | 7 +- .../templates/external-secret.yaml | 13 + .../templates/relayer-statefulset.yaml | 2 + .../templates/scraper-statefulset.yaml | 2 + .../templates/validator-statefulset.yaml | 2 + .../src/settings/parser/connection_parser.rs | 178 +++++++- .../config/environments/mainnet3/agent.ts | 48 +++ typescript/infra/src/agents/index.ts | 31 +- typescript/infra/src/config/agent/agent.ts | 15 + typescript/infra/src/utils/helm.ts | 9 +- typescript/sdk/src/index.ts | 6 + typescript/sdk/src/metadata/agentConfig.ts | 62 +++ 21 files changed, 1190 insertions(+), 293 deletions(-) create mode 100644 .changeset/spotty-guests-dance.md create mode 100644 rust/main/chains/hyperlane-sealevel/src/priority_fee.rs create mode 100644 rust/main/chains/hyperlane-sealevel/src/tx_submitter.rs diff --git a/.changeset/long-llamas-fly.md b/.changeset/long-llamas-fly.md index c0d6d01e9b..f9da56dd0f 100644 --- a/.changeset/long-llamas-fly.md +++ b/.changeset/long-llamas-fly.md @@ -1,5 +1,5 @@ --- -"@hyperlane-xyz/cli": patch +'@hyperlane-xyz/cli': patch --- Suppress help on CLI failures diff --git a/.changeset/polite-bulldogs-sit.md b/.changeset/polite-bulldogs-sit.md index 8acf555fe5..6c68774325 100644 --- a/.changeset/polite-bulldogs-sit.md +++ b/.changeset/polite-bulldogs-sit.md @@ -1,5 +1,5 @@ --- -"@hyperlane-xyz/cli": patch +'@hyperlane-xyz/cli': patch --- Fix strategy flag propagation diff --git a/.changeset/spotty-guests-dance.md b/.changeset/spotty-guests-dance.md new file mode 100644 index 0000000000..39eb9b0f49 --- /dev/null +++ b/.changeset/spotty-guests-dance.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/sdk': minor +--- + +Added new Sealevel tx submission and priority fee oracle params to agent config types diff --git a/rust/main/chains/hyperlane-sealevel/src/lib.rs b/rust/main/chains/hyperlane-sealevel/src/lib.rs index 90a2e01b66..6121bbe27f 100644 --- a/rust/main/chains/hyperlane-sealevel/src/lib.rs +++ b/rust/main/chains/hyperlane-sealevel/src/lib.rs @@ -23,8 +23,10 @@ mod log_meta_composer; mod mailbox; mod merkle_tree_hook; mod multisig_ism; +mod priority_fee; mod provider; mod rpc; mod trait_builder; +mod tx_submitter; mod utils; mod validator_announce; diff --git a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs index 66e58131aa..034f24c027 100644 --- a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs +++ b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs @@ -22,61 +22,37 @@ use hyperlane_sealevel_message_recipient_interface::{ }; use lazy_static::lazy_static; use serializable_account_meta::SimulationReturnData; -use solana_client::{rpc_client::SerializableTransaction, rpc_response::Response}; use solana_program::pubkey; use solana_sdk::{ account::Account, - bs58, clock::Slot, commitment_config::CommitmentConfig, - compute_budget::ComputeBudgetInstruction, instruction::{AccountMeta, Instruction}, pubkey::Pubkey, - signature::Signature, signer::{keypair::Keypair, Signer as _}, - transaction::Transaction, }; -use solana_transaction_status::TransactionStatus; use tracing::{debug, info, instrument, warn}; use hyperlane_core::{ config::StrOrIntParseError, ChainCommunicationError, ChainResult, ContractLocator, Decode as _, Encode as _, FixedPointNumber, HyperlaneChain, HyperlaneContract, HyperlaneDomain, - HyperlaneMessage, HyperlaneProvider, Indexed, Indexer, KnownHyperlaneDomain, LogMeta, Mailbox, - MerkleTreeHook, ReorgPeriod, SequenceAwareIndexer, TxCostEstimate, TxOutcome, H256, H512, U256, + HyperlaneMessage, HyperlaneProvider, Indexed, Indexer, LogMeta, Mailbox, MerkleTreeHook, + ReorgPeriod, SequenceAwareIndexer, TxCostEstimate, TxOutcome, H256, H512, U256, }; -use crate::account::{search_accounts_by_discriminator, search_and_validate_account}; use crate::log_meta_composer::{ is_message_delivery_instruction, is_message_dispatch_instruction, LogMetaComposer, }; +use crate::tx_submitter::TransactionSubmitter; +use crate::{ + account::{search_accounts_by_discriminator, search_and_validate_account}, + priority_fee::PriorityFeeOracle, +}; use crate::{ConnectionConf, SealevelProvider, SealevelRpcClient}; const SYSTEM_PROGRAM: &str = "11111111111111111111111111111111"; const SPL_NOOP: &str = "noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV"; -// The max amount of compute units for a transaction. -// TODO: consider a more sane value and/or use IGP gas payments instead. -const PROCESS_COMPUTE_UNITS: u32 = 1_400_000; - -/// 0.0005 SOL, in lamports. -/// A typical tx fee without a prioritization fee is 0.000005 SOL, or -/// 5000 lamports. (Example: https://explorer.solana.com/tx/fNd3xVeBzFHeuzr8dXQxLGiHMzTeYpykSV25xWzNRaHtzzjvY9A3MzXh1ZsK2JncRHkwtuWrGEwGXVhFaUCYhtx) -/// See average priority fees here https://solanacompass.com/statistics/fees -/// to inform what to spend here. -const PROCESS_DESIRED_PRIORITIZATION_FEE_LAMPORTS_PER_TX: u64 = 500000; - -/// In micro-lamports. Multiply this by the compute units to figure out -/// the additional cost of processing a message, in addition to the mandatory -/// "base" cost of signature verification. -/// Unused at the moment, but kept for future reference. -#[allow(dead_code)] -const PROCESS_COMPUTE_UNIT_PRICE_MICRO_LAMPORTS: u64 = - // Convert to micro-lamports - (PROCESS_DESIRED_PRIORITIZATION_FEE_LAMPORTS_PER_TX * 1_000_000) - // Divide by the max compute units - / PROCESS_COMPUTE_UNITS as u64; - // Earlier versions of collateral warp routes were deployed off a version where the mint // was requested as a writeable account for handle instruction. This is not necessary, // and generally requires a higher priority fee to be paid. @@ -96,6 +72,7 @@ lazy_static! { (pubkey!("CuQmsT4eSF4dYiiGUGYYQxJ7c58pUAD5ADE3BbFGzQKx"), pubkey!("EKpQGSJtjMFqKZ9KQanSqYXRcF8fBopzLHYxdM65zcjm")), ]); } + /// A reference to a Mailbox contract on some Sealevel chain pub struct SealevelMailbox { pub(crate) program_id: Pubkey, @@ -103,6 +80,8 @@ pub struct SealevelMailbox { pub(crate) outbox: (Pubkey, u8), pub(crate) provider: SealevelProvider, payer: Option, + priority_fee_oracle: Box, + tx_submitter: Box, } impl SealevelMailbox { @@ -127,8 +106,12 @@ impl SealevelMailbox { program_id, inbox, outbox, - provider, payer, + priority_fee_oracle: conf.priority_fee_oracle.create_oracle(), + tx_submitter: conf + .transaction_submitter + .create_submitter(provider.rpc().url()), + provider, }) } @@ -301,122 +284,88 @@ impl SealevelMailbox { self.get_account_metas(instruction).await } - fn use_jito(&self) -> bool { - matches!( - self.domain(), - HyperlaneDomain::Known(KnownHyperlaneDomain::SolanaMainnet) - ) - } - - async fn send_and_confirm_transaction( + async fn get_process_instruction( &self, - transaction: &Transaction, - ) -> ChainResult { - if self.use_jito() { - self.send_and_confirm_transaction_with_jito(transaction) - .await - } else { - self.provider - .rpc() - .send_and_confirm_transaction(transaction) - .await - } - } + message: &HyperlaneMessage, + metadata: &[u8], + ) -> ChainResult { + let recipient: Pubkey = message.recipient.0.into(); + let mut encoded_message = vec![]; + message.write_to(&mut encoded_message).unwrap(); - /// Send a transaction to Jito and wait for it to be confirmed. - /// Logic stolen from Solana's non-blocking client. - pub async fn send_and_confirm_transaction_with_jito( - &self, - transaction: &impl SerializableTransaction, - ) -> ChainResult { - let signature = transaction.get_signature(); + let payer = self.get_payer()?; - let base58_txn = bs58::encode( - bincode::serialize(&transaction).map_err(ChainCommunicationError::from_other)?, + let (process_authority_key, _process_authority_bump) = Pubkey::try_find_program_address( + mailbox_process_authority_pda_seeds!(&recipient), + &self.program_id, ) - .into_string(); - - const SEND_RETRIES: usize = 1; - const GET_STATUS_RETRIES: usize = usize::MAX; - - 'sending: for _ in 0..SEND_RETRIES { - let jito_request_body = serde_json::json!({ - "jsonrpc": "2.0", - "id": 1, - "method": "sendBundle", - "params": [ - [base58_txn] - ], + .ok_or_else(|| { + ChainCommunicationError::from_other_str( + "Could not find program address for process authority", + ) + })?; + let (processed_message_account_key, _processed_message_account_bump) = + Pubkey::try_find_program_address( + mailbox_processed_message_pda_seeds!(message.id()), + &self.program_id, + ) + .ok_or_else(|| { + ChainCommunicationError::from_other_str( + "Could not find program address for processed message account", + ) + })?; + + // Get the account metas required for the recipient.InterchainSecurityModule instruction. + let ism_getter_account_metas = self.get_ism_getter_account_metas(recipient).await?; + + // Get the recipient ISM. + let ism = self + .get_recipient_ism(recipient, ism_getter_account_metas.clone()) + .await?; + + let ixn = + hyperlane_sealevel_mailbox::instruction::Instruction::InboxProcess(InboxProcess { + metadata: metadata.to_vec(), + message: encoded_message.clone(), }); + let ixn_data = ixn + .into_instruction_data() + .map_err(ChainCommunicationError::from_other)?; - tracing::info!( - ?jito_request_body, - ?signature, - "Sending sealevel transaction to Jito as bundle" - ); + // Craft the accounts for the transaction. + let mut accounts: Vec = vec![ + AccountMeta::new_readonly(payer.pubkey(), true), + AccountMeta::new_readonly(Pubkey::from_str(SYSTEM_PROGRAM).unwrap(), false), + AccountMeta::new(self.inbox.0, false), + AccountMeta::new_readonly(process_authority_key, false), + AccountMeta::new(processed_message_account_key, false), + ]; + accounts.extend(ism_getter_account_metas); + accounts.extend([ + AccountMeta::new_readonly(Pubkey::from_str(SPL_NOOP).unwrap(), false), + AccountMeta::new_readonly(ism, false), + ]); - let jito_response = reqwest::Client::new() - .post("https://mainnet.block-engine.jito.wtf:443/api/v1/bundles") - .json(&jito_request_body) - .send() - .await - .map_err(ChainCommunicationError::from_other)?; - let jito_response_text = jito_response.text().await; - - tracing::info!( - ?signature, - ?jito_response_text, - "Got Jito response for sealevel transaction bundle" - ); + // Get the account metas required for the ISM.Verify instruction. + let ism_verify_account_metas = self + .get_ism_verify_account_metas(ism, metadata.into(), encoded_message) + .await?; + accounts.extend(ism_verify_account_metas); - let recent_blockhash = if transaction.uses_durable_nonce() { - self.provider - .rpc() - .get_latest_blockhash_with_commitment(CommitmentConfig::processed()) - .await? - } else { - *transaction.get_recent_blockhash() - }; - - for status_retry in 0..GET_STATUS_RETRIES { - let signature_statuses: Response>> = self - .provider - .rpc() - .get_signature_statuses(&[*signature]) - .await?; - let signature_status = signature_statuses.value.first().cloned().flatten(); - match signature_status { - Some(_) => return Ok(*signature), - None => { - if !self - .provider - .rpc() - .is_blockhash_valid(&recent_blockhash) - .await? - { - // Block hash is not found by some reason - break 'sending; - } else if cfg!(not(test)) - // Ignore sleep at last step. - && status_retry < GET_STATUS_RETRIES - { - // Retry twice a second - tokio::time::sleep(std::time::Duration::from_millis(500)).await; - continue; - } - } - } - } - } + // The recipient. + accounts.extend([AccountMeta::new_readonly(recipient, false)]); - Err(ChainCommunicationError::from_other( - solana_client::rpc_request::RpcError::ForUser( - "unable to confirm transaction. \ - This can happen in situations such as transaction expiration \ - and insufficient fee-payer funds" - .to_string(), - ), - )) + // Get account metas required for the Handle instruction + let handle_account_metas = self.get_handle_account_metas(message).await?; + accounts.extend(handle_account_metas); + + let process_instruction = Instruction { + program_id: self.program_id, + data: ixn_data, + accounts, + }; + + Ok(process_instruction) } async fn get_inbox(&self) -> ChainResult> { @@ -429,6 +378,12 @@ impl SealevelMailbox { .into_inner(); Ok(inbox) } + + fn get_payer(&self) -> ChainResult<&Keypair> { + self.payer + .as_ref() + .ok_or_else(|| ChainCommunicationError::SignerUnavailable) + } } impl HyperlaneContract for SealevelMailbox { @@ -508,133 +463,42 @@ impl Mailbox for SealevelMailbox { metadata: &[u8], _tx_gas_limit: Option, ) -> ChainResult { - let recipient: Pubkey = message.recipient.0.into(); - let mut encoded_message = vec![]; - message.write_to(&mut encoded_message).unwrap(); - - let payer = self - .payer - .as_ref() - .ok_or_else(|| ChainCommunicationError::SignerUnavailable)?; - - let mut instructions = Vec::with_capacity(3); - // Set the compute unit limit. - instructions.push(ComputeBudgetInstruction::set_compute_unit_limit( - PROCESS_COMPUTE_UNITS, - )); - - // If we're using Jito, we need to send a tip to the Jito fee account. - // Otherwise, we need to set the compute unit price. - if self.use_jito() { - let tip: u64 = std::env::var("JITO_TIP_LAMPORTS") - .ok() - .and_then(|s| s.parse::().ok()) - .unwrap_or(PROCESS_DESIRED_PRIORITIZATION_FEE_LAMPORTS_PER_TX); - - // The tip is a standalone transfer to a Jito fee account. - // See https://github.com/jito-labs/mev-protos/blob/master/json_rpc/http.md#sendbundle. - instructions.push(solana_sdk::system_instruction::transfer( - &payer.pubkey(), - // A random Jito fee account, taken from the getFeeAccount RPC response: - // https://github.com/jito-labs/mev-protos/blob/master/json_rpc/http.md#gettipaccounts - &solana_sdk::pubkey!("DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh"), - tip, - )); - } // "processed" level commitment does not guarantee finality. // roughly 5% of blocks end up on a dropped fork. // However we don't want this function to be a bottleneck and there already // is retry logic in the agents. let commitment = CommitmentConfig::processed(); - let (process_authority_key, _process_authority_bump) = Pubkey::try_find_program_address( - mailbox_process_authority_pda_seeds!(&recipient), - &self.program_id, - ) - .ok_or_else(|| { - ChainCommunicationError::from_other_str( - "Could not find program address for process authority", - ) - })?; - let (processed_message_account_key, _processed_message_account_bump) = - Pubkey::try_find_program_address( - mailbox_processed_message_pda_seeds!(message.id()), - &self.program_id, - ) - .ok_or_else(|| { - ChainCommunicationError::from_other_str( - "Could not find program address for processed message account", - ) - })?; + let process_instruction = self.get_process_instruction(message, metadata).await?; - // Get the account metas required for the recipient.InterchainSecurityModule instruction. - let ism_getter_account_metas = self.get_ism_getter_account_metas(recipient).await?; - - // Get the recipient ISM. - let ism = self - .get_recipient_ism(recipient, ism_getter_account_metas.clone()) - .await?; - - let ixn = - hyperlane_sealevel_mailbox::instruction::Instruction::InboxProcess(InboxProcess { - metadata: metadata.to_vec(), - message: encoded_message.clone(), - }); - let ixn_data = ixn - .into_instruction_data() - .map_err(ChainCommunicationError::from_other)?; - - // Craft the accounts for the transaction. - let mut accounts: Vec = vec![ - AccountMeta::new_readonly(payer.pubkey(), true), - AccountMeta::new_readonly(Pubkey::from_str(SYSTEM_PROGRAM).unwrap(), false), - AccountMeta::new(self.inbox.0, false), - AccountMeta::new_readonly(process_authority_key, false), - AccountMeta::new(processed_message_account_key, false), - ]; - accounts.extend(ism_getter_account_metas); - accounts.extend([ - AccountMeta::new_readonly(Pubkey::from_str(SPL_NOOP).unwrap(), false), - AccountMeta::new_readonly(ism, false), - ]); - - // Get the account metas required for the ISM.Verify instruction. - let ism_verify_account_metas = self - .get_ism_verify_account_metas(ism, metadata.into(), encoded_message) + let tx = self + .provider + .rpc() + .build_estimated_tx_for_instruction( + process_instruction, + self.get_payer()?, + &*self.tx_submitter, + &*self.priority_fee_oracle, + ) .await?; - accounts.extend(ism_verify_account_metas); - // The recipient. - accounts.extend([AccountMeta::new_readonly(recipient, false)]); + tracing::info!(?tx, "Created sealevel transaction to process message"); - // Get account metas required for the Handle instruction - let handle_account_metas = self.get_handle_account_metas(message).await?; - accounts.extend(handle_account_metas); + let signature = self.tx_submitter.send_transaction(&tx, true).await?; - let inbox_instruction = Instruction { - program_id: self.program_id, - data: ixn_data, - accounts, - }; - instructions.push(inbox_instruction); - let recent_blockhash = self - .rpc() - .get_latest_blockhash_with_commitment(commitment) - .await?; + tracing::info!(?tx, ?signature, "Sealevel transaction sent"); - let txn = Transaction::new_signed_with_payer( - &instructions, - Some(&payer.pubkey()), - &[payer], - recent_blockhash, - ); + let send_instant = std::time::Instant::now(); - tracing::info!(?txn, "Created sealevel transaction to process message"); + // Wait for the transaction to be confirmed. + self.rpc().wait_for_transaction_confirmation(&tx).await?; - let signature = self.send_and_confirm_transaction(&txn).await?; - - tracing::info!(?txn, ?signature, "Sealevel transaction sent"); + // We expect time_to_confirm to fluctuate depending on the commitment level when submitting the + // tx, but still use it as a proxy for tx latency to help debug. + tracing::info!(?tx, ?signature, time_to_confirm=?send_instant.elapsed(), "Sealevel transaction confirmed"); + // TODO: not sure if this actually checks if the transaction was executed / reverted? + // Confirm the transaction. let executed = self .rpc() .confirm_transaction_with_commitment(&signature, commitment) @@ -655,10 +519,30 @@ impl Mailbox for SealevelMailbox { #[instrument(err, ret, skip(self))] async fn process_estimate_costs( &self, - _message: &HyperlaneMessage, - _metadata: &[u8], + message: &HyperlaneMessage, + metadata: &[u8], ) -> ChainResult { - // TODO use correct data upon integrating IGP support + // Getting a process instruction in Sealevel is a pretty expensive operation + // that involves some view calls. Consider reusing the instruction with subsequent + // calls to `process` to avoid this cost. + let process_instruction = self.get_process_instruction(message, metadata).await?; + + // The returned costs are unused at the moment - we simply want to perform a simulation to + // determine if the message will revert or not. + let _ = self + .rpc() + .get_estimated_costs_for_instruction( + process_instruction, + self.get_payer()?, + &*self.tx_submitter, + &*self.priority_fee_oracle, + ) + .await?; + + // TODO use correct data upon integrating IGP support. + // NOTE: providing a real gas limit here will result in accurately enforcing + // gas payments. Be careful rolling this out to not impact existing contracts + // that may not be paying for super accurate gas amounts. Ok(TxCostEstimate { gas_limit: U256::zero(), gas_price: FixedPointNumber::zero(), diff --git a/rust/main/chains/hyperlane-sealevel/src/priority_fee.rs b/rust/main/chains/hyperlane-sealevel/src/priority_fee.rs new file mode 100644 index 0000000000..2df395cce6 --- /dev/null +++ b/rust/main/chains/hyperlane-sealevel/src/priority_fee.rs @@ -0,0 +1,218 @@ +use async_trait::async_trait; +use derive_new::new; +use hyperlane_core::{ChainCommunicationError, ChainResult}; +use reqwest::Client; +use serde::Deserialize; +use solana_sdk::{bs58, transaction::Transaction}; + +use crate::{HeliusPriorityFeeLevel, HeliusPriorityFeeOracleConfig}; + +/// A trait for fetching the priority fee for a transaction. +#[async_trait] +pub trait PriorityFeeOracle: Send + Sync { + /// Fetch the priority fee in microlamports for a transaction. + async fn get_priority_fee(&self, transaction: &Transaction) -> ChainResult; +} + +/// A priority fee oracle that returns a constant fee. +#[derive(Debug, Clone, new)] +pub struct ConstantPriorityFeeOracle { + fee: u64, +} + +#[async_trait] +impl PriorityFeeOracle for ConstantPriorityFeeOracle { + async fn get_priority_fee(&self, _transaction: &Transaction) -> ChainResult { + Ok(self.fee) + } +} + +/// A priority fee oracle that fetches the fee from the Helius API. +/// https://docs.helius.dev/solana-apis/priority-fee-api +#[derive(Debug, Clone)] +pub struct HeliusPriorityFeeOracle { + client: Client, + config: HeliusPriorityFeeOracleConfig, +} + +impl HeliusPriorityFeeOracle { + pub fn new(config: HeliusPriorityFeeOracleConfig) -> Self { + Self { + client: reqwest::Client::new(), + config, + } + } + + fn get_priority_fee_estimate_options(&self) -> serde_json::Value { + // It's an odd interface, but if using the Recommended fee level, the API requires `recommended: true`, + // otherwise it requires `priorityLevel: ""`. + + let (key, value) = match &self.config.fee_level { + HeliusPriorityFeeLevel::Recommended => ("recommended", serde_json::json!(true)), + level => ("priorityLevel", serde_json::json!(level)), + }; + + serde_json::json!({ + key: value, + "transactionEncoding": "base58", + }) + } +} + +#[async_trait] +impl PriorityFeeOracle for HeliusPriorityFeeOracle { + async fn get_priority_fee(&self, transaction: &Transaction) -> ChainResult { + let base58_tx = bs58::encode( + bincode::serialize(transaction).map_err(ChainCommunicationError::from_other)?, + ) + .into_string(); + + let request_body = serde_json::json!({ + "jsonrpc": "2.0", + "id": "1", + "method": "getPriorityFeeEstimate", + "params": [ + { + "transaction": base58_tx, + "options": self.get_priority_fee_estimate_options(), + } + ], + }); + + let response = self + .client + .post(self.config.url.clone()) + .json(&request_body) + .send() + .await + .map_err(ChainCommunicationError::from_other)?; + + let response: JsonRpcResult = response + .json() + .await + .map_err(ChainCommunicationError::from_other)?; + + tracing::debug!(?response, "Fetched priority fee from Helius API"); + + let fee = response.result.priority_fee_estimate.round() as u64; + + Ok(fee) + } +} + +/// The result of a JSON-RPC request to the Helius API. +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct JsonRpcResult { + #[allow(dead_code)] + jsonrpc: String, + #[allow(dead_code)] + id: String, + result: T, +} + +/// The result of a `getPriorityFeeEstimate` request to the Helius API. +#[derive(Debug, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +struct GetPriorityFeeEstimateResult { + priority_fee_estimate: f64, +} + +#[cfg(test)] +mod test { + use solana_sdk::{bs58, transaction::Transaction}; + + use crate::{ + priority_fee::{HeliusPriorityFeeOracle, PriorityFeeOracle}, + HeliusPriorityFeeLevel, HeliusPriorityFeeOracleConfig, + }; + + use super::{GetPriorityFeeEstimateResult, JsonRpcResult}; + + #[tokio::test] + async fn test_helius_get_priority_fee() { + let helius_url = if let Ok(url) = std::env::var("HELIUS_URL") { + url + } else { + // Skip test if HELIUS_URL is not set + return; + }; + + let oracle = super::HeliusPriorityFeeOracle::new(super::HeliusPriorityFeeOracleConfig { + url: url::Url::parse(&helius_url).unwrap(), + fee_level: super::HeliusPriorityFeeLevel::Medium, + }); + + // Example process transaction + // https://solscan.io/tx/W9fXtRD8mPkkUmuoLi9QxSCgFuy32rCVa8kfxtPjWXWRH2D1AWzuDEGuvexWGyWhQDXnEmaADZMeYu5RVjWZyAB + let process_tx_base58 = "BPBE2dE4sPJX3nm4svEZ181qBfX9yvUp5H67uTt3aqRGtC6a77hW5vrQk9zJ3KkNuK63KoJCeqp1kkFwsbF5KL1UHf5Hrj8GXpiRxmKD8NybEZUWhjdVW9azMxJdnxxiFqH7wFQtZGkQxhx6oJz1qi5Xc64LEbPJEwSTAp5US1VCnnhWGRqJ297kvS8hWaVLuUxr4jEqYNG2LSusXZmzABBqEvRv753PBxcKiBE2moo9VKZ8n3ai6rmQGnSzsoAfwnjCx6iUdNSWqpYFHcq2xhMXJx8US5kv837KsT5tKQBbujsWUoRGGJ8vkmm7RJSYyR3DYEMa5ira9fiDwnK5qP3EgP2hrG73YYBxZ9naRrYzHG2GiEGWEUgNPHaUtK3JsbjTLiNjyZU8ERTdMxi4rBLppREJfHDWYUNgN9hTL81LYv4YoJY3UUTQphzT268f6oZoiyavngb8t3Lq8pbyc3gPiw7AcWXmn2ERDAcHvS59AaoxxcwZyn8UWUdynwCzvNbWhb97qVHSzBY1S79sxHFuqyBhbbD5YhkMhFGLjPUEDvncxE2hLt9iQCQaEQzCNRMmnZw7yJ1YxoKDKfmUTXJ6rmT4p2pz7f8x4jJwQ2pC2YxobcfHrNvD7929vXSvpomyZmaEXYAN2bqGBUe2KazpnobVCwafjKMVN4AaTJRMTXi92VKuShuKJEuZo9ZM7TScEqRZC5hLFU8SbCdASEUoQjpDzivUf1m9gQtT2ob5FPwJzcuZpqTWgixd59BRHTB1L5c4fDvtYr1QJFpJRN4DsXGryK4eTMu2oAs3imGpg1rHRLpuBTbcrchEivz7bD17bBj8VeHogfkPcehD9yaHzmYPRF47aWZ52GSFSSpc5kJRRQyghUKNPFBnycLGAbfkRYDdVzUgdrr3CNYksJCu45TChg54tMWWwrqSD3k5RPv7A6bXbAH4PzW83vzE2vGJFYpwUgNEnjuA1rVnYJHXsFdWBrqrsz3UvdTs5kUxyoxjNNKvoXSaTeXMXEt1HUdmQ3sw1dW9wRkYdHwWzksM6n7P7MLnVY6qv3BVUpJiX4K355BXhMhyozzcBQX2vvyC7J8UxPBofMrBRVtbMsXmfp3sphos1pog6wpN2MiEaJqm6KK5yQguANnQzN8mK7MREkjYXtCnczf84CrcHqpp2onQUaR4TPn8zCPVAxY4HVkCoDWTwKj8Am9M4L3a7wmF37epgKnQuypTH7dqbJPRTALe7tndrtvJCuoTFP8wPXQXxvwnBPXeLmhK9E2mpskTA33KfqvVBu4R5SFYNtGoKbvuHaDf83Lf2xx1YPUogXuEWZMx5zcaHWMmvutpfdnPe3Rb7GL4hPVKj4t9MNgiAg3QbjaR9nqYBUPT4kUpxVCJWEadDVh5pgLwnkg4DJ5ArNfgH5"; + let process_tx_bytes = bs58::decode(process_tx_base58).into_vec().unwrap(); + let transaction: Transaction = bincode::deserialize(&process_tx_bytes).unwrap(); + + oracle.get_priority_fee(&transaction).await.unwrap(); + } + + #[test] + fn test_helius_get_priority_fee_estimate_options_ser() { + let get_oracle = |fee_level| { + HeliusPriorityFeeOracle::new(HeliusPriorityFeeOracleConfig { + url: url::Url::parse("http://localhost:8080").unwrap(), + fee_level, + }) + }; + + // When the fee level is Recommended, ensure `recommended` is set to true + let oracle = get_oracle(HeliusPriorityFeeLevel::Recommended); + + let options = oracle.get_priority_fee_estimate_options(); + let expected = serde_json::json!({ + "recommended": true, + "transactionEncoding": "base58", + }); + assert_eq!(options, expected); + + // When the fee level is not Recommended, ensure `priorityLevel` is set + let oracle = get_oracle(HeliusPriorityFeeLevel::Medium); + + let options = oracle.get_priority_fee_estimate_options(); + let expected = serde_json::json!({ + "priorityLevel": "Medium", + "transactionEncoding": "base58", + }); + assert_eq!(options, expected); + + // Ensure the serialization of HeliusPriorityFeeLevel is PascalCase, + // as required by the API https://docs.helius.dev/solana-apis/priority-fee-api#helius-priority-fee-api + let serialized = serde_json::json!([ + HeliusPriorityFeeLevel::Recommended, + HeliusPriorityFeeLevel::Min, + HeliusPriorityFeeLevel::Low, + HeliusPriorityFeeLevel::Medium, + HeliusPriorityFeeLevel::High, + HeliusPriorityFeeLevel::VeryHigh, + HeliusPriorityFeeLevel::UnsafeMax, + ]); + let expected = serde_json::json!([ + "Recommended", + "Min", + "Low", + "Medium", + "High", + "VeryHigh", + "UnsafeMax" + ]); + assert_eq!(serialized, expected); + } + + #[test] + fn test_helius_get_priority_fee_estimate_deser() { + let text = r#"{"jsonrpc":"2.0","result":{"priorityFeeEstimate":1000.0},"id":"1"}"#; + let response: JsonRpcResult = + serde_json::from_str(text).unwrap(); + + let expected = GetPriorityFeeEstimateResult { + priority_fee_estimate: 1000.0, + }; + assert_eq!(response.result, expected); + } +} diff --git a/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs b/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs index 59adb671f3..22b44497c8 100644 --- a/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs +++ b/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs @@ -2,12 +2,18 @@ use base64::Engine; use borsh::{BorshDeserialize, BorshSerialize}; use serializable_account_meta::{SerializableAccountMeta, SimulationReturnData}; use solana_client::{ - nonblocking::rpc_client::RpcClient, rpc_config::RpcBlockConfig, - rpc_config::RpcProgramAccountsConfig, rpc_config::RpcTransactionConfig, rpc_response::Response, + nonblocking::rpc_client::RpcClient, + rpc_client::SerializableTransaction, + rpc_config::{ + RpcBlockConfig, RpcProgramAccountsConfig, RpcSendTransactionConfig, + RpcSimulateTransactionConfig, RpcTransactionConfig, + }, + rpc_response::{Response, RpcSimulateTransactionResult}, }; use solana_sdk::{ account::Account, commitment_config::CommitmentConfig, + compute_budget::ComputeBudgetInstruction, hash::Hash, instruction::{AccountMeta, Instruction}, message::Message, @@ -17,16 +23,27 @@ use solana_sdk::{ }; use solana_transaction_status::{ EncodedConfirmedTransactionWithStatusMeta, TransactionStatus, UiConfirmedBlock, - UiReturnDataEncoding, UiTransactionEncoding, UiTransactionReturnData, + UiReturnDataEncoding, UiTransactionEncoding, }; use hyperlane_core::{ChainCommunicationError, ChainResult, U256}; -use crate::error::HyperlaneSealevelError; +use crate::{ + error::HyperlaneSealevelError, priority_fee::PriorityFeeOracle, + tx_submitter::TransactionSubmitter, +}; + +pub struct SealevelTxCostEstimate { + compute_units: u32, + compute_unit_price_micro_lamports: u64, +} pub struct SealevelRpcClient(RpcClient); impl SealevelRpcClient { + /// The max amount of compute units for a transaction. + const MAX_COMPUTE_UNITS: u32 = 1_400_000; + pub fn new(rpc_endpoint: String) -> Self { Self(RpcClient::new_with_commitment( rpc_endpoint, @@ -199,16 +216,74 @@ impl SealevelRpcClient { .map_err(ChainCommunicationError::from_other) } - pub async fn send_and_confirm_transaction( + pub async fn send_transaction( &self, transaction: &Transaction, + skip_preflight: bool, ) -> ChainResult { self.0 - .send_and_confirm_transaction(transaction) + .send_transaction_with_config( + transaction, + RpcSendTransactionConfig { + skip_preflight, + ..Default::default() + }, + ) .await .map_err(ChainCommunicationError::from_other) } + /// Polls the RPC until the transaction is confirmed or the blockhash + /// expires. + /// Standalone logic stolen from Solana's non-blocking client, + /// decoupled from the sending of a transaction. + pub async fn wait_for_transaction_confirmation( + &self, + transaction: &impl SerializableTransaction, + ) -> ChainResult<()> { + let signature = transaction.get_signature(); + + const GET_STATUS_RETRIES: usize = usize::MAX; + + let recent_blockhash = if transaction.uses_durable_nonce() { + self.get_latest_blockhash_with_commitment(CommitmentConfig::processed()) + .await? + } else { + *transaction.get_recent_blockhash() + }; + + for status_retry in 0..GET_STATUS_RETRIES { + let signature_statuses: Response>> = + self.get_signature_statuses(&[*signature]).await?; + let signature_status = signature_statuses.value.first().cloned().flatten(); + match signature_status { + Some(_) => return Ok(()), + None => { + if !self.is_blockhash_valid(&recent_blockhash).await? { + // Block hash is not found by some reason + break; + } else if cfg!(not(test)) + // Ignore sleep at last step. + && status_retry < GET_STATUS_RETRIES + { + // Retry twice a second + tokio::time::sleep(std::time::Duration::from_millis(500)).await; + continue; + } + } + } + } + + Err(ChainCommunicationError::from_other( + solana_client::rpc_request::RpcError::ForUser( + "unable to confirm transaction. \ + This can happen in situations such as transaction expiration \ + and insufficient fee-payer funds" + .to_string(), + ), + )) + } + /// Simulates an instruction, and attempts to deserialize it into a T. /// If no return data at all was returned, returns Ok(None). /// If some return data was returned but deserialization was unsuccessful, @@ -227,9 +302,9 @@ impl SealevelRpcClient { Some(&payer.pubkey()), &recent_blockhash, )); - let return_data = self.simulate_transaction(&transaction).await?; + let simulation = self.simulate_transaction(&transaction).await?; - if let Some(return_data) = return_data { + if let Some(return_data) = simulation.return_data { let bytes = match return_data.data.1 { UiReturnDataEncoding::Base64 => base64::engine::general_purpose::STANDARD .decode(return_data.data.0) @@ -245,19 +320,176 @@ impl SealevelRpcClient { Ok(None) } - async fn simulate_transaction( + pub async fn simulate_transaction( &self, transaction: &Transaction, - ) -> ChainResult> { - let return_data = self + ) -> ChainResult { + let result = self .0 - .simulate_transaction(transaction) + .simulate_transaction_with_config( + transaction, + RpcSimulateTransactionConfig { + sig_verify: false, + replace_recent_blockhash: true, + ..Default::default() + }, + ) .await .map_err(ChainCommunicationError::from_other)? - .value - .return_data; + .value; + + Ok(result) + } + + /// Gets the estimated costs for a given instruction. + pub async fn get_estimated_costs_for_instruction( + &self, + instruction: Instruction, + payer: &Keypair, + tx_submitter: &dyn TransactionSubmitter, + priority_fee_oracle: &dyn PriorityFeeOracle, + ) -> ChainResult { + // Build a transaction that sets the max compute units and a dummy compute unit price. + // This is used for simulation to get the actual compute unit limit. We set dummy values + // for the compute unit limit and price because we want to include the instructions that + // set these in the cost estimate. + let simulation_tx = self + .create_transaction_for_instruction( + Self::MAX_COMPUTE_UNITS, + 0, + instruction.clone(), + payer, + tx_submitter, + false, + ) + .await?; + + let simulation_result = self.simulate_transaction(&simulation_tx).await?; + + // If there was an error in the simulation result, return an error. + if simulation_result.err.is_some() { + tracing::error!(?simulation_result, "Got simulation result for transaction"); + return Err(ChainCommunicationError::from_other_str( + format!("Error in simulation result: {:?}", simulation_result.err).as_str(), + )); + } else { + tracing::debug!(?simulation_result, "Got simulation result for transaction"); + } + + // Get the compute units used in the simulation result, requiring + // that it is greater than 0. + let simulation_compute_units: u32 = simulation_result + .units_consumed + .unwrap_or_default() + .try_into() + .map_err(ChainCommunicationError::from_other)?; + if simulation_compute_units == 0 { + return Err(ChainCommunicationError::from_other_str( + "Empty or zero compute units returned in simulation result", + )); + } + + // Bump the compute units by 10% to ensure we have enough, but cap it at the max. + let simulation_compute_units = + Self::MAX_COMPUTE_UNITS.min((simulation_compute_units * 11) / 10); + + let priority_fee = priority_fee_oracle.get_priority_fee(&simulation_tx).await?; + + Ok(SealevelTxCostEstimate { + compute_units: simulation_compute_units, + compute_unit_price_micro_lamports: priority_fee, + }) + } + + /// Builds a transaction with estimated costs for a given instruction. + pub async fn build_estimated_tx_for_instruction( + &self, + instruction: Instruction, + payer: &Keypair, + tx_submitter: &dyn TransactionSubmitter, + priority_fee_oracle: &dyn PriorityFeeOracle, + ) -> ChainResult { + // Get the estimated costs for the instruction. + let SealevelTxCostEstimate { + compute_units, + compute_unit_price_micro_lamports, + } = self + .get_estimated_costs_for_instruction( + instruction.clone(), + payer, + tx_submitter, + priority_fee_oracle, + ) + .await?; + + tracing::info!( + ?compute_units, + ?compute_unit_price_micro_lamports, + "Got compute units and compute unit price / priority fee for transaction" + ); + + // Build the final transaction with the correct compute unit limit and price. + let tx = self + .create_transaction_for_instruction( + compute_units, + compute_unit_price_micro_lamports, + instruction, + payer, + tx_submitter, + true, + ) + .await?; + + Ok(tx) + } + + /// Creates a transaction for a given instruction, compute unit limit, and compute unit price. + /// If `sign` is true, the transaction will be signed. + pub async fn create_transaction_for_instruction( + &self, + compute_unit_limit: u32, + compute_unit_price_micro_lamports: u64, + instruction: Instruction, + payer: &Keypair, + tx_submitter: &dyn TransactionSubmitter, + sign: bool, + ) -> ChainResult { + let instructions = vec![ + // Set the compute unit limit. + ComputeBudgetInstruction::set_compute_unit_limit(compute_unit_limit), + // Set the priority fee / tip + tx_submitter.get_priority_fee_instruction( + compute_unit_price_micro_lamports, + compute_unit_limit.into(), + &payer.pubkey(), + ), + instruction, + ]; + + let tx = if sign { + // Getting the finalized blockhash eliminates the chance the blockhash + // gets reorged out, causing the tx to be invalid. The tradeoff is this + // will cause the tx to expire in about 47 seconds (instead of the typical 60). + let recent_blockhash = self + .get_latest_blockhash_with_commitment(CommitmentConfig::finalized()) + .await + .map_err(ChainCommunicationError::from_other)?; + + Transaction::new_signed_with_payer( + &instructions, + Some(&payer.pubkey()), + &[payer], + recent_blockhash, + ) + } else { + Transaction::new_unsigned(Message::new(&instructions, Some(&payer.pubkey()))) + }; + + Ok(tx) + } - Ok(return_data) + pub fn url(&self) -> String { + self.0.url() } } diff --git a/rust/main/chains/hyperlane-sealevel/src/trait_builder.rs b/rust/main/chains/hyperlane-sealevel/src/trait_builder.rs index e0b7c5cb37..13cc0b90a4 100644 --- a/rust/main/chains/hyperlane-sealevel/src/trait_builder.rs +++ b/rust/main/chains/hyperlane-sealevel/src/trait_builder.rs @@ -1,6 +1,12 @@ use hyperlane_core::{config::OperationBatchConfig, ChainCommunicationError, NativeToken}; +use serde::Serialize; use url::Url; +use crate::{ + priority_fee::{ConstantPriorityFeeOracle, HeliusPriorityFeeOracle, PriorityFeeOracle}, + tx_submitter::{JitoTransactionSubmitter, RpcTransactionSubmitter, TransactionSubmitter}, +}; + /// Sealevel connection configuration #[derive(Debug, Clone)] pub struct ConnectionConf { @@ -10,6 +16,10 @@ pub struct ConnectionConf { pub operation_batch: OperationBatchConfig, /// Native token and its denomination pub native_token: NativeToken, + /// Priority fee oracle configuration + pub priority_fee_oracle: PriorityFeeOracleConfig, + /// Transaction submitter configuration + pub transaction_submitter: TransactionSubmitterConfig, } /// An error type when parsing a connection configuration. @@ -23,6 +33,106 @@ pub enum ConnectionConfError { InvalidConnectionUrl(String, url::ParseError), } +/// Configuration to of how the priority fee should be determined +#[derive(Debug, Clone)] +pub enum PriorityFeeOracleConfig { + /// A constant value, in micro lamports + Constant(u64), + /// A Helius priority fee oracle + Helius(HeliusPriorityFeeOracleConfig), +} + +impl Default for PriorityFeeOracleConfig { + fn default() -> Self { + PriorityFeeOracleConfig::Constant(0) + } +} + +impl PriorityFeeOracleConfig { + /// Create a new priority fee oracle from the configuration + pub fn create_oracle(&self) -> Box { + match self { + PriorityFeeOracleConfig::Constant(fee) => { + Box::new(ConstantPriorityFeeOracle::new(*fee)) + } + PriorityFeeOracleConfig::Helius(config) => { + Box::new(HeliusPriorityFeeOracle::new(config.clone())) + } + } + } +} + +/// Configuration for the Helius priority fee oracle +#[derive(Debug, Clone)] +pub struct HeliusPriorityFeeOracleConfig { + /// The Helius URL to use + pub url: Url, + /// The fee level to use + pub fee_level: HeliusPriorityFeeLevel, +} + +/// The priority fee level to use +#[derive(Debug, Clone, Serialize, Default)] +pub enum HeliusPriorityFeeLevel { + /// 50th percentile, but a floor of 10k microlamports. + /// The floor results in a staked Helius connection being used. (https://docs.helius.dev/guides/sending-transactions-on-solana#staked-connections) + #[default] + Recommended, + /// 0th percentile + Min, + /// 10th percentile + Low, + /// 50th percentile + Medium, + /// 75th percentile + High, + /// 90th percentile + VeryHigh, + /// 100th percentile + UnsafeMax, +} + +/// Configuration for the transaction submitter +#[derive(Debug, Clone)] +pub enum TransactionSubmitterConfig { + /// Use the RPC transaction submitter + Rpc { + /// The URL to use. If not provided, a default RPC URL will be used + url: Option, + }, + /// Use the Jito transaction submitter + Jito { + /// The URL to use. If not provided, a default Jito URL will be used + url: Option, + }, +} + +impl Default for TransactionSubmitterConfig { + fn default() -> Self { + TransactionSubmitterConfig::Rpc { url: None } + } +} + +impl TransactionSubmitterConfig { + /// Create a new transaction submitter from the configuration + pub fn create_submitter(&self, default_rpc_url: String) -> Box { + match self { + TransactionSubmitterConfig::Rpc { url } => Box::new(RpcTransactionSubmitter::new( + url.clone().unwrap_or(default_rpc_url), + )), + TransactionSubmitterConfig::Jito { url } => { + // Default to a bundle-only URL (i.e. revert protected) + Box::new(JitoTransactionSubmitter::new(url.clone().unwrap_or_else( + || { + "https://mainnet.block-engine.jito.wtf/api/v1/transactions?bundleOnly=true" + .to_string() + }, + ))) + } + } + } +} + #[derive(thiserror::Error, Debug)] #[error(transparent)] struct SealevelNewConnectionError(#[from] anyhow::Error); diff --git a/rust/main/chains/hyperlane-sealevel/src/tx_submitter.rs b/rust/main/chains/hyperlane-sealevel/src/tx_submitter.rs new file mode 100644 index 0000000000..c7468d0f24 --- /dev/null +++ b/rust/main/chains/hyperlane-sealevel/src/tx_submitter.rs @@ -0,0 +1,115 @@ +use async_trait::async_trait; +use hyperlane_core::ChainResult; +use solana_sdk::{ + compute_budget::ComputeBudgetInstruction, instruction::Instruction, pubkey::Pubkey, + signature::Signature, transaction::Transaction, +}; + +use crate::SealevelRpcClient; + +/// A trait for submitting transactions to the chain. +#[async_trait] +pub trait TransactionSubmitter: Send + Sync { + /// Get the instruction to set the compute unit price. + fn get_priority_fee_instruction( + &self, + compute_unit_price_micro_lamports: u64, + compute_units: u64, + payer: &Pubkey, + ) -> Instruction; + + /// Send a transaction to the chain. + async fn send_transaction( + &self, + transaction: &Transaction, + skip_preflight: bool, + ) -> ChainResult; +} + +/// A transaction submitter that uses the vanilla RPC to submit transactions. +#[derive(Debug)] +pub struct RpcTransactionSubmitter { + rpc_client: SealevelRpcClient, +} + +impl RpcTransactionSubmitter { + pub fn new(url: String) -> Self { + Self { + rpc_client: SealevelRpcClient::new(url), + } + } +} + +#[async_trait] +impl TransactionSubmitter for RpcTransactionSubmitter { + fn get_priority_fee_instruction( + &self, + compute_unit_price_micro_lamports: u64, + _compute_units: u64, + _payer: &Pubkey, + ) -> Instruction { + ComputeBudgetInstruction::set_compute_unit_price(compute_unit_price_micro_lamports) + } + + async fn send_transaction( + &self, + transaction: &Transaction, + skip_preflight: bool, + ) -> ChainResult { + self.rpc_client + .send_transaction(transaction, skip_preflight) + .await + } +} + +/// A transaction submitter that uses the Jito API to submit transactions. +#[derive(Debug)] +pub struct JitoTransactionSubmitter { + rpc_client: SealevelRpcClient, +} + +impl JitoTransactionSubmitter { + /// The minimum tip to include in a transaction. + /// From https://docs.jito.wtf/lowlatencytxnsend/#sendtransaction + const MINIMUM_TIP_LAMPORTS: u64 = 1000; + + pub fn new(url: String) -> Self { + Self { + rpc_client: SealevelRpcClient::new(url), + } + } +} + +#[async_trait] +impl TransactionSubmitter for JitoTransactionSubmitter { + fn get_priority_fee_instruction( + &self, + compute_unit_price_micro_lamports: u64, + compute_units: u64, + payer: &Pubkey, + ) -> Instruction { + // Divide by 1_000_000 to convert from microlamports to lamports. + let tip_lamports = (compute_units * compute_unit_price_micro_lamports) / 1_000_000; + let tip_lamports = tip_lamports.max(Self::MINIMUM_TIP_LAMPORTS); + + // The tip is a standalone transfer to a Jito fee account. + // See https://github.com/jito-labs/mev-protos/blob/master/json_rpc/http.md#sendbundle. + solana_sdk::system_instruction::transfer( + payer, + // A random Jito fee account, taken from the getFeeAccount RPC response: + // https://github.com/jito-labs/mev-protos/blob/master/json_rpc/http.md#gettipaccounts + &solana_sdk::pubkey!("DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh"), + tip_lamports, + ) + } + + async fn send_transaction( + &self, + transaction: &Transaction, + skip_preflight: bool, + ) -> ChainResult { + self.rpc_client + .send_transaction(transaction, skip_preflight) + .await + } +} diff --git a/rust/main/config/mainnet_config.json b/rust/main/config/mainnet_config.json index 5b465b90cf..e7704ff783 100644 --- a/rust/main/config/mainnet_config.json +++ b/rust/main/config/mainnet_config.json @@ -785,7 +785,7 @@ "index": { "from": 1, "mode": "sequence", - "chunk": 100 + "chunk": 20 }, "interchainGasPaymaster": "ABb3i11z7wKoGCfeRQNQbVYWjAm7jG7HzZnDLV4RKRbK", "mailbox": "EitxJuv2iBjsg2d7jVy2LDC1e2zBrx4GB5Y9h2Ko3A9Y", @@ -2762,7 +2762,8 @@ "gasCurrencyCoinGeckoId": "sei-network", "gnosisSafeTransactionServiceUrl": "https://transaction.sei-safe.protofire.io", "index": { - "from": 80809403 + "from": 80809403, + "chunk": 1000 }, "interchainAccountIsm": "0xf35dc7B9eE4Ebf0cd3546Bd6EE3b403dE2b9F5D6", "interchainAccountRouter": "0xBcaedE97a98573A88242B3b0CB0A255F3f90d4d5", @@ -2828,7 +2829,7 @@ "index": { "from": 1, "mode": "sequence", - "chunk": 100 + "chunk": 20 }, "interchainGasPaymaster": "JAvHW21tYXE9dtdG83DReqU2b4LUexFuCbtJT5tF8X6M", "mailbox": "E588QtVUvresuXq2KoNEwAmoifCzYGpRBdHByN9KQMbi", diff --git a/rust/main/helm/hyperlane-agent/templates/external-secret.yaml b/rust/main/helm/hyperlane-agent/templates/external-secret.yaml index 9be0700b75..f3a6980d21 100644 --- a/rust/main/helm/hyperlane-agent/templates/external-secret.yaml +++ b/rust/main/helm/hyperlane-agent/templates/external-secret.yaml @@ -30,6 +30,14 @@ spec: {{- if eq .protocol "cosmos" }} HYP_CHAINS_{{ .name | upper }}_CUSTOMGRPCURLS: {{ printf "'{{ .%s_grpcs | mustFromJson | join \",\" }}'" .name }} {{- end }} + {{- if eq .protocol "sealevel" }} + {{- if eq ((.priorityFeeOracle).type) "helius" }} + HYP_CHAINS_{{ .name | upper }}_PRIORITYFEEORACLE_URL: {{ printf "'{{ .%s_helius }}'" .name }} + {{- end }} + {{- if eq ((.transactionSubmitter).url) "helius" }} + HYP_CHAINS_{{ .name | upper }}_TRANSACTIONSUBMITTER_URL: {{ printf "'{{ .%s_helius }}'" .name }} + {{- end }} + {{- end }} {{- end }} data: {{- /* @@ -45,4 +53,9 @@ spec: remoteRef: key: {{ printf "%s-grpc-endpoints-%s" $.Values.hyperlane.runEnv .name }} {{- end }} + {{- if and (eq .protocol "sealevel") (or (eq ((.priorityFeeOracle).type) "helius") (eq ((.transactionSubmitter).url) "helius")) }} + - secretKey: {{ printf "%s_helius" .name }} + remoteRef: + key: {{ printf "%s-rpc-endpoint-helius-%s" $.Values.hyperlane.runEnv .name }} + {{- end }} {{- end }} diff --git a/rust/main/helm/hyperlane-agent/templates/relayer-statefulset.yaml b/rust/main/helm/hyperlane-agent/templates/relayer-statefulset.yaml index da69543d8e..47be9ad2e9 100644 --- a/rust/main/helm/hyperlane-agent/templates/relayer-statefulset.yaml +++ b/rust/main/helm/hyperlane-agent/templates/relayer-statefulset.yaml @@ -17,7 +17,9 @@ spec: metadata: annotations: checksum/configmap: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + checksum/external-secret: {{ include (print $.Template.BasePath "/external-secret.yaml") . | sha256sum }} checksum/relayer-configmap: {{ include (print $.Template.BasePath "/relayer-configmap.yaml") . | sha256sum }} + checksum/relayer-external-secret: {{ include (print $.Template.BasePath "/relayer-external-secret.yaml") . | sha256sum }} {{- with .Values.podAnnotations }} {{- toYaml . | nindent 8 }} {{- end }} diff --git a/rust/main/helm/hyperlane-agent/templates/scraper-statefulset.yaml b/rust/main/helm/hyperlane-agent/templates/scraper-statefulset.yaml index 06326e260c..1b419e1123 100644 --- a/rust/main/helm/hyperlane-agent/templates/scraper-statefulset.yaml +++ b/rust/main/helm/hyperlane-agent/templates/scraper-statefulset.yaml @@ -17,6 +17,8 @@ spec: metadata: annotations: checksum/configmap: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + checksum/external-secret: {{ include (print $.Template.BasePath "/external-secret.yaml") . | sha256sum }} + checksum/scraper-external-secret: {{ include (print $.Template.BasePath "/scraper-external-secret.yaml") . | sha256sum }} {{- with .Values.podAnnotations }} {{- toYaml . | nindent 8 }} {{- end }} diff --git a/rust/main/helm/hyperlane-agent/templates/validator-statefulset.yaml b/rust/main/helm/hyperlane-agent/templates/validator-statefulset.yaml index 1b0a87dd41..b5929bfd99 100644 --- a/rust/main/helm/hyperlane-agent/templates/validator-statefulset.yaml +++ b/rust/main/helm/hyperlane-agent/templates/validator-statefulset.yaml @@ -17,7 +17,9 @@ spec: metadata: annotations: checksum/configmap: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + checksum/external-secret: {{ include (print $.Template.BasePath "/external-secret.yaml") . | sha256sum }} checksum/validator-configmap: {{ include (print $.Template.BasePath "/validator-configmap.yaml") . | sha256sum }} + checksum/scraper-external-secret: {{ include (print $.Template.BasePath "/scraper-external-secret.yaml") . | sha256sum }} {{- with .Values.podAnnotations }} {{- toYaml . | nindent 8 }} {{- end }} diff --git a/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs b/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs index 70e1b81835..b3f91ee88e 100644 --- a/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs +++ b/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs @@ -1,4 +1,7 @@ use eyre::eyre; +use hyperlane_sealevel::{ + HeliusPriorityFeeLevel, HeliusPriorityFeeOracleConfig, PriorityFeeOracleConfig, +}; use url::Url; use h_eth::TransactionOverrides; @@ -162,12 +165,24 @@ fn build_sealevel_connection_conf( chain: &ValueParser, err: &mut ConfigParsingError, operation_batch: OperationBatchConfig, -) -> h_sealevel::ConnectionConf { +) -> Option { + let mut local_err = ConfigParsingError::default(); + let native_token = parse_native_token(chain, err, 9); - h_sealevel::ConnectionConf { - url: url.clone(), - operation_batch, - native_token, + let priority_fee_oracle = parse_sealevel_priority_fee_oracle_config(chain, &mut local_err); + let transaction_submitter = parse_transaction_submitter_config(chain, &mut local_err); + + if !local_err.is_ok() { + err.merge(local_err); + None + } else { + Some(ChainConnectionConf::Sealevel(h_sealevel::ConnectionConf { + url: url.clone(), + operation_batch, + native_token, + priority_fee_oracle: priority_fee_oracle.unwrap(), + transaction_submitter: transaction_submitter.unwrap(), + })) } } @@ -196,6 +211,147 @@ fn parse_native_token( } } +fn parse_sealevel_priority_fee_oracle_config( + chain: &ValueParser, + err: &mut ConfigParsingError, +) -> Option { + let value_parser = chain.chain(err).get_opt_key("priorityFeeOracle").end(); + + let priority_fee_oracle = if let Some(value_parser) = value_parser { + let oracle_type = value_parser + .chain(err) + .get_key("type") + .parse_string() + .end() + .or_else(|| { + err.push( + &value_parser.cwp + "type", + eyre!("Missing priority fee oracle type"), + ); + None + }) + .unwrap_or_default(); + + match oracle_type { + "constant" => { + let fee = value_parser + .chain(err) + .get_key("fee") + .parse_u64() + .end() + .unwrap_or(0); + Some(PriorityFeeOracleConfig::Constant(fee)) + } + "helius" => { + let fee_level = parse_helius_priority_fee_level(&value_parser, err); + if !err.is_ok() { + return None; + } + let config = HeliusPriorityFeeOracleConfig { + url: value_parser + .chain(err) + .get_key("url") + .parse_from_str("Invalid url") + .end() + .unwrap(), + fee_level: fee_level.unwrap(), + }; + Some(PriorityFeeOracleConfig::Helius(config)) + } + _ => { + err.push( + &value_parser.cwp + "type", + eyre!("Unknown priority fee oracle type"), + ); + None + } + } + } else { + // If not specified at all, use default + Some(PriorityFeeOracleConfig::default()) + }; + + priority_fee_oracle +} + +fn parse_helius_priority_fee_level( + value_parser: &ValueParser, + err: &mut ConfigParsingError, +) -> Option { + let level = value_parser + .chain(err) + .get_opt_key("feeLevel") + .parse_string() + .end(); + + if let Some(level) = level { + match level.to_lowercase().as_str() { + "recommended" => Some(HeliusPriorityFeeLevel::Recommended), + "min" => Some(HeliusPriorityFeeLevel::Min), + "low" => Some(HeliusPriorityFeeLevel::Low), + "medium" => Some(HeliusPriorityFeeLevel::Medium), + "high" => Some(HeliusPriorityFeeLevel::High), + "veryhigh" => Some(HeliusPriorityFeeLevel::VeryHigh), + "unsafemax" => Some(HeliusPriorityFeeLevel::UnsafeMax), + _ => { + err.push( + &value_parser.cwp + "feeLevel", + eyre!("Unknown priority fee level"), + ); + None + } + } + } else { + // If not specified at all, use the default + Some(HeliusPriorityFeeLevel::default()) + } +} + +fn parse_transaction_submitter_config( + chain: &ValueParser, + err: &mut ConfigParsingError, +) -> Option { + let submitter_type = chain + .chain(err) + .get_opt_key("transactionSubmitter") + .get_opt_key("type") + .parse_string() + .end(); + + if let Some(submitter_type) = submitter_type { + match submitter_type.to_lowercase().as_str() { + "rpc" => { + let url = chain + .chain(err) + .get_opt_key("transactionSubmitter") + .get_opt_key("url") + .parse_from_str("Invalid url") + .end(); + Some(h_sealevel::TransactionSubmitterConfig::Rpc { url }) + } + "jito" => { + let url = chain + .chain(err) + .get_opt_key("transactionSubmitter") + .get_opt_key("url") + .parse_from_str("Invalid url") + .end(); + Some(h_sealevel::TransactionSubmitterConfig::Jito { url }) + } + _ => { + err.push( + &chain.cwp + "transactionSubmitter.type", + eyre!("Unknown transaction submitter type"), + ); + None + } + } + } else { + // If not specified at all, use default + Some(h_sealevel::TransactionSubmitterConfig::default()) + } +} + pub fn build_connection_conf( domain_protocol: HyperlaneDomainProtocol, rpcs: &[Url], @@ -216,14 +372,10 @@ pub fn build_connection_conf( .iter() .next() .map(|url| ChainConnectionConf::Fuel(h_fuel::ConnectionConf { url: url.clone() })), - HyperlaneDomainProtocol::Sealevel => rpcs.iter().next().map(|url| { - ChainConnectionConf::Sealevel(build_sealevel_connection_conf( - url, - chain, - err, - operation_batch, - )) - }), + HyperlaneDomainProtocol::Sealevel => rpcs + .iter() + .next() + .and_then(|url| build_sealevel_connection_conf(url, chain, err, operation_batch)), HyperlaneDomainProtocol::Cosmos => { build_cosmos_connection_conf(rpcs, chain, err, operation_batch) } diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index 3756344809..f81a40bea3 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -1,4 +1,10 @@ import { + AgentSealevelHeliusFeeLevel, + AgentSealevelPriorityFeeOracle, + AgentSealevelPriorityFeeOracleType, + AgentSealevelTransactionSubmitter, + AgentSealevelTransactionSubmitterType, + ChainName, GasPaymentEnforcement, GasPaymentEnforcementPolicyType, MatchingList, @@ -7,6 +13,7 @@ import { import { AgentChainConfig, + HELIUS_SECRET_URL_MARKER, RootAgentConfig, getAgentChainNamesFromConfig, } from '../../../src/config/agent/agent.js'; @@ -396,6 +403,43 @@ export const hyperlaneContextAgentChainNames = getAgentChainNamesFromConfig( mainnet3SupportedChainNames, ); +const sealevelPriorityFeeOracleConfigGetter = ( + chain: ChainName, +): AgentSealevelPriorityFeeOracle => { + // Special case for Solana mainnet + if (chain === 'solanamainnet') { + return { + type: AgentSealevelPriorityFeeOracleType.Helius, + feeLevel: AgentSealevelHeliusFeeLevel.Recommended, + // URL is auto populated by the external secrets in the helm chart + url: '', + }; + } + + // For all other chains, we use the constant fee oracle with a fee of 0 + return { + type: AgentSealevelPriorityFeeOracleType.Constant, + fee: '0', + }; +}; + +const sealevelTransactionSubmitterConfigGetter = ( + chain: ChainName, +): AgentSealevelTransactionSubmitter => { + // Special case for Solana mainnet + if (chain === 'solanamainnet') { + return { + type: AgentSealevelTransactionSubmitterType.Rpc, + url: HELIUS_SECRET_URL_MARKER, + }; + } + + // For all other chains, use the default RPC transaction submitter + return { + type: AgentSealevelTransactionSubmitterType.Rpc, + }; +}; + const contextBase = { namespace: environment, runEnv: environment, @@ -403,6 +447,10 @@ const contextBase = { aws: { region: 'us-east-1', }, + sealevel: { + priorityFeeOracleConfigGetter: sealevelPriorityFeeOracleConfigGetter, + transactionSubmitterConfigGetter: sealevelTransactionSubmitterConfigGetter, + }, } as const; const gasPaymentEnforcement: GasPaymentEnforcement[] = [ diff --git a/typescript/infra/src/agents/index.ts b/typescript/infra/src/agents/index.ts index b7be0e0098..24fccbac07 100644 --- a/typescript/infra/src/agents/index.ts +++ b/typescript/infra/src/agents/index.ts @@ -1,8 +1,14 @@ import fs from 'fs'; import { join } from 'path'; -import { ChainName, RelayerConfig, RpcConsensusType } from '@hyperlane-xyz/sdk'; -import { objOmitKeys } from '@hyperlane-xyz/utils'; +import { + AgentSealevelPriorityFeeOracle, + AgentSealevelTransactionSubmitter, + ChainName, + RelayerConfig, + RpcConsensusType, +} from '@hyperlane-xyz/sdk'; +import { ProtocolType, objOmitKeys } from '@hyperlane-xyz/utils'; import { Contexts } from '../../config/contexts.js'; import { getChain } from '../../config/registry.js'; @@ -87,12 +93,33 @@ export abstract class AgentHelmManager extends HelmManager if (reorgPeriod === undefined) { throw new Error(`No reorg period found for chain ${chain}`); } + + let priorityFeeOracle: AgentSealevelPriorityFeeOracle | undefined; + if (getChain(chain).protocol === ProtocolType.Sealevel) { + priorityFeeOracle = + this.config.rawConfig.sealevel?.priorityFeeOracleConfigGetter?.( + chain, + ); + } + + let transactionSubmitter: + | AgentSealevelTransactionSubmitter + | undefined; + if (getChain(chain).protocol === ProtocolType.Sealevel) { + transactionSubmitter = + this.config.rawConfig.sealevel?.transactionSubmitterConfigGetter?.( + chain, + ); + } + return { name: chain, rpcConsensusType: this.rpcConsensusType(chain), protocol: metadata.protocol, blocks: { reorgPeriod }, maxBatchSize: 32, + priorityFeeOracle, + transactionSubmitter, }; }), }, diff --git a/typescript/infra/src/config/agent/agent.ts b/typescript/infra/src/config/agent/agent.ts index 987a05f0ea..7d2dbe54b6 100644 --- a/typescript/infra/src/config/agent/agent.ts +++ b/typescript/infra/src/config/agent/agent.ts @@ -1,5 +1,7 @@ import { AgentChainMetadata, + AgentSealevelPriorityFeeOracle, + AgentSealevelTransactionSubmitter, AgentSignerAwsKey, AgentSignerKeyType, ChainName, @@ -85,8 +87,21 @@ export interface AgentContextConfig extends AgentEnvConfig { rolesWithKeys: Role[]; // Names of chains this context cares about (subset of environmentChainNames) contextChainNames: AgentChainNames; + sealevel?: SealevelAgentConfig; } +export interface SealevelAgentConfig { + priorityFeeOracleConfigGetter?: ( + chain: ChainName, + ) => AgentSealevelPriorityFeeOracle; + transactionSubmitterConfigGetter?: ( + chain: ChainName, + ) => AgentSealevelTransactionSubmitter; +} + +// An ugly way to mark a URL as a the secret Helius URL when Helm templating +export const HELIUS_SECRET_URL_MARKER = 'helius'; + // incomplete common agent configuration for a role interface AgentRoleConfig { // K8s-specific diff --git a/typescript/infra/src/utils/helm.ts b/typescript/infra/src/utils/helm.ts index 855393473a..4ed4cb478f 100644 --- a/typescript/infra/src/utils/helm.ts +++ b/typescript/infra/src/utils/helm.ts @@ -13,12 +13,13 @@ export enum HelmCommand { } export function helmifyValues(config: any, prefix?: string): string[] { + if (config === null || config === undefined) { + return []; + } + if (typeof config !== 'object') { // Helm incorrectly splits on unescaped commas. - const value = - config !== undefined - ? JSON.stringify(config).replaceAll(',', '\\,') - : undefined; + const value = JSON.stringify(config).replaceAll(',', '\\,'); return [`--set ${prefix}=${value}`]; } diff --git a/typescript/sdk/src/index.ts b/typescript/sdk/src/index.ts index dd94504fcd..87e524110e 100644 --- a/typescript/sdk/src/index.ts +++ b/typescript/sdk/src/index.ts @@ -209,6 +209,12 @@ export { AgentCosmosGasPrice, AgentLogFormat, AgentLogLevel, + AgentSealevelChainMetadata, + AgentSealevelHeliusFeeLevel, + AgentSealevelPriorityFeeOracle, + AgentSealevelPriorityFeeOracleType, + AgentSealevelTransactionSubmitter, + AgentSealevelTransactionSubmitterType, AgentSigner, AgentSignerAwsKey, AgentSignerHexKey, diff --git a/typescript/sdk/src/metadata/agentConfig.ts b/typescript/sdk/src/metadata/agentConfig.ts index cb570e29eb..9ff999c0dc 100644 --- a/typescript/sdk/src/metadata/agentConfig.ts +++ b/typescript/sdk/src/metadata/agentConfig.ts @@ -51,6 +51,26 @@ export enum AgentSignerKeyType { Cosmos = 'cosmosKey', } +export enum AgentSealevelPriorityFeeOracleType { + Helius = 'helius', + Constant = 'constant', +} + +export enum AgentSealevelHeliusFeeLevel { + Recommended = 'recommended', + Min = 'min', + Low = 'low', + Medium = 'medium', + High = 'high', + VeryHigh = 'veryHigh', + UnsafeMax = 'unsafeMax', +} + +export enum AgentSealevelTransactionSubmitterType { + Rpc = 'rpc', + Jito = 'jito', +} + const AgentSignerHexKeySchema = z .object({ type: z.literal(AgentSignerKeyType.Hex).optional(), @@ -120,6 +140,40 @@ export type AgentCosmosGasPrice = z.infer< typeof AgentCosmosChainMetadataSchema >['gasPrice']; +const AgentSealevelChainMetadataSchema = z.object({ + priorityFeeOracle: z + .union([ + z.object({ + type: z.literal(AgentSealevelPriorityFeeOracleType.Helius), + url: z.string(), + // TODO add options + feeLevel: z.nativeEnum(AgentSealevelHeliusFeeLevel), + }), + z.object({ + type: z.literal(AgentSealevelPriorityFeeOracleType.Constant), + // In microlamports + fee: ZUWei, + }), + ]) + .optional(), + transactionSubmitter: z + .object({ + type: z.nativeEnum(AgentSealevelTransactionSubmitterType), + url: z.string().optional(), + }) + .optional(), +}); + +export type AgentSealevelChainMetadata = z.infer< + typeof AgentSealevelChainMetadataSchema +>; + +export type AgentSealevelPriorityFeeOracle = + AgentSealevelChainMetadata['priorityFeeOracle']; + +export type AgentSealevelTransactionSubmitter = + AgentSealevelChainMetadata['transactionSubmitter']; + export const AgentChainMetadataSchema = ChainMetadataSchemaObject.merge( HyperlaneDeploymentArtifactsSchema, ) @@ -155,6 +209,7 @@ export const AgentChainMetadataSchema = ChainMetadataSchemaObject.merge( .optional(), }) .merge(AgentCosmosChainMetadataSchema.partial()) + .merge(AgentSealevelChainMetadataSchema.partial()) .refine((metadata) => { // Make sure that the signer is valid for the protocol @@ -201,6 +256,13 @@ export const AgentChainMetadataSchema = ChainMetadataSchemaObject.merge( } } + // If the protocol type is Sealevel, require everything in AgentSealevelChainMetadataSchema + if (metadata.protocol === ProtocolType.Sealevel) { + if (!AgentSealevelChainMetadataSchema.safeParse(metadata).success) { + return false; + } + } + return true; }); From 7dfcc60a96416088a4c21d192104e2ecad947787 Mon Sep 17 00:00:00 2001 From: Mohammed Hussan Date: Tue, 24 Dec 2024 14:20:32 +0000 Subject: [PATCH 17/37] feat(warpMonitor): Always use collateral token symbol for building warp routes id (#5045) ### Description - We are introducing a warp route where there are different symbols in the config (PumpBTC) - This will mean that metric scrapping will have different warp route ids for collateral and synthetic balances - This change ensures that the collateral symbol is used for the warp route id for both metrics ### Testing Manual --- .../scripts/warp-routes/monitor/metrics.ts | 3 +- .../monitor/monitor-warp-route-balances.ts | 51 ++++++++++++++++++- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/typescript/infra/scripts/warp-routes/monitor/metrics.ts b/typescript/infra/scripts/warp-routes/monitor/metrics.ts index 52c2604f96..4879415f60 100644 --- a/typescript/infra/scripts/warp-routes/monitor/metrics.ts +++ b/typescript/infra/scripts/warp-routes/monitor/metrics.ts @@ -59,6 +59,7 @@ export function updateTokenBalanceMetrics( warpCore: WarpCore, token: Token, balanceInfo: WarpRouteBalance, + collateralTokenSymbol: string, ) { const metrics: WarpRouteMetrics = { chain_name: token.chainName, @@ -67,7 +68,7 @@ export function updateTokenBalanceMetrics( wallet_address: token.addressOrDenom, token_standard: token.standard, warp_route_id: createWarpRouteConfigId( - token.symbol, + collateralTokenSymbol, warpCore.getTokenChains(), ), related_chain_names: warpCore diff --git a/typescript/infra/scripts/warp-routes/monitor/monitor-warp-route-balances.ts b/typescript/infra/scripts/warp-routes/monitor/monitor-warp-route-balances.ts index 4eecc1f736..c04ed7418c 100644 --- a/typescript/infra/scripts/warp-routes/monitor/monitor-warp-route-balances.ts +++ b/typescript/infra/scripts/warp-routes/monitor/monitor-warp-route-balances.ts @@ -89,12 +89,18 @@ async function pollAndUpdateWarpRouteMetrics( chainMetadata, apiKey: await getCoinGeckoApiKey(), }); + const collateralTokenSymbol = getWarpRouteCollateralTokenSymbol(warpCore); while (true) { await tryFn(async () => { await Promise.all( warpCore.tokens.map((token) => - updateTokenMetrics(warpCore, token, tokenPriceGetter), + updateTokenMetrics( + warpCore, + token, + tokenPriceGetter, + collateralTokenSymbol, + ), ), ); }, 'Updating warp route metrics'); @@ -107,6 +113,7 @@ async function updateTokenMetrics( warpCore: WarpCore, token: Token, tokenPriceGetter: CoinGeckoTokenPriceGetter, + collateralTokenSymbol: string, ) { const promises = [ tryFn(async () => { @@ -118,7 +125,12 @@ async function updateTokenMetrics( if (!balanceInfo) { return; } - updateTokenBalanceMetrics(warpCore, token, balanceInfo); + updateTokenBalanceMetrics( + warpCore, + token, + balanceInfo, + collateralTokenSymbol, + ); }, 'Getting bridged balance and value'), ]; @@ -310,6 +322,41 @@ async function getCoinGeckoApiKey(): Promise { return apiKey; } +function getWarpRouteCollateralTokenSymbol(warpCore: WarpCore): string { + // We need to have a deterministic way to determine the symbol of the warp route + // as its used to identify the warp route in metrics. This method should support routes where: + // - All tokens have the same symbol, token standards can be all collateral, all synthetic or a mix + // - All tokens have different symbol, but there is a collateral token to break the tie, where there are multiple collateral tokens, alphabetically first is chosen + // - All tokens have different symbol, but there is no collateral token to break the tie, pick the alphabetically first symbol + + // Get all unique symbols from the tokens array + const uniqueSymbols = new Set(warpCore.tokens.map((token) => token.symbol)); + + // If all tokens have the same symbol, return that symbol + if (uniqueSymbols.size === 1) { + return warpCore.tokens[0].symbol; + } + + // Find all collateralized tokens + const collateralTokens = warpCore.tokens.filter( + (token) => + token.isCollateralized() || + token.standard === TokenStandard.EvmHypXERC20Lockbox, + ); + + if (collateralTokens.length === 0) { + // If there are no collateralized tokens, return the alphabetically first symbol + return [...uniqueSymbols].sort()[0]; + } + + // if there is a single unique collateral symbol return it or + // ifthere are multiple, return the alphabetically first symbol + const collateralSymbols = collateralTokens.map((token) => token.symbol); + const uniqueCollateralSymbols = [...new Set(collateralSymbols)]; + + return uniqueCollateralSymbols.sort()[0]; +} + main().catch((err) => { logger.error('Error in main:', err); process.exit(1); From 98521723beef44a739833ffa06949c70bc699bcd Mon Sep 17 00:00:00 2001 From: Kunal Arora <55632507+aroralanuk@users.noreply.github.com> Date: Thu, 26 Dec 2024 19:12:18 +0530 Subject: [PATCH 18/37] feat(infra): warp monitor for pumpBTC warp route b/w ethereum<>sei (#5004) ### Description ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --------- Co-authored-by: Mo Hussan <22501692+Mo-Hussain@users.noreply.github.com> --- .../getEthereumSeiPumpBTCWarpConfig.ts | 42 +++++++++++++++++++ .../environments/mainnet3/warp/warpIds.ts | 1 + typescript/infra/config/warp.ts | 2 + typescript/infra/src/config/warp.ts | 1 + typescript/infra/src/warp/helm.ts | 2 +- 5 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumSeiPumpBTCWarpConfig.ts diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumSeiPumpBTCWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumSeiPumpBTCWarpConfig.ts new file mode 100644 index 0000000000..9d073230a8 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumSeiPumpBTCWarpConfig.ts @@ -0,0 +1,42 @@ +import { ethers } from 'ethers'; + +import { + ChainMap, + HypTokenRouterConfig, + OwnableConfig, + TokenType, +} from '@hyperlane-xyz/sdk'; + +import { getOwnerConfigForAddress } from '../../../../../src/config/environment.js'; +import { + RouterConfigWithoutOwner, + tokens, +} from '../../../../../src/config/warp.js'; + +const ethereumOwner = '0x77A0545Dc1Dc6bAee8d9c1d436c6688a75Ae5777'; +const seiOwner = '0x14A359aE2446eaC89495b3F28b7a29cE2A17f392'; + +export const getEthereumSeiPumpBTCWarpConfig = async ( + routerConfig: ChainMap, + _abacusWorksEnvOwnerConfig: ChainMap, +): Promise> => { + const ethereum: HypTokenRouterConfig = { + ...routerConfig.ethereum, + ...getOwnerConfigForAddress(ethereumOwner), + type: TokenType.collateral, + token: tokens.ethereum.pumpBTCsei, + interchainSecurityModule: ethers.constants.AddressZero, + }; + + const sei: HypTokenRouterConfig = { + ...routerConfig.sei, + ...getOwnerConfigForAddress(seiOwner), + type: TokenType.synthetic, + interchainSecurityModule: ethers.constants.AddressZero, + }; + + return { + ethereum, + sei, + }; +}; diff --git a/typescript/infra/config/environments/mainnet3/warp/warpIds.ts b/typescript/infra/config/environments/mainnet3/warp/warpIds.ts index d9bf87760d..1cd98d1c8d 100644 --- a/typescript/infra/config/environments/mainnet3/warp/warpIds.ts +++ b/typescript/infra/config/environments/mainnet3/warp/warpIds.ts @@ -22,6 +22,7 @@ export enum WarpRouteIds { EthereumInevmUSDC = 'USDC/ethereum-inevm', EthereumInevmUSDT = 'USDT/ethereum-inevm', EthereumSeiFastUSD = 'FASTUSD/ethereum-sei', + EthereumSeiPumpBTC = 'pumpBTCsei/ethereum-sei', EthereumVanaETH = 'ETH/ethereum-vana', EthereumVanaVANA = 'VANA/ethereum-vana', EthereumVictionETH = 'ETH/ethereum-viction', diff --git a/typescript/infra/config/warp.ts b/typescript/infra/config/warp.ts index c68e2221d9..d87a010be1 100644 --- a/typescript/infra/config/warp.ts +++ b/typescript/infra/config/warp.ts @@ -33,6 +33,7 @@ import { getEthereumFlowCbBTCWarpConfig } from './environments/mainnet3/warp/con import { getEthereumInevmUSDCWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumInevmUSDCWarpConfig.js'; import { getEthereumInevmUSDTWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumInevmUSDTWarpConfig.js'; import { getEthereumSeiFastUSDWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumSeiFastUSDWarpConfig.js'; +import { getEthereumSeiPumpBTCWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumSeiPumpBTCWarpConfig.js'; import { getEthereumVictionETHWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumVictionETHWarpConfig.js'; import { getEthereumVictionUSDCWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumVictionUSDCWarpConfig.js'; import { getEthereumVictionUSDTWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumVictionUSDTWarpConfig.js'; @@ -61,6 +62,7 @@ export const warpConfigGetterMap: Record = { [WarpRouteIds.InevmInjectiveINJ]: getInevmInjectiveINJWarpConfig, [WarpRouteIds.EthereumFlowCbBTC]: getEthereumFlowCbBTCWarpConfig, [WarpRouteIds.EthereumSeiFastUSD]: getEthereumSeiFastUSDWarpConfig, + [WarpRouteIds.EthereumSeiPumpBTC]: getEthereumSeiPumpBTCWarpConfig, [WarpRouteIds.EthereumVictionETH]: getEthereumVictionETHWarpConfig, [WarpRouteIds.EthereumVictionUSDC]: getEthereumVictionUSDCWarpConfig, [WarpRouteIds.EthereumVictionUSDT]: getEthereumVictionUSDTWarpConfig, diff --git a/typescript/infra/src/config/warp.ts b/typescript/infra/src/config/warp.ts index 2ceb1c8cb1..3a0ddb4110 100644 --- a/typescript/infra/src/config/warp.ts +++ b/typescript/infra/src/config/warp.ts @@ -12,6 +12,7 @@ export const tokens: ChainMap> = { USDT: '0xdac17f958d2ee523a2206206994597c13d831ec7', WBTC: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', weETHs: '0x917cee801a67f933f2e6b33fc0cd1ed2d5909d88', + pumpBTCsei: '0xe9ebd666954B7F0B5B044704c86B126651f6235d', Re7LRT: '0x84631c0d0081FDe56DeB72F6DE77abBbF6A9f93a', }, sei: { diff --git a/typescript/infra/src/warp/helm.ts b/typescript/infra/src/warp/helm.ts index 80c2f2353b..1d0f43585a 100644 --- a/typescript/infra/src/warp/helm.ts +++ b/typescript/infra/src/warp/helm.ts @@ -28,7 +28,7 @@ export class WarpRouteMonitorHelmManager extends HelmManager { return { image: { repository: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: '7e520fb-20241215-234731', + tag: '3cacafd-20241220-092417', }, warpRouteId: this.warpRouteId, fullnameOverride: this.helmReleaseName, From 18310b8caeb80531e3f021c5ebc597a56f6f00af Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Thu, 26 Dec 2024 15:29:39 +0000 Subject: [PATCH 19/37] fix: fix SVM scraper dispatch and delivery bugs (#5071) ### Description Context here https://discord.com/channels/935678348330434570/1320897742779977849 Fixes two bugs, adding tests for them: - Txs that had reverted with an error are no longer considered valid in the scraper's log meta population codepath. We ran into an instance where two attempted process txs landed in the same block. We'd get two potentially matching txs in the same block when indexing the process, which causes issues - Versioned txs were not supported. We ran into a versioned dispatch tx for the first time (https://eclipsescan.xyz/tx/Ku7gtQzEztksncHMHYkXq8PNst5rZsznrXMDmVqoQjc7SR4PsXz33qjNMckNcHriCxuhZJGZequo8moNsRP9GZK). The list of accounts wasn't populated correctly because some were dynamic (from an address lookup table). This caused us to think that the Mailbox wasn't being used in a tx, when it was. The solution here is to ensure the list is ordered correctly and has all the accounts, including the dynamic ones. ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- .../src/log_meta_composer.rs | 27 +- ...age_block_multiple_txs_one_successful.json | 3388 +++++++++++++++++ .../delivery_message_reverted_txn.json | 175 + .../dispatch_message_versioned_txn.json | 565 +++ .../src/log_meta_composer/tests.rs | 90 +- .../config/environments/mainnet3/agent.ts | 2 +- 6 files changed, 4243 insertions(+), 4 deletions(-) create mode 100644 rust/main/chains/hyperlane-sealevel/src/log_meta_composer/delivery_message_block_multiple_txs_one_successful.json create mode 100644 rust/main/chains/hyperlane-sealevel/src/log_meta_composer/delivery_message_reverted_txn.json create mode 100644 rust/main/chains/hyperlane-sealevel/src/log_meta_composer/dispatch_message_versioned_txn.json diff --git a/rust/main/chains/hyperlane-sealevel/src/log_meta_composer.rs b/rust/main/chains/hyperlane-sealevel/src/log_meta_composer.rs index f530b04e0f..5e3da84942 100644 --- a/rust/main/chains/hyperlane-sealevel/src/log_meta_composer.rs +++ b/rust/main/chains/hyperlane-sealevel/src/log_meta_composer.rs @@ -220,6 +220,11 @@ fn filter_by_validity( tx: UiTransaction, meta: UiTransactionStatusMeta, ) -> Option<(H512, Vec, Vec)> { + // If the transaction has an error, we skip it + if meta.err.is_some() { + return None; + } + let Some(transaction_hash) = tx .signatures .first() @@ -238,9 +243,29 @@ fn filter_by_validity( return None; }; + // Orders the account keys in line with the behavior of compiled instructions. + let account_keys = match &meta.loaded_addresses { + OptionSerializer::Some(addresses) => { + // If there are loaded addresses, we have a versioned transaction + // that may include dynamically loaded addresses (e.g. from a lookup table). + // The order of these is [static, dynamic writeable, dynamic readonly] and + // follows the iter ordering of https://docs.rs/solana-sdk/latest/solana_sdk/message/struct.AccountKeys.html. + [ + message.account_keys, + addresses.writable.clone(), + addresses.readonly.clone(), + ] + .concat() + } + OptionSerializer::None | OptionSerializer::Skip => { + // There are only static addresses in the transaction. + message.account_keys + } + }; + let instructions = instructions(message.instructions, meta); - Some((transaction_hash, message.account_keys, instructions)) + Some((transaction_hash, account_keys, instructions)) } fn filter_by_encoding( diff --git a/rust/main/chains/hyperlane-sealevel/src/log_meta_composer/delivery_message_block_multiple_txs_one_successful.json b/rust/main/chains/hyperlane-sealevel/src/log_meta_composer/delivery_message_block_multiple_txs_one_successful.json new file mode 100644 index 0000000000..d90fb4eabc --- /dev/null +++ b/rust/main/chains/hyperlane-sealevel/src/log_meta_composer/delivery_message_block_multiple_txs_one_successful.json @@ -0,0 +1,3388 @@ +{ + "blockHeight": 35744889, + "blockTime": 1734974925, + "blockhash": "DvtcDha1ZnwXbggRkP7pw3EDNortvgCWWA7Lyv5wVTcZ", + "parentSlot": 35781851, + "previousBlockhash": "9RgfP18ApUKiWXyVv74ZBCoWX1X8pWb463FrVQCj4oM", + "rewards": [ + { + "commission": null, + "lamports": 1900, + "postBalance": 8937590330, + "pubkey": "AJwEBmY7PcLNtZKhpVvmF13VgosvMDva2kH79dHaasb9", + "rewardType": "Fee" + } + ], + "transactions": [ + { + "meta": { + "computeUnitsConsumed": 0, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program Vote111111111111111111111111111111111111111 invoke [1]", + "Program Vote111111111111111111111111111111111111111 success" + ], + "postBalances": [ + 8937588480, + 272411, + 1 + ], + "postTokenBalances": [], + "preBalances": [ + 8937588530, + 272411, + 1 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "AJwEBmY7PcLNtZKhpVvmF13VgosvMDva2kH79dHaasb9", + "C6PeLNxv75pYFFMnWctc75tAPHvbnQk3p5SgcGPemhqg", + "Vote111111111111111111111111111111111111111" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 1, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 1, + 0 + ], + "data": "Fk63Pvnh9eEmYV25eHYJGiBBVdJ7QaJuHpWU5p1xAELYC92HNBkLkf7vwiANvYagfemtwLFqN5YQdrT1eCHaWb78mx36Efr6yN4rEdV6WTC1K3cCpTpR1XXfJUvvWS4xZ4Ueuep2jg1z4FPi8rj2cNxPoSeCwZ", + "programIdIndex": 2, + "stackHeight": null + } + ], + "recentBlockhash": "82X78RuKVZMjZ4wwMJpjiaYVGW3q9PatQzWSsokJLCa3" + }, + "signatures": [ + "3dLqABPPT8JkeyZfub57E99rPtU4dRXvtqcdNU6iNa3iuaLHExmYpAa8isksMo5RthPkAkypaTa8Hp8CMmxG9Vec" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 0, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program Vote111111111111111111111111111111111111111 invoke [1]", + "Program Vote111111111111111111111111111111111111111 success" + ], + "postBalances": [ + 8994813620, + 264520, + 1 + ], + "postTokenBalances": [], + "preBalances": [ + 8994813670, + 264520, + 1 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "7jtuen1N95bBNyAV5dbcx6enYT24qKqHobyHQZnyRoTD", + "Fuwdpk6BQq6hPsEWPtmvyujchKYAb6gMA7Z9rKezuoxS", + "Vote111111111111111111111111111111111111111" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 1, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 1, + 0 + ], + "data": "Fk63Pvnh9eEmYV25eHYJGiBBVdJ7QaJuHpWU5p1xAELYC92HNBkLkf7vwiANvYagfemtwLFqN5YQdrT1eCHaWb78mx36Efr6yN4rEdV6WTC1K3cCpTpR1XXfJUvvWS4xZ4Ueuep2jg1z4FPi8rj2cNxPoSeCwZ", + "programIdIndex": 2, + "stackHeight": null + } + ], + "recentBlockhash": "82X78RuKVZMjZ4wwMJpjiaYVGW3q9PatQzWSsokJLCa3" + }, + "signatures": [ + "2o1YTyd4AWK5j23DBsYDzw6c6qqQ8JAEm1TQYEhK8BHXZaxyZsQDUfbpndoQPgvXqu21GktDqvnKSkcpW45YSvqY" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 0, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program Vote111111111111111111111111111111111111111 invoke [1]", + "Program Vote111111111111111111111111111111111111111 success" + ], + "postBalances": [ + 8900415217, + 272411, + 1 + ], + "postTokenBalances": [], + "preBalances": [ + 8900415267, + 272411, + 1 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "2gb1u6kcg3V5MfhoycoEL6m6JWgQAmdkpJSXoUa4ogVf", + "Fo7TvsvnVzsVsntZX4bnfH9h3MffzxTGtzQyvm8sTLtP", + "Vote111111111111111111111111111111111111111" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 1, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 1, + 0 + ], + "data": "Fk63Pvnh9eEmYV25eHYJGiBBVdJ7QaJuHpWU5p1xAELYC92HNBkLkf7vwiANvYagfemtwLFqN5YQdrT1eCHaWb78mx36Efr6yN4rEdV6WTC1K3cCpTpR1XXfJUvvWS4xZ4Ueuep2jg1z4FPi8rj2cNxPoSeCwZ", + "programIdIndex": 2, + "stackHeight": null + } + ], + "recentBlockhash": "82X78RuKVZMjZ4wwMJpjiaYVGW3q9PatQzWSsokJLCa3" + }, + "signatures": [ + "4ztQLtKoowNM3QBvSi8H7BJ6MAuJKRKCfhsV85ptcC8JM1cXqYr3qq5p4JjT1W1jcRTk6gFyPYXS5ZRQyuQbNwfD" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 24927, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program ComputeBudget111111111111111111111111111111 invoke [1]", + "Program ComputeBudget111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [1]", + "Program 11111111111111111111111111111111 success", + "Program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr invoke [1]", + "Program log: Memo (len 66): \"0xe377a5c244b12079b9bd5baad4d3ae7e36165721b774557980fb2f2c4bb859f8\"", + "Program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr consumed 24927 of 29913 compute units", + "Program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr success" + ], + "postBalances": [ + 1495953, + 6722589421, + 1, + 1, + 5095104 + ], + "postTokenBalances": [], + "preBalances": [ + 51496003, + 6672589421, + 1, + 1, + 5095104 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "ByMrVM5LUgHKwPeQSWKEPnyKQVHqfxFTkQuuj1Vi9cVD", + "F7p3dFrjRTbtRp8FRF6qHLomXbKRBzpvBLjtQcfcgmNe", + "ComputeBudget111111111111111111111111111111", + "11111111111111111111111111111111", + "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" + ], + "addressTableLookups": [], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 3, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [], + "data": "KeSwJP", + "programIdIndex": 2, + "stackHeight": null + }, + { + "accounts": [ + 0, + 1 + ], + "data": "3Bxs4NRZ15a54oAf", + "programIdIndex": 3, + "stackHeight": null + }, + { + "accounts": [], + "data": "KszPnaqCF2i8y7gn5x5f9sgXMgoyBncGcNtYb8WoiD9nqQVRZWawYwMBK234hSpsQ9zxR6sb8DyByvm377GFFo5w75", + "programIdIndex": 4, + "stackHeight": null + } + ], + "recentBlockhash": "FgiA2obJyS9f5QjsbHf3YGjTusWN9Mu9aqB9zSJKEsLh" + }, + "signatures": [ + "4vhWbER158mpYWNNfKgN3EGytrt1mE8PmcHvr2eCAWJiCqZDZxBZy3vCa76FrZefxdyiVSiovDZQD9qKyxkvdZzJ" + ] + }, + "version": 0 + }, + { + "meta": { + "computeUnitsConsumed": 25192, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN invoke [1]", + "Program log: Instruction: Click", + "Program log: Clicker Cp6knuLPhSihkEX7RvhxVAqsjfaxZ8mBdaDSp6Fh3Pgx", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN consumed 25192 of 200000 compute units", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN success" + ], + "postBalances": [ + 431050, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "postTokenBalances": [], + "preBalances": [ + 431100, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "Cp6knuLPhSihkEX7RvhxVAqsjfaxZ8mBdaDSp6Fh3Pgx", + "FLKwjYChrU5BSjWCwjzvk1tdiR1bntTsQd4F4FJhZrDJ", + "9FXCusMeR26k1LkDixLF2dw1AnBX8SsB2cspSSi3BcKE", + "FPZ9A7WnsY8q5sarCFAHJqCLuHkrAo5Jqh1nbCiwFQ5e", + "Sysvar1nstructions1111111111111111111111111", + "turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 4, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 3, + 1, + 2, + 0, + 4 + ], + "data": "9Yid3B3uRXxd", + "programIdIndex": 5, + "stackHeight": null + } + ], + "recentBlockhash": "Gva3FJcjwjirKpGdQdxreMWnQxd32jDMEwh2pg7NsnuN" + }, + "signatures": [ + "5Nmrbx7J6hhUu3YNYfxdHgh4BioCWAYDWMmXKjR2Ps7xKwfKcJXQVPpTqNgWJirjePb65fwEVZe7G5Pz4GTzPdGR" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 25114, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN invoke [1]", + "Program log: Instruction: Click", + "Program log: Clicker 6yWRb7MA4PTputZS2GgQ1BKQHC5pu1124HqpKroLs2LG", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN consumed 25114 of 200000 compute units", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN success" + ], + "postBalances": [ + 519850, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "postTokenBalances": [], + "preBalances": [ + 519900, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "6yWRb7MA4PTputZS2GgQ1BKQHC5pu1124HqpKroLs2LG", + "Hu94iEVDhfAhUVUYTe2ePXBVUNtPsTTAtkZKPNf2pq8Z", + "9FXCusMeR26k1LkDixLF2dw1AnBX8SsB2cspSSi3BcKE", + "B9TNgu8ndXGYB73nF992K6wYKv6Ty7LxLBoxnE5tYYmR", + "Sysvar1nstructions1111111111111111111111111", + "turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 4, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 3, + 1, + 2, + 0, + 4 + ], + "data": "9Yid3B3uRXzE", + "programIdIndex": 5, + "stackHeight": null + } + ], + "recentBlockhash": "3veDZBERK9bD5PzCuygjYquZ85aUnU24kRuUwWRzTtkc" + }, + "signatures": [ + "4dm9BrvJsFmM614MuNFyS4EkeEZenJ79g8HMj4bDPErh4DrdN26pHFZr7txJWrbVLEoT3Wbp82Jgmdk7SeGxasde" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 25192, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN invoke [1]", + "Program log: Instruction: Click", + "Program log: Clicker DJiDXfe1jSRmgYuKjzDCbnYciZkXCRbdRR8ezDbM15iF", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN consumed 25192 of 200000 compute units", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN success" + ], + "postBalances": [ + 4260450, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "postTokenBalances": [], + "preBalances": [ + 4260500, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "DJiDXfe1jSRmgYuKjzDCbnYciZkXCRbdRR8ezDbM15iF", + "6qQAxeLkfS1Qfsg7WbyMriGtsuPY1T6NwSiXKrqb3gqs", + "9FXCusMeR26k1LkDixLF2dw1AnBX8SsB2cspSSi3BcKE", + "HU4JccsEKv5HKdNDe1wiiKBTacB78QEdSV4xJdrCnFZN", + "Sysvar1nstructions1111111111111111111111111", + "turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 4, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 3, + 1, + 2, + 0, + 4 + ], + "data": "9Yid3B3uRXyq", + "programIdIndex": 5, + "stackHeight": null + } + ], + "recentBlockhash": "AXAiaXycACYJT9WyET1nM9zJkNWWv9h1SAz6VBX9gwdg" + }, + "signatures": [ + "4x13vs877UsCCugnPPVH9jJ3reby5RfKqQm6fgXkieVMgoLBzognyN4PJFcV6hpeksFN78Sz3eyw5bkaLK9Piv8u" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 23575, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN invoke [1]", + "Program log: Instruction: Click", + "Program log: Clicker 55x98feYeH7taBmGj5MnvVyALkj51ctsvrkJgUooiT2d", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN consumed 23575 of 200000 compute units", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN success" + ], + "postBalances": [ + 4669050, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "postTokenBalances": [], + "preBalances": [ + 4669100, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "55x98feYeH7taBmGj5MnvVyALkj51ctsvrkJgUooiT2d", + "3WUFeYoWxzMKwngWsB2GsKmByZfZvhp2F2Y3QUswxUfh", + "9FXCusMeR26k1LkDixLF2dw1AnBX8SsB2cspSSi3BcKE", + "9JhY9KffbdVxED3GY4jNUrDNv2dv7JQc61ioszdkNrXS", + "Sysvar1nstructions1111111111111111111111111", + "turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 4, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 3, + 1, + 2, + 0, + 4 + ], + "data": "9Yid3B3uRXyV", + "programIdIndex": 5, + "stackHeight": null + } + ], + "recentBlockhash": "HrGwSEeS1L3DKDjHDCeVycwTKrxN5n861eZY6m3G8Fkm" + }, + "signatures": [ + "4PyVsQnjZnnLC5pmgtAsSD5ZYZyp3NuL1At8J5VR5si18eLcCQFmiXKuALQ2Z5VbesKcRVs2zD9PrtzAJciGr8aS" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 29692, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN invoke [1]", + "Program log: Instruction: Click", + "Program log: Clicker CfXww3YzNBQUEtS4ZJeEMZtAEbsym7BAKYUdPsQ1qqrJ", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN consumed 29692 of 200000 compute units", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN success" + ], + "postBalances": [ + 2947200, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "postTokenBalances": [], + "preBalances": [ + 2947250, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "CfXww3YzNBQUEtS4ZJeEMZtAEbsym7BAKYUdPsQ1qqrJ", + "12p6G2ShLgRbxBpCqDue2zed3UGqnLmrxHNmCrhTR1QS", + "9FXCusMeR26k1LkDixLF2dw1AnBX8SsB2cspSSi3BcKE", + "AogE6CYJgaLGXug6D6oTuzZsKJAptaCRD2sLTAMqg1Dm", + "Sysvar1nstructions1111111111111111111111111", + "turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 4, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 3, + 1, + 2, + 0, + 4 + ], + "data": "9Yid3B3uRXx9", + "programIdIndex": 5, + "stackHeight": null + } + ], + "recentBlockhash": "745aVzBnpkgPpDCpG4F5fnpFy9WhhRHDJeezHxKHHzDr" + }, + "signatures": [ + "4feKSCpn9AFR3UfkoqHuC6HRoqGjkiWqAH5PESV2U3CXjv8obBwooMKCP8xfB9VM8WdjGDHb8BKgBZCbScr5kCjR" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 25153, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN invoke [1]", + "Program log: Instruction: Click", + "Program log: Clicker 9T5ntKpqFJKx32hM2sZrGtSKUzRj4iyq74LrYGsdM8Dp", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN consumed 25153 of 200000 compute units", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN success" + ], + "postBalances": [ + 992750, + 11492, + 11492, + 14212, + 0, + 11152 + ], + "postTokenBalances": [], + "preBalances": [ + 992800, + 11492, + 11492, + 14212, + 0, + 11152 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "9T5ntKpqFJKx32hM2sZrGtSKUzRj4iyq74LrYGsdM8Dp", + "5YDa9bxTHEBsWfERWRxNVAe7NgjGtpRmjtGuMSXjGhdu", + "4SCCH7gkNRBXtY2j1a6rwCfsWdgDTZDap7msk72Rkefo", + "9FXCusMeR26k1LkDixLF2dw1AnBX8SsB2cspSSi3BcKE", + "Sysvar1nstructions1111111111111111111111111", + "turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 4, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 2, + 1, + 3, + 0, + 4 + ], + "data": "9Yid3B3uRXz6", + "programIdIndex": 5, + "stackHeight": null + } + ], + "recentBlockhash": "GX81sTiYtmXrs7p99NPP74vY3Q6TLHueueQ1xXPzt4bk" + }, + "signatures": [ + "a7sKgx3MotvqiK7KsnAUfWaEHqW1oJ2mLScaRQWvEiHH146yTokGfuKUGDzG7tgZta3LVd1TMjk2yWa4r7hqndE" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 23705, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN invoke [1]", + "Program log: Instruction: Click", + "Program log: Clicker FZDe7R2oeEqPXUZg7GdbP98yfScU5j3WM9BwDdzA3ZLx", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN consumed 23705 of 200000 compute units", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN success" + ], + "postBalances": [ + 2058300, + 11492, + 11492, + 14212, + 0, + 11152 + ], + "postTokenBalances": [], + "preBalances": [ + 2058350, + 11492, + 11492, + 14212, + 0, + 11152 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "FZDe7R2oeEqPXUZg7GdbP98yfScU5j3WM9BwDdzA3ZLx", + "Cy3u4YK7jv3mfqhoFTSMvxWE5wyarYi9fXWR7bEe9NAk", + "35b813JrG1f4gJ3GpBRqPkuFRiEgE5himtQHXVj8YvjY", + "9FXCusMeR26k1LkDixLF2dw1AnBX8SsB2cspSSi3BcKE", + "Sysvar1nstructions1111111111111111111111111", + "turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 4, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 2, + 1, + 3, + 0, + 4 + ], + "data": "9Yid3B3uRXvm", + "programIdIndex": 5, + "stackHeight": null + } + ], + "recentBlockhash": "745aVzBnpkgPpDCpG4F5fnpFy9WhhRHDJeezHxKHHzDr" + }, + "signatures": [ + "3qExduzZcYxeKTSTKEY9gs5UTFeg5azfYytswpgFFVeu1BW3XVJJtyfHwHJh9ZLUyn2pHvDfGFaEyMvrtvLgpenu" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 23705, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN invoke [1]", + "Program log: Instruction: Click", + "Program log: Clicker FZDe7R2oeEqPXUZg7GdbP98yfScU5j3WM9BwDdzA3ZLx", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN consumed 23705 of 200000 compute units", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN success" + ], + "postBalances": [ + 2058250, + 11492, + 11492, + 14212, + 0, + 11152 + ], + "postTokenBalances": [], + "preBalances": [ + 2058300, + 11492, + 11492, + 14212, + 0, + 11152 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "FZDe7R2oeEqPXUZg7GdbP98yfScU5j3WM9BwDdzA3ZLx", + "Cy3u4YK7jv3mfqhoFTSMvxWE5wyarYi9fXWR7bEe9NAk", + "35b813JrG1f4gJ3GpBRqPkuFRiEgE5himtQHXVj8YvjY", + "9FXCusMeR26k1LkDixLF2dw1AnBX8SsB2cspSSi3BcKE", + "Sysvar1nstructions1111111111111111111111111", + "turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 4, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 2, + 1, + 3, + 0, + 4 + ], + "data": "9Yid3B3uRXyG", + "programIdIndex": 5, + "stackHeight": null + } + ], + "recentBlockhash": "745aVzBnpkgPpDCpG4F5fnpFy9WhhRHDJeezHxKHHzDr" + }, + "signatures": [ + "CG5epQJHrdo6WME2VRwHhdasACE7CV5kwCuEbvVk5QitMJjhhpJ5V6u1hFk1cMG7oozXFoFJWeNExnoYwPeSVmm" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 25114, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN invoke [1]", + "Program log: Instruction: Click", + "Program log: Clicker 6yWRb7MA4PTputZS2GgQ1BKQHC5pu1124HqpKroLs2LG", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN consumed 25114 of 200000 compute units", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN success" + ], + "postBalances": [ + 519800, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "postTokenBalances": [], + "preBalances": [ + 519850, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "6yWRb7MA4PTputZS2GgQ1BKQHC5pu1124HqpKroLs2LG", + "Hu94iEVDhfAhUVUYTe2ePXBVUNtPsTTAtkZKPNf2pq8Z", + "9FXCusMeR26k1LkDixLF2dw1AnBX8SsB2cspSSi3BcKE", + "B9TNgu8ndXGYB73nF992K6wYKv6Ty7LxLBoxnE5tYYmR", + "Sysvar1nstructions1111111111111111111111111", + "turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 4, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 3, + 1, + 2, + 0, + 4 + ], + "data": "9Yid3B3uRXxS", + "programIdIndex": 5, + "stackHeight": null + } + ], + "recentBlockhash": "3veDZBERK9bD5PzCuygjYquZ85aUnU24kRuUwWRzTtkc" + }, + "signatures": [ + "3jueLij1hqvENMjToMiEUM119WPZehXCrSCBYLniRZzC1sbwzwGxYGRRB6wKtHkc82ngqbzJJba9GZ7XztCHMjbr" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 25192, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN invoke [1]", + "Program log: Instruction: Click", + "Program log: Clicker Cp6knuLPhSihkEX7RvhxVAqsjfaxZ8mBdaDSp6Fh3Pgx", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN consumed 25192 of 200000 compute units", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN success" + ], + "postBalances": [ + 431000, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "postTokenBalances": [], + "preBalances": [ + 431050, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "Cp6knuLPhSihkEX7RvhxVAqsjfaxZ8mBdaDSp6Fh3Pgx", + "FLKwjYChrU5BSjWCwjzvk1tdiR1bntTsQd4F4FJhZrDJ", + "9FXCusMeR26k1LkDixLF2dw1AnBX8SsB2cspSSi3BcKE", + "FPZ9A7WnsY8q5sarCFAHJqCLuHkrAo5Jqh1nbCiwFQ5e", + "Sysvar1nstructions1111111111111111111111111", + "turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 4, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 3, + 1, + 2, + 0, + 4 + ], + "data": "9Yid3B3uRXzT", + "programIdIndex": 5, + "stackHeight": null + } + ], + "recentBlockhash": "Gva3FJcjwjirKpGdQdxreMWnQxd32jDMEwh2pg7NsnuN" + }, + "signatures": [ + "4JES3o1EfQFxUsEuNxYwbyvZzvhoiY6kHvoizya4J5aeuqYBXbmMiY8s76TNoEyT98KY72fe3dp2QHtL3L5zicDs" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 23575, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN invoke [1]", + "Program log: Instruction: Click", + "Program log: Clicker 55x98feYeH7taBmGj5MnvVyALkj51ctsvrkJgUooiT2d", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN consumed 23575 of 200000 compute units", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN success" + ], + "postBalances": [ + 4669000, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "postTokenBalances": [], + "preBalances": [ + 4669050, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "55x98feYeH7taBmGj5MnvVyALkj51ctsvrkJgUooiT2d", + "3WUFeYoWxzMKwngWsB2GsKmByZfZvhp2F2Y3QUswxUfh", + "9FXCusMeR26k1LkDixLF2dw1AnBX8SsB2cspSSi3BcKE", + "9JhY9KffbdVxED3GY4jNUrDNv2dv7JQc61ioszdkNrXS", + "Sysvar1nstructions1111111111111111111111111", + "turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 4, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 3, + 1, + 2, + 0, + 4 + ], + "data": "9Yid3B3uRXvb", + "programIdIndex": 5, + "stackHeight": null + } + ], + "recentBlockhash": "4bXsMeojJxqqLbDsN9DnWRwoSyrorP7CQ8HAZBPxhyND" + }, + "signatures": [ + "2N3PrKS7Yd6sDTUhN471dvsHeTP8cs5RC5VKZGvRN3TnmRYVqaZR1y8Hr69o811YMvKpnWVtqSAknbeYKSXbZrcN" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 25153, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN invoke [1]", + "Program log: Instruction: Click", + "Program log: Clicker 9T5ntKpqFJKx32hM2sZrGtSKUzRj4iyq74LrYGsdM8Dp", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN consumed 25153 of 200000 compute units", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN success" + ], + "postBalances": [ + 992700, + 11492, + 11492, + 14212, + 0, + 11152 + ], + "postTokenBalances": [], + "preBalances": [ + 992750, + 11492, + 11492, + 14212, + 0, + 11152 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "9T5ntKpqFJKx32hM2sZrGtSKUzRj4iyq74LrYGsdM8Dp", + "5YDa9bxTHEBsWfERWRxNVAe7NgjGtpRmjtGuMSXjGhdu", + "4SCCH7gkNRBXtY2j1a6rwCfsWdgDTZDap7msk72Rkefo", + "9FXCusMeR26k1LkDixLF2dw1AnBX8SsB2cspSSi3BcKE", + "Sysvar1nstructions1111111111111111111111111", + "turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 4, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 2, + 1, + 3, + 0, + 4 + ], + "data": "9Yid3B3uRXzJ", + "programIdIndex": 5, + "stackHeight": null + } + ], + "recentBlockhash": "4bXsMeojJxqqLbDsN9DnWRwoSyrorP7CQ8HAZBPxhyND" + }, + "signatures": [ + "8f5FFcU8KgnDgwRdx6YV1FZEbz8fcjDUhp6M8TkXJbDeY434QSpM3fHa1LgTYLfvViXz9e63ePfXmmT5KVXg4Rt" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 26066, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program ComputeBudget111111111111111111111111111111 invoke [1]", + "Program ComputeBudget111111111111111111111111111111 success", + "Program FUCHhfHbuxXBWiRBfVdhmiog84sUJw11aAq3ibAUGL6e invoke [1]", + "Program FUCHhfHbuxXBWiRBfVdhmiog84sUJw11aAq3ibAUGL6e consumed 13033 of 27000 compute units", + "Program FUCHhfHbuxXBWiRBfVdhmiog84sUJw11aAq3ibAUGL6e success", + "Program FUCHhfHbuxXBWiRBfVdhmiog84sUJw11aAq3ibAUGL6e invoke [1]", + "Program FUCHhfHbuxXBWiRBfVdhmiog84sUJw11aAq3ibAUGL6e consumed 13033 of 13967 compute units", + "Program FUCHhfHbuxXBWiRBfVdhmiog84sUJw11aAq3ibAUGL6e success" + ], + "postBalances": [ + 55400350, + 233920, + 233920, + 1, + 11152 + ], + "postTokenBalances": [], + "preBalances": [ + 55400400, + 233920, + 233920, + 1, + 11152 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "4bEk3CLwmnRHQxUEqDtNcXabz8RSdQyMpsAKJTaM8NUc", + "4bbK5m6YSPyALDa5uE9WYMLwnqEATHDzWdYqFZJ6Ezrf", + "E4wLiGrpMi2RtH7c1WrTzCdVVEi58za1XzPygtr7Kmz1", + "ComputeBudget111111111111111111111111111111", + "FUCHhfHbuxXBWiRBfVdhmiog84sUJw11aAq3ibAUGL6e" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 2, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [], + "data": "HAaSfh", + "programIdIndex": 3, + "stackHeight": null + }, + { + "accounts": [ + 2, + 0 + ], + "data": "vaJro6ZLfqS8Tk6mcBehNesxGGBBDUfeYjo9XpzxmW6pTfUBhDh7eNT", + "programIdIndex": 4, + "stackHeight": null + }, + { + "accounts": [ + 1, + 0 + ], + "data": "svao1p5ZoTbEBpMq8HCJ7B2jKsqFvGGBTjackJ79Q8sb67xwyCid5wd", + "programIdIndex": 4, + "stackHeight": null + } + ], + "recentBlockhash": "HrGwSEeS1L3DKDjHDCeVycwTKrxN5n861eZY6m3G8Fkm" + }, + "signatures": [ + "32xkNevmZipE6UdriH8UdB7bT7LajuAXZF6Y1J7ZJL5q4DEYvZa4SRHL9c6njmmPHjiEDKsEM7CvNPkx9KXb8NaZ" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 262874, + "err": null, + "fee": 150, + "innerInstructions": [ + { + "index": 1, + "instructions": [ + { + "accounts": [ + 0, + 4 + ], + "data": "1111JX81xkJCwniZ1Erg4Sybstk6tqT9u8gU37B5SnCv3ruqnd5SbCwNAcbMvfg2xgbbn", + "programIdIndex": 12, + "stackHeight": 2 + }, + { + "accounts": [ + 0, + 6 + ], + "data": "1111JX81xkJCwniZ1Erg4Sybstk6tqT9u8gU37B5SnCv3ruqnd5SbCwNAcbMvfg2xgbbn", + "programIdIndex": 12, + "stackHeight": 2 + }, + { + "accounts": [ + 0, + 8 + ], + "data": "3Bxs4Tm5txe73V9q", + "programIdIndex": 12, + "stackHeight": 2 + }, + { + "accounts": [ + 0, + 7 + ], + "data": "3Bxs4Tm5txe73V9q", + "programIdIndex": 12, + "stackHeight": 2 + }, + { + "accounts": [ + 3, + 0, + 10, + 0, + 2, + 1, + 9, + 5, + 11, + 18, + 13, + 16, + 12 + ], + "data": "9ZxXEgRKtxM", + "programIdIndex": 15, + "stackHeight": 2 + }, + { + "accounts": [ + 0, + 2 + ], + "data": "11114hL6PWxs4U7NCYRNXyZk7j7iV5YvbM2Y4EYYnUoc78BhWkZV7EbHKZpZnbJM3FMyFq", + "programIdIndex": 12, + "stackHeight": 3 + }, + { + "accounts": [ + 2 + ], + "data": "GC422sqZqSiuDzjorrJVQ1wDyBHerctVdibtxN47F9PXCNokjD5NBjqdUEZ7q7gULrAbayLZ33AbQKyBCSPFNnZM1u", + "programIdIndex": 18, + "stackHeight": 3 + }, + { + "accounts": [ + 2 + ], + "data": "GyEZt3T6vJ45ToJP8DQByidGXsy4KJEGWVWgP1hEpCP6MRsdGJabNdMf1B9yvA6BX5x37UchRjzKErQP9GT1YjQp1G", + "programIdIndex": 18, + "stackHeight": 3 + }, + { + "accounts": [ + 2 + ], + "data": "bPFz9iKJZS3rDbU6zowCjSwEjkgEPMo9i3Vmbodo9YYN1dGaEta9eWisUYepxtz2rgAZBo5ZFNFCJ6MvKF9e8V7ceoa", + "programIdIndex": 18, + "stackHeight": 3 + }, + { + "accounts": [ + 2, + 3, + 2, + 3 + ], + "data": "YzZQyW1KJYUcGMonWYpqEXb51jrqpC6SG3vMmUSJpjmTHzgi5rH5QercoJFoQY2wRHDwBmhF3iD7TFuBnY1ETUnuBcVVooT7LgnFcL3XatbSFEdD9SP9HdmRrR1AbAzTR1qauF9DxCfUoHMv7g5GfXSF8H3PcTKcy4HeZAqD6GP7ApLu5bS", + "programIdIndex": 18, + "stackHeight": 3 + }, + { + "accounts": [ + 0, + 1 + ], + "data": "11113Md75Dq7V8gjbzJMw6r5QL5XX645yU1bhj7iFnRmnhAT1ske6hnTQ4LYv8avqJdZ2Z", + "programIdIndex": 12, + "stackHeight": 3 + }, + { + "accounts": [ + 1, + 2, + 3, + 9, + 3 + ], + "data": "SSqVPNo92jX", + "programIdIndex": 16, + "stackHeight": 3 + }, + { + "accounts": [ + 0, + 11, + 0, + 2, + 12, + 18 + ], + "data": "1", + "programIdIndex": 13, + "stackHeight": 3 + }, + { + "accounts": [ + 2 + ], + "data": "84eT", + "programIdIndex": 18, + "stackHeight": 4 + }, + { + "accounts": [ + 0, + 11 + ], + "data": "11112Um5QtBM9RVwhzeb2i4U2mPJ7krjBF48k17qACUVhhTNRzdmH1adRRGPABHBdaEx67", + "programIdIndex": 12, + "stackHeight": 4 + }, + { + "accounts": [ + 11 + ], + "data": "P", + "programIdIndex": 18, + "stackHeight": 4 + }, + { + "accounts": [ + 11, + 2 + ], + "data": "6U1ZUf261fPY3ULHBWy3hurmHY2X4qaWfcvvCwszYF11e", + "programIdIndex": 18, + "stackHeight": 4 + }, + { + "accounts": [ + 2, + 11, + 3 + ], + "data": "6AuM4xMCPFhR", + "programIdIndex": 18, + "stackHeight": 3 + }, + { + "accounts": [ + 2, + 3 + ], + "data": "31tb", + "programIdIndex": 18, + "stackHeight": 3 + }, + { + "accounts": [ + 2, + 3 + ], + "data": "C2X7q5sVBvi2E5BSotT3ogtLHb3igj3sP7xdQF1f2suStR2MUMN7QBq", + "programIdIndex": 18, + "stackHeight": 3 + }, + { + "accounts": [ + 2, + 3 + ], + "data": "5SL6KejFcJAFCech12rmhDMH11RyzCaprR64rHCLCuRW48Wy42PgxL4nfYsZSejosCQQsJve6HruQaoknGeML2GF", + "programIdIndex": 18, + "stackHeight": 3 + } + ] + } + ], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program ComputeBudget111111111111111111111111111111 invoke [1]", + "Program ComputeBudget111111111111111111111111111111 success", + "Program RariUNM3vz1rwxPg8UJyRAN7rSKXxgd2ncS2ddCa4ZE invoke [1]", + "Program log: Instruction: MintWithControls", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program Rari9ftBd6vFdtpn8TDLwN2ze24KKkM5MLEETNiBMNn invoke [2]", + "Program log: Instruction: Mint", + "Program log: Invoke create account 7sroipZDNYGDCrBfRorNRtKu38wFRqExv1kMTJJiwrag,BQ2TPCCzmxNMvmk5snbHGfVDL5JDzvXJBvhGc7adsY2o", + "Program 11111111111111111111111111111111 invoke [3]", + "Program 11111111111111111111111111111111 success", + "Program log: Invoke initialise metadata pointer extension", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [3]", + "Program log: MetadataPointerInstruction::Initialize", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 2442 of 766976 compute units", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [3]", + "Program log: GroupMemberPointerInstruction::Initialize", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 2505 of 762558 compute units", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success", + "Program log: Invoke initialise mint", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [3]", + "Program log: Instruction: InitializeMint2", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 4078 of 758195 compute units", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success", + "Program log: Initialise metadata if needed", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [3]", + "Program log: TokenMetadataInstruction: Initialize", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 9992 of 751137 compute units", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success", + "Program log: Invoke create account 7sroipZDNYGDCrBfRorNRtKu38wFRqExv1kMTJJiwrag,BQ2TPCCzmxNMvmk5snbHGfVDL5JDzvXJBvhGc7adsY2o", + "Program 11111111111111111111111111111111 invoke [3]", + "Program 11111111111111111111111111111111 success", + "Program RariGDYwEF1jQA4kisHxBxiv1TDuBPVHNNoXFNYriFb invoke [3]", + "Program log: Instruction: InitializeMember", + "Program RariGDYwEF1jQA4kisHxBxiv1TDuBPVHNNoXFNYriFb consumed 3062 of 712812 compute units", + "Program RariGDYwEF1jQA4kisHxBxiv1TDuBPVHNNoXFNYriFb success", + "Program log: Finished", + "Program log: MINT NON-FUNGIBLE TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "Program log: 7sroipZDNYGDCrBfRorNRtKu38wFRqExv1kMTJJiwrag", + "Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL invoke [3]", + "Program log: Create", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [4]", + "Program log: Instruction: GetAccountDataSize", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 3038 of 670644 compute units", + "Program return: TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb qgAAAAAAAAA=", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success", + "Program 11111111111111111111111111111111 invoke [4]", + "Program 11111111111111111111111111111111 success", + "Program log: Initialize the associated token account", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [4]", + "Program log: Instruction: InitializeImmutableOwner", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 1924 of 662747 compute units", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [4]", + "Program log: Instruction: InitializeAccount3", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 5392 of 658406 compute units", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success", + "Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL consumed 24807 of 677448 compute units", + "Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL success", + "Program log: Minting TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [3]", + "Program log: Instruction: MintTo", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 5568 of 638961 compute units", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success", + "Program log: Removing mint authority", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [3]", + "Program log: Instruction: SetAuthority", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 2740 of 631171 compute units", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success", + "Program log: done", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [3]", + "Program log: TokenMetadataInstruction: UpdateField", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 12096 of 621425 compute units", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [3]", + "Program log: TokenMetadataInstruction: UpdateField", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 13658 of 606546 compute units", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success", + "Program Rari9ftBd6vFdtpn8TDLwN2ze24KKkM5MLEETNiBMNn consumed 214421 of 805330 compute units", + "Program Rari9ftBd6vFdtpn8TDLwN2ze24KKkM5MLEETNiBMNn success", + "Program RariUNM3vz1rwxPg8UJyRAN7rSKXxgd2ncS2ddCa4ZE consumed 262874 of 850000 compute units", + "Program RariUNM3vz1rwxPg8UJyRAN7rSKXxgd2ncS2ddCa4ZE success" + ], + "postBalances": [ + 1504115, + 14144, + 48484, + 142256, + 15368, + 53924, + 15368, + 216518752, + 139608770978, + 14416, + 95200, + 20264, + 1, + 7277632, + 1, + 11152, + 11152, + 11152, + 11152 + ], + "postTokenBalances": [ + { + "accountIndex": 11, + "mint": "BQ2TPCCzmxNMvmk5snbHGfVDL5JDzvXJBvhGc7adsY2o", + "owner": "7sroipZDNYGDCrBfRorNRtKu38wFRqExv1kMTJJiwrag", + "programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "uiTokenAmount": { + "amount": "1", + "decimals": 0, + "uiAmount": 1, + "uiAmountString": "1" + } + } + ], + "preBalances": [ + 2777893, + 0, + 0, + 142256, + 0, + 53924, + 0, + 215938752, + 139608190978, + 14416, + 95200, + 0, + 1, + 7277632, + 1, + 11152, + 11152, + 11152, + 11152 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "7sroipZDNYGDCrBfRorNRtKu38wFRqExv1kMTJJiwrag", + "9toVdfGuM4qwW9uyfadz3JA9SED6kzJzJaqRfKnvZif2", + "BQ2TPCCzmxNMvmk5snbHGfVDL5JDzvXJBvhGc7adsY2o", + "23buSpWBskpcVtkQsWtcugmbfqsYqHpsDLe4tj5mmAqN", + "5oaouyNATLwQjixXpYT5sEqKaKafafQi5oEM8gcFS1p9", + "71HcxJnmvJrfyYA1gkfK5SAXecGzuWN3B3w9T6dHNa8H", + "8SUKSg5QaqkXBczx27hKisrLnoFpdTekYVyvjcBcwgcw", + "8y6yGuhdyPYEaiujsK62V6ctHcrtYu7f2LxLDXCHnb4d", + "AsSKqK7CkxFUf3KaoQzzr8ZLPm5fFguUtVE5QwGALQQn", + "DLDzmqUwGhBnRkdoUPjAYcHcx17FYjfAAk6q8qj7MPJJ", + "Dq6A6TNFWHjFKuLqWPYjFcTjhSuanpKAgWXxGzJncwtT", + "EoHBPKH6A18QECuWth1pUYafF95W5mfmwS7nAZp9LZLH", + "11111111111111111111111111111111", + "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL", + "ComputeBudget111111111111111111111111111111", + "Rari9ftBd6vFdtpn8TDLwN2ze24KKkM5MLEETNiBMNn", + "RariGDYwEF1jQA4kisHxBxiv1TDuBPVHNNoXFNYriFb", + "RariUNM3vz1rwxPg8UJyRAN7rSKXxgd2ncS2ddCa4ZE", + "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 7, + "numRequiredSignatures": 3 + }, + "instructions": [ + { + "accounts": [], + "data": "GA6zB1", + "programIdIndex": 14, + "stackHeight": null + }, + { + "accounts": [ + 3, + 10, + 0, + 4, + 6, + 2, + 1, + 9, + 5, + 8, + 11, + 7, + 18, + 13, + 16, + 12, + 15 + ], + "data": "5gMQbgxkTMLLTytDRsM8B", + "programIdIndex": 17, + "stackHeight": null + } + ], + "recentBlockhash": "GRKHwZHCpQCkdCfLB5uDfNZ1DnbhFaegxpv351WGZESk" + }, + "signatures": [ + "4YYiYWvcT7wqWQ18Jcfwf3D8mPXNVWWWQ1Qapuubqu7FFriFentR8pv6wcDKYAaEe2xNNZXWrDwB6cFXu9g6AUU2", + "4mKjD6UP9fHRaW5BAFp2fzyDFbYZBUF5n2eYfipTYTpXYdbj9o7bysHgJWbNf4HKaXpWtUw5iG6FNhcBbc87rkNS", + "637DgH3g2Giu3m4gD7ALUXGjDAiietgubWDo6yTjkvbqUtNttYTyxQjRBf8j2W8iqsX6AacGGW6j54NXE3pkGTE3" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 0, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program Vote111111111111111111111111111111111111111 invoke [1]", + "Program Vote111111111111111111111111111111111111111 success" + ], + "postBalances": [ + 8937588430, + 272411, + 1 + ], + "postTokenBalances": [], + "preBalances": [ + 8937588480, + 272411, + 1 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "AJwEBmY7PcLNtZKhpVvmF13VgosvMDva2kH79dHaasb9", + "C6PeLNxv75pYFFMnWctc75tAPHvbnQk3p5SgcGPemhqg", + "Vote111111111111111111111111111111111111111" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 1, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 1, + 0 + ], + "data": "Fk63Pvyak29TbGXpE2Lwz5sSCzhNdG5DLVnfiBZBkMgazajyeLBZzKECbBUxETBJhpzkWTodGw6pMLoLygdjREwer3urL9cNoKzndAFuwucV6nMKUePk649zMvZXuP4WCUgk6pjuXigR8Wux32vCszFfJBfqGj", + "programIdIndex": 2, + "stackHeight": null + } + ], + "recentBlockhash": "9RgfP18ApUKiWXyVv74ZBCoWX1X8pWb463FrVQCj4oM" + }, + "signatures": [ + "4QjwbwDvkGXJ3PiKpEcmbvoiVJKUnyfN3RRJY3xfRYjwkVJ4PQ6kDBiLFWnuirDnqVpSRaPP7NK5JZgxuwC7Y9hy" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 25192, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN invoke [1]", + "Program log: Instruction: Click", + "Program log: Clicker Cp6knuLPhSihkEX7RvhxVAqsjfaxZ8mBdaDSp6Fh3Pgx", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN consumed 25192 of 200000 compute units", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN success" + ], + "postBalances": [ + 430950, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "postTokenBalances": [], + "preBalances": [ + 431000, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "Cp6knuLPhSihkEX7RvhxVAqsjfaxZ8mBdaDSp6Fh3Pgx", + "FLKwjYChrU5BSjWCwjzvk1tdiR1bntTsQd4F4FJhZrDJ", + "9FXCusMeR26k1LkDixLF2dw1AnBX8SsB2cspSSi3BcKE", + "FPZ9A7WnsY8q5sarCFAHJqCLuHkrAo5Jqh1nbCiwFQ5e", + "Sysvar1nstructions1111111111111111111111111", + "turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 4, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 3, + 1, + 2, + 0, + 4 + ], + "data": "9Yid3B3uRXy8", + "programIdIndex": 5, + "stackHeight": null + } + ], + "recentBlockhash": "Gva3FJcjwjirKpGdQdxreMWnQxd32jDMEwh2pg7NsnuN" + }, + "signatures": [ + "pTYUghgJjDE9jj9TD3izbmKbsoZwyTvQnVXEa9Xumd1enH1TUq3C7tTT7UzXNjCV75V5XNSzvF1yWTZ4Tq4c2Ay" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 23575, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN invoke [1]", + "Program log: Instruction: Click", + "Program log: Clicker 55x98feYeH7taBmGj5MnvVyALkj51ctsvrkJgUooiT2d", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN consumed 23575 of 200000 compute units", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN success" + ], + "postBalances": [ + 4668950, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "postTokenBalances": [], + "preBalances": [ + 4669000, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "55x98feYeH7taBmGj5MnvVyALkj51ctsvrkJgUooiT2d", + "3WUFeYoWxzMKwngWsB2GsKmByZfZvhp2F2Y3QUswxUfh", + "9FXCusMeR26k1LkDixLF2dw1AnBX8SsB2cspSSi3BcKE", + "9JhY9KffbdVxED3GY4jNUrDNv2dv7JQc61ioszdkNrXS", + "Sysvar1nstructions1111111111111111111111111", + "turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 4, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 3, + 1, + 2, + 0, + 4 + ], + "data": "9Yid3B3uRXxV", + "programIdIndex": 5, + "stackHeight": null + } + ], + "recentBlockhash": "745aVzBnpkgPpDCpG4F5fnpFy9WhhRHDJeezHxKHHzDr" + }, + "signatures": [ + "2DRJDZLuP2bxw5rLvZ4AvX6RhgEnfJ9fRaWPTbGDNEPj43kNof3g2Vp6ZqifU5zPA9FTmZQnTjHKEM8CBhkedHED" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 25153, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN invoke [1]", + "Program log: Instruction: Click", + "Program log: Clicker 9T5ntKpqFJKx32hM2sZrGtSKUzRj4iyq74LrYGsdM8Dp", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN consumed 25153 of 200000 compute units", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN success" + ], + "postBalances": [ + 992650, + 11492, + 11492, + 14212, + 0, + 11152 + ], + "postTokenBalances": [], + "preBalances": [ + 992700, + 11492, + 11492, + 14212, + 0, + 11152 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "9T5ntKpqFJKx32hM2sZrGtSKUzRj4iyq74LrYGsdM8Dp", + "5YDa9bxTHEBsWfERWRxNVAe7NgjGtpRmjtGuMSXjGhdu", + "4SCCH7gkNRBXtY2j1a6rwCfsWdgDTZDap7msk72Rkefo", + "9FXCusMeR26k1LkDixLF2dw1AnBX8SsB2cspSSi3BcKE", + "Sysvar1nstructions1111111111111111111111111", + "turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 4, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 2, + 1, + 3, + 0, + 4 + ], + "data": "9Yid3B3uRXwN", + "programIdIndex": 5, + "stackHeight": null + } + ], + "recentBlockhash": "745aVzBnpkgPpDCpG4F5fnpFy9WhhRHDJeezHxKHHzDr" + }, + "signatures": [ + "2tB9PPDwziUWj8m6HtPn9A3gn74rwVDSekD2Z5XEtV8DLPY8Fm9S7hZEuFWq8NGcncbb8agyDwNXLB5uoeLbtsff" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 25153, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN invoke [1]", + "Program log: Instruction: Click", + "Program log: Clicker 9T5ntKpqFJKx32hM2sZrGtSKUzRj4iyq74LrYGsdM8Dp", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN consumed 25153 of 200000 compute units", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN success" + ], + "postBalances": [ + 992600, + 11492, + 11492, + 14212, + 0, + 11152 + ], + "postTokenBalances": [], + "preBalances": [ + 992650, + 11492, + 11492, + 14212, + 0, + 11152 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "9T5ntKpqFJKx32hM2sZrGtSKUzRj4iyq74LrYGsdM8Dp", + "5YDa9bxTHEBsWfERWRxNVAe7NgjGtpRmjtGuMSXjGhdu", + "4SCCH7gkNRBXtY2j1a6rwCfsWdgDTZDap7msk72Rkefo", + "9FXCusMeR26k1LkDixLF2dw1AnBX8SsB2cspSSi3BcKE", + "Sysvar1nstructions1111111111111111111111111", + "turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 4, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 2, + 1, + 3, + 0, + 4 + ], + "data": "9Yid3B3uRXxv", + "programIdIndex": 5, + "stackHeight": null + } + ], + "recentBlockhash": "2d8BhKG9E8nMARkU9SkMN9N5UmS4VkPyYYZNoUcekqCX" + }, + "signatures": [ + "VHivewViQLW3Y7W37y6aLB9ZR7k4oBTDizUcL6s69Fsegz3obiz1TBATzDGQsYRn73JLKWA7so64iDe4HavbEoA" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 25192, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN invoke [1]", + "Program log: Instruction: Click", + "Program log: Clicker Cp6knuLPhSihkEX7RvhxVAqsjfaxZ8mBdaDSp6Fh3Pgx", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN consumed 25192 of 200000 compute units", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN success" + ], + "postBalances": [ + 430900, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "postTokenBalances": [], + "preBalances": [ + 430950, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "Cp6knuLPhSihkEX7RvhxVAqsjfaxZ8mBdaDSp6Fh3Pgx", + "FLKwjYChrU5BSjWCwjzvk1tdiR1bntTsQd4F4FJhZrDJ", + "9FXCusMeR26k1LkDixLF2dw1AnBX8SsB2cspSSi3BcKE", + "FPZ9A7WnsY8q5sarCFAHJqCLuHkrAo5Jqh1nbCiwFQ5e", + "Sysvar1nstructions1111111111111111111111111", + "turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 4, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 3, + 1, + 2, + 0, + 4 + ], + "data": "9Yid3B3uRXyV", + "programIdIndex": 5, + "stackHeight": null + } + ], + "recentBlockhash": "14tvFMnmt5uugsBUyWWvdBC6YiD6bcWbNUQc2Tr1aLc5" + }, + "signatures": [ + "4hx1wQotXicZS5LcCzoTn8b9rXoAkRNcTTh1hp7yhb22iR3RpyFDab19PhHVTWpHzqrFjYZbRxpgK6nSUT4JGAeS" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 25192, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN invoke [1]", + "Program log: Instruction: Click", + "Program log: Clicker Cp6knuLPhSihkEX7RvhxVAqsjfaxZ8mBdaDSp6Fh3Pgx", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN consumed 25192 of 200000 compute units", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN success" + ], + "postBalances": [ + 430850, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "postTokenBalances": [], + "preBalances": [ + 430900, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "Cp6knuLPhSihkEX7RvhxVAqsjfaxZ8mBdaDSp6Fh3Pgx", + "FLKwjYChrU5BSjWCwjzvk1tdiR1bntTsQd4F4FJhZrDJ", + "9FXCusMeR26k1LkDixLF2dw1AnBX8SsB2cspSSi3BcKE", + "FPZ9A7WnsY8q5sarCFAHJqCLuHkrAo5Jqh1nbCiwFQ5e", + "Sysvar1nstructions1111111111111111111111111", + "turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 4, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 3, + 1, + 2, + 0, + 4 + ], + "data": "9Yid3B3uRXzW", + "programIdIndex": 5, + "stackHeight": null + } + ], + "recentBlockhash": "9PWtA56bgsBEkMS45eBbkUUzzNUYTUeML3VcGHJTK3CR" + }, + "signatures": [ + "3LWqKcfdwLgte6N7xozj4ESm79n2icUdAx1CowBMbaaHEwDFumFnYeQE1HAfqxpWCx1ALFJpaF22pUWZCDPMjUYh" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 25192, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN invoke [1]", + "Program log: Instruction: Click", + "Program log: Clicker Cp6knuLPhSihkEX7RvhxVAqsjfaxZ8mBdaDSp6Fh3Pgx", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN consumed 25192 of 200000 compute units", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN success" + ], + "postBalances": [ + 430800, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "postTokenBalances": [], + "preBalances": [ + 430850, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "Cp6knuLPhSihkEX7RvhxVAqsjfaxZ8mBdaDSp6Fh3Pgx", + "FLKwjYChrU5BSjWCwjzvk1tdiR1bntTsQd4F4FJhZrDJ", + "9FXCusMeR26k1LkDixLF2dw1AnBX8SsB2cspSSi3BcKE", + "FPZ9A7WnsY8q5sarCFAHJqCLuHkrAo5Jqh1nbCiwFQ5e", + "Sysvar1nstructions1111111111111111111111111", + "turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 4, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 3, + 1, + 2, + 0, + 4 + ], + "data": "9Yid3B3uRXwu", + "programIdIndex": 5, + "stackHeight": null + } + ], + "recentBlockhash": "Gva3FJcjwjirKpGdQdxreMWnQxd32jDMEwh2pg7NsnuN" + }, + "signatures": [ + "5DG3XrVfnfTKimSYuz8mWgnqF5mBLuqAcqDZcv1Tq7TGUdmbwwoygEjHjTkbpeuJ79s3Sp6BGhBe8JzYmT6VXbd1" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 25192, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN invoke [1]", + "Program log: Instruction: Click", + "Program log: Clicker Cp6knuLPhSihkEX7RvhxVAqsjfaxZ8mBdaDSp6Fh3Pgx", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN consumed 25192 of 200000 compute units", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN success" + ], + "postBalances": [ + 430750, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "postTokenBalances": [], + "preBalances": [ + 430800, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "Cp6knuLPhSihkEX7RvhxVAqsjfaxZ8mBdaDSp6Fh3Pgx", + "FLKwjYChrU5BSjWCwjzvk1tdiR1bntTsQd4F4FJhZrDJ", + "9FXCusMeR26k1LkDixLF2dw1AnBX8SsB2cspSSi3BcKE", + "FPZ9A7WnsY8q5sarCFAHJqCLuHkrAo5Jqh1nbCiwFQ5e", + "Sysvar1nstructions1111111111111111111111111", + "turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 4, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 3, + 1, + 2, + 0, + 4 + ], + "data": "9Yid3B3uRXzN", + "programIdIndex": 5, + "stackHeight": null + } + ], + "recentBlockhash": "2ASwJhrEruoZdH9BXDEvL7z25w2PX8ztHy9qDgFqhd2X" + }, + "signatures": [ + "5CevNdHzh7yAgNsEAQQqpC2UmckgtpSkRdHqHo6uW1ow5ZoMCcWCj9dFWkBV28PeDA3xicVeZ2urmpyspoQoNUta" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 0, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program Vote111111111111111111111111111111111111111 invoke [1]", + "Program Vote111111111111111111111111111111111111111 success" + ], + "postBalances": [ + 9235663620, + 272411, + 1 + ], + "postTokenBalances": [], + "preBalances": [ + 9235663670, + 272411, + 1 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "8t2XBEjM7db1Fv24FhNaayPx32daDZ95L5xTETKCk2pK", + "GtvJ5ydPqRrq1oSDdFr4mKynoFFqkDDDYfkPPJnjsH53", + "Vote111111111111111111111111111111111111111" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 1, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 1, + 0 + ], + "data": "Fk63Pvyak29TbGXpE2Lwz5sSCzhNdG5DLVnfiBZBkMgazajyeLBZzKECbBUxETBJhpzkWTodGw6pMLoLygdjREwer3urL9cNoKzndAFuwucV6nMKUePk649zMvZXuP4WCUgk6pjuXigR8Wux32vCszFfJBfqGj", + "programIdIndex": 2, + "stackHeight": null + } + ], + "recentBlockhash": "9RgfP18ApUKiWXyVv74ZBCoWX1X8pWb463FrVQCj4oM" + }, + "signatures": [ + "232wBqGGxFPQi22horR7YvofKdtrpc4xnX84D36VFR9QZa8FmTEMteUURgav2eogG33srJ5wKrwmQXw5EhKxNNWB" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 25192, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN invoke [1]", + "Program log: Instruction: Click", + "Program log: Clicker Cp6knuLPhSihkEX7RvhxVAqsjfaxZ8mBdaDSp6Fh3Pgx", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN consumed 25192 of 200000 compute units", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN success" + ], + "postBalances": [ + 430700, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "postTokenBalances": [], + "preBalances": [ + 430750, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "Cp6knuLPhSihkEX7RvhxVAqsjfaxZ8mBdaDSp6Fh3Pgx", + "FLKwjYChrU5BSjWCwjzvk1tdiR1bntTsQd4F4FJhZrDJ", + "9FXCusMeR26k1LkDixLF2dw1AnBX8SsB2cspSSi3BcKE", + "FPZ9A7WnsY8q5sarCFAHJqCLuHkrAo5Jqh1nbCiwFQ5e", + "Sysvar1nstructions1111111111111111111111111", + "turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 4, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 3, + 1, + 2, + 0, + 4 + ], + "data": "9Yid3B3uRXyM", + "programIdIndex": 5, + "stackHeight": null + } + ], + "recentBlockhash": "14tvFMnmt5uugsBUyWWvdBC6YiD6bcWbNUQc2Tr1aLc5" + }, + "signatures": [ + "cNqG9iaTpv89mcmfkMXKrY6iUDGHFbJRBMEoWuFPfPV5aXmvf9AUy6xVeot9BxMXpCoFFScQnQT4yPdrpBxBYmq" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 25192, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN invoke [1]", + "Program log: Instruction: Click", + "Program log: Clicker Cp6knuLPhSihkEX7RvhxVAqsjfaxZ8mBdaDSp6Fh3Pgx", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN consumed 25192 of 200000 compute units", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN success" + ], + "postBalances": [ + 430650, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "postTokenBalances": [], + "preBalances": [ + 430700, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "Cp6knuLPhSihkEX7RvhxVAqsjfaxZ8mBdaDSp6Fh3Pgx", + "FLKwjYChrU5BSjWCwjzvk1tdiR1bntTsQd4F4FJhZrDJ", + "9FXCusMeR26k1LkDixLF2dw1AnBX8SsB2cspSSi3BcKE", + "FPZ9A7WnsY8q5sarCFAHJqCLuHkrAo5Jqh1nbCiwFQ5e", + "Sysvar1nstructions1111111111111111111111111", + "turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 4, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 3, + 1, + 2, + 0, + 4 + ], + "data": "9Yid3B3uRXyw", + "programIdIndex": 5, + "stackHeight": null + } + ], + "recentBlockhash": "14tvFMnmt5uugsBUyWWvdBC6YiD6bcWbNUQc2Tr1aLc5" + }, + "signatures": [ + "x1x9c9YJwvMe9fKJ1etuSEQvjstXHsHH4AGFLBFAGv564ezffyeDmfPi5oHLLhHZ15pBENLVyyBGZRVY3GXu7NK" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 0, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program Vote111111111111111111111111111111111111111 invoke [1]", + "Program Vote111111111111111111111111111111111111111 success" + ], + "postBalances": [ + 8900415167, + 272411, + 1 + ], + "postTokenBalances": [], + "preBalances": [ + 8900415217, + 272411, + 1 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "2gb1u6kcg3V5MfhoycoEL6m6JWgQAmdkpJSXoUa4ogVf", + "Fo7TvsvnVzsVsntZX4bnfH9h3MffzxTGtzQyvm8sTLtP", + "Vote111111111111111111111111111111111111111" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 1, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 1, + 0 + ], + "data": "Fk63Pvyak29TbGXpE2Lwz5sSCzhNdG5DLVnfiBZBkMgazajyeLBZzKECbBUxETBJhpzkWTodGw6pMLoLygdjREwer3urL9cNoKzndAFuwucV6nMKUePk649zMvZXuP4WCUgk6pjuXigR8Wux32vCszFfJBfqGj", + "programIdIndex": 2, + "stackHeight": null + } + ], + "recentBlockhash": "9RgfP18ApUKiWXyVv74ZBCoWX1X8pWb463FrVQCj4oM" + }, + "signatures": [ + "4GvrQHF1BuoDyPvT7Vs3TLSfzcSMJscB1fz8Ro36dMncFGYRgg5nJnSpy423MBLqewdn2UNz4Jz7Ze1xfKagz9eH" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 0, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program Vote111111111111111111111111111111111111111 invoke [1]", + "Program Vote111111111111111111111111111111111111111 success" + ], + "postBalances": [ + 8994813570, + 264520, + 1 + ], + "postTokenBalances": [], + "preBalances": [ + 8994813620, + 264520, + 1 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "7jtuen1N95bBNyAV5dbcx6enYT24qKqHobyHQZnyRoTD", + "Fuwdpk6BQq6hPsEWPtmvyujchKYAb6gMA7Z9rKezuoxS", + "Vote111111111111111111111111111111111111111" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 1, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 1, + 0 + ], + "data": "Fk63Pvyak29TbGXpE2Lwz5sSCzhNdG5DLVnfiBZBkMgazajyeLBZzKECbBUxETBJhpzkWTodGw6pMLoLygdjREwer3urL9cNoKzndAFuwucV6nMKUePk649zMvZXuP4WCUgk6pjuXigR8Wux32vCszFfJBfqGj", + "programIdIndex": 2, + "stackHeight": null + } + ], + "recentBlockhash": "9RgfP18ApUKiWXyVv74ZBCoWX1X8pWb463FrVQCj4oM" + }, + "signatures": [ + "2hWDjjzuEPtuWXyxh7UJscffuDDV7wjgwDrB4LKezEMpJe3PvwZsCHH2tQaYapRaYgoCuxxMxusHsr8gUcsWaDyh" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 29692, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN invoke [1]", + "Program log: Instruction: Click", + "Program log: Clicker CfXww3YzNBQUEtS4ZJeEMZtAEbsym7BAKYUdPsQ1qqrJ", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN consumed 29692 of 200000 compute units", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN success" + ], + "postBalances": [ + 2947150, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "postTokenBalances": [], + "preBalances": [ + 2947200, + 11492, + 14212, + 11492, + 0, + 11152 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "CfXww3YzNBQUEtS4ZJeEMZtAEbsym7BAKYUdPsQ1qqrJ", + "12p6G2ShLgRbxBpCqDue2zed3UGqnLmrxHNmCrhTR1QS", + "9FXCusMeR26k1LkDixLF2dw1AnBX8SsB2cspSSi3BcKE", + "AogE6CYJgaLGXug6D6oTuzZsKJAptaCRD2sLTAMqg1Dm", + "Sysvar1nstructions1111111111111111111111111", + "turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 4, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 3, + 1, + 2, + 0, + 4 + ], + "data": "9Yid3B3uRXyz", + "programIdIndex": 5, + "stackHeight": null + } + ], + "recentBlockhash": "5ieYGqrkBLQBp5QgEw1qk9TSM7bh1agZF16RT5nJQsjj" + }, + "signatures": [ + "5hwa9WgTik1y13XNZxxXViCbjRqhzgAGtALYAmuvQi934FNFNrjPAiREQiW5HnJGUDiyp9ANMeB5Sahk3NfNDRR6" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 328824, + "err": null, + "fee": 50, + "innerInstructions": [ + { + "index": 2, + "instructions": [ + { + "accounts": [ + 17 + ], + "data": "8YGwT5LUTP4", + "programIdIndex": 15, + "stackHeight": 2 + }, + { + "accounts": [ + 16 + ], + "data": "AsRPEhA6Qs4gQCF7F6cofTsuBDbrBbFbaw5CQ6ceeNF4myVnsfFJtriYBe6adGrQatTrdAY7a1TzMsGRFLBUtZVoToquyyDt3x3G5D2X5g7pUa8DuEZ2AHFK4jJthUV3ytsRDUgDVnPjq2dKcWBc5ZDQKw4EGxaS5U7bofeCxX2vZfWNvCswBx1RABdTaPJJPyhxyDqiT6RfYkL2m6EGAW2dmjLpR4pHmQhC85MnCGKJRd7JR93L5h1EujCBE2gto6wdUUnBFDHGL5SWEKdcEkudNrtUGjCj4R3EnnTBMdmVKFDw8unwhdvGbXnZxxnYDJo31p2e7vVntzaH9MoJ1AGiCAj3VmDYujbJvknMz92Ehk9dihov3uoBrwiJo7X2H7kS7HkfdYS3mGKdom4ZRFAv4vKLjg2Uk9NtE54wVQcqAUCT92e696RnU8gxAc8oeSKf8gFJGHFg1Qrekk9LdaR95Bm7w36iDTB7xovZHkLQay16ZbCv3SC8YRyeqmBfMguY8GJDKXobXg6njWvFVZtVYViLMy9t7o8dZmy9swmuQFYPeRs6fddgQKYQxw", + "programIdIndex": 13, + "stackHeight": 2 + }, + { + "accounts": [ + 0, + 1 + ], + "data": "11112L11uQJhYT72kZ1iSnLC4yTJBhW25Zvv2dhge4xi6PgvdPK711MPj6dqaW9Q1Act58", + "programIdIndex": 6, + "stackHeight": 2 + }, + { + "accounts": [ + 12, + 6, + 17, + 10, + 8, + 11, + 3, + 2, + 4 + ], + "data": "7MHiQP8ahsZ7exLBXWsfg2PXgutcWwygoSu2YxVxohLeXDRFUKhVLRz9XRvPvP7wvMK68dufmtWRJYfJgGQkKVJh4UoibKpaSHDjWZd1d1GEnkLMdBPhmZcVGV7Df8LXknCM8gAfqE1gtF8Mey8WfZbuq", + "programIdIndex": 15, + "stackHeight": 2 + }, + { + "accounts": [ + 4, + 2, + 10, + 3, + 6, + 8 + ], + "data": "2", + "programIdIndex": 11, + "stackHeight": 3 + }, + { + "accounts": [ + 3 + ], + "data": "84eT", + "programIdIndex": 8, + "stackHeight": 4 + }, + { + "accounts": [ + 4, + 2 + ], + "data": "11112Um5QtBM9RVwhzeb2i4U2mPJ7krjBF48k17qACUVhhTNRzdmH1adRRGPABHBdaEx67", + "programIdIndex": 6, + "stackHeight": 4 + }, + { + "accounts": [ + 2 + ], + "data": "P", + "programIdIndex": 8, + "stackHeight": 4 + }, + { + "accounts": [ + 2, + 3 + ], + "data": "6SsgWxU3VsJsq1Mht5HyhzQLbcTLc5nrKuFyytmLcmRv9", + "programIdIndex": 8, + "stackHeight": 4 + }, + { + "accounts": [ + 3, + 2, + 3 + ], + "data": "ndTgmR9QMvT7W", + "programIdIndex": 8, + "stackHeight": 3 + } + ] + } + ], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program ComputeBudget111111111111111111111111111111 invoke [1]", + "Program ComputeBudget111111111111111111111111111111 success", + "Program ComputeBudget111111111111111111111111111111 invoke [1]", + "Program ComputeBudget111111111111111111111111111111 success", + "Program EitxJuv2iBjsg2d7jVy2LDC1e2zBrx4GB5Y9h2Ko3A9Y invoke [1]", + "Program FJu4E1BDYKVg7aTWdwATZRUvytJZ8ZZ2gQuvPfMWAz9y invoke [2]", + "Program FJu4E1BDYKVg7aTWdwATZRUvytJZ8ZZ2gQuvPfMWAz9y consumed 4505 of 323327 compute units", + "Program return: FJu4E1BDYKVg7aTWdwATZRUvytJZ8ZZ2gQuvPfMWAz9y AA==", + "Program FJu4E1BDYKVg7aTWdwATZRUvytJZ8ZZ2gQuvPfMWAz9y success", + "Program BgG35GxoaMgmiam3EJzcwivwQ2DTYGPTLfUCg7bhiH6V invoke [2]", + "Program BgG35GxoaMgmiam3EJzcwivwQ2DTYGPTLfUCg7bhiH6V consumed 184250 of 312286 compute units", + "Program BgG35GxoaMgmiam3EJzcwivwQ2DTYGPTLfUCg7bhiH6V success", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program FJu4E1BDYKVg7aTWdwATZRUvytJZ8ZZ2gQuvPfMWAz9y invoke [2]", + "Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL invoke [3]", + "Program log: CreateIdempotent", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [4]", + "Program log: Instruction: GetAccountDataSize", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 2941 of 91911 compute units", + "Program return: TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb qgAAAAAAAAA=", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success", + "Program 11111111111111111111111111111111 invoke [4]", + "Program 11111111111111111111111111111111 success", + "Program log: Initialize the associated token account", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [4]", + "Program log: Instruction: InitializeImmutableOwner", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 1924 of 84111 compute units", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [4]", + "Program log: Instruction: InitializeAccount3", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 5175 of 79770 compute units", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success", + "Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL consumed 24597 of 98798 compute units", + "Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL success", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [3]", + "Program log: Instruction: MintToChecked", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 5383 of 70773 compute units", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success", + "Program log: Warp route transfer completed from origin: 1399811149, recipient: 6jyr2GWhaTbzjscMz8nNWRuD7Zkng3adCLp8QBeoUHVB, remote_amount: 80000000", + "Program FJu4E1BDYKVg7aTWdwATZRUvytJZ8ZZ2gQuvPfMWAz9y consumed 73912 of 119663 compute units", + "Program FJu4E1BDYKVg7aTWdwATZRUvytJZ8ZZ2gQuvPfMWAz9y success", + "Program log: Hyperlane inbox processed message 0x95a06dba7265e86a56ad2c2f0fa9e2888cb304f0cd0d2bddbdb4915f5df16f5a", + "Program EitxJuv2iBjsg2d7jVy2LDC1e2zBrx4GB5Y9h2Ko3A9Y consumed 328824 of 361706 compute units", + "Program EitxJuv2iBjsg2d7jVy2LDC1e2zBrx4GB5Y9h2Ko3A9Y success" + ], + "postBalances": [ + 29970800, + 12580, + 20264, + 41072, + 92638960, + 11832, + 1, + 1, + 11152, + 0, + 101150754, + 7277632, + 0, + 11152, + 11152, + 11152, + 78336, + 28492 + ], + "postTokenBalances": [ + { + "accountIndex": 2, + "mint": "BeRUj3h7BqkbdfFU7FBNYbodgf8GCHodzKvF9aVjNNfL", + "owner": "6jyr2GWhaTbzjscMz8nNWRuD7Zkng3adCLp8QBeoUHVB", + "programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "uiTokenAmount": { + "amount": "80000000", + "decimals": 9, + "uiAmount": 0.08, + "uiAmountString": "0.08" + } + } + ], + "preBalances": [ + 29983430, + 0, + 0, + 41072, + 92659224, + 11832, + 1, + 1, + 11152, + 0, + 101150754, + 7277632, + 0, + 11152, + 11152, + 11152, + 78336, + 28492 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "G6fWKEBSrDtR8Uzx4kdVRRhrsHZ6zmGX1G2TYMmMgK6u", + "2BZdvWiiTfeeUZKAK9R8UPrV8s8HLNGbdEUtjED52ayf", + "5Py6bEKmK84Wp4Bm9fG9JnjoDazyDrEM4KRrLZLbLexR", + "BeRUj3h7BqkbdfFU7FBNYbodgf8GCHodzKvF9aVjNNfL", + "CijxTbPs9JZxTUfo8Hmz2imxzHtKnDFD3kZP3RPy34uJ", + "GKkC5NTFvHkjkPprY4hWbtpsCAt4bhVG9zPx7N8rbSuA", + "11111111111111111111111111111111", + "ComputeBudget111111111111111111111111111111", + "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV", + "6jyr2GWhaTbzjscMz8nNWRuD7Zkng3adCLp8QBeoUHVB", + "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL", + "AUiZ4KyQ25f9uRFPJ7GRaYLwFfd8TqL76xYfymWQ7H41", + "BgG35GxoaMgmiam3EJzcwivwQ2DTYGPTLfUCg7bhiH6V", + "EitxJuv2iBjsg2d7jVy2LDC1e2zBrx4GB5Y9h2Ko3A9Y", + "FJu4E1BDYKVg7aTWdwATZRUvytJZ8ZZ2gQuvPfMWAz9y", + "HRmKtAk47FtpiJuNvwZh66CmWkCFuXN18DN4z4b4cRzH", + "HXLdLtBLj3yn384tpZCdN3SMDdN9hDzBsvNxCND3gQCt" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 12, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [], + "data": "L5k7BD", + "programIdIndex": 7, + "stackHeight": null + }, + { + "accounts": [], + "data": "3DTZbgwsozUF", + "programIdIndex": 7, + "stackHeight": null + }, + { + "accounts": [ + 0, + 6, + 5, + 12, + 1, + 17, + 9, + 13, + 16, + 15, + 6, + 17, + 10, + 8, + 11, + 3, + 2, + 4 + ], + "data": "FTTpGG1B19Dvee6GQUXvdRAGVDD2KsGVzEYf41TUxTUZR4AtU9NZiyDkKioWpAgvXfTcn6B3JKbwvQy8n1bWorY2VvWnzwaJivpNKea2HxaSWqZNCJQMUM4CcmeWVnUfjCp8R8V5Pii6RY4MAd66MJLioPNnL9UQYyZ6eT64ncWhuoeLqC8yuESqcsxGRSRmR7cX6fASBoDZVEMxoQ9X4ct9Q2a9PWKivVkTReLZyEHHT4NTTWek13DawFDehydmDcWpLSUJKSmuFwKUisJSTfuzTsgqfSxKtp6KE2hMzVQ46PZpw5XSFqNUp4rGuTYYPcH2uF51wLKNQ8ABY6iXLAe6Lwri7C491RqKvKKM1xcUdSQNNKykNjVcq1pLZ4jKuf2idakmgfACytryMQxAeyJxxZ2d1pDmHxy2kwZTF8LW3quTnxHvaLroQJfr1vjFddfBhnnM4MRuwQnEwhmLcohPRncqcRroByhEwGugYw1XJMvyH8D8sP1MWm9SRKRxSz88QgGfGhtg8eGPVE5fZPnQY46JFUi7JyznxjBoRTjd472aCpP", + "programIdIndex": 14, + "stackHeight": null + } + ], + "recentBlockhash": "5ieYGqrkBLQBp5QgEw1qk9TSM7bh1agZF16RT5nJQsjj" + }, + "signatures": [ + "4atym7S78qpT4k9mUFWc2tu7KAHcqUxDn8fxP7RL8utooTi6frJtq9xFbM6MSSqDffTGpRSmEAMtCYisRe5m8KXb" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 25153, + "err": null, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN invoke [1]", + "Program log: Instruction: Click", + "Program log: Clicker 9T5ntKpqFJKx32hM2sZrGtSKUzRj4iyq74LrYGsdM8Dp", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN consumed 25153 of 200000 compute units", + "Program turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN success" + ], + "postBalances": [ + 992550, + 11492, + 11492, + 14212, + 0, + 11152 + ], + "postTokenBalances": [], + "preBalances": [ + 992600, + 11492, + 11492, + 14212, + 0, + 11152 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "9T5ntKpqFJKx32hM2sZrGtSKUzRj4iyq74LrYGsdM8Dp", + "5YDa9bxTHEBsWfERWRxNVAe7NgjGtpRmjtGuMSXjGhdu", + "4SCCH7gkNRBXtY2j1a6rwCfsWdgDTZDap7msk72Rkefo", + "9FXCusMeR26k1LkDixLF2dw1AnBX8SsB2cspSSi3BcKE", + "Sysvar1nstructions1111111111111111111111111", + "turboe9kMc3mSR8BosPkVzoHUfn5RVNzZhkrT2hdGxN" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 4, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 2, + 1, + 3, + 0, + 4 + ], + "data": "9Yid3B3uRXy9", + "programIdIndex": 5, + "stackHeight": null + } + ], + "recentBlockhash": "3veDZBERK9bD5PzCuygjYquZ85aUnU24kRuUwWRzTtkc" + }, + "signatures": [ + "3sTCx7dEzUALDq3D2zkLyftawaxQZBpHaiqtkvSxkd6QUuuuyjgLUvnrhmkMt5j2rVooqqEscJUAEaQB2GHgXgAf" + ] + }, + "version": "legacy" + }, + { + "meta": { + "computeUnitsConsumed": 38067, + "err": { + "InstructionError": [ + 1, + { + "Custom": 5 + } + ] + }, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program ComputeBudget111111111111111111111111111111 invoke [1]", + "Program ComputeBudget111111111111111111111111111111 success", + "Program EitxJuv2iBjsg2d7jVy2LDC1e2zBrx4GB5Y9h2Ko3A9Y invoke [1]", + "Program log: Custom program error: 0x5", + "Program EitxJuv2iBjsg2d7jVy2LDC1e2zBrx4GB5Y9h2Ko3A9Y consumed 38067 of 1400000 compute units", + "Program EitxJuv2iBjsg2d7jVy2LDC1e2zBrx4GB5Y9h2Ko3A9Y failed: custom program error: 0x5" + ], + "postBalances": [ + 3105342080, + 12580, + 20264, + 41072, + 92638960, + 11832, + 1, + 1, + 11152, + 0, + 101150754, + 7277632, + 0, + 11152, + 11152, + 11152, + 78336, + 28492 + ], + "postTokenBalances": [ + { + "accountIndex": 2, + "mint": "BeRUj3h7BqkbdfFU7FBNYbodgf8GCHodzKvF9aVjNNfL", + "owner": "6jyr2GWhaTbzjscMz8nNWRuD7Zkng3adCLp8QBeoUHVB", + "programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "uiTokenAmount": { + "amount": "80000000", + "decimals": 9, + "uiAmount": 0.08, + "uiAmountString": "0.08" + } + } + ], + "preBalances": [ + 3105342130, + 12580, + 20264, + 41072, + 92638960, + 11832, + 1, + 1, + 11152, + 0, + 101150754, + 7277632, + 0, + 11152, + 11152, + 11152, + 78336, + 28492 + ], + "preTokenBalances": [ + { + "accountIndex": 2, + "mint": "BeRUj3h7BqkbdfFU7FBNYbodgf8GCHodzKvF9aVjNNfL", + "owner": "6jyr2GWhaTbzjscMz8nNWRuD7Zkng3adCLp8QBeoUHVB", + "programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "uiTokenAmount": { + "amount": "80000000", + "decimals": 9, + "uiAmount": 0.08, + "uiAmountString": "0.08" + } + } + ], + "rewards": [], + "status": { + "Err": { + "InstructionError": [ + 1, + { + "Custom": 5 + } + ] + } + } + }, + "transaction": { + "message": { + "accountKeys": [ + "G5FM3UKwcBJ47PwLWLLY1RQpqNtTMgnqnd6nZGcJqaBp", + "2BZdvWiiTfeeUZKAK9R8UPrV8s8HLNGbdEUtjED52ayf", + "5Py6bEKmK84Wp4Bm9fG9JnjoDazyDrEM4KRrLZLbLexR", + "BeRUj3h7BqkbdfFU7FBNYbodgf8GCHodzKvF9aVjNNfL", + "CijxTbPs9JZxTUfo8Hmz2imxzHtKnDFD3kZP3RPy34uJ", + "GKkC5NTFvHkjkPprY4hWbtpsCAt4bhVG9zPx7N8rbSuA", + "11111111111111111111111111111111", + "ComputeBudget111111111111111111111111111111", + "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV", + "6jyr2GWhaTbzjscMz8nNWRuD7Zkng3adCLp8QBeoUHVB", + "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL", + "AUiZ4KyQ25f9uRFPJ7GRaYLwFfd8TqL76xYfymWQ7H41", + "BgG35GxoaMgmiam3EJzcwivwQ2DTYGPTLfUCg7bhiH6V", + "EitxJuv2iBjsg2d7jVy2LDC1e2zBrx4GB5Y9h2Ko3A9Y", + "FJu4E1BDYKVg7aTWdwATZRUvytJZ8ZZ2gQuvPfMWAz9y", + "HRmKtAk47FtpiJuNvwZh66CmWkCFuXN18DN4z4b4cRzH", + "HXLdLtBLj3yn384tpZCdN3SMDdN9hDzBsvNxCND3gQCt" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 12, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [], + "data": "K1FDJ7", + "programIdIndex": 7, + "stackHeight": null + }, + { + "accounts": [ + 0, + 6, + 5, + 12, + 1, + 17, + 9, + 13, + 16, + 15, + 6, + 17, + 10, + 8, + 11, + 3, + 2, + 4 + ], + "data": "FTTpGG1B19Dvee6GQUXvdRAGVDD2KsGVzEYf41TUxTUZR4AtU9NZiyDkKioWpAgvXfTcn6B3JKbwvQy8n1bWorY2VvWnzwaJivpNKea2HxaSWqZNCJQMUM4CcmeWVnUfjCp8R8V5Pii6RY4MAd66MJLioPNnL9UQYyZ6eT64ncWhuoeLqC8yuESqcsxGRSRmR7cX6fASBoDZVEMxoQ9X4ct9Q2a9PWKivVkTReLZyEHHT4NTTWek13DawFDehydmDcWpLSUJKSmuFwKUisJSTfuzTsgqfSxKtp6KE2hMzVQ46PZpw5XSFqNUp4rGuTYYPcH2uF51wLKNQ8ABY6iXLAe6Lwri7C491RqKvKKM1xcUdSQNNKykNjVcq1pLZ4jKuf2idakmgfACytryMQxAeyJxxZ2d1pDmHxy2kwZTF8LW3quTnxHvaLroQJfr1vjFddfBhnnM4MRuwQnEwhmLcohPRncqcRroByhEwGugYw1XJMvyH8D8sP1MWm9SRKRxSz88QgGfGhtg8eGPVE5fZPnQY46JFUi7JyznxjBoRTjd472aCpP", + "programIdIndex": 14, + "stackHeight": null + } + ], + "recentBlockhash": "9RgfP18ApUKiWXyVv74ZBCoWX1X8pWb463FrVQCj4oM" + }, + "signatures": [ + "5F5PUWAZcBmuGaejN4qr9BjbXCMNSY2ujQ2eUTmiftYaKEE3VoU5uowttiY73ksqpGM3R4CBB7TFKBP1j3R3EUM9" + ] + }, + "version": "legacy" + } + ] +} \ No newline at end of file diff --git a/rust/main/chains/hyperlane-sealevel/src/log_meta_composer/delivery_message_reverted_txn.json b/rust/main/chains/hyperlane-sealevel/src/log_meta_composer/delivery_message_reverted_txn.json new file mode 100644 index 0000000000..0001db5074 --- /dev/null +++ b/rust/main/chains/hyperlane-sealevel/src/log_meta_composer/delivery_message_reverted_txn.json @@ -0,0 +1,175 @@ +{ + "blockTime": 1734974925, + "meta": { + "computeUnitsConsumed": 38067, + "err": { + "InstructionError": [ + 1, + { + "Custom": 5 + } + ] + }, + "fee": 50, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program ComputeBudget111111111111111111111111111111 invoke [1]", + "Program ComputeBudget111111111111111111111111111111 success", + "Program EitxJuv2iBjsg2d7jVy2LDC1e2zBrx4GB5Y9h2Ko3A9Y invoke [1]", + "Program log: Custom program error: 0x5", + "Program EitxJuv2iBjsg2d7jVy2LDC1e2zBrx4GB5Y9h2Ko3A9Y consumed 38067 of 1400000 compute units", + "Program EitxJuv2iBjsg2d7jVy2LDC1e2zBrx4GB5Y9h2Ko3A9Y failed: custom program error: 0x5" + ], + "postBalances": [ + 3105342080, + 12580, + 20264, + 41072, + 92638960, + 11832, + 1, + 1, + 11152, + 0, + 101150754, + 7277632, + 0, + 11152, + 11152, + 11152, + 78336, + 28492 + ], + "postTokenBalances": [ + { + "accountIndex": 2, + "mint": "BeRUj3h7BqkbdfFU7FBNYbodgf8GCHodzKvF9aVjNNfL", + "owner": "6jyr2GWhaTbzjscMz8nNWRuD7Zkng3adCLp8QBeoUHVB", + "programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "uiTokenAmount": { + "amount": "80000000", + "decimals": 9, + "uiAmount": 0.08, + "uiAmountString": "0.08" + } + } + ], + "preBalances": [ + 3105342130, + 12580, + 20264, + 41072, + 92638960, + 11832, + 1, + 1, + 11152, + 0, + 101150754, + 7277632, + 0, + 11152, + 11152, + 11152, + 78336, + 28492 + ], + "preTokenBalances": [ + { + "accountIndex": 2, + "mint": "BeRUj3h7BqkbdfFU7FBNYbodgf8GCHodzKvF9aVjNNfL", + "owner": "6jyr2GWhaTbzjscMz8nNWRuD7Zkng3adCLp8QBeoUHVB", + "programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "uiTokenAmount": { + "amount": "80000000", + "decimals": 9, + "uiAmount": 0.08, + "uiAmountString": "0.08" + } + } + ], + "rewards": [], + "status": { + "Err": { + "InstructionError": [ + 1, + { + "Custom": 5 + } + ] + } + } + }, + "slot": 35781852, + "transaction": { + "message": { + "accountKeys": [ + "G5FM3UKwcBJ47PwLWLLY1RQpqNtTMgnqnd6nZGcJqaBp", + "2BZdvWiiTfeeUZKAK9R8UPrV8s8HLNGbdEUtjED52ayf", + "5Py6bEKmK84Wp4Bm9fG9JnjoDazyDrEM4KRrLZLbLexR", + "BeRUj3h7BqkbdfFU7FBNYbodgf8GCHodzKvF9aVjNNfL", + "CijxTbPs9JZxTUfo8Hmz2imxzHtKnDFD3kZP3RPy34uJ", + "GKkC5NTFvHkjkPprY4hWbtpsCAt4bhVG9zPx7N8rbSuA", + "11111111111111111111111111111111", + "ComputeBudget111111111111111111111111111111", + "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV", + "6jyr2GWhaTbzjscMz8nNWRuD7Zkng3adCLp8QBeoUHVB", + "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL", + "AUiZ4KyQ25f9uRFPJ7GRaYLwFfd8TqL76xYfymWQ7H41", + "BgG35GxoaMgmiam3EJzcwivwQ2DTYGPTLfUCg7bhiH6V", + "EitxJuv2iBjsg2d7jVy2LDC1e2zBrx4GB5Y9h2Ko3A9Y", + "FJu4E1BDYKVg7aTWdwATZRUvytJZ8ZZ2gQuvPfMWAz9y", + "HRmKtAk47FtpiJuNvwZh66CmWkCFuXN18DN4z4b4cRzH", + "HXLdLtBLj3yn384tpZCdN3SMDdN9hDzBsvNxCND3gQCt" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 12, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [], + "data": "K1FDJ7", + "programIdIndex": 7, + "stackHeight": null + }, + { + "accounts": [ + 0, + 6, + 5, + 12, + 1, + 17, + 9, + 13, + 16, + 15, + 6, + 17, + 10, + 8, + 11, + 3, + 2, + 4 + ], + "data": "FTTpGG1B19Dvee6GQUXvdRAGVDD2KsGVzEYf41TUxTUZR4AtU9NZiyDkKioWpAgvXfTcn6B3JKbwvQy8n1bWorY2VvWnzwaJivpNKea2HxaSWqZNCJQMUM4CcmeWVnUfjCp8R8V5Pii6RY4MAd66MJLioPNnL9UQYyZ6eT64ncWhuoeLqC8yuESqcsxGRSRmR7cX6fASBoDZVEMxoQ9X4ct9Q2a9PWKivVkTReLZyEHHT4NTTWek13DawFDehydmDcWpLSUJKSmuFwKUisJSTfuzTsgqfSxKtp6KE2hMzVQ46PZpw5XSFqNUp4rGuTYYPcH2uF51wLKNQ8ABY6iXLAe6Lwri7C491RqKvKKM1xcUdSQNNKykNjVcq1pLZ4jKuf2idakmgfACytryMQxAeyJxxZ2d1pDmHxy2kwZTF8LW3quTnxHvaLroQJfr1vjFddfBhnnM4MRuwQnEwhmLcohPRncqcRroByhEwGugYw1XJMvyH8D8sP1MWm9SRKRxSz88QgGfGhtg8eGPVE5fZPnQY46JFUi7JyznxjBoRTjd472aCpP", + "programIdIndex": 14, + "stackHeight": null + } + ], + "recentBlockhash": "9RgfP18ApUKiWXyVv74ZBCoWX1X8pWb463FrVQCj4oM" + }, + "signatures": [ + "5F5PUWAZcBmuGaejN4qr9BjbXCMNSY2ujQ2eUTmiftYaKEE3VoU5uowttiY73ksqpGM3R4CBB7TFKBP1j3R3EUM9" + ] + }, + "version": "legacy" +} \ No newline at end of file diff --git a/rust/main/chains/hyperlane-sealevel/src/log_meta_composer/dispatch_message_versioned_txn.json b/rust/main/chains/hyperlane-sealevel/src/log_meta_composer/dispatch_message_versioned_txn.json new file mode 100644 index 0000000000..0fcd409407 --- /dev/null +++ b/rust/main/chains/hyperlane-sealevel/src/log_meta_composer/dispatch_message_versioned_txn.json @@ -0,0 +1,565 @@ +{ + "blockTime": 1734978752, + "meta": { + "computeUnitsConsumed": 285017, + "err": null, + "fee": 50150, + "innerInstructions": [ + { + "index": 4, + "instructions": [ + { + "accounts": [ + 14 + ], + "data": "84eT", + "programIdIndex": 24, + "stackHeight": 2 + }, + { + "accounts": [ + 0, + 3 + ], + "data": "11112Um5QtBM9RVwhzeb2i4U2mPJ7krjBF48k17qACUVhhTNRzdmH1adRRGPABHBdaEx67", + "programIdIndex": 9, + "stackHeight": 2 + }, + { + "accounts": [ + 3 + ], + "data": "P", + "programIdIndex": 24, + "stackHeight": 2 + }, + { + "accounts": [ + 3, + 14 + ], + "data": "6U6TaA8c9FiMd3yMvYM3RZr5L251Z6XBhE7CZMpM6UqrA", + "programIdIndex": 24, + "stackHeight": 2 + } + ] + }, + { + "index": 5, + "instructions": [ + { + "accounts": [ + 1, + 22, + 16, + 0 + ], + "data": "iSvRA2gPzQThi", + "programIdIndex": 10, + "stackHeight": 2 + }, + { + "accounts": [ + 17, + 14, + 3, + 15 + ], + "data": "iQVR3BvfMWGrC", + "programIdIndex": 24, + "stackHeight": 2 + } + ] + }, + { + "index": 7, + "instructions": [ + { + "accounts": [ + 3, + 14, + 0, + 0 + ], + "data": "tBUevJEscuw5z", + "programIdIndex": 24, + "stackHeight": 2 + }, + { + "accounts": [ + 19, + 29, + 9, + 26, + 0, + 2, + 5 + ], + "data": "2cRRtjzqbhv2aDpjzg6LXy6C7SLpDEsUatfoywKz3m1ifhrSUVpNtpkHK1dj5rchz1z3j8CtHuW5T2ezWvBJVLET7tCge9vDB2AF5EM8LPUdk1EGpKZam3tQNV1W7xMmt4iDto9DAhMQrbR6uG1Vb66vdH6kjEbHWTMbhUim4BP661Gpk1yyPTHjQUB", + "programIdIndex": 28, + "stackHeight": 2 + }, + { + "accounts": [ + 0, + 19 + ], + "data": "3Bxs3zrfFUZbEPqZ", + "programIdIndex": 9, + "stackHeight": 3 + }, + { + "accounts": [ + 0, + 5 + ], + "data": "1111614MkWaU9KqTDJLQwrzvidXGxXXwfY2mfoKeoqQBJrMaZskjDkgQ6pE2yERkoudxWv", + "programIdIndex": 9, + "stackHeight": 3 + }, + { + "accounts": [ + 9, + 0, + 20, + 2, + 6, + 21, + 31 + ], + "data": "6ZKyf8sS97C2Z34TUpXiGQBLPqCXXtBbZ2gHBxwLacXBZ6P8XPoaBUwFzxs1H", + "programIdIndex": 30, + "stackHeight": 2 + }, + { + "accounts": [ + 0, + 21 + ], + "data": "3Bxs46hkTBuBteUP", + "programIdIndex": 9, + "stackHeight": 3 + }, + { + "accounts": [ + 0, + 6 + ], + "data": "11115GPcU8wJysz1fCGMm1QuqnxK4pGnpF8XeC1S6D1r5qH2HggEcqE6oAfk4xFS7paC4J", + "programIdIndex": 9, + "stackHeight": 3 + } + ] + } + ], + "loadedAddresses": { + "readonly": [ + "So11111111111111111111111111111111111111112", + "SysvarRent111111111111111111111111111111111", + "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr", + "noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV", + "HXLdLtBLj3yn384tpZCdN3SMDdN9hDzBsvNxCND3gQCt", + "EitxJuv2iBjsg2d7jVy2LDC1e2zBrx4GB5Y9h2Ko3A9Y", + "HP8WWjbxdeGhW3NRdBpWpARKgphEAQP5htG4AfbXqvRE", + "Hs7KVBU67nBnWhDPZkEFwWqrFMUfJbmY2DQ4gmCZfaZp", + "3Wp4qKkgf4tjXz1soGyTSndCgBPLZFSrZkiDZ8Qp9EEj" + ], + "writable": [ + "BeRUj3h7BqkbdfFU7FBNYbodgf8GCHodzKvF9aVjNNfL", + "E6AFbRkMwidQyBQ872e9kbVT2ZqybmM6dJ2Zaa6sVxJq", + "Du5B9P5uPhZ1nWubRTLrhoh6Va1kpuVXM1FpM7wJSPSu", + "FQjMvPVo9ReLZwbojjBkdjda4kSjdqoRjrARfN9qX9FJ", + "GgHsBMbgswDHEDoMvoYGhqsepjdsxCAjk2W22yEvz1Em", + "FKKDGYumoKjQjVEejff6MD1FpKuBs6SdgAobVdJdE21B", + "FvGvXJf6bd2wx8FxzsYNzd2uHaPy7JTkmuKiVvSTt7jm", + "ABb3i11z7wKoGCfeRQNQbVYWjAm7jG7HzZnDLV4RKRbK" + ] + }, + "logMessages": [ + "Program ComputeBudget111111111111111111111111111111 invoke [1]", + "Program ComputeBudget111111111111111111111111111111 success", + "Program ComputeBudget111111111111111111111111111111 invoke [1]", + "Program ComputeBudget111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [1]", + "Program 11111111111111111111111111111111 success", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [1]", + "Program log: Instruction: InitializeAccount", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 3442 of 500000 compute units", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", + "Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL invoke [1]", + "Program log: CreateIdempotent", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [2]", + "Program log: Instruction: GetAccountDataSize", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 2941 of 488247 compute units", + "Program return: TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb qgAAAAAAAAA=", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program log: Initialize the associated token account", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [2]", + "Program log: Instruction: InitializeImmutableOwner", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 1924 of 480447 compute units", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [2]", + "Program log: Instruction: InitializeAccount3", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 5175 of 476106 compute units", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success", + "Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL consumed 26000 of 496558 compute units", + "Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL success", + "Program whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc invoke [1]", + "Program log: Instruction: SwapV2", + "Program log: fee_growth: 6962398081", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]", + "Program log: Instruction: TransferChecked", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 6239 of 425251 compute units", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [2]", + "Program log: Instruction: TransferChecked", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 8356 of 410679 compute units", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success", + "Program whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc consumed 71018 of 470558 compute units", + "Program whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc success", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [1]", + "Program log: Instruction: CloseAccount", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 2914 of 399540 compute units", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", + "Program FJu4E1BDYKVg7aTWdwATZRUvytJZ8ZZ2gQuvPfMWAz9y invoke [1]", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [2]", + "Program log: Instruction: BurnChecked", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 5921 of 376214 compute units", + "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success", + "Program EitxJuv2iBjsg2d7jVy2LDC1e2zBrx4GB5Y9h2Ko3A9Y invoke [2]", + "Program 11111111111111111111111111111111 invoke [3]", + "Program 11111111111111111111111111111111 success", + "Program log: Protocol fee of 0 paid from 7xkuDw5Lxs5nnVGQTBr65sdwXBRjgmuzXC2hsEfHBhRC to FKKDGYumoKjQjVEejff6MD1FpKuBs6SdgAobVdJdE21B", + "Program 11111111111111111111111111111111 invoke [3]", + "Program 11111111111111111111111111111111 success", + "Program log: Dispatched message to 1399811149, ID 0xdf5b603df4e3e3e318f222cf6c2ea3aea44893dab86ab34d3dd88f42c87b27d0", + "Program EitxJuv2iBjsg2d7jVy2LDC1e2zBrx4GB5Y9h2Ko3A9Y consumed 81465 of 365453 compute units", + "Program return: EitxJuv2iBjsg2d7jVy2LDC1e2zBrx4GB5Y9h2Ko3A9Y 31tgPfTj4+MY8iLPbC6jrqRIk9q4arNNPdiPQsh7J9A=", + "Program EitxJuv2iBjsg2d7jVy2LDC1e2zBrx4GB5Y9h2Ko3A9Y success", + "Program Hs7KVBU67nBnWhDPZkEFwWqrFMUfJbmY2DQ4gmCZfaZp invoke [2]", + "Program 11111111111111111111111111111111 invoke [3]", + "Program 11111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [3]", + "Program 11111111111111111111111111111111 success", + "Program log: Paid IGP ABb3i11z7wKoGCfeRQNQbVYWjAm7jG7HzZnDLV4RKRbK for 244000 gas for message 0xdf5b…27d0 to 1399811149", + "Program Hs7KVBU67nBnWhDPZkEFwWqrFMUfJbmY2DQ4gmCZfaZp consumed 56083 of 280947 compute units", + "Program Hs7KVBU67nBnWhDPZkEFwWqrFMUfJbmY2DQ4gmCZfaZp success", + "Program log: Warp route transfer completed to destination: 1399811149, recipient: 0x9a83…6889, remote_amount: 17884084", + "Program FJu4E1BDYKVg7aTWdwATZRUvytJZ8ZZ2gQuvPfMWAz9y consumed 181643 of 396626 compute units", + "Program FJu4E1BDYKVg7aTWdwATZRUvytJZ8ZZ2gQuvPfMWAz9y success", + "Program 11111111111111111111111111111111 invoke [1]", + "Program 11111111111111111111111111111111 success" + ], + "postBalances": [ + 14487952, + 0, + 0, + 20264, + 687888, + 21896, + 18292, + 4187500, + 1, + 1, + 9076640, + 7277632, + 11152, + 11152, + 41072, + 53108, + 41900320071, + 19924, + 0, + 84796, + 9928, + 2660002254, + 1000000000, + 9860, + 11152, + 5095104, + 0, + 28492, + 11152, + 8704, + 11152, + 18700 + ], + "postTokenBalances": [ + { + "accountIndex": 3, + "mint": "BeRUj3h7BqkbdfFU7FBNYbodgf8GCHodzKvF9aVjNNfL", + "owner": "7xkuDw5Lxs5nnVGQTBr65sdwXBRjgmuzXC2hsEfHBhRC", + "programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "uiTokenAmount": { + "amount": "0", + "decimals": 9, + "uiAmount": null, + "uiAmountString": "0" + } + }, + { + "accountIndex": 16, + "mint": "So11111111111111111111111111111111111111112", + "owner": "E6AFbRkMwidQyBQ872e9kbVT2ZqybmM6dJ2Zaa6sVxJq", + "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "uiTokenAmount": { + "amount": "41900300147", + "decimals": 9, + "uiAmount": 41.900300147, + "uiAmountString": "41.900300147" + } + }, + { + "accountIndex": 17, + "mint": "BeRUj3h7BqkbdfFU7FBNYbodgf8GCHodzKvF9aVjNNfL", + "owner": "E6AFbRkMwidQyBQ872e9kbVT2ZqybmM6dJ2Zaa6sVxJq", + "programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "uiTokenAmount": { + "amount": "1100478938255", + "decimals": 9, + "uiAmount": 1100.478938255, + "uiAmountString": "1100.478938255" + } + } + ], + "preBalances": [ + 15923196, + 0, + 0, + 0, + 687888, + 0, + 0, + 4182500, + 1, + 1, + 9076640, + 7277632, + 11152, + 11152, + 41072, + 53108, + 41899325071, + 19924, + 0, + 84796, + 9928, + 2659677612, + 1000000000, + 9860, + 11152, + 5095104, + 0, + 28492, + 11152, + 8704, + 11152, + 18700 + ], + "preTokenBalances": [ + { + "accountIndex": 16, + "mint": "So11111111111111111111111111111111111111112", + "owner": "E6AFbRkMwidQyBQ872e9kbVT2ZqybmM6dJ2Zaa6sVxJq", + "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "uiTokenAmount": { + "amount": "41899305147", + "decimals": 9, + "uiAmount": 41.899305147, + "uiAmountString": "41.899305147" + } + }, + { + "accountIndex": 17, + "mint": "BeRUj3h7BqkbdfFU7FBNYbodgf8GCHodzKvF9aVjNNfL", + "owner": "E6AFbRkMwidQyBQ872e9kbVT2ZqybmM6dJ2Zaa6sVxJq", + "programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "uiTokenAmount": { + "amount": "1100496822339", + "decimals": 9, + "uiAmount": 1100.496822339, + "uiAmountString": "1100.496822339" + } + } + ], + "rewards": [], + "status": { + "Ok": null + } + }, + "slot": 35792433, + "transaction": { + "message": { + "accountKeys": [ + "7xkuDw5Lxs5nnVGQTBr65sdwXBRjgmuzXC2hsEfHBhRC", + "AHE9Hs4nZF9NM1VJZHg61jkFBx5cBVaabAYAJFzPAiCk", + "2xieFW4TnPYwRyCf1aT9m6Xry4rBeCRKyPsScByyiAAG", + "GZKxYbwFzZgPti86aMjFTVtFL3pP4wUq38TfQjpGHqg5", + "AkEJURpBDvPEaQcnDum3BywUB5vLHHLoBLm3z7jmMKtG", + "9g87Di4xiYVvBE5F8Atk8xorbbVD8yKqbdHRkFu5HEgw", + "gJRnC4X1pnctZEzdo67Pwx4wfRw5MLFdTa2ostjGv9C", + "BWbHzqYBnWmU6kRg8Upi61hq3gf5u8F3hB886MBhg7wh", + "ComputeBudget111111111111111111111111111111", + "11111111111111111111111111111111", + "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL", + "whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc", + "FJu4E1BDYKVg7aTWdwATZRUvytJZ8ZZ2gQuvPfMWAz9y" + ], + "addressTableLookups": [ + { + "accountKey": "F4omQWQnyM6Fny5CA9KY4xyyNemDrp87ZHXZDwME5M8q", + "readonlyIndexes": [ + 7, + 1, + 3, + 4, + 14, + 15, + 16, + 18, + 21, + 24 + ], + "writableIndexes": [ + 8, + 6, + 9, + 11, + 13, + 17, + 22, + 25 + ] + } + ], + "header": { + "numReadonlySignedAccounts": 1, + "numReadonlyUnsignedAccounts": 6, + "numRequiredSignatures": 3 + }, + "instructions": [ + { + "accounts": [], + "data": "EvSMNP", + "programIdIndex": 8, + "stackHeight": null + }, + { + "accounts": [], + "data": "3gJqkocMWaMm", + "programIdIndex": 8, + "stackHeight": null + }, + { + "accounts": [ + 0, + 1 + ], + "data": "111169tnWyB6yf7otj9gwMZ223az8WhRhw17kSToNJKZLqyX9pDi2Cd13sTA9NPWFfoQpx", + "programIdIndex": 9, + "stackHeight": null + }, + { + "accounts": [ + 1, + 22, + 0, + 23 + ], + "data": "2", + "programIdIndex": 10, + "stackHeight": null + }, + { + "accounts": [ + 0, + 3, + 0, + 14, + 9, + 24 + ], + "data": "2", + "programIdIndex": 11, + "stackHeight": null + }, + { + "accounts": [ + 10, + 24, + 25, + 0, + 15, + 22, + 14, + 1, + 16, + 3, + 17, + 4, + 4, + 4, + 18 + ], + "data": "4AoQRYXBdnCR4Ye4kfVqUSk92cVTu2wqdAePuVxcoJsMUVket7q7Sp2DCT1", + "programIdIndex": 12, + "stackHeight": null + }, + { + "accounts": [ + 1, + 0, + 0 + ], + "data": "A", + "programIdIndex": 10, + "stackHeight": null + }, + { + "accounts": [ + 9, + 26, + 27, + 28, + 19, + 29, + 0, + 2, + 5, + 30, + 20, + 6, + 31, + 21, + 24, + 14, + 3 + ], + "data": "RpjV6TtUSvsqoqZDmaVYK3Bu3EKPjEdZSC4i9viiVTSQ7fcSFrXZVvXuHzpQxzu8MT913h4XokFBcMrqJJrqDufh1zrzR885gUDpnk23", + "programIdIndex": 13, + "stackHeight": null + }, + { + "accounts": [ + 0, + 7 + ], + "data": "3Bxs4PckVVt51W8w", + "programIdIndex": 9, + "stackHeight": null + } + ], + "recentBlockhash": "9BcgXdeCcvL42fcsM9Gszs5Rjg4QvjyCTQZb2AAyrLaC" + }, + "signatures": [ + "Ku7gtQzEztksncHMHYkXq8PNst5rZsznrXMDmVqoQjc7SR4PsXz33qjNMckNcHriCxuhZJGZequo8moNsRP9GZK", + "3koYYZbZiHFBuNiUjFUqHGJw6hw8A8qxudK7v18TVvW9pwzER3XEcWWcMovYok83ihGxMhGxUdW6WbFcdajdWZUU", + "5e5xsKD3s4GWCHpiji2XmioRZG6SWKAYyQb5V7KoTjFueTYxKqbD8QYvWV7iLGJm2fVEZpDx9pY5cRV7Az8Vmk1w" + ] + }, + "version": 0 +} \ No newline at end of file diff --git a/rust/main/chains/hyperlane-sealevel/src/log_meta_composer/tests.rs b/rust/main/chains/hyperlane-sealevel/src/log_meta_composer/tests.rs index d1097b1b08..3d666d3461 100644 --- a/rust/main/chains/hyperlane-sealevel/src/log_meta_composer/tests.rs +++ b/rust/main/chains/hyperlane-sealevel/src/log_meta_composer/tests.rs @@ -1,13 +1,16 @@ use std::fs; use std::path::PathBuf; -use solana_transaction_status::EncodedTransactionWithStatusMeta; +use hyperlane_core::{LogMeta, U256}; +use solana_transaction_status::{EncodedTransactionWithStatusMeta, UiConfirmedBlock}; use crate::log_meta_composer::{ is_interchain_payment_instruction, is_message_delivery_instruction, is_message_dispatch_instruction, search_transactions, }; -use crate::utils::decode_pubkey; +use crate::utils::{decode_h256, decode_h512, decode_pubkey}; + +use super::LogMetaComposer; #[test] pub fn test_search_dispatched_message_transaction() { @@ -29,6 +32,26 @@ pub fn test_search_dispatched_message_transaction() { assert!(!transaction_hashes.is_empty()); } +#[test] +pub fn test_search_dispatched_message_versioned_transaction() { + // given + let mailbox_program_id = decode_pubkey("EitxJuv2iBjsg2d7jVy2LDC1e2zBrx4GB5Y9h2Ko3A9Y").unwrap(); + let dispatched_message_pda_account = + decode_pubkey("9g87Di4xiYVvBE5F8Atk8xorbbVD8yKqbdHRkFu5HEgw").unwrap(); + let transactions = transactions(&read_json("dispatch_message_versioned_txn.json")); + + // when + let transaction_hashes = search_transactions( + transactions, + &mailbox_program_id, + &dispatched_message_pda_account, + is_message_dispatch_instruction, + ); + + // then + assert!(!transaction_hashes.is_empty()); +} + #[test] pub fn test_search_delivered_message_transaction() { // given @@ -49,6 +72,27 @@ pub fn test_search_delivered_message_transaction() { assert!(!transaction_hashes.is_empty()); } +#[test] +pub fn test_search_delivered_message_reverted_transaction() { + // given + let mailbox_program_id = decode_pubkey("EitxJuv2iBjsg2d7jVy2LDC1e2zBrx4GB5Y9h2Ko3A9Y").unwrap(); + // From the successful version of the delivery in https://eclipsescan.xyz/tx/4atym7S78qpT4k9mUFWc2tu7KAHcqUxDn8fxP7RL8utooTi6frJtq9xFbM6MSSqDffTGpRSmEAMtCYisRe5m8KXb + let delivered_message_pda_account = + decode_pubkey("2BZdvWiiTfeeUZKAK9R8UPrV8s8HLNGbdEUtjED52ayf").unwrap(); + let transactions = transactions(&read_json("delivery_message_reverted_txn.json")); + + // when + let transaction_hashes = search_transactions( + transactions, + &mailbox_program_id, + &delivered_message_pda_account, + is_message_delivery_instruction, + ); + + // then + assert!(transaction_hashes.is_empty()); +} + #[test] pub fn test_search_interchain_payment_transaction() { // given @@ -70,6 +114,48 @@ pub fn test_search_interchain_payment_transaction() { assert!(!transaction_hashes.is_empty()); } +#[test] +fn test_log_meta_block_with_multiple_txs_only_one_successful() { + // This test case uses an example of a block where delivery was attempted + // in 2 transactions, but only one was successful. + // Successful: https://eclipsescan.xyz/tx/4atym7S78qpT4k9mUFWc2tu7KAHcqUxDn8fxP7RL8utooTi6frJtq9xFbM6MSSqDffTGpRSmEAMtCYisRe5m8KXb + // Reverted: https://eclipsescan.xyz/tx/5F5PUWAZcBmuGaejN4qr9BjbXCMNSY2ujQ2eUTmiftYaKEE3VoU5uowttiY73ksqpGM3R4CBB7TFKBP1j3R3EUM9 + + // given + let mailbox_program_id = decode_pubkey("EitxJuv2iBjsg2d7jVy2LDC1e2zBrx4GB5Y9h2Ko3A9Y").unwrap(); + let composer = LogMetaComposer::new( + mailbox_program_id, + "message delivery".to_owned(), + is_message_delivery_instruction, + ); + // From the successful version of the delivery in https://eclipsescan.xyz/tx/4atym7S78qpT4k9mUFWc2tu7KAHcqUxDn8fxP7RL8utooTi6frJtq9xFbM6MSSqDffTGpRSmEAMtCYisRe5m8KXb + let delivered_message_pda_account = + decode_pubkey("2BZdvWiiTfeeUZKAK9R8UPrV8s8HLNGbdEUtjED52ayf").unwrap(); + let block = serde_json::from_str::(&read_json( + "delivery_message_block_multiple_txs_one_successful.json", + )) + .unwrap(); + let log_index = U256::zero(); + let pda_slot = block.block_height.unwrap(); + let blockhash = decode_h256(&block.blockhash).unwrap(); + + // when + let log_meta = composer + .log_meta(block, log_index, &delivered_message_pda_account, &pda_slot) + .unwrap(); + + // then + assert_eq!(log_meta, LogMeta { + address: mailbox_program_id.to_bytes().into(), + block_number: pda_slot, + block_hash: blockhash, + // The successful transaction and its index in the block + transaction_id: decode_h512("4atym7S78qpT4k9mUFWc2tu7KAHcqUxDn8fxP7RL8utooTi6frJtq9xFbM6MSSqDffTGpRSmEAMtCYisRe5m8KXb").unwrap(), + transaction_index: 33, + log_index, + }); +} + fn read_json(path: &str) -> String { let relative = PathBuf::new().join("src/log_meta_composer/").join(path); let absolute = fs::canonicalize(relative).expect("cannot find path"); diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index f81a40bea3..6fa159c2cf 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -625,7 +625,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'd84d8da-20241217-172447', + tag: '3812453-20241224-020703', }, resources: scraperResources, }, From 8834a8c922027887597e5dfbf7715dbe6c5c6801 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Thu, 26 Dec 2024 16:02:54 +0000 Subject: [PATCH 20/37] feat: bump relayer memory usage to 20GB, start using new SVM submission in relayer (#5063) ### Description - deploys #4959 - Sets a small priority fee for Eclipse following some recent spikes there, https://discord.com/channels/935678348330434570/1320851852157255701 for context - Makes the relayer request 20GB of memory, forcing relayers to have their own node in our bigger node pool (capacity 29 GB) - Driveby to use G (Gigabytes) instead of Gi (Gibibytes) to match what GCP uses in their UI, also a bit more intuitive - Added a `--concurrency ` flag that can be used when deploying agents. Especially useful when releasing to all validators, which we have tons of now. - Concurrently refetching remote repositories on helm poses issues, so I made this not happen by default anymore. It's not configurable at the CLI level to enable it, but we have the ability to easily add it later. Because our agent deploy doesn't actually require any remote helm repos, it's not necessary - already rolled out ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- .changeset/stupid-seahorses-yell.md | 5 ++++ .../config/environments/mainnet3/agent.ts | 23 +++++++++++---- typescript/infra/scripts/agent-utils.ts | 7 +++++ typescript/infra/scripts/agents/utils.ts | 22 +++++++++++---- typescript/infra/src/helloworld/kathy.ts | 8 ++++-- typescript/infra/src/utils/helm.ts | 28 ++++++++++++++++--- typescript/utils/src/async.ts | 2 ++ 7 files changed, 78 insertions(+), 17 deletions(-) create mode 100644 .changeset/stupid-seahorses-yell.md diff --git a/.changeset/stupid-seahorses-yell.md b/.changeset/stupid-seahorses-yell.md new file mode 100644 index 0000000000..61230350a4 --- /dev/null +++ b/.changeset/stupid-seahorses-yell.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/utils': patch +--- + +Require concurrency > 0 for concurrentMap diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index 6fa159c2cf..32259150d6 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -414,6 +414,19 @@ const sealevelPriorityFeeOracleConfigGetter = ( // URL is auto populated by the external secrets in the helm chart url: '', }; + } else if (chain === 'eclipsemainnet') { + // As of Dec 23: + // Eclipse has recently seen some increased usage with their referral program, + // and we have had intermittent issues landing txs. Not many txs on Eclipse use + // priority fees, so we use a low priority fee. + return { + type: AgentSealevelPriorityFeeOracleType.Constant, + // 2000 micro lamports of ETH, which at a compute unit limit of 400K + // and an ETH price of $3450 (Dec 23, 2024) comes to about $0.00276 USD: + // >>> (((2000 / 1e6) * 400000) / 1e9) * 3450 + // 0.00276 + fee: '2000', + }; } // For all other chains, we use the constant fee oracle with a fee of 0 @@ -530,21 +543,21 @@ const metricAppContextsGetter = (): MetricAppContext[] => { const relayerResources = { requests: { cpu: '14000m', - memory: '15Gi', + memory: '20G', }, }; const validatorResources = { requests: { cpu: '500m', - memory: '1Gi', + memory: '1G', }, }; const scraperResources = { requests: { cpu: '2000m', - memory: '4Gi', + memory: '4G', }, }; @@ -605,7 +618,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '7c0c967-20241218-173053', + tag: 'e9911bb-20241223-211526', }, blacklist, gasPaymentEnforcement: gasPaymentEnforcement, @@ -640,7 +653,7 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '7c0c967-20241218-173053', + tag: 'e9911bb-20241223-211526', }, blacklist, // We're temporarily (ab)using the RC relayer as a way to increase diff --git a/typescript/infra/scripts/agent-utils.ts b/typescript/infra/scripts/agent-utils.ts index e60d137f95..1832889cac 100644 --- a/typescript/infra/scripts/agent-utils.ts +++ b/typescript/infra/scripts/agent-utils.ts @@ -261,6 +261,13 @@ export function withConcurrentDeploy(args: Argv) { .default('concurrentDeploy', false); } +export function withConcurrency(args: Argv) { + return args + .describe('concurrency', 'Number of concurrent deploys') + .number('concurrency') + .default('concurrency', 1); +} + export function withRpcUrls(args: Argv) { return args .describe( diff --git a/typescript/infra/scripts/agents/utils.ts b/typescript/infra/scripts/agents/utils.ts index fa3a1da4ce..9621705a00 100644 --- a/typescript/infra/scripts/agents/utils.ts +++ b/typescript/infra/scripts/agents/utils.ts @@ -1,3 +1,5 @@ +import { concurrentMap } from '@hyperlane-xyz/utils'; + import { AgentHelmManager, RelayerHelmManager, @@ -7,12 +9,13 @@ import { import { RootAgentConfig } from '../../src/config/agent/agent.js'; import { EnvironmentConfig } from '../../src/config/environment.js'; import { Role } from '../../src/roles.js'; -import { HelmCommand } from '../../src/utils/helm.js'; +import { HelmCommand, HelmManager } from '../../src/utils/helm.js'; import { assertCorrectKubeContext, getArgs, withAgentRolesRequired, withChains, + withConcurrency, withContext, } from '../agent-utils.js'; import { getConfigsBasedOnArgs } from '../core-utils.js'; @@ -24,6 +27,7 @@ export class AgentCli { initialized = false; dryRun = false; chains?: string[]; + concurrency = 1; public async runHelmCommand(command: HelmCommand) { await this.init(); @@ -63,15 +67,20 @@ export class AgentCli { console.log('Dry run values:\n', JSON.stringify(values, null, 2)); } - for (const m of Object.values(managers)) { - await m.runHelmCommand(command, this.dryRun); - } + await concurrentMap( + this.concurrency, + Object.entries(managers), + async ([key, manager]) => { + console.log(`Running helm command for ${key}`); + await manager.runHelmCommand(command, { dryRun: this.dryRun }); + }, + ); } protected async init() { if (this.initialized) return; - const argv = await withChains( - withAgentRolesRequired(withContext(getArgs())), + const argv = await withConcurrency( + withChains(withAgentRolesRequired(withContext(getArgs()))), ) .describe('dry-run', 'Run through the steps without making any changes') .boolean('dry-run').argv; @@ -92,5 +101,6 @@ export class AgentCli { this.dryRun = argv.dryRun || false; this.initialized = true; this.chains = argv.chains; + this.concurrency = argv.concurrency; } } diff --git a/typescript/infra/src/helloworld/kathy.ts b/typescript/infra/src/helloworld/kathy.ts index 47dafc851c..dbb8aded61 100644 --- a/typescript/infra/src/helloworld/kathy.ts +++ b/typescript/infra/src/helloworld/kathy.ts @@ -15,6 +15,7 @@ import { import { Role } from '../roles.js'; import { HelmCommand, + HelmCommandOptions, HelmManager, HelmValues, helmifyValues, @@ -90,7 +91,10 @@ export class KathyHelmManager extends HelmManager { }; } - async runHelmCommand(action: HelmCommand, dryRun?: boolean): Promise { + async runHelmCommand( + action: HelmCommand, + options?: HelmCommandOptions, + ): Promise { // If using AWS keys, ensure the Kathy user and key has been created if (this.agentConfig.aws) { const awsUser = new AgentAwsUser( @@ -112,6 +116,6 @@ export class KathyHelmManager extends HelmManager { ); await kathyKey.createIfNotExists(); - super.runHelmCommand(action, dryRun); + await super.runHelmCommand(action, options); } } diff --git a/typescript/infra/src/utils/helm.ts b/typescript/infra/src/utils/helm.ts index 4ed4cb478f..4bd23f1047 100644 --- a/typescript/infra/src/utils/helm.ts +++ b/typescript/infra/src/utils/helm.ts @@ -90,8 +90,17 @@ export function getDeployableHelmChartName(helmChartConfig: HelmChartConfig) { return helmChartConfig.name; } -export function buildHelmChartDependencies(chartPath: string) { - return execCmd(`cd ${chartPath} && helm dependency build`, {}, false, true); +export function buildHelmChartDependencies( + chartPath: string, + updateRepoCache: boolean, +) { + const flags = updateRepoCache ? '' : '--skip-refresh'; + return execCmd( + `cd ${chartPath} && helm dependency build ${flags}`, + {}, + false, + true, + ); } // Convenience function to remove a helm release without having a HelmManger for it. @@ -101,6 +110,11 @@ export function removeHelmRelease(releaseName: string, namespace: string) { export type HelmValues = Record; +export interface HelmCommandOptions { + dryRun?: boolean; + updateRepoCache?: boolean; +} + export abstract class HelmManager { abstract readonly helmReleaseName: string; abstract readonly helmChartPath: string; @@ -112,7 +126,13 @@ export abstract class HelmManager { */ abstract helmValues(): Promise; - async runHelmCommand(action: HelmCommand, dryRun?: boolean): Promise { + async runHelmCommand( + action: HelmCommand, + options?: HelmCommandOptions, + ): Promise { + const dryRun = options?.dryRun ?? false; + const updateRepoCache = options?.updateRepoCache ?? false; + const cmd = ['helm', action]; if (dryRun) cmd.push('--dry-run'); @@ -142,7 +162,7 @@ export abstract class HelmManager { } } - await buildHelmChartDependencies(this.helmChartPath); + await buildHelmChartDependencies(this.helmChartPath, updateRepoCache); cmd.push( this.helmReleaseName, this.helmChartPath, diff --git a/typescript/utils/src/async.ts b/typescript/utils/src/async.ts index f9bda74eb1..505318e32a 100644 --- a/typescript/utils/src/async.ts +++ b/typescript/utils/src/async.ts @@ -1,4 +1,5 @@ import { rootLogger } from './logging.js'; +import { assert } from './validation.js'; /** * Return a promise that resolves in ms milliseconds. @@ -159,6 +160,7 @@ export async function concurrentMap( mapFn: (val: A, idx: number) => Promise, ): Promise { let res: B[] = []; + assert(concurrency > 0, 'concurrency must be greater than 0'); for (let i = 0; i < xs.length; i += concurrency) { const remaining = xs.length - i; const sliceSize = Math.min(remaining, concurrency); From ea9aeb1f78d985d275e868175f2b1e44aa53bc20 Mon Sep 17 00:00:00 2001 From: Kunal Arora <55632507+aroralanuk@users.noreply.github.com> Date: Thu, 26 Dec 2024 23:05:48 +0530 Subject: [PATCH 21/37] feat(relayer): relayer exposes latest tree index as metric (#5074) ### Description - Added `latest_tree_insertion_count` to CoreMetrics which can then be imported to Grafana. - In the `MerkleTreeProcessor`, we set this metric via `max_leaf_index_gauge` to reflect the latest leaf index processed and inserted locally to the tree. ### Drive-by changes none ### Related issues - fixes https://github.com/hyperlane-xyz/issues/issues/1390 ### Backward compatibility Yes ### Testing Manual --- .../relayer/src/merkle_tree/processor.rs | 20 ++++++++++--------- rust/main/agents/relayer/src/relayer.rs | 2 +- rust/main/hyperlane-base/src/metrics/core.rs | 20 +++++++++++++++++++ 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/rust/main/agents/relayer/src/merkle_tree/processor.rs b/rust/main/agents/relayer/src/merkle_tree/processor.rs index 9ddcc2ee0c..e12a7fc343 100644 --- a/rust/main/agents/relayer/src/merkle_tree/processor.rs +++ b/rust/main/agents/relayer/src/merkle_tree/processor.rs @@ -7,7 +7,10 @@ use std::{ use async_trait::async_trait; use derive_new::new; use eyre::Result; -use hyperlane_base::db::{HyperlaneDb, HyperlaneRocksDB}; +use hyperlane_base::{ + db::{HyperlaneDb, HyperlaneRocksDB}, + CoreMetrics, +}; use hyperlane_core::{HyperlaneDomain, MerkleTreeInsertion}; use prometheus::IntGauge; use tokio::sync::RwLock; @@ -71,8 +74,9 @@ impl MerkleTreeProcessor { .retrieve_merkle_tree_insertion_by_leaf_index(&self.leaf_index)? { // Update the metrics + // we assume that leaves are inserted in order so this will be monotonically increasing self.metrics - .max_leaf_index_gauge + .latest_leaf_index_gauge .set(insertion.index() as i64); Some(insertion) } else { @@ -85,17 +89,15 @@ impl MerkleTreeProcessor { #[derive(Debug)] pub struct MerkleTreeProcessorMetrics { - max_leaf_index_gauge: IntGauge, + latest_leaf_index_gauge: IntGauge, } impl MerkleTreeProcessorMetrics { - pub fn new() -> Self { + pub fn new(metrics: &CoreMetrics, origin: &HyperlaneDomain) -> Self { Self { - max_leaf_index_gauge: IntGauge::new( - "max_leaf_index_gauge", - "The max merkle tree leaf index", - ) - .unwrap(), + latest_leaf_index_gauge: metrics + .latest_leaf_index() + .with_label_values(&[origin.name()]), } } } diff --git a/rust/main/agents/relayer/src/relayer.rs b/rust/main/agents/relayer/src/relayer.rs index 6bd1a63a8d..b1f013b6ae 100644 --- a/rust/main/agents/relayer/src/relayer.rs +++ b/rust/main/agents/relayer/src/relayer.rs @@ -587,7 +587,7 @@ impl Relayer { origin: &HyperlaneDomain, task_monitor: TaskMonitor, ) -> Instrumented> { - let metrics = MerkleTreeProcessorMetrics::new(); + let metrics = MerkleTreeProcessorMetrics::new(&self.core.metrics, origin); let merkle_tree_processor = MerkleTreeProcessor::new( self.dbs.get(origin).unwrap().clone(), metrics, diff --git a/rust/main/hyperlane-base/src/metrics/core.rs b/rust/main/hyperlane-base/src/metrics/core.rs index 5603ece225..d3cbaf3db4 100644 --- a/rust/main/hyperlane-base/src/metrics/core.rs +++ b/rust/main/hyperlane-base/src/metrics/core.rs @@ -38,6 +38,7 @@ pub struct CoreMetrics { span_counts: IntCounterVec, span_events: IntCounterVec, last_known_message_nonce: IntGaugeVec, + latest_leaf_index: IntGaugeVec, submitter_queue_length: IntGaugeVec, operations_processed_count: IntCounterVec, @@ -112,6 +113,16 @@ impl CoreMetrics { registry )?; + let latest_leaf_index = register_int_gauge_vec_with_registry!( + opts!( + namespaced!("latest_leaf_index"), + "Latest leaf index inserted into the merkle tree", + const_labels_ref + ), + &["origin"], + registry + )?; + let observed_validator_latest_index = register_int_gauge_vec_with_registry!( opts!( namespaced!("observed_validator_latest_index"), @@ -177,6 +188,7 @@ impl CoreMetrics { span_counts, span_events, last_known_message_nonce, + latest_leaf_index, submitter_queue_length, @@ -309,6 +321,14 @@ impl CoreMetrics { self.last_known_message_nonce.clone() } + /// Reports the current highest leaf index which was inserted into the merkle tree. + /// + /// Labels: + /// - `origin`: Origin chain the leaf index is being tracked at. + pub fn latest_leaf_index(&self) -> IntGaugeVec { + self.latest_leaf_index.clone() + } + /// Latest message nonce in the validator. /// /// Phase: From 234704da39388eaa036b540170ace9449644abb7 Mon Sep 17 00:00:00 2001 From: xeno097 Date: Thu, 26 Dec 2024 15:13:25 -0400 Subject: [PATCH 22/37] test: cli e2e warp init command tests (#5077) ### Description Adds e2e tests for the `warp init` command ### Drive-by changes - ### Related issues - ### Backward compatibility - YES ### Testing - E2E --- .github/workflows/test.yml | 1 + typescript/cli/src/tests/commands/helpers.ts | 45 +++- typescript/cli/src/tests/commands/warp.ts | 16 +- .../cli/src/tests/warp-init.e2e-test.ts | 205 ++++++++++++++++++ 4 files changed, 262 insertions(+), 5 deletions(-) create mode 100644 typescript/cli/src/tests/warp-init.e2e-test.ts diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d2e5f9a40b..5235c9dd16 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -130,6 +130,7 @@ jobs: - core-deploy - core-read - relay + - warp-init - warp-read - warp-apply - warp-deploy diff --git a/typescript/cli/src/tests/commands/helpers.ts b/typescript/cli/src/tests/commands/helpers.ts index 7853815459..66f8f2e7f4 100644 --- a/typescript/cli/src/tests/commands/helpers.ts +++ b/typescript/cli/src/tests/commands/helpers.ts @@ -1,5 +1,5 @@ import { ethers } from 'ethers'; -import { $ } from 'zx'; +import { $, ProcessPromise } from 'zx'; import { ERC20Test__factory, ERC4626Test__factory } from '@hyperlane-xyz/core'; import { ChainAddresses } from '@hyperlane-xyz/registry'; @@ -8,7 +8,7 @@ import { WarpCoreConfig, WarpCoreConfigSchema, } from '@hyperlane-xyz/sdk'; -import { Address } from '@hyperlane-xyz/utils'; +import { Address, sleep } from '@hyperlane-xyz/utils'; import { getContext } from '../../context/context.js'; import { readYamlOrJson, writeYamlOrJson } from '../../utils/files.js'; @@ -44,6 +44,39 @@ export const WARP_CORE_CONFIG_PATH_2 = `${REGISTRY_PATH}/deployments/warp_routes export const DEFAULT_E2E_TEST_TIMEOUT = 100_000; // Long timeout since these tests can take a while +export enum KeyBoardKeys { + ARROW_DOWN = '\x1b[B', + ARROW_UP = '\x1b[A', + ENTER = '\n', + TAB = '\t', +} + +export async function asyncStreamInputWrite( + stream: NodeJS.WritableStream, + data: string | Buffer, +): Promise { + stream.write(data); + // Adding a slight delay to allow the buffer to update the output + await sleep(500); +} + +export async function selectAnvil2AndAnvil3( + stream: ProcessPromise, +): Promise { + // Scroll down through the mainnet chains list and select anvil2 + await asyncStreamInputWrite( + stream.stdin, + `${KeyBoardKeys.ARROW_DOWN.repeat(3)}${KeyBoardKeys.TAB}`, + ); + // Scroll down through the mainnet chains list again and select anvil3 + await asyncStreamInputWrite( + stream.stdin, + `${KeyBoardKeys.ARROW_DOWN.repeat(2)}${KeyBoardKeys.TAB}${ + KeyBoardKeys.ENTER + }`, + ); +} + /** * Retrieves the deployed Warp address from the Warp core config. */ @@ -154,7 +187,11 @@ export async function getDomainId( return String(chainMetadata?.domainId); } -export async function deployToken(privateKey: string, chain: string) { +export async function deployToken( + privateKey: string, + chain: string, + decimals = 18, +) { const { multiProvider } = await getContext({ registryUri: REGISTRY_PATH, registryOverrideUri: '', @@ -166,7 +203,7 @@ export async function deployToken(privateKey: string, chain: string) { const token = await new ERC20Test__factory( multiProvider.getSigner(chain), - ).deploy('token', 'token', '100000000000000000000', 18); + ).deploy('token', 'token', '100000000000000000000', decimals); await token.deployed(); return token; diff --git a/typescript/cli/src/tests/commands/warp.ts b/typescript/cli/src/tests/commands/warp.ts index dc80b6ad24..467446e045 100644 --- a/typescript/cli/src/tests/commands/warp.ts +++ b/typescript/cli/src/tests/commands/warp.ts @@ -1,4 +1,4 @@ -import { $ } from 'zx'; +import { $, ProcessPromise } from 'zx'; import { WarpRouteDeployConfig } from '@hyperlane-xyz/sdk'; @@ -8,6 +8,20 @@ import { ANVIL_KEY, REGISTRY_PATH, getDeployedWarpAddress } from './helpers.js'; $.verbose = true; +/** + * Deploys the Warp route to the specified chain using the provided config. + */ +export function hyperlaneWarpInit(warpCorePath: string): ProcessPromise { + // --overrides is " " to allow local testing to work + return $`yarn workspace @hyperlane-xyz/cli run hyperlane warp init \ + --registry ${REGISTRY_PATH} \ + --overrides " " \ + --out ${warpCorePath} \ + --key ${ANVIL_KEY} \ + --verbosity debug \ + --yes`; +} + /** * Deploys the Warp route to the specified chain using the provided config. */ diff --git a/typescript/cli/src/tests/warp-init.e2e-test.ts b/typescript/cli/src/tests/warp-init.e2e-test.ts new file mode 100644 index 0000000000..b3314a30a9 --- /dev/null +++ b/typescript/cli/src/tests/warp-init.e2e-test.ts @@ -0,0 +1,205 @@ +import { expect } from 'chai'; +import { Wallet } from 'ethers'; + +import { ChainAddresses } from '@hyperlane-xyz/registry'; +import { + ChainMap, + ChainName, + TokenType, + WarpRouteDeployConfig, +} from '@hyperlane-xyz/sdk'; +import { Address } from '@hyperlane-xyz/utils'; + +import { readYamlOrJson } from '../utils/files.js'; + +import { + ANVIL_KEY, + CHAIN_NAME_2, + CHAIN_NAME_3, + CORE_CONFIG_PATH, + DEFAULT_E2E_TEST_TIMEOUT, + KeyBoardKeys, + WARP_CONFIG_PATH_2, + asyncStreamInputWrite, + deployOrUseExistingCore, + deployToken, + selectAnvil2AndAnvil3, +} from './commands/helpers.js'; +import { hyperlaneWarpInit } from './commands/warp.js'; + +describe('hyperlane warp init e2e tests', async function () { + this.timeout(2 * DEFAULT_E2E_TEST_TIMEOUT); + + let chain2Addresses: ChainAddresses = {}; + let chain3Addresses: ChainAddresses = {}; + let initialOwnerAddress: Address; + let chainMapAddresses: ChainMap = {}; + + before(async function () { + [chain2Addresses, chain3Addresses] = await Promise.all([ + deployOrUseExistingCore(CHAIN_NAME_2, CORE_CONFIG_PATH, ANVIL_KEY), + deployOrUseExistingCore(CHAIN_NAME_3, CORE_CONFIG_PATH, ANVIL_KEY), + ]); + + chainMapAddresses = { + [CHAIN_NAME_2]: chain2Addresses, + [CHAIN_NAME_3]: chain3Addresses, + }; + + const wallet = new Wallet(ANVIL_KEY); + initialOwnerAddress = wallet.address; + }); + + describe('hyperlane warp init --yes', () => { + function assertWarpConfig( + warpConfig: WarpRouteDeployConfig, + chainMapAddresses: ChainMap, + chainName: ChainName, + ) { + expect(warpConfig[chainName]).not.to.be.undefined; + + const chain2TokenConfig = warpConfig[chainName]; + expect(chain2TokenConfig.mailbox).equal( + chainMapAddresses[chainName].mailbox, + ); + expect(chain2TokenConfig.owner).equal(initialOwnerAddress); + expect(chain2TokenConfig.type).equal(TokenType.native); + } + + it('it should generate a warp deploy config with a single chain', async function () { + const output = hyperlaneWarpInit(WARP_CONFIG_PATH_2).stdio('pipe'); + + for await (const out of output.stdout) { + const currentLine: string = out.toString(); + + if ( + currentLine.includes('Creating a new warp route deployment config...') + ) { + // Select mainnet chains + await asyncStreamInputWrite(output.stdin, KeyBoardKeys.ENTER); + } else if (currentLine.includes('--Mainnet Chains--')) { + // Scroll down through the mainnet chains list and select anvil2 + await asyncStreamInputWrite( + output.stdin, + `${KeyBoardKeys.ARROW_DOWN.repeat(3)}${KeyBoardKeys.TAB}${ + KeyBoardKeys.ENTER + }`, + ); + } else if (currentLine.includes('token type')) { + // Scroll up through the token type list and select native + await asyncStreamInputWrite( + output.stdin, + `${KeyBoardKeys.ARROW_UP.repeat(2)}${KeyBoardKeys.ENTER}`, + ); + } else if (currentLine.includes('Detected owner address as')) { + // Confirm owner prompts + await asyncStreamInputWrite(output.stdin, KeyBoardKeys.ENTER); + } + } + + await output; + + const warpConfig: WarpRouteDeployConfig = + readYamlOrJson(WARP_CONFIG_PATH_2); + + assertWarpConfig(warpConfig, chainMapAddresses, CHAIN_NAME_2); + }); + + it('it should generate a warp deploy config with a 2 chains warp route (native->native)', async function () { + const output = hyperlaneWarpInit(WARP_CONFIG_PATH_2).stdio('pipe'); + + for await (const out of output.stdout) { + const currentLine: string = out.toString(); + + if ( + currentLine.includes('Creating a new warp route deployment config...') + ) { + // Select mainnet chains + await asyncStreamInputWrite(output.stdin, KeyBoardKeys.ENTER); + } else if (currentLine.includes('--Mainnet Chains--')) { + await selectAnvil2AndAnvil3(output); + } else if (currentLine.match(/Select .+?'s token type/)) { + // Scroll up through the token type list and select native + await asyncStreamInputWrite( + output.stdin, + `${KeyBoardKeys.ARROW_UP.repeat(2)}${KeyBoardKeys.ENTER}`, + ); + } else if (currentLine.includes('Detected owner address as')) { + // Confirm owner prompts + await asyncStreamInputWrite(output.stdin, KeyBoardKeys.ENTER); + } + } + + await output; + + const warpConfig: WarpRouteDeployConfig = + readYamlOrJson(WARP_CONFIG_PATH_2); + + [CHAIN_NAME_2, CHAIN_NAME_3].map((chainName) => + assertWarpConfig(warpConfig, chainMapAddresses, chainName), + ); + }); + + it('it should generate a warp deploy config with a 2 chains warp route (collateral->synthetic)', async function () { + const erc20Token = await deployToken(ANVIL_KEY, CHAIN_NAME_2, 6); + + const output = hyperlaneWarpInit(WARP_CONFIG_PATH_2).stdio('pipe'); + + let tokenStep = 0; + for await (const out of output.stdout) { + const currentLine: string = out.toString(); + + if ( + currentLine.includes('Creating a new warp route deployment config...') + ) { + // Select mainnet chains + await asyncStreamInputWrite(output.stdin, KeyBoardKeys.ENTER); + } else if (currentLine.includes('--Mainnet Chains--')) { + await selectAnvil2AndAnvil3(output); + } else if ( + currentLine.includes('Enter the existing token address on chain') + ) { + await asyncStreamInputWrite( + output.stdin, + `${erc20Token.address}${KeyBoardKeys.ENTER}`, + ); + } else if (currentLine.match(/Select .+?'s token type/)) { + if (tokenStep === 0) { + // Scroll down through the token type list and select collateral + await asyncStreamInputWrite( + output.stdin, + `${KeyBoardKeys.ARROW_DOWN.repeat(4)}${KeyBoardKeys.ENTER}`, + ); + } else if (tokenStep === 1) { + // Select the synthetic token type + await asyncStreamInputWrite(output.stdin, KeyBoardKeys.ENTER); + } + tokenStep++; + } else if (currentLine.includes('Detected owner address as')) { + // Confirm owner prompts + await asyncStreamInputWrite(output.stdin, KeyBoardKeys.ENTER); + } + } + + await output; + + const warpConfig: WarpRouteDeployConfig = + readYamlOrJson(WARP_CONFIG_PATH_2); + + expect(warpConfig[CHAIN_NAME_2]).not.to.be.undefined; + + const chain2TokenConfig = warpConfig[CHAIN_NAME_2]; + expect(chain2TokenConfig.mailbox).equal(chain2Addresses.mailbox); + expect(chain2TokenConfig.owner).equal(initialOwnerAddress); + expect(chain2TokenConfig.type).equal(TokenType.collateral); + expect((chain2TokenConfig as any).token).equal(erc20Token.address); + + expect(warpConfig[CHAIN_NAME_3]).not.to.be.undefined; + + const chain3TokenConfig = warpConfig[CHAIN_NAME_3]; + expect(chain3TokenConfig.mailbox).equal(chain3Addresses.mailbox); + expect(chain3TokenConfig.owner).equal(initialOwnerAddress); + expect(chain3TokenConfig.type).equal(TokenType.synthetic); + }); + }); +}); From beaf8727e7d7a067b1e988c8bb0a46d7a235e7fe Mon Sep 17 00:00:00 2001 From: Kunal Arora <55632507+aroralanuk@users.noreply.github.com> Date: Sat, 28 Dec 2024 02:19:38 +0530 Subject: [PATCH 23/37] feat: RELAYER UPDATE - update hyperlane,neutron context to tag `234704d-20241226-192528` (#5082) ### Description ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- typescript/infra/config/environments/mainnet3/agent.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index 32259150d6..1cbc6d1d24 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -618,7 +618,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'e9911bb-20241223-211526', + tag: '234704d-20241226-192528', }, blacklist, gasPaymentEnforcement: gasPaymentEnforcement, @@ -653,7 +653,7 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'e9911bb-20241223-211526', + tag: '234704d-20241226-192528', }, blacklist, // We're temporarily (ab)using the RC relayer as a way to increase @@ -687,7 +687,7 @@ const neutron: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '25a927d-20241114-171323', + tag: '234704d-20241226-192528', }, blacklist, gasPaymentEnforcement, From de119065659fe06325b1699e8de3342fa1cdda60 Mon Sep 17 00:00:00 2001 From: J M Rossy Date: Fri, 27 Dec 2024 16:36:54 -0500 Subject: [PATCH 24/37] chore: Export missing token maps from the SDK (#5083) ### Description Export TOKEN_STANDARD_TO_PROVIDER_TYPE, XERC20_STANDARDS, and MINT_LIMITED_STANDARDS maps ### Backward compatibility Yes --- .changeset/spotty-bees-worry.md | 5 +++ typescript/sdk/src/index.ts | 55 +++++++++++++++++---------------- 2 files changed, 34 insertions(+), 26 deletions(-) create mode 100644 .changeset/spotty-bees-worry.md diff --git a/.changeset/spotty-bees-worry.md b/.changeset/spotty-bees-worry.md new file mode 100644 index 0000000000..7697c5fd9b --- /dev/null +++ b/.changeset/spotty-bees-worry.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/sdk': patch +--- + +Export TOKEN_STANDARD_TO_PROVIDER_TYPE, XERC20_STANDARDS, and MINT_LIMITED_STANDARDS maps diff --git a/typescript/sdk/src/index.ts b/typescript/sdk/src/index.ts index 87e524110e..68b7a6b338 100644 --- a/typescript/sdk/src/index.ts +++ b/typescript/sdk/src/index.ts @@ -19,12 +19,12 @@ export { } from './consts/multisigIsm.js'; export { SEALEVEL_SPL_NOOP_ADDRESS } from './consts/sealevel.js'; export { - TestChainName, multiProtocolTestChainMetadata, test1, test2, test3, testChainMetadata, + TestChainName, testChains, testCosmosChain, testSealevelChain, @@ -220,13 +220,13 @@ export { AgentSignerHexKey, AgentSignerKeyType, AgentSignerNode, + buildAgentConfig, GasPaymentEnforcement, GasPaymentEnforcementPolicyType, RelayerConfig, RpcConsensusType, ScraperConfig, ValidatorConfig, - buildAgentConfig, } from './metadata/agentConfig.js'; export { ChainMetadataManager, @@ -242,15 +242,15 @@ export { EthJsonRpcBlockParameterTag, ExplorerFamily, ExplorerFamilyValue, - NativeToken, - RpcUrl, - RpcUrlSchema, getChainIdNumber, getDomainId, getReorgPeriod, isValidChainMetadata, mergeChainMetadata, mergeChainMetadataMap, + NativeToken, + RpcUrl, + RpcUrlSchema, } from './metadata/chainMetadataTypes.js'; export { ZChainName, ZHash } from './metadata/customZodTypes.js'; export { @@ -305,9 +305,6 @@ export { MultiProviderOptions, } from './providers/MultiProvider.js'; export { - ProviderBuilderFn, - ProviderBuilderMap, - TypedProviderBuilderFn, defaultEthersV5ProviderBuilder, defaultFuelProviderBuilder, defaultProviderBuilder, @@ -315,6 +312,9 @@ export { defaultSolProviderBuilder, defaultViemProviderBuilder, protocolToDefaultProviderBuilder, + ProviderBuilderFn, + ProviderBuilderMap, + TypedProviderBuilderFn, } from './providers/providerBuilders.js'; export { CosmJsContract, @@ -354,9 +354,9 @@ export { HyperlaneEtherscanProvider } from './providers/SmartProvider/HyperlaneE export { HyperlaneJsonRpcProvider } from './providers/SmartProvider/HyperlaneJsonRpcProvider.js'; export { AllProviderMethods, + excludeProviderMethods, IProviderMethods, ProviderMethod, - excludeProviderMethods, } from './providers/SmartProvider/ProviderMethods.js'; export { HyperlaneSmartProvider } from './providers/SmartProvider/SmartProvider.js'; export { @@ -418,13 +418,14 @@ export { export { ChainGasOracleParams, GasPriceConfig, - NativeTokenPriceConfig, getCosmosChainGasPrice, getGasPrice, getLocalStorageGasOracleConfig, getTokenExchangeRateFromValues, + NativeTokenPriceConfig, } from './gas/utils.js'; export { GcpValidator } from './gcp/validator.js'; +export { EvmHookModule } from './hook/EvmHookModule.js'; export { DerivedIcaRouterConfig, DerivedIcaRouterConfigSchema, @@ -464,17 +465,17 @@ export { MailboxClientConfig, MailboxClientConfigSchema, ProxiedFactories, + proxiedFactories, ProxiedRouterConfig, RemoteRouters, RouterAddress, RouterConfig, RouterViolation, RouterViolationType, - proxiedFactories, } from './router/types.js'; export { - CosmIbcToWarpTokenAdapter, CosmIbcTokenAdapter, + CosmIbcToWarpTokenAdapter, CosmNativeTokenAdapter, } from './token/adapters/CosmosTokenAdapter.js'; export { @@ -497,8 +498,8 @@ export { export { IHypTokenAdapter, IHypXERC20Adapter, - ITokenAdapter, InterchainGasQuote, + ITokenAdapter, TransferParams, TransferRemoteParams, } from './token/adapters/ITokenAdapter.js'; @@ -511,9 +512,9 @@ export { SealevelTokenAdapter, } from './token/adapters/SealevelTokenAdapter.js'; export { - SealevelHypTokenInstruction, SealevelHyperlaneTokenData, SealevelHyperlaneTokenDataSchema, + SealevelHypTokenInstruction, SealevelTransferRemoteInstruction, SealevelTransferRemoteSchema, } from './token/adapters/serialization.js'; @@ -521,11 +522,11 @@ export { HypERC20App } from './token/app.js'; export { HypERC20Checker } from './token/checker.js'; export { TokenType } from './token/config.js'; export { + hypERC20contracts, HypERC20Factories, + hypERC20factories, HypERC721Factories, TokenFactories, - hypERC20contracts, - hypERC20factories, } from './token/contracts.js'; export { HypERC20Deployer, HypERC721Deployer } from './token/deploy.js'; export { EvmERC20WarpModule } from './token/EvmERC20WarpModule.js'; @@ -534,16 +535,17 @@ export { IToken, TokenArgs, TokenConfigSchema } from './token/IToken.js'; export { Token } from './token/Token.js'; export { TokenAmount } from './token/TokenAmount.js'; export { + getTokenConnectionId, HyperlaneTokenConnection, IbcToHyperlaneTokenConnection, IbcTokenConnection, + parseTokenConnectionId, TokenConnection, TokenConnectionConfigSchema, TokenConnectionType, - getTokenConnectionId, - parseTokenConnectionId, } from './token/TokenConnection.js'; export { + MINT_LIMITED_STANDARDS, PROTOCOL_TO_NATIVE_STANDARD, TOKEN_COLLATERALIZED_STANDARDS, TOKEN_COSMWASM_STANDARDS, @@ -551,8 +553,10 @@ export { TOKEN_MULTI_CHAIN_STANDARDS, TOKEN_NFT_STANDARDS, TOKEN_STANDARD_TO_PROTOCOL, + TOKEN_STANDARD_TO_PROVIDER_TYPE, TOKEN_TYPE_TO_STANDARD, TokenStandard, + XERC20_STANDARDS, } from './token/TokenStandard.js'; export { CollateralRebaseTokenConfigSchema, @@ -562,6 +566,12 @@ export { HypTokenConfigSchema, HypTokenRouterConfig, HypTokenRouterConfigSchema, + isCollateralRebaseTokenConfig, + isCollateralTokenConfig, + isNativeTokenConfig, + isSyntheticRebaseTokenConfig, + isSyntheticTokenConfig, + isTokenMetadata, NativeTokenConfig, NativeTokenConfigSchema, SyntheticRebaseTokenConfig, @@ -573,12 +583,6 @@ export { WarpRouteDeployConfig, WarpRouteDeployConfigSchema, WarpRouteDeployConfigSchemaErrors, - isCollateralRebaseTokenConfig, - isCollateralTokenConfig, - isNativeTokenConfig, - isSyntheticRebaseTokenConfig, - isSyntheticTokenConfig, - isTokenMetadata, } from './token/types.js'; export { ChainMap, @@ -616,9 +620,9 @@ export { multisigIsmVerificationCost, normalizeConfig } from './utils/ism.js'; export { MultiGeneric } from './utils/MultiGeneric.js'; export { isCompliant, validateZodResult } from './utils/schemas.js'; export { + getSealevelAccountDataSchema, SealevelAccountDataWrapper, SealevelInstructionWrapper, - getSealevelAccountDataSchema, } from './utils/sealevelSerialization.js'; export { getChainIdFromTxs } from './utils/transactions.js'; export { @@ -634,4 +638,3 @@ export { WarpTypedTransaction, } from './warp/types.js'; export { WarpCore, WarpCoreOptions } from './warp/WarpCore.js'; -export { EvmHookModule } from './hook/EvmHookModule.js'; From a7344833160d50ca635c46df5d8dc4021fa9e35b Mon Sep 17 00:00:00 2001 From: Mohammed Hussan Date: Sat, 28 Dec 2024 07:04:51 +0000 Subject: [PATCH 25/37] chore(funding): Set Morph desired balance to 0.1 (#5076) ### Description - Set Morph desired balance to 0.1 --- typescript/infra/config/environments/mainnet3/funding.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typescript/infra/config/environments/mainnet3/funding.ts b/typescript/infra/config/environments/mainnet3/funding.ts index e16bb07c90..f1fea75213 100644 --- a/typescript/infra/config/environments/mainnet3/funding.ts +++ b/typescript/infra/config/environments/mainnet3/funding.ts @@ -90,7 +90,7 @@ export const keyFunderConfig: KeyFunderConfig< mode: '0.2', molten: '3', moonbeam: '100', - morph: '0.05', + morph: '0.1', oortmainnet: '2000', optimism: '0.5', orderly: '0.05', From f929a429a0367f9a398cd25d010bb82e31788e58 Mon Sep 17 00:00:00 2001 From: Marco Date: Sat, 28 Dec 2024 23:56:58 +0200 Subject: [PATCH 26/37] chore: Fix typos in comments (#5088) This pull request addresses minor grammatical inconsistencies and corrects typos across multiple files in the repository. Specifically, it replaces incorrect usage of "a" with "an" where appropriate and corrects other minor typographical errors. These changes improve code readability and maintain grammatical accuracy. ### Changes - **incremental.rs**: Updated "a incremental" to "an incremental". - **MailboxClient.sol**: Updated "an Hyperlane" to "a Hyperlane". - **Router.sol**: Updated "a Application Router" to "an Application Router". - **AbstractMessageIdAuthorizedIsm.sol**: Updated "a authorized" to "an authorized". - **stakeRegistry.ts**: Updated "a expiry" to "an expiry". - **utils.ts**: Updated "an standard" to "a standard". - **ids.ts**: Updated "an Hyperlane ID" to "a Hyperlane ID". ### Drive-by Changes - No additional changes outside the scope of typo corrections. ### Related Issues - None. ### Backward Compatibility - These changes are backward compatible and do not affect functionality. ### Testing - All changes have been manually reviewed for grammatical correctness. - Verified correctness of usage within the context of each file. --- rust/main/hyperlane-core/src/accumulator/incremental.rs | 2 +- solidity/contracts/client/MailboxClient.sol | 2 +- solidity/contracts/client/Router.sol | 2 +- solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol | 2 +- typescript/cli/src/avs/stakeRegistry.ts | 2 +- typescript/infra/scripts/helloworld/utils.ts | 2 +- typescript/utils/src/ids.ts | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/rust/main/hyperlane-core/src/accumulator/incremental.rs b/rust/main/hyperlane-core/src/accumulator/incremental.rs index 5595bbc8cf..e71b9cbbe1 100644 --- a/rust/main/hyperlane-core/src/accumulator/incremental.rs +++ b/rust/main/hyperlane-core/src/accumulator/incremental.rs @@ -82,7 +82,7 @@ impl IncrementalMerkle { merkle_root_from_branch(item, &branch, 32, index) } - /// Verify a incremental merkle proof of inclusion + /// Verify an incremental merkle proof of inclusion pub fn verify(&self, proof: &Proof) -> bool { let computed = IncrementalMerkle::branch_root(proof.leaf, proof.path, proof.index); computed == self.root() diff --git a/solidity/contracts/client/MailboxClient.sol b/solidity/contracts/client/MailboxClient.sol index 9c986a0cce..d5359f1d77 100644 --- a/solidity/contracts/client/MailboxClient.sol +++ b/solidity/contracts/client/MailboxClient.sol @@ -58,7 +58,7 @@ abstract contract MailboxClient is OwnableUpgradeable, PackageVersioned { } /** - * @notice Only accept messages from an Hyperlane Mailbox contract + * @notice Only accept messages from a Hyperlane Mailbox contract */ modifier onlyMailbox() { require( diff --git a/solidity/contracts/client/Router.sol b/solidity/contracts/client/Router.sol index 5d98f2cb9c..9ae6310dc5 100644 --- a/solidity/contracts/client/Router.sol +++ b/solidity/contracts/client/Router.sol @@ -145,7 +145,7 @@ abstract contract Router is MailboxClient, IMessageRecipient { } /** - * @notice Assert that the given domain has a Application Router registered and return its address + * @notice Assert that the given domain has an Application Router registered and return its address * @param _domain The domain of the chain for which to get the Application Router * @return _router The address of the remote Application Router on _domain */ diff --git a/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol b/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol index a133de5d8d..ac5601125d 100644 --- a/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol +++ b/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol @@ -27,7 +27,7 @@ import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Ini /** * @title AbstractMessageIdAuthorizedIsm - * @notice Uses external verification options to verify interchain messages which need a authorized caller + * @notice Uses external verification options to verify interchain messages which need an authorized caller */ abstract contract AbstractMessageIdAuthorizedIsm is IInterchainSecurityModule, diff --git a/typescript/cli/src/avs/stakeRegistry.ts b/typescript/cli/src/avs/stakeRegistry.ts index a9c50918da..bfe0157021 100644 --- a/typescript/cli/src/avs/stakeRegistry.ts +++ b/typescript/cli/src/avs/stakeRegistry.ts @@ -136,7 +136,7 @@ async function getOperatorSignature( // random salt is ok, because we register the operator right after const salt = utils.hexZeroPad(utils.randomBytes(32), 32); - // give a expiry timestamp 1 hour from now + // give an expiry timestamp 1 hour from now const expiry = utils.hexZeroPad( utils.hexlify(Math.floor(Date.now() / 1000) + 60 * 60), 32, diff --git a/typescript/infra/scripts/helloworld/utils.ts b/typescript/infra/scripts/helloworld/utils.ts index 729dd6a4fd..2d95e72d37 100644 --- a/typescript/infra/scripts/helloworld/utils.ts +++ b/typescript/infra/scripts/helloworld/utils.ts @@ -127,7 +127,7 @@ export async function getHelloWorldMultiProtocolApp( ); // TODO we need a MultiProtocolIgp - // Using an standard IGP for just evm chains for now + // Using a standard IGP for just evm chains for now // Unfortunately this requires hacking surgically around certain addresses const filteredAddresses = filterChainMapToProtocol( envAddresses, diff --git a/typescript/utils/src/ids.ts b/typescript/utils/src/ids.ts index ff7bca0420..c3f3296377 100644 --- a/typescript/utils/src/ids.ts +++ b/typescript/utils/src/ids.ts @@ -20,7 +20,7 @@ export function canonizeId(data: BytesLike): Uint8Array { } /** - * Converts an Hyperlane ID of 20 or 32 bytes to the corresponding EVM Address. + * Converts a Hyperlane ID of 20 or 32 bytes to the corresponding EVM Address. * * For 32-byte IDs this enforces the EVM convention of using the LAST 20 bytes. * From f73dcc302b59707489e5eeba9a957ec05e604489 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Sun, 29 Dec 2024 15:30:09 +0000 Subject: [PATCH 27/37] feat: remove polygon tx overrides (#5084) ### Description ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- rust/main/config/mainnet_config.json | 6 +----- .../infra/config/environments/mainnet3/chains.ts | 16 ++++++++-------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/rust/main/config/mainnet_config.json b/rust/main/config/mainnet_config.json index e7704ff783..8f81beeec4 100644 --- a/rust/main/config/mainnet_config.json +++ b/rust/main/config/mainnet_config.json @@ -2318,11 +2318,7 @@ "validatorAnnounce": "0x454E1a1E1CA8B51506090f1b5399083658eA4Fc5", "staticMerkleRootWeightedMultisigIsmFactory": "0x07CE1B0cFfa436AE2fb7Fbd7318648774FdA53f9", "staticMessageIdWeightedMultisigIsmFactory": "0x9e22945bE593946618383B108CC5bce09eBA4C26", - "technicalStack": "other", - "transactionOverrides": { - "maxFeePerGas": 800000000000, - "maxPriorityFeePerGas": 50000000000 - } + "technicalStack": "other" }, "polygonzkevm": { "aggregationHook": "0x8464aF853363B8d6844070F68b0AB34Cb6523d0F", diff --git a/typescript/infra/config/environments/mainnet3/chains.ts b/typescript/infra/config/environments/mainnet3/chains.ts index 62c5dffdad..7cdc82abd0 100644 --- a/typescript/infra/config/environments/mainnet3/chains.ts +++ b/typescript/infra/config/environments/mainnet3/chains.ts @@ -18,14 +18,6 @@ export const chainMetadataOverrides: ChainMap> = { gasPrice: 3 * 10 ** 9, // 3 gwei }, }, - polygon: { - transactionOverrides: { - // A very high max fee per gas is used as Polygon is susceptible - // to large swings in gas prices. - maxFeePerGas: 800 * 10 ** 9, // 800 gwei - maxPriorityFeePerGas: 50 * 10 ** 9, // 50 gwei - }, - }, polygonzkevm: { transactionOverrides: { gasPrice: 1 * 10 ** 9, // 1 gwei @@ -91,6 +83,14 @@ export const chainMetadataOverrides: ChainMap> = { // maxPriorityFeePerGas: 10 * 10 ** 9, // 10 gwei // }, // }, + // polygon: { + // transactionOverrides: { + // // A very high max fee per gas is used as Polygon is susceptible + // // to large swings in gas prices. + // maxFeePerGas: 800 * 10 ** 9, // 800 gwei + // maxPriorityFeePerGas: 50 * 10 ** 9, // 50 gwei + // }, + // }, }; export const getRegistry = async ( From e3f5a0a3746375345ac01ea43732233a7f896dfc Mon Sep 17 00:00:00 2001 From: J M Rossy Date: Mon, 30 Dec 2024 11:44:58 -0500 Subject: [PATCH 28/37] fix: Allow empty data field in ethers5TxToWagmiTx (#5081) ### Description Widgets lib fix: Allow empty data field in ethers5TxToWagmiTx ### Backward compatibility Yes --- .changeset/serious-beers-lay.md | 5 +++++ typescript/widgets/src/walletIntegrations/utils.ts | 3 +-- 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 .changeset/serious-beers-lay.md diff --git a/.changeset/serious-beers-lay.md b/.changeset/serious-beers-lay.md new file mode 100644 index 0000000000..6f8fbffc34 --- /dev/null +++ b/.changeset/serious-beers-lay.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/widgets': patch +--- + +Allow empty data field in ethers5TxToWagmiTx diff --git a/typescript/widgets/src/walletIntegrations/utils.ts b/typescript/widgets/src/walletIntegrations/utils.ts index b0a994f1d8..6c2d485857 100644 --- a/typescript/widgets/src/walletIntegrations/utils.ts +++ b/typescript/widgets/src/walletIntegrations/utils.ts @@ -11,11 +11,10 @@ export function ethers5TxToWagmiTx( tx: Ethers5Transaction, ): SendTransactionParameters { if (!tx.to) throw new Error('No tx recipient address specified'); - if (!tx.data) throw new Error('No tx data specified'); return { to: tx.to as `0x${string}`, value: ethersBnToBigInt(tx.value || EthersBN.from('0')), - data: tx.data as `0x{string}`, + data: tx.data as `0x{string}` | undefined, nonce: tx.nonce, chainId: tx.chainId, gas: tx.gasLimit ? ethersBnToBigInt(tx.gasLimit) : undefined, From b09f97eca0fc953e8fa93bce02afd5b3897c1f29 Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Mon, 30 Dec 2024 17:57:53 +0000 Subject: [PATCH 29/37] chore: update relayer image (#5091) ### Description - update image in config to the current relayer image tag - add USDC/ethereum-inevm test tx to blacklist https://explorer.hyperlane.xyz/message/0x998746dc822dc15332b8683fb8a29aec22ed3e2f2fb8245c40f56303c5cb6032 top ### Drive-by changes ### Related issues ### Backward compatibility ### Testing Signed-off-by: pbio <10051819+paulbalaji@users.noreply.github.com> --- typescript/infra/config/environments/mainnet3/agent.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index 1cbc6d1d24..f886bd802b 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -602,6 +602,10 @@ const blacklistedMessageIds = [ // txs between unenrolled routers of // USDT/arbitrum-ethereum-mantle-mode-polygon-scroll-zeronetwork '0x10159bf1b5b2142b882cb060d1da9f9123d82974ca265ba432138221e52c2a27', + + // test tx when route was first deployed, no merkle tree insertion + // USDC/ethereum-inevm + '0x998746dc822dc15332b8683fb8a29aec22ed3e2f2fb8245c40f56303c5cb6032', ]; // Blacklist matching list intended to be used by all contexts. @@ -618,7 +622,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '234704d-20241226-192528', + tag: 'f73dcc3-20241229-154524', }, blacklist, gasPaymentEnforcement: gasPaymentEnforcement, From cd6558eaaf777f9df7ee68b8b426d0cf11c79f95 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Mon, 30 Dec 2024 20:27:24 +0000 Subject: [PATCH 30/37] feat: deploy JitoSOL and kySOL warp route monitors (#5085) ### Description - Deployed the JitoSOL and kySOL warp route monitors - Ensured ATA payer balances are sufficient - Also deployed the relayer with the new app contexts - Drive-by to lower the ethereum gas price (applied onchain) ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- typescript/infra/config/environments/mainnet3/gasPrices.json | 2 +- typescript/infra/config/environments/mainnet3/warp/warpIds.ts | 2 ++ typescript/infra/src/warp/helm.ts | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/typescript/infra/config/environments/mainnet3/gasPrices.json b/typescript/infra/config/environments/mainnet3/gasPrices.json index b260dfee48..c9698725ad 100644 --- a/typescript/infra/config/environments/mainnet3/gasPrices.json +++ b/typescript/infra/config/environments/mainnet3/gasPrices.json @@ -132,7 +132,7 @@ "decimals": 9 }, "ethereum": { - "amount": "32.059172476", + "amount": "10.0", "decimals": 9 }, "everclear": { diff --git a/typescript/infra/config/environments/mainnet3/warp/warpIds.ts b/typescript/infra/config/environments/mainnet3/warp/warpIds.ts index 1cd98d1c8d..58d8c7912e 100644 --- a/typescript/infra/config/environments/mainnet3/warp/warpIds.ts +++ b/typescript/infra/config/environments/mainnet3/warp/warpIds.ts @@ -14,6 +14,8 @@ export enum WarpRouteIds { EclipseEthereumWeETHs = 'weETHs/eclipsemainnet-ethereum', EclipseSolanaEzSOL = 'EZSOL/eclipsemainnet-solanamainnet', EclipseSolanaORCA = 'ORCA/eclipsemainnet-solanamainnet', + EclipseSolanaJitoSOL = 'jitoSOL/eclipsemainnet-solanamainnet', + EclipseSolanaKySOL = 'kySOL/eclipsemainnet-solanamainnet', EclipseSolanaSOL = 'SOL/eclipsemainnet-solanamainnet', EclipseSolanaWIF = 'WIF/eclipsemainnet-solanamainnet', EclipseStrideSTTIA = 'stTIA/eclipsemainnet-stride', diff --git a/typescript/infra/src/warp/helm.ts b/typescript/infra/src/warp/helm.ts index 1d0f43585a..4c7ddf60ef 100644 --- a/typescript/infra/src/warp/helm.ts +++ b/typescript/infra/src/warp/helm.ts @@ -28,7 +28,7 @@ export class WarpRouteMonitorHelmManager extends HelmManager { return { image: { repository: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: '3cacafd-20241220-092417', + tag: 'de11906-20241227-214834', }, warpRouteId: this.warpRouteId, fullnameOverride: this.helmReleaseName, From a7abf47d20649ba1459203ebbabef2a87ec26b39 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Mon, 30 Dec 2024 21:31:08 +0000 Subject: [PATCH 31/37] feat: retry program deployments in SVM tooling with higher fees (#4904) ### Description - A common failure case on Solana has been having trouble getting program deployment txs to land. Parts of the program bytecode are written to a buffer in hundreds of different txs, and some of these fail. Now we will: - specify a buffer account - automatically retry with the same buffer account (building upon any successful writes to it) and with a higher compute unit price What will probably follow at some point: - programmatically decide what fees to start using ### Drive-by changes - Always use existing keys if they are present, removing some code complexity here ### Related issues ### Backward compatibility ### Testing --- rust/main/utils/run-locally/src/solana.rs | 5 +- rust/sealevel/client/src/cmd_utils.rs | 152 ++++++++++++------ rust/sealevel/client/src/core.rs | 49 ++---- rust/sealevel/client/src/igp.rs | 20 +-- rust/sealevel/client/src/main.rs | 2 - rust/sealevel/client/src/multisig_ism.rs | 20 +-- rust/sealevel/client/src/router.rs | 17 +- ...sealevel_token-sealeveltest2-keypair.json} | 0 ...l_token_native-sealeveltest1-keypair.json} | 0 9 files changed, 144 insertions(+), 121 deletions(-) rename rust/sealevel/environments/local-e2e/warp-routes/testwarproute/keys/{hyperlane_sealevel_token-sealeveltest2.json => hyperlane_sealevel_token-sealeveltest2-keypair.json} (100%) rename rust/sealevel/environments/local-e2e/warp-routes/testwarproute/keys/{hyperlane_sealevel_token_native-sealeveltest1.json => hyperlane_sealevel_token_native-sealeveltest1-keypair.json} (100%) diff --git a/rust/main/utils/run-locally/src/solana.rs b/rust/main/utils/run-locally/src/solana.rs index 022d086a65..6dd137857f 100644 --- a/rust/main/utils/run-locally/src/solana.rs +++ b/rust/main/utils/run-locally/src/solana.rs @@ -159,7 +159,7 @@ pub fn build_solana_programs(solana_cli_tools_path: PathBuf) -> PathBuf { .working_dir(&out_path) .run() .join(); - log!("Remove temporary solana files"); + log!("Removing temporary solana files"); fs::remove_file(concat_path(&out_path, "spl.tar.gz")) .expect("Failed to remove solana program archive"); @@ -232,8 +232,7 @@ pub fn start_solana_test_validator( .arg("environment", SOLANA_ENV_NAME) .arg("environments-dir", SOLANA_ENVS_DIR) .arg("built-so-dir", SBF_OUT_PATH) - .arg("overhead-config-file", SOLANA_OVERHEAD_CONFIG_FILE) - .flag("use-existing-keys"); + .arg("overhead-config-file", SOLANA_OVERHEAD_CONFIG_FILE); sealevel_client_deploy_core .clone() diff --git a/rust/sealevel/client/src/cmd_utils.rs b/rust/sealevel/client/src/cmd_utils.rs index f387d3c4fd..4b8da171e1 100644 --- a/rust/sealevel/client/src/cmd_utils.rs +++ b/rust/sealevel/client/src/cmd_utils.rs @@ -1,14 +1,17 @@ use std::{ collections::HashMap, fs::File, - io::Write, + io::{self, Write}, path::{Path, PathBuf}, process::{Command, Stdio}, thread::sleep, time::Duration, }; -use solana_client::{client_error::ClientError, rpc_client::RpcClient}; +use solana_client::{ + client_error::{ClientError, ClientErrorKind}, + rpc_client::RpcClient, +}; use solana_sdk::{ commitment_config::CommitmentConfig, pubkey::Pubkey, @@ -44,37 +47,91 @@ pub(crate) fn account_exists(client: &RpcClient, account: &Pubkey) -> Result Result<(), ClientError> { +) -> Result { + let (program_keypair, program_keypair_path) = create_or_get_keypair( + program_key_dir, + format!("{}-keypair.json", program_name).as_str(), + ); + let program_id = program_keypair.pubkey(); + let client = RpcClient::new(url.to_string()); - if !account_exists(&client, &program_keypair.pubkey())? { - deploy_program( + if account_exists(&client, &program_keypair.pubkey())? { + println!("Program {} already deployed", program_keypair.pubkey()); + return Ok(program_id); + } + + let (buffer_keypair, buffer_keypair_path) = create_or_get_keypair( + program_key_dir, + format!("{}-buffer.json", program_name).as_str(), + ); + + let mut compute_unit_price = get_compute_unit_price_micro_lamports_for_id(local_domain); + + for attempt in 0..10 { + println!("Attempting program deploy Program ID: {}, buffer pubkey: {}, compute unit price: {}, attempt number {}", program_id, buffer_keypair.pubkey(), compute_unit_price, attempt); + + if attempt > 0 { + println!( + "As this is not the first deploy attempt, the buffer {} is re-used", + buffer_keypair.pubkey() + ); + } + + if attempt_program_deploy( payer_keypair_path, - program_keypair_path, + program_name, program_path, + &program_keypair_path, + &buffer_keypair_path, url, - local_domain, + compute_unit_price, + ) + .is_ok() + { + // Success! + return Ok(program_id); + } + + // Failed to deploy program, try again with a higher compute unit price + + println!( + "Failed to deploy program with compute unit price {}", + compute_unit_price ); - } else { - println!("Program {} already deployed", program_keypair.pubkey()); + + // Bump by 10% each time if non-zero, otherwise start at 1000 micro lamports + compute_unit_price = if compute_unit_price > 0 { + compute_unit_price * 11 / 10 + } else { + 1000 + }; + + println!( + "Sleeping 1s, then retrying with new compute unit price {}", + compute_unit_price + ); + sleep(Duration::from_secs(1)); } - Ok(()) + Err(ClientErrorKind::Custom(format!("Failed to deploy program {}", program_name)).into()) } -pub(crate) fn deploy_program( +fn attempt_program_deploy( payer_keypair_path: &str, - program_keypair_path: &str, + program_name: &str, program_path: &str, + program_keypair_path: &Path, + buffer_keypair_path: &Path, url: &str, - local_domain: u32, -) { + compute_unit_price: u64, +) -> Result<(), ClientError> { let mut command = vec![ "solana", "--url", @@ -87,19 +144,28 @@ pub(crate) fn deploy_program( "--upgrade-authority", payer_keypair_path, "--program-id", - program_keypair_path, + program_keypair_path.to_str().unwrap(), + "--buffer", + buffer_keypair_path.to_str().unwrap(), ]; - let compute_unit_price = get_compute_unit_price_micro_lamports_for_id(local_domain).to_string(); - if local_domain.eq(&SOLANA_DOMAIN) { - command.extend(vec!["--with-compute-unit-price", &compute_unit_price]); - } + let compute_unit_price_str = compute_unit_price.to_string(); + command.extend(vec!["--with-compute-unit-price", &compute_unit_price_str]); - build_cmd(command.as_slice(), None, None); + // Success! + if let Ok(true) = run_cmd(command.as_slice(), None, None) { + // TODO: use commitment level instead of just sleeping here? + println!("Sleeping for 2 seconds to fully allow program to be deployed"); + sleep(Duration::from_secs(2)); + + return Ok(()); + } - // TODO: use commitment level instead of just sleeping here? - println!("Sleeping for 2 seconds to allow program to be deployed"); - sleep(Duration::from_secs(2)); + Err(ClientErrorKind::Custom(format!( + "Attempted program deploy failed for {}", + program_name + )) + .into()) } pub(crate) fn create_new_directory(parent_dir: &Path, name: &str) -> PathBuf { @@ -109,20 +175,14 @@ pub(crate) fn create_new_directory(parent_dir: &Path, name: &str) -> PathBuf { path } -pub(crate) fn create_and_write_keypair( - key_dir: &Path, - key_name: &str, - use_existing_key: bool, -) -> (Keypair, PathBuf) { +pub(crate) fn create_or_get_keypair(key_dir: &Path, key_name: &str) -> (Keypair, PathBuf) { let path = key_dir.join(key_name); - if use_existing_key { - if let Ok(file) = File::open(path.clone()) { - println!("Using existing key at path {}", path.display()); - let keypair_bytes: Vec = serde_json::from_reader(file).unwrap(); - let keypair = Keypair::from_bytes(&keypair_bytes[..]).unwrap(); - return (keypair, path); - } + if let Ok(file) = File::open(path.clone()) { + println!("Using existing key at path {}", path.display()); + let keypair_bytes: Vec = serde_json::from_reader(file).unwrap(); + let keypair = Keypair::from_bytes(&keypair_bytes[..]).unwrap(); + return (keypair, path); } let keypair = Keypair::new(); @@ -136,8 +196,14 @@ pub(crate) fn create_and_write_keypair( (keypair, path) } -fn build_cmd(cmd: &[&str], wd: Option<&str>, env: Option<&HashMap<&str, &str>>) { +fn run_cmd(cmd: &[&str], wd: Option<&str>, env: Option<&HashMap<&str, &str>>) -> io::Result { assert!(!cmd.is_empty(), "Must specify a command!"); + if cmd.is_empty() { + return Err(io::Error::new( + io::ErrorKind::Other, + "Must specify a command!", + )); + } let mut c = Command::new(cmd[0]); c.args(&cmd[1..]); c.stdout(Stdio::inherit()); @@ -149,10 +215,6 @@ fn build_cmd(cmd: &[&str], wd: Option<&str>, env: Option<&HashMap<&str, &str>>) c.envs(env); } println!("Running command: {:?}", c); - let status = c.status().expect("Failed to run command"); - assert!( - status.success(), - "Command returned non-zero exit code: {}", - cmd.join(" ") - ); + let status = c.status()?; + Ok(status.success()) } diff --git a/rust/sealevel/client/src/core.rs b/rust/sealevel/client/src/core.rs index e3abedf035..72ecfcb5ca 100644 --- a/rust/sealevel/client/src/core.rs +++ b/rust/sealevel/client/src/core.rs @@ -3,7 +3,6 @@ use hyperlane_sealevel_mailbox::protocol_fee::ProtocolFee; use serde::{Deserialize, Serialize}; use solana_program::pubkey::Pubkey; -use solana_sdk::signature::Signer; use solana_sdk::{compute_budget, compute_budget::ComputeBudgetInstruction}; use std::collections::HashMap; @@ -13,7 +12,7 @@ use crate::cmd_utils::get_compute_unit_price_micro_lamports_for_chain_name; use crate::ONE_SOL_IN_LAMPORTS; use crate::{ artifacts::{read_json, write_json}, - cmd_utils::{create_and_write_keypair, create_new_directory, deploy_program}, + cmd_utils::{create_new_directory, deploy_program}, multisig_ism::deploy_multisig_ism_message_id, Context, CoreCmd, CoreDeploy, CoreSubCmd, }; @@ -89,7 +88,6 @@ pub(crate) fn process_core_cmd(mut ctx: Context, cmd: CoreCmd) { let ism_program_id = deploy_multisig_ism_message_id( &mut ctx, &core.built_so_dir, - core.use_existing_keys, &key_dir, core.local_domain, ); @@ -123,23 +121,18 @@ fn deploy_mailbox( default_ism: Pubkey, local_domain: u32, ) -> Pubkey { - let (keypair, keypair_path) = create_and_write_keypair( - key_dir, - "hyperlane_sealevel_mailbox-keypair.json", - core.use_existing_keys, - ); - let program_id = keypair.pubkey(); - - deploy_program( + let program_id = deploy_program( ctx.payer_keypair_path(), - keypair_path.to_str().unwrap(), + key_dir, + "hyperlane_sealevel_mailbox", core.built_so_dir .join("hyperlane_sealevel_mailbox.so") .to_str() .unwrap(), &ctx.client.url(), local_domain, - ); + ) + .unwrap(); println!("Deployed Mailbox at program ID {}", program_id); @@ -182,23 +175,18 @@ fn deploy_validator_announce( key_dir: &Path, mailbox_program_id: Pubkey, ) -> Pubkey { - let (keypair, keypair_path) = create_and_write_keypair( - key_dir, - "hyperlane_sealevel_validator_announce-keypair.json", - core.use_existing_keys, - ); - let program_id = keypair.pubkey(); - - deploy_program( + let program_id = deploy_program( ctx.payer_keypair_path(), - keypair_path.to_str().unwrap(), + key_dir, + "hyperlane_sealevel_validator_announce", core.built_so_dir .join("hyperlane_sealevel_validator_announce.so") .to_str() .unwrap(), &ctx.client.url(), core.local_domain, - ); + ) + .unwrap(); println!("Deployed ValidatorAnnounce at program ID {}", program_id); @@ -225,13 +213,6 @@ fn deploy_igp(ctx: &mut Context, core: &CoreDeploy, key_dir: &Path) -> (Pubkey, instruction::{GasOracleConfig, GasOverheadConfig}, }; - let (keypair, keypair_path) = create_and_write_keypair( - key_dir, - "hyperlane_sealevel_igp-keypair.json", - core.use_existing_keys, - ); - let program_id = keypair.pubkey(); - let mut gas_oracle_configs = core .gas_oracle_config_file .as_deref() @@ -275,16 +256,18 @@ fn deploy_igp(ctx: &mut Context, core: &CoreDeploy, key_dir: &Path) -> (Pubkey, .into_values() .collect::>(); - deploy_program( + let program_id = deploy_program( ctx.payer_keypair_path(), - keypair_path.to_str().unwrap(), + key_dir, + "hyperlane_sealevel_igp", core.built_so_dir .join("hyperlane_sealevel_igp.so") .to_str() .unwrap(), &ctx.client.url(), core.local_domain, - ); + ) + .unwrap(); println!("Deployed IGP at program ID {}", program_id); diff --git a/rust/sealevel/client/src/igp.rs b/rust/sealevel/client/src/igp.rs index 0d4c5c0e9e..cf9d6042b2 100644 --- a/rust/sealevel/client/src/igp.rs +++ b/rust/sealevel/client/src/igp.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use crate::{ artifacts::{read_json, try_read_json, write_json, SingularProgramIdArtifact}, - cmd_utils::{create_and_write_keypair, create_new_directory, deploy_program}, + cmd_utils::{create_new_directory, deploy_program}, read_core_program_ids, router::ChainMetadata, Context, GasOverheadSubCmd, GetSetCmd, IgpCmd, IgpSubCmd, @@ -75,7 +75,7 @@ pub(crate) fn process_igp_cmd(mut ctx: Context, cmd: IgpCmd) { .expect("Invalid chain name"); let program_id = - deploy_igp_program(&mut ctx, &deploy.built_so_dir, true, &key_dir, local_domain); + deploy_igp_program(&mut ctx, &deploy.built_so_dir, &key_dir, local_domain); write_json::( &chain_dir.join("program-ids.json"), @@ -413,27 +413,21 @@ pub(crate) fn process_igp_cmd(mut ctx: Context, cmd: IgpCmd) { fn deploy_igp_program( ctx: &mut Context, built_so_dir: &Path, - use_existing_keys: bool, key_dir: &Path, local_domain: u32, ) -> Pubkey { - let (keypair, keypair_path) = create_and_write_keypair( - key_dir, - "hyperlane_sealevel_igp-keypair.json", - use_existing_keys, - ); - let program_id = keypair.pubkey(); - - deploy_program( + let program_id = deploy_program( ctx.payer_keypair_path(), - keypair_path.to_str().unwrap(), + key_dir, + "hyperlane_sealevel_igp", built_so_dir .join("hyperlane_sealevel_igp.so") .to_str() .unwrap(), &ctx.client.url(), local_domain, - ); + ) + .unwrap(); println!("Deployed IGP at program ID {}", program_id); diff --git a/rust/sealevel/client/src/main.rs b/rust/sealevel/client/src/main.rs index bf014785c1..4b76a7cf0a 100644 --- a/rust/sealevel/client/src/main.rs +++ b/rust/sealevel/client/src/main.rs @@ -184,8 +184,6 @@ struct CoreDeploy { overhead_config_file: Option, #[arg(long)] chain: String, - #[arg(long)] - use_existing_keys: bool, #[arg(long, num_args = 1.., value_delimiter = ',')] remote_domains: Vec, #[arg(long)] diff --git a/rust/sealevel/client/src/multisig_ism.rs b/rust/sealevel/client/src/multisig_ism.rs index 9f1f2c5c89..753e89c7ab 100644 --- a/rust/sealevel/client/src/multisig_ism.rs +++ b/rust/sealevel/client/src/multisig_ism.rs @@ -3,11 +3,10 @@ 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}, + cmd_utils::{create_new_directory, deploy_program}, router::ChainMetadata, Context, MultisigIsmMessageIdCmd, MultisigIsmMessageIdSubCmd, }; @@ -64,7 +63,6 @@ pub(crate) fn process_multisig_ism_message_id_cmd(mut ctx: Context, cmd: Multisi let ism_program_id = deploy_multisig_ism_message_id( &mut ctx, &deploy.built_so_dir, - true, &key_dir, local_domain, ); @@ -168,27 +166,21 @@ pub(crate) fn process_multisig_ism_message_id_cmd(mut ctx: Context, cmd: Multisi pub(crate) fn deploy_multisig_ism_message_id( ctx: &mut Context, built_so_dir: &Path, - use_existing_keys: bool, key_dir: &Path, local_domain: u32, ) -> Pubkey { - let (keypair, keypair_path) = create_and_write_keypair( - key_dir, - "hyperlane_sealevel_multisig_ism_message_id-keypair.json", - use_existing_keys, - ); - let program_id = keypair.pubkey(); - - deploy_program( + let program_id = deploy_program( ctx.payer_keypair_path(), - keypair_path.to_str().unwrap(), + key_dir, + "hyperlane_sealevel_multisig_ism_message_id", built_so_dir .join("hyperlane_sealevel_multisig_ism_message_id.so") .to_str() .unwrap(), &ctx.client.url(), local_domain, - ); + ) + .unwrap(); println!( "Deployed Multisig ISM Message ID at program ID {}", diff --git a/rust/sealevel/client/src/router.rs b/rust/sealevel/client/src/router.rs index ad7ee36950..2b75ea5cae 100644 --- a/rust/sealevel/client/src/router.rs +++ b/rust/sealevel/client/src/router.rs @@ -8,7 +8,7 @@ use std::{ use solana_client::rpc_client::RpcClient; use solana_program::instruction::Instruction; -use solana_sdk::{commitment_config::CommitmentConfig, pubkey::Pubkey, signature::Signer}; +use solana_sdk::{commitment_config::CommitmentConfig, pubkey::Pubkey}; use account_utils::DiscriminatorData; use hyperlane_sealevel_connection_client::router::RemoteRouterConfig; @@ -17,7 +17,7 @@ use hyperlane_sealevel_igp::accounts::{Igp, InterchainGasPaymasterType, Overhead use crate::{ adjust_gas_price_if_needed, artifacts::{write_json, HexAndBase58ProgramIdArtifact}, - cmd_utils::{create_and_write_keypair, create_new_directory, deploy_program_idempotent}, + cmd_utils::{create_new_directory, deploy_program}, read_core_program_ids, warp_route, Context, CoreProgramIds, }; @@ -183,17 +183,12 @@ pub(crate) trait RouterDeployer: }) }) .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(); + let chain_program_name = format!("{}-{}", program_name, chain_config.name); - deploy_program_idempotent( + let program_id = deploy_program( ctx.payer_keypair_path(), - &keypair, - keypair_path.to_str().unwrap(), + key_dir, + &chain_program_name, built_so_dir .join(format!("{}.so", program_name)) .to_str() diff --git a/rust/sealevel/environments/local-e2e/warp-routes/testwarproute/keys/hyperlane_sealevel_token-sealeveltest2.json b/rust/sealevel/environments/local-e2e/warp-routes/testwarproute/keys/hyperlane_sealevel_token-sealeveltest2-keypair.json similarity index 100% rename from rust/sealevel/environments/local-e2e/warp-routes/testwarproute/keys/hyperlane_sealevel_token-sealeveltest2.json rename to rust/sealevel/environments/local-e2e/warp-routes/testwarproute/keys/hyperlane_sealevel_token-sealeveltest2-keypair.json diff --git a/rust/sealevel/environments/local-e2e/warp-routes/testwarproute/keys/hyperlane_sealevel_token_native-sealeveltest1.json b/rust/sealevel/environments/local-e2e/warp-routes/testwarproute/keys/hyperlane_sealevel_token_native-sealeveltest1-keypair.json similarity index 100% rename from rust/sealevel/environments/local-e2e/warp-routes/testwarproute/keys/hyperlane_sealevel_token_native-sealeveltest1.json rename to rust/sealevel/environments/local-e2e/warp-routes/testwarproute/keys/hyperlane_sealevel_token_native-sealeveltest1-keypair.json From c15257662840419cb6131360543e58af5c401eeb Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Thu, 2 Jan 2025 14:18:31 +0000 Subject: [PATCH 32/37] chore(relayer): add middleware logs, instrumentation cleanup (#5089) ### Description - Instruments all ether-rs middleware (except for the Metrics one) to improve observability of evm tx submission flow. ethers-rs diff: https://github.com/hyperlane-xyz/ethers-rs/compare/edf703a6e515266245b88bb4f1daad24f7c6566c...31653ac8c54ae3fe78b91bf5d92a1ab71604d07e Screenshot_2024-12-30_at_00 07 55 - Reduces noisiness of log fields where possible ### Related issues ### Backward compatibility ### Testing E2E and RC --- rust/main/Cargo.lock | 21 ++++++++++--------- rust/main/Cargo.toml | 10 ++++----- .../agents/relayer/src/msg/pending_message.rs | 2 +- .../src/contracts/mailbox.rs | 2 +- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/rust/main/Cargo.lock b/rust/main/Cargo.lock index c14e211f03..4f728ab69f 100644 --- a/rust/main/Cargo.lock +++ b/rust/main/Cargo.lock @@ -2901,7 +2901,7 @@ dependencies = [ [[package]] name = "ethers" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-12-16#c5c2a74660675ab968cf585893325e2faa807d14" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-12-30#31653ac8c54ae3fe78b91bf5d92a1ab71604d07e" dependencies = [ "ethers-addressbook", "ethers-contract", @@ -2915,7 +2915,7 @@ dependencies = [ [[package]] name = "ethers-addressbook" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-12-16#c5c2a74660675ab968cf585893325e2faa807d14" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-12-30#31653ac8c54ae3fe78b91bf5d92a1ab71604d07e" dependencies = [ "ethers-core", "once_cell", @@ -2926,7 +2926,7 @@ dependencies = [ [[package]] name = "ethers-contract" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-12-16#c5c2a74660675ab968cf585893325e2faa807d14" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-12-30#31653ac8c54ae3fe78b91bf5d92a1ab71604d07e" dependencies = [ "ethers-contract-abigen", "ethers-contract-derive", @@ -2944,7 +2944,7 @@ dependencies = [ [[package]] name = "ethers-contract-abigen" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-12-16#c5c2a74660675ab968cf585893325e2faa807d14" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-12-30#31653ac8c54ae3fe78b91bf5d92a1ab71604d07e" dependencies = [ "Inflector", "cfg-if", @@ -2968,7 +2968,7 @@ dependencies = [ [[package]] name = "ethers-contract-derive" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-12-16#c5c2a74660675ab968cf585893325e2faa807d14" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-12-30#31653ac8c54ae3fe78b91bf5d92a1ab71604d07e" dependencies = [ "ethers-contract-abigen", "ethers-core", @@ -2982,7 +2982,7 @@ dependencies = [ [[package]] name = "ethers-core" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-12-16#c5c2a74660675ab968cf585893325e2faa807d14" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-12-30#31653ac8c54ae3fe78b91bf5d92a1ab71604d07e" dependencies = [ "arrayvec", "bytes", @@ -3012,7 +3012,7 @@ dependencies = [ [[package]] name = "ethers-etherscan" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-12-16#c5c2a74660675ab968cf585893325e2faa807d14" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-12-30#31653ac8c54ae3fe78b91bf5d92a1ab71604d07e" dependencies = [ "ethers-core", "getrandom 0.2.15", @@ -3028,7 +3028,7 @@ dependencies = [ [[package]] name = "ethers-middleware" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-12-16#c5c2a74660675ab968cf585893325e2faa807d14" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-12-30#31653ac8c54ae3fe78b91bf5d92a1ab71604d07e" dependencies = [ "async-trait", "auto_impl 0.5.0", @@ -3047,6 +3047,7 @@ dependencies = [ "tokio", "tracing", "tracing-futures", + "tracing-test", "url", ] @@ -3076,7 +3077,7 @@ dependencies = [ [[package]] name = "ethers-providers" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-12-16#c5c2a74660675ab968cf585893325e2faa807d14" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-12-30#31653ac8c54ae3fe78b91bf5d92a1ab71604d07e" dependencies = [ "async-trait", "auto_impl 1.2.0", @@ -3112,7 +3113,7 @@ dependencies = [ [[package]] name = "ethers-signers" version = "1.0.2" -source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-12-16#c5c2a74660675ab968cf585893325e2faa807d14" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-12-30#31653ac8c54ae3fe78b91bf5d92a1ab71604d07e" dependencies = [ "async-trait", "coins-bip32 0.7.0", diff --git a/rust/main/Cargo.toml b/rust/main/Cargo.toml index 55cfc1573a..8b1a9b58e0 100644 --- a/rust/main/Cargo.toml +++ b/rust/main/Cargo.toml @@ -198,27 +198,27 @@ overflow-checks = true [workspace.dependencies.ethers] features = [] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2024-12-16" +tag = "2024-12-30" [workspace.dependencies.ethers-contract] features = ["legacy"] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2024-12-16" +tag = "2024-12-30" [workspace.dependencies.ethers-core] features = [] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2024-12-16" +tag = "2024-12-30" [workspace.dependencies.ethers-providers] features = [] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2024-12-16" +tag = "2024-12-30" [workspace.dependencies.ethers-signers] features = ["aws"] git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2024-12-16" +tag = "2024-12-30" [patch.crates-io.curve25519-dalek] branch = "v3.2.2-relax-zeroize" diff --git a/rust/main/agents/relayer/src/msg/pending_message.rs b/rust/main/agents/relayer/src/msg/pending_message.rs index d63b7b03f3..3c03094fc5 100644 --- a/rust/main/agents/relayer/src/msg/pending_message.rs +++ b/rust/main/agents/relayer/src/msg/pending_message.rs @@ -338,7 +338,7 @@ impl PendingOperation for PendingMessage { PendingOperationResult::Success } - #[instrument] + #[instrument(skip(self), fields(id=?self.id(), domain=%self.destination_domain()))] async fn submit(&mut self) -> PendingOperationResult { if self.submitted { // this message has already been submitted, possibly not by us diff --git a/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs index 1e9bfaa041..85a709223b 100644 --- a/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs @@ -496,7 +496,7 @@ where .into()) } - #[instrument(skip(self), fields(metadata=%bytes_to_hex(metadata)))] + #[instrument(skip(self, message, metadata), fields(metadata=%bytes_to_hex(metadata)))] async fn process( &self, message: &HyperlaneMessage, From 03a91236b2723785b0e2362ce0478b47d03e4684 Mon Sep 17 00:00:00 2001 From: xeno097 Date: Thu, 2 Jan 2025 11:37:04 -0400 Subject: [PATCH 33/37] test(cli): core commands e2e tests (#5090) ### Description Adds more E2E tests to the core commands in the CLI ### Drive-by changes - Removes the unused `--skip-confirmation` from the `core deploy` command - Updates the `warp init` tests to use the `handlePrompts` function - Moves the core e2e tests into the `test/core` folder ### Related issues - #5080 ### Backward compatibility - YES ### Testing - Manual - E2E --- .github/workflows/test.yml | 5 + typescript/cli/src/commands/core.ts | 2 - typescript/cli/src/tests/commands/core.ts | 98 +++++- typescript/cli/src/tests/commands/helpers.ts | 99 +++++- .../cli/src/tests/core-deploy.e2e-test.ts | 108 ------- .../tests/{ => core}/core-apply.e2e-test.ts | 7 +- .../cli/src/tests/core/core-check.e2e-test.ts | 93 ++++++ .../src/tests/core/core-deploy.e2e-test.ts | 286 ++++++++++++++++++ .../cli/src/tests/core/core-init.e2e-test.ts | 216 +++++++++++++ .../tests/{ => core}/core-read.e2e-test.ts | 7 +- .../cli/src/tests/warp-init.e2e-test.ts | 159 +++++----- 11 files changed, 854 insertions(+), 226 deletions(-) delete mode 100644 typescript/cli/src/tests/core-deploy.e2e-test.ts rename typescript/cli/src/tests/{ => core}/core-apply.e2e-test.ts (98%) create mode 100644 typescript/cli/src/tests/core/core-check.e2e-test.ts create mode 100644 typescript/cli/src/tests/core/core-deploy.e2e-test.ts create mode 100644 typescript/cli/src/tests/core/core-init.e2e-test.ts rename typescript/cli/src/tests/{ => core}/core-read.e2e-test.ts (90%) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5235c9dd16..7f491f8f11 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -126,10 +126,15 @@ jobs: fail-fast: false matrix: test: + # Core Commands - core-apply + - core-check - core-deploy + - core-init - core-read + # Other commands - relay + # Warp Commands - warp-init - warp-read - warp-apply diff --git a/typescript/cli/src/commands/core.ts b/typescript/cli/src/commands/core.ts index a75f6ae61e..51801d9db0 100644 --- a/typescript/cli/src/commands/core.ts +++ b/typescript/cli/src/commands/core.ts @@ -35,7 +35,6 @@ import { fromAddressCommandOption, inputFileCommandOption, outputFileCommandOption, - skipConfirmationOption, } from './options.js'; /** @@ -117,7 +116,6 @@ export const deploy: CommandModuleWithWriteContext<{ ), 'dry-run': dryRunCommandOption, 'from-address': fromAddressCommandOption, - 'skip-confirmation': skipConfirmationOption, }, handler: async ({ context, chain, config: configFilePath, dryRun }) => { logCommandHeader(`Hyperlane Core deployment${dryRun ? ' dry-run' : ''}`); diff --git a/typescript/cli/src/tests/commands/core.ts b/typescript/cli/src/tests/commands/core.ts index 5feabbe2cb..e1cbe0cb59 100644 --- a/typescript/cli/src/tests/commands/core.ts +++ b/typescript/cli/src/tests/commands/core.ts @@ -1,11 +1,45 @@ -import { $ } from 'zx'; +import { $, ProcessPromise } from 'zx'; import { DerivedCoreConfig } from '@hyperlane-xyz/sdk'; +import { Address } from '@hyperlane-xyz/utils'; import { readYamlOrJson } from '../../utils/files.js'; import { ANVIL_KEY, REGISTRY_PATH } from './helpers.js'; +/** + * Deploys the Hyperlane core contracts to the specified chain using the provided config. + */ +export function hyperlaneCoreDeployRaw( + coreInputPath: string, + privateKey?: string, + skipConfirmationPrompts?: boolean, + hypKey?: string, +): ProcessPromise { + if (hypKey) { + return $`HYP_KEY=${hypKey} yarn workspace @hyperlane-xyz/cli run hyperlane core deploy \ + --registry ${REGISTRY_PATH} \ + --config ${coreInputPath} \ + --verbosity debug \ + ${skipConfirmationPrompts ? '--yes' : ''}`; + } + + if (privateKey) { + return $`yarn workspace @hyperlane-xyz/cli run hyperlane core deploy \ + --registry ${REGISTRY_PATH} \ + --config ${coreInputPath} \ + --key ${privateKey} \ + --verbosity debug \ + ${skipConfirmationPrompts ? '--yes' : ''}`; + } + + return $`yarn workspace @hyperlane-xyz/cli run hyperlane core deploy \ + --registry ${REGISTRY_PATH} \ + --config ${coreInputPath} \ + --verbosity debug \ + ${skipConfirmationPrompts ? '--yes' : ''}`; +} + /** * Deploys the Hyperlane core contracts to the specified chain using the provided config. */ @@ -34,6 +68,68 @@ export async function hyperlaneCoreRead(chain: string, coreOutputPath: string) { --yes`; } +/** + * Verifies that a Hyperlane core deployment matches the provided config on the specified chain. + */ +export function hyperlaneCoreCheck( + chain: string, + coreOutputPath: string, + mailbox?: Address, +): ProcessPromise { + if (mailbox) { + return $`yarn workspace @hyperlane-xyz/cli run hyperlane core check \ + --registry ${REGISTRY_PATH} \ + --config ${coreOutputPath} \ + --chain ${chain} \ + --mailbox ${mailbox} \ + --verbosity debug \ + --yes`; + } + + return $`yarn workspace @hyperlane-xyz/cli run hyperlane core check \ + --registry ${REGISTRY_PATH} \ + --config ${coreOutputPath} \ + --chain ${chain} \ + --verbosity debug \ + --yes`; +} + +/** + * Creates a Hyperlane core deployment config + */ +export function hyperlaneCoreInit( + coreOutputPath: string, + privateKey?: string, + hyp_key?: string, +): ProcessPromise { + if (hyp_key) { + return $`${ + hyp_key ? `HYP_KEY=${hyp_key}` : '' + } yarn workspace @hyperlane-xyz/cli run hyperlane core init \ + --registry ${REGISTRY_PATH} \ + --config ${coreOutputPath} \ + --verbosity debug \ + --yes`; + } + + if (privateKey) { + return $`${ + hyp_key ? 'HYP_KEY=${hyp_key}' : '' + } yarn workspace @hyperlane-xyz/cli run hyperlane core init \ + --registry ${REGISTRY_PATH} \ + --config ${coreOutputPath} \ + --verbosity debug \ + --key ${privateKey} \ + --yes`; + } + + return $`yarn workspace @hyperlane-xyz/cli run hyperlane core init \ + --registry ${REGISTRY_PATH} \ + --config ${coreOutputPath} \ + --verbosity debug \ + --yes`; +} + /** * Updates a Hyperlane core deployment on the specified chain using the provided config. */ diff --git a/typescript/cli/src/tests/commands/helpers.ts b/typescript/cli/src/tests/commands/helpers.ts index 66f8f2e7f4..7fdb407324 100644 --- a/typescript/cli/src/tests/commands/helpers.ts +++ b/typescript/cli/src/tests/commands/helpers.ts @@ -1,5 +1,5 @@ import { ethers } from 'ethers'; -import { $, ProcessPromise } from 'zx'; +import { $, ProcessOutput, ProcessPromise } from 'zx'; import { ERC20Test__factory, ERC4626Test__factory } from '@hyperlane-xyz/core'; import { ChainAddresses } from '@hyperlane-xyz/registry'; @@ -34,6 +34,7 @@ export const CHAIN_NAME_3 = 'anvil3'; export const EXAMPLES_PATH = './examples'; export const CORE_CONFIG_PATH = `${EXAMPLES_PATH}/core-config.yaml`; +export const CORE_CONFIG_PATH_2 = `${TEMP_PATH}/${CHAIN_NAME_2}/core-config.yaml`; export const CORE_READ_CONFIG_PATH_2 = `${TEMP_PATH}/${CHAIN_NAME_2}/core-config-read.yaml`; export const CHAIN_2_METADATA_PATH = `${REGISTRY_PATH}/chains/${CHAIN_NAME_2}/metadata.yaml`; export const CHAIN_3_METADATA_PATH = `${REGISTRY_PATH}/chains/${CHAIN_NAME_3}/metadata.yaml`; @@ -60,23 +61,89 @@ export async function asyncStreamInputWrite( await sleep(500); } -export async function selectAnvil2AndAnvil3( - stream: ProcessPromise, -): Promise { - // Scroll down through the mainnet chains list and select anvil2 - await asyncStreamInputWrite( - stream.stdin, - `${KeyBoardKeys.ARROW_DOWN.repeat(3)}${KeyBoardKeys.TAB}`, - ); - // Scroll down through the mainnet chains list again and select anvil3 - await asyncStreamInputWrite( - stream.stdin, - `${KeyBoardKeys.ARROW_DOWN.repeat(2)}${KeyBoardKeys.TAB}${ - KeyBoardKeys.ENTER - }`, - ); +export type TestPromptAction = { + check: (currentOutput: string) => boolean; + input: string; +}; + +/** + * Takes a {@link ProcessPromise} and a list of inputs that will be supplied + * in the provided order when the check in the {@link TestPromptAction} matches the output + * of the {@link ProcessPromise}. + */ +export async function handlePrompts( + processPromise: Readonly, + actions: TestPromptAction[], +): Promise { + let expectedStep = 0; + for await (const out of processPromise.stdout) { + const currentLine: string = out.toString(); + + const currentAction = actions[expectedStep]; + if (currentAction && currentAction.check(currentLine)) { + // Select mainnet chains + await asyncStreamInputWrite(processPromise.stdin, currentAction.input); + expectedStep++; + } + } + + return processPromise; } +export const SELECT_ANVIL_2_FROM_MULTICHAIN_PICKER = `${KeyBoardKeys.ARROW_DOWN.repeat( + 3, +)}${KeyBoardKeys.TAB}`; + +export const SELECT_ANVIL_3_AFTER_ANVIL_2_FROM_MULTICHAIN_PICKER = `${KeyBoardKeys.ARROW_DOWN.repeat( + 2, +)}${KeyBoardKeys.TAB}`; + +export const SELECT_MAINNET_CHAIN_TYPE_STEP: TestPromptAction = { + check: (currentOutput: string) => + currentOutput.includes('Select network type'), + // Select mainnet chains + input: KeyBoardKeys.ENTER, +}; + +export const SELECT_ANVIL_2_AND_ANVIL_3_STEPS: ReadonlyArray = + [ + { + check: (currentOutput: string) => + currentOutput.includes('--Mainnet Chains--'), + input: `${SELECT_ANVIL_2_FROM_MULTICHAIN_PICKER}`, + }, + { + check: (currentOutput: string) => + currentOutput.includes('--Mainnet Chains--'), + input: `${SELECT_ANVIL_3_AFTER_ANVIL_2_FROM_MULTICHAIN_PICKER}${KeyBoardKeys.ENTER}`, + }, + ]; + +export const CONFIRM_DETECTED_OWNER_STEP: Readonly = { + check: (currentOutput: string) => + currentOutput.includes('Detected owner address as'), + input: KeyBoardKeys.ENTER, +}; + +export const SETUP_CHAIN_SIGNERS_MANUALLY_STEPS: ReadonlyArray = + [ + { + check: (currentOutput) => + currentOutput.includes('Please enter the private key for chain'), + input: `${ANVIL_KEY}${KeyBoardKeys.ENTER}`, + }, + { + check: (currentOutput) => + currentOutput.includes('Please enter the private key for chain'), + input: `${ANVIL_KEY}${KeyBoardKeys.ENTER}`, + }, + { + check: (currentOutput) => + currentOutput.includes('Please enter the private key for chain'), + input: `${ANVIL_KEY}${KeyBoardKeys.ENTER}`, + }, + ]; + /** * Retrieves the deployed Warp address from the Warp core config. */ diff --git a/typescript/cli/src/tests/core-deploy.e2e-test.ts b/typescript/cli/src/tests/core-deploy.e2e-test.ts deleted file mode 100644 index 192f0121b4..0000000000 --- a/typescript/cli/src/tests/core-deploy.e2e-test.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { expect } from 'chai'; -import { Signer, Wallet, ethers } from 'ethers'; - -import { - ChainMetadata, - CoreConfig, - ProtocolFeeHookConfig, - randomAddress, -} from '@hyperlane-xyz/sdk'; -import { Address } from '@hyperlane-xyz/utils'; - -import { readYamlOrJson, writeYamlOrJson } from '../utils/files.js'; - -import { hyperlaneCoreDeploy, readCoreConfig } from './commands/core.js'; -import { - ANVIL_KEY, - CHAIN_2_METADATA_PATH, - CHAIN_NAME_2, - CORE_CONFIG_PATH, - CORE_READ_CONFIG_PATH_2, - DEFAULT_E2E_TEST_TIMEOUT, -} from './commands/helpers.js'; - -describe('hyperlane core deploy e2e tests', async function () { - this.timeout(DEFAULT_E2E_TEST_TIMEOUT); - - let signer: Signer; - let initialOwnerAddress: Address; - - before(async () => { - const chainMetadata: ChainMetadata = readYamlOrJson(CHAIN_2_METADATA_PATH); - - const provider = new ethers.providers.JsonRpcProvider( - chainMetadata.rpcUrls[0].http, - ); - - const wallet = new Wallet(ANVIL_KEY); - signer = wallet.connect(provider); - - initialOwnerAddress = await signer.getAddress(); - }); - - it('should create a core deployment with the signer as the mailbox owner', async () => { - await hyperlaneCoreDeploy(CHAIN_NAME_2, CORE_CONFIG_PATH); - - const coreConfig: CoreConfig = await readCoreConfig( - CHAIN_NAME_2, - CORE_READ_CONFIG_PATH_2, - ); - - expect(coreConfig.owner).to.equal(initialOwnerAddress); - expect(coreConfig.proxyAdmin?.owner).to.equal(initialOwnerAddress); - // Assuming that the ProtocolFeeHook is used for deployment - expect((coreConfig.requiredHook as ProtocolFeeHookConfig).owner).to.equal( - initialOwnerAddress, - ); - }); - - it('should create a core deployment with the mailbox owner set to the address in the config', async () => { - const coreConfig: CoreConfig = await readYamlOrJson(CORE_CONFIG_PATH); - - const newOwner = randomAddress().toLowerCase(); - - coreConfig.owner = newOwner; - writeYamlOrJson(CORE_READ_CONFIG_PATH_2, coreConfig); - - // Deploy the core contracts with the updated mailbox owner - await hyperlaneCoreDeploy(CHAIN_NAME_2, CORE_READ_CONFIG_PATH_2); - - // Verify that the owner has been set correctly without modifying any other owner values - const updatedConfig: CoreConfig = await readCoreConfig( - CHAIN_NAME_2, - CORE_READ_CONFIG_PATH_2, - ); - - expect(updatedConfig.owner.toLowerCase()).to.equal(newOwner); - expect(updatedConfig.proxyAdmin?.owner).to.equal(initialOwnerAddress); - // Assuming that the ProtocolFeeHook is used for deployment - expect( - (updatedConfig.requiredHook as ProtocolFeeHookConfig).owner, - ).to.equal(initialOwnerAddress); - }); - - it('should create a core deployment with ProxyAdmin owner of the mailbox set to the address in the config', async () => { - const coreConfig: CoreConfig = await readYamlOrJson(CORE_CONFIG_PATH); - - const newOwner = randomAddress().toLowerCase(); - - coreConfig.proxyAdmin = { owner: newOwner }; - writeYamlOrJson(CORE_READ_CONFIG_PATH_2, coreConfig); - - // Deploy the core contracts with the updated mailbox owner - await hyperlaneCoreDeploy(CHAIN_NAME_2, CORE_READ_CONFIG_PATH_2); - - // Verify that the owner has been set correctly without modifying any other owner values - const updatedConfig: CoreConfig = await readCoreConfig( - CHAIN_NAME_2, - CORE_READ_CONFIG_PATH_2, - ); - - expect(updatedConfig.owner).to.equal(initialOwnerAddress); - expect(updatedConfig.proxyAdmin?.owner.toLowerCase()).to.equal(newOwner); - // Assuming that the ProtocolFeeHook is used for deployment - expect( - (updatedConfig.requiredHook as ProtocolFeeHookConfig).owner, - ).to.equal(initialOwnerAddress); - }); -}); diff --git a/typescript/cli/src/tests/core-apply.e2e-test.ts b/typescript/cli/src/tests/core/core-apply.e2e-test.ts similarity index 98% rename from typescript/cli/src/tests/core-apply.e2e-test.ts rename to typescript/cli/src/tests/core/core-apply.e2e-test.ts index 27667fd719..c062d5b43b 100644 --- a/typescript/cli/src/tests/core-apply.e2e-test.ts +++ b/typescript/cli/src/tests/core/core-apply.e2e-test.ts @@ -11,13 +11,12 @@ import { } from '@hyperlane-xyz/sdk'; import { Address, Domain, addressToBytes32 } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../utils/files.js'; - +import { readYamlOrJson, writeYamlOrJson } from '../../utils/files.js'; import { hyperlaneCoreApply, hyperlaneCoreDeploy, readCoreConfig, -} from './commands/core.js'; +} from '../commands/core.js'; import { ANVIL_KEY, CHAIN_2_METADATA_PATH, @@ -28,7 +27,7 @@ import { CORE_READ_CONFIG_PATH_2, DEFAULT_E2E_TEST_TIMEOUT, TEMP_PATH, -} from './commands/helpers.js'; +} from '../commands/helpers.js'; const CORE_READ_CHAIN_2_CONFIG_PATH = `${TEMP_PATH}/${CHAIN_NAME_2}/core-config-read.yaml`; const CORE_READ_CHAIN_3_CONFIG_PATH = `${TEMP_PATH}/${CHAIN_NAME_3}/core-config-read.yaml`; diff --git a/typescript/cli/src/tests/core/core-check.e2e-test.ts b/typescript/cli/src/tests/core/core-check.e2e-test.ts new file mode 100644 index 0000000000..f1dd609914 --- /dev/null +++ b/typescript/cli/src/tests/core/core-check.e2e-test.ts @@ -0,0 +1,93 @@ +import { expect } from 'chai'; +import { $ } from 'zx'; + +import { randomAddress } from '@hyperlane-xyz/sdk'; + +import { writeYamlOrJson } from '../../utils/files.js'; +import { + hyperlaneCoreCheck, + hyperlaneCoreDeploy, + readCoreConfig, +} from '../commands/core.js'; +import { + ANVIL_KEY, + CHAIN_NAME_2, + CORE_CONFIG_PATH, + CORE_READ_CONFIG_PATH_2, + DEFAULT_E2E_TEST_TIMEOUT, + REGISTRY_PATH, + deployOrUseExistingCore, +} from '../commands/helpers.js'; + +describe('hyperlane core check e2e tests', async function () { + this.timeout(2 * DEFAULT_E2E_TEST_TIMEOUT); + + before(async () => { + await deployOrUseExistingCore(CHAIN_NAME_2, CORE_CONFIG_PATH, ANVIL_KEY); + }); + + it('should throw an error if the --chain param is not provided', async () => { + const wrongCommand = + $`yarn workspace @hyperlane-xyz/cli run hyperlane core check \ + --registry ${REGISTRY_PATH} \ + --config ${CORE_CONFIG_PATH} \ + --verbosity debug \ + --yes`.nothrow(); + + const output = await wrongCommand; + + expect(output.exitCode).to.equal(1); + expect(output.text().includes('Missing required argument: chain')).to.be + .true; + }); + + it('should successfully run the core check command', async () => { + await readCoreConfig(CHAIN_NAME_2, CORE_READ_CONFIG_PATH_2); + + const output = await hyperlaneCoreCheck( + CHAIN_NAME_2, + CORE_READ_CONFIG_PATH_2, + ); + + expect(output.exitCode).to.equal(0); + expect(output.text().includes('No violations found')).to.be.true; + }); + + it('should find differences between the local and onchain config', async () => { + const coreConfig = await readCoreConfig( + CHAIN_NAME_2, + CORE_READ_CONFIG_PATH_2, + ); + coreConfig.owner = randomAddress(); + writeYamlOrJson(CORE_READ_CONFIG_PATH_2, coreConfig); + const expectedDiffText = `EXPECTED: "${coreConfig.owner}"\n`; + const expectedActualText = `ACTUAL: "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"\n`; + + const output = await hyperlaneCoreCheck( + CHAIN_NAME_2, + CORE_READ_CONFIG_PATH_2, + ).nothrow(); + + expect(output.exitCode).to.equal(1); + expect(output.text().includes(expectedDiffText)).to.be.true; + expect(output.text().includes(expectedActualText)).to.be.true; + }); + + it('should successfully check the config when provided with a custom mailbox', async () => { + await hyperlaneCoreDeploy(CHAIN_NAME_2, CORE_CONFIG_PATH); + const coreConfig = await readCoreConfig( + CHAIN_NAME_2, + CORE_READ_CONFIG_PATH_2, + ); + expect(coreConfig.interchainAccountRouter?.mailbox).not.to.be.undefined; + + const output = await hyperlaneCoreCheck( + CHAIN_NAME_2, + CORE_READ_CONFIG_PATH_2, + coreConfig.interchainAccountRouter!.mailbox, + ); + + expect(output.exitCode).to.equal(0); + expect(output.text().includes('No violations found')).to.be.true; + }); +}); diff --git a/typescript/cli/src/tests/core/core-deploy.e2e-test.ts b/typescript/cli/src/tests/core/core-deploy.e2e-test.ts new file mode 100644 index 0000000000..f7544fc8f7 --- /dev/null +++ b/typescript/cli/src/tests/core/core-deploy.e2e-test.ts @@ -0,0 +1,286 @@ +import { expect } from 'chai'; +import { Signer, Wallet, ethers } from 'ethers'; + +import { + ChainMetadata, + CoreConfig, + HookType, + ProtocolFeeHookConfig, + randomAddress, +} from '@hyperlane-xyz/sdk'; +import { Address } from '@hyperlane-xyz/utils'; + +import { readYamlOrJson, writeYamlOrJson } from '../../utils/files.js'; +import { + hyperlaneCoreDeploy, + hyperlaneCoreDeployRaw, + readCoreConfig, +} from '../commands/core.js'; +import { + ANVIL_KEY, + CHAIN_2_METADATA_PATH, + CHAIN_NAME_2, + CORE_CONFIG_PATH, + CORE_READ_CONFIG_PATH_2, + DEFAULT_E2E_TEST_TIMEOUT, + KeyBoardKeys, + SELECT_MAINNET_CHAIN_TYPE_STEP, + SETUP_CHAIN_SIGNERS_MANUALLY_STEPS, + TestPromptAction, + handlePrompts, +} from '../commands/helpers.js'; + +describe('hyperlane core deploy e2e tests', async function () { + this.timeout(DEFAULT_E2E_TEST_TIMEOUT); + + let signer: Signer; + let initialOwnerAddress: Address; + + before(async () => { + const chainMetadata: ChainMetadata = readYamlOrJson(CHAIN_2_METADATA_PATH); + + const provider = new ethers.providers.JsonRpcProvider( + chainMetadata.rpcUrls[0].http, + ); + + const wallet = new Wallet(ANVIL_KEY); + signer = wallet.connect(provider); + + initialOwnerAddress = await signer.getAddress(); + }); + + describe('hyperlane core deploy', () => { + it('should create a core deployment with the signer as the mailbox owner', async () => { + const steps: TestPromptAction[] = [ + ...SETUP_CHAIN_SIGNERS_MANUALLY_STEPS, + SELECT_MAINNET_CHAIN_TYPE_STEP, + { + check: (currentOutput: string) => + currentOutput.includes('--Mainnet Chains--'), + // Scroll down through the mainnet chains list and select anvil2 + input: `${KeyBoardKeys.ARROW_DOWN.repeat(2)}}${KeyBoardKeys.ENTER}`, + }, + { + // When running locally the e2e tests, the chains folder might already have the chain contracts + check: (currentOutput) => + currentOutput.includes('Mailbox already exists at') || + currentOutput.includes('Is this deployment plan correct?'), + input: `yes${KeyBoardKeys.ENTER}`, + }, + { + check: (currentOutput) => + currentOutput.includes('Is this deployment plan correct?'), + input: KeyBoardKeys.ENTER, + }, + ]; + + const output = hyperlaneCoreDeployRaw(CORE_CONFIG_PATH).stdio('pipe'); + + const finalOutput = await handlePrompts(output, steps); + + expect(finalOutput.exitCode).to.equal(0); + + const coreConfig: CoreConfig = await readCoreConfig( + CHAIN_NAME_2, + CORE_READ_CONFIG_PATH_2, + ); + expect(coreConfig.owner).to.equal(initialOwnerAddress); + expect(coreConfig.proxyAdmin?.owner).to.equal(initialOwnerAddress); + // Assuming that the ProtocolFeeHook is used for deployment + const requiredHookConfig = coreConfig.requiredHook as Exclude< + CoreConfig['requiredHook'], + string + >; + expect(requiredHookConfig.type).to.equal(HookType.PROTOCOL_FEE); + expect((requiredHookConfig as ProtocolFeeHookConfig).owner).to.equal( + initialOwnerAddress, + ); + }); + }); + + describe('hyperlane core deploy --yes', () => { + it('should fail if the --chain flag is not provided but the --yes flag is', async () => { + const steps: TestPromptAction[] = [...SETUP_CHAIN_SIGNERS_MANUALLY_STEPS]; + + const output = hyperlaneCoreDeployRaw(CORE_CONFIG_PATH, undefined, true) + .nothrow() + .stdio('pipe'); + + const finalOutput = await handlePrompts(output, steps); + + expect(finalOutput.exitCode).to.equal(1); + expect(finalOutput.text().includes('No chain provided')).to.be.true; + }); + }); + + describe('hyperlane core deploy --key ...', () => { + it('should create a core deployment with the signer as the mailbox owner', async () => { + const steps: TestPromptAction[] = [ + SELECT_MAINNET_CHAIN_TYPE_STEP, + { + check: (currentOutput: string) => + currentOutput.includes('--Mainnet Chains--'), + // Scroll down through the mainnet chains list and select anvil2 + input: `${KeyBoardKeys.ARROW_DOWN.repeat(2)}}${KeyBoardKeys.ENTER}`, + }, + { + // When running locally the e2e tests, the chains folder might already have the chain contracts + check: (currentOutput) => + currentOutput.includes('Mailbox already exists at') || + currentOutput.includes('Is this deployment plan correct?'), + input: `yes${KeyBoardKeys.ENTER}`, + }, + { + check: (currentOutput) => + currentOutput.includes('Is this deployment plan correct?'), + input: KeyBoardKeys.ENTER, + }, + ]; + + const output = hyperlaneCoreDeployRaw(CORE_CONFIG_PATH, ANVIL_KEY).stdio( + 'pipe', + ); + + const finalOutput = await handlePrompts(output, steps); + + expect(finalOutput.exitCode).to.equal(0); + + const coreConfig: CoreConfig = await readCoreConfig( + CHAIN_NAME_2, + CORE_READ_CONFIG_PATH_2, + ); + expect(coreConfig.owner).to.equal(initialOwnerAddress); + expect(coreConfig.proxyAdmin?.owner).to.equal(initialOwnerAddress); + // Assuming that the ProtocolFeeHook is used for deployment + const requiredHookConfig = coreConfig.requiredHook as Exclude< + CoreConfig['requiredHook'], + string + >; + expect(requiredHookConfig.type).to.equal(HookType.PROTOCOL_FEE); + expect((requiredHookConfig as ProtocolFeeHookConfig).owner).to.equal( + initialOwnerAddress, + ); + }); + }); + + describe('HYP_KEY= ... hyperlane core deploy', () => { + it('should create a core deployment with the signer as the mailbox owner', async () => { + const steps: TestPromptAction[] = [ + SELECT_MAINNET_CHAIN_TYPE_STEP, + { + check: (currentOutput: string) => + currentOutput.includes('--Mainnet Chains--'), + // Scroll down through the mainnet chains list and select anvil2 + input: `${KeyBoardKeys.ARROW_DOWN.repeat(2)}}${KeyBoardKeys.ENTER}`, + }, + { + // When running locally the e2e tests, the chains folder might already have the chain contracts + check: (currentOutput) => + currentOutput.includes('Mailbox already exists at') || + currentOutput.includes('Is this deployment plan correct?'), + input: `yes${KeyBoardKeys.ENTER}`, + }, + { + check: (currentOutput) => + currentOutput.includes('Is this deployment plan correct?'), + input: KeyBoardKeys.ENTER, + }, + ]; + + const output = hyperlaneCoreDeployRaw( + CORE_CONFIG_PATH, + undefined, + undefined, + ANVIL_KEY, + ).stdio('pipe'); + + const finalOutput = await handlePrompts(output, steps); + + expect(finalOutput.exitCode).to.equal(0); + + const coreConfig: CoreConfig = await readCoreConfig( + CHAIN_NAME_2, + CORE_READ_CONFIG_PATH_2, + ); + expect(coreConfig.owner).to.equal(initialOwnerAddress); + expect(coreConfig.proxyAdmin?.owner).to.equal(initialOwnerAddress); + // Assuming that the ProtocolFeeHook is used for deployment + const requiredHookConfig = coreConfig.requiredHook as Exclude< + CoreConfig['requiredHook'], + string + >; + expect(requiredHookConfig.type).to.equal(HookType.PROTOCOL_FEE); + expect((requiredHookConfig as ProtocolFeeHookConfig).owner).to.equal( + initialOwnerAddress, + ); + }); + }); + + describe('hyperlane core deploy --yes --key ...', () => { + it('should create a core deployment with the signer as the mailbox owner', async () => { + await hyperlaneCoreDeploy(CHAIN_NAME_2, CORE_CONFIG_PATH); + + const coreConfig: CoreConfig = await readCoreConfig( + CHAIN_NAME_2, + CORE_READ_CONFIG_PATH_2, + ); + + expect(coreConfig.owner).to.equal(initialOwnerAddress); + expect(coreConfig.proxyAdmin?.owner).to.equal(initialOwnerAddress); + // Assuming that the ProtocolFeeHook is used for deployment + expect((coreConfig.requiredHook as ProtocolFeeHookConfig).owner).to.equal( + initialOwnerAddress, + ); + }); + + it('should create a core deployment with the mailbox owner set to the address in the config', async () => { + const coreConfig: CoreConfig = await readYamlOrJson(CORE_CONFIG_PATH); + + const newOwner = randomAddress().toLowerCase(); + + coreConfig.owner = newOwner; + writeYamlOrJson(CORE_READ_CONFIG_PATH_2, coreConfig); + + // Deploy the core contracts with the updated mailbox owner + await hyperlaneCoreDeploy(CHAIN_NAME_2, CORE_READ_CONFIG_PATH_2); + + // Verify that the owner has been set correctly without modifying any other owner values + const updatedConfig: CoreConfig = await readCoreConfig( + CHAIN_NAME_2, + CORE_READ_CONFIG_PATH_2, + ); + + expect(updatedConfig.owner.toLowerCase()).to.equal(newOwner); + expect(updatedConfig.proxyAdmin?.owner).to.equal(initialOwnerAddress); + // Assuming that the ProtocolFeeHook is used for deployment + expect( + (updatedConfig.requiredHook as ProtocolFeeHookConfig).owner, + ).to.equal(initialOwnerAddress); + }); + + it('should create a core deployment with ProxyAdmin owner of the mailbox set to the address in the config', async () => { + const coreConfig: CoreConfig = await readYamlOrJson(CORE_CONFIG_PATH); + + const newOwner = randomAddress().toLowerCase(); + + coreConfig.proxyAdmin = { owner: newOwner }; + writeYamlOrJson(CORE_READ_CONFIG_PATH_2, coreConfig); + + // Deploy the core contracts with the updated mailbox owner + await hyperlaneCoreDeploy(CHAIN_NAME_2, CORE_READ_CONFIG_PATH_2); + + // Verify that the owner has been set correctly without modifying any other owner values + const updatedConfig: CoreConfig = await readCoreConfig( + CHAIN_NAME_2, + CORE_READ_CONFIG_PATH_2, + ); + + expect(updatedConfig.owner).to.equal(initialOwnerAddress); + expect(updatedConfig.proxyAdmin?.owner.toLowerCase()).to.equal(newOwner); + // Assuming that the ProtocolFeeHook is used for deployment + expect( + (updatedConfig.requiredHook as ProtocolFeeHookConfig).owner, + ).to.equal(initialOwnerAddress); + }); + }); +}); diff --git a/typescript/cli/src/tests/core/core-init.e2e-test.ts b/typescript/cli/src/tests/core/core-init.e2e-test.ts new file mode 100644 index 0000000000..5c308df4a5 --- /dev/null +++ b/typescript/cli/src/tests/core/core-init.e2e-test.ts @@ -0,0 +1,216 @@ +import { expect } from 'chai'; +import { Wallet } from 'ethers'; + +import { + CoreConfig, + HookType, + MerkleTreeHookConfig, + ProtocolFeeHookConfig, + randomAddress, +} from '@hyperlane-xyz/sdk'; +import { Address, normalizeAddress } from '@hyperlane-xyz/utils'; + +import { readYamlOrJson } from '../../utils/files.js'; +import { hyperlaneCoreInit } from '../commands/core.js'; +import { + ANVIL_KEY, + CONFIRM_DETECTED_OWNER_STEP, + CORE_CONFIG_PATH_2, + DEFAULT_E2E_TEST_TIMEOUT, + KeyBoardKeys, + TestPromptAction, + handlePrompts, +} from '../commands/helpers.js'; + +describe('hyperlane core init e2e tests', async function () { + this.timeout(2 * DEFAULT_E2E_TEST_TIMEOUT); + + function assertCoreInitConfig( + coreConfig: CoreConfig, + owner: Address, + feeHookOwner: Address = owner, + feeHookBeneficiary: Address = feeHookOwner, + ): void { + expect(coreConfig.owner).to.equal(owner); + expect(coreConfig.proxyAdmin?.owner).to.equal(owner); + + const defaultHookConfig = coreConfig.defaultHook as MerkleTreeHookConfig; + expect(defaultHookConfig.type).to.equal(HookType.MERKLE_TREE); + + const requiredHookConfig = coreConfig.requiredHook as ProtocolFeeHookConfig; + expect(requiredHookConfig.type).to.equal(HookType.PROTOCOL_FEE); + expect(normalizeAddress(requiredHookConfig.owner)).to.equal(feeHookOwner); + expect(normalizeAddress(requiredHookConfig.beneficiary)).to.equal( + feeHookBeneficiary, + ); + } + + describe('hyperlane core init', () => { + it('should successfully generate the core contract deployment config', async () => { + const output = hyperlaneCoreInit(CORE_CONFIG_PATH_2).stdio('pipe'); + + const owner = normalizeAddress(randomAddress()); + const feeHookOwner = normalizeAddress(randomAddress()); + const steps: TestPromptAction[] = [ + { + check: (currentOutput) => + currentOutput.includes('Enter the desired owner address:'), + input: `${owner}${KeyBoardKeys.ENTER}`, + }, + { + check: (currentOutput) => + currentOutput.includes( + 'For trusted relayer ISM, enter relayer address:', + ), + input: `${owner}${KeyBoardKeys.ENTER}`, + }, + { + check: (currentOutput) => + currentOutput.includes( + 'For Protocol Fee Hook, enter owner address:', + ), + input: `${feeHookOwner}${KeyBoardKeys.ENTER}`, + }, + { + check: (currentOutput) => + !!currentOutput.match(/Use this same address \((.*?)\) for/), + input: KeyBoardKeys.ENTER, + }, + ]; + + const finalOutput = await handlePrompts(output, steps); + + expect(finalOutput.exitCode).to.equal(0); + + const deploymentCoreConfig: CoreConfig = + readYamlOrJson(CORE_CONFIG_PATH_2); + assertCoreInitConfig(deploymentCoreConfig, owner, feeHookOwner); + }); + }); + + describe('HYP_KEY=... hyperlane core init', () => { + it('should successfully generate the core contract deployment config when confirming owner prompts', async () => { + const owner = new Wallet(ANVIL_KEY).address; + const steps: TestPromptAction[] = [ + CONFIRM_DETECTED_OWNER_STEP, + { + check: (currentOutput) => + !!currentOutput.match(/Use this same address \((.*?)\) for/), + input: KeyBoardKeys.ENTER, + }, + ]; + + const output = hyperlaneCoreInit( + CORE_CONFIG_PATH_2, + undefined, + ANVIL_KEY, + ).stdio('pipe'); + + const finalOutput = await handlePrompts(output, steps); + + expect(finalOutput.exitCode).to.equal(0); + + const deploymentCoreConfig: CoreConfig = + readYamlOrJson(CORE_CONFIG_PATH_2); + assertCoreInitConfig(deploymentCoreConfig, owner); + }); + + it('should successfully generate the core contract deployment config when not confirming owner prompts', async () => { + const owner = new Wallet(ANVIL_KEY).address; + const feeHookOwner = normalizeAddress(randomAddress()); + const steps: TestPromptAction[] = [ + CONFIRM_DETECTED_OWNER_STEP, + { + check: (currentOutput) => + !!currentOutput.match(/Use this same address \((.*?)\) for/), + input: `no${KeyBoardKeys.ENTER}`, + }, + { + check: (currentOutput) => + currentOutput.includes('Enter beneficiary address for'), + input: `${feeHookOwner}${KeyBoardKeys.ENTER}`, + }, + ]; + + const output = hyperlaneCoreInit( + CORE_CONFIG_PATH_2, + undefined, + ANVIL_KEY, + ).stdio('pipe'); + + const finalOutput = await handlePrompts(output, steps); + + expect(finalOutput.exitCode).to.equal(0); + + const deploymentCoreConfig: CoreConfig = + readYamlOrJson(CORE_CONFIG_PATH_2); + assertCoreInitConfig( + deploymentCoreConfig, + owner, + undefined, + feeHookOwner, + ); + }); + }); + + describe('hyperlane core init --key ...', () => { + it('should successfully generate the core contract deployment config when confirming owner prompts', async () => { + const owner = new Wallet(ANVIL_KEY).address; + const steps: TestPromptAction[] = [ + CONFIRM_DETECTED_OWNER_STEP, + { + check: (currentOutput) => + !!currentOutput.match(/Use this same address \((.*?)\) for/), + input: KeyBoardKeys.ENTER, + }, + ]; + + const output = hyperlaneCoreInit(CORE_CONFIG_PATH_2, ANVIL_KEY).stdio( + 'pipe', + ); + + const finalOutput = await handlePrompts(output, steps); + + expect(finalOutput.exitCode).to.equal(0); + + const deploymentCoreConfig: CoreConfig = + readYamlOrJson(CORE_CONFIG_PATH_2); + assertCoreInitConfig(deploymentCoreConfig, owner); + }); + + it('should successfully generate the core contract deployment config when not confirming owner prompts', async () => { + const owner = new Wallet(ANVIL_KEY).address; + const feeHookOwner = normalizeAddress(randomAddress()); + const steps: TestPromptAction[] = [ + CONFIRM_DETECTED_OWNER_STEP, + { + check: (currentOutput) => + !!currentOutput.match(/Use this same address \((.*?)\) for/), + input: `no${KeyBoardKeys.ENTER}`, + }, + { + check: (currentOutput) => + currentOutput.includes('Enter beneficiary address for'), + input: `${feeHookOwner}${KeyBoardKeys.ENTER}`, + }, + ]; + + const output = hyperlaneCoreInit(CORE_CONFIG_PATH_2, ANVIL_KEY).stdio( + 'pipe', + ); + + const finalOutput = await handlePrompts(output, steps); + + expect(finalOutput.exitCode).to.equal(0); + + const deploymentCoreConfig: CoreConfig = + readYamlOrJson(CORE_CONFIG_PATH_2); + assertCoreInitConfig( + deploymentCoreConfig, + owner, + undefined, + feeHookOwner, + ); + }); + }); +}); diff --git a/typescript/cli/src/tests/core-read.e2e-test.ts b/typescript/cli/src/tests/core/core-read.e2e-test.ts similarity index 90% rename from typescript/cli/src/tests/core-read.e2e-test.ts rename to typescript/cli/src/tests/core/core-read.e2e-test.ts index 7390e8dca4..9ed0a3ca01 100644 --- a/typescript/cli/src/tests/core-read.e2e-test.ts +++ b/typescript/cli/src/tests/core/core-read.e2e-test.ts @@ -8,9 +8,8 @@ import { } from '@hyperlane-xyz/sdk'; import { Address } from '@hyperlane-xyz/utils'; -import { readYamlOrJson } from '../utils/files.js'; - -import { hyperlaneCoreDeploy, readCoreConfig } from './commands/core.js'; +import { readYamlOrJson } from '../../utils/files.js'; +import { hyperlaneCoreDeploy, readCoreConfig } from '../commands/core.js'; import { ANVIL_KEY, CHAIN_2_METADATA_PATH, @@ -18,7 +17,7 @@ import { CORE_CONFIG_PATH, CORE_READ_CONFIG_PATH_2, DEFAULT_E2E_TEST_TIMEOUT, -} from './commands/helpers.js'; +} from '../commands/helpers.js'; describe('hyperlane core read e2e tests', async function () { this.timeout(DEFAULT_E2E_TEST_TIMEOUT); diff --git a/typescript/cli/src/tests/warp-init.e2e-test.ts b/typescript/cli/src/tests/warp-init.e2e-test.ts index b3314a30a9..66cfa3bfc7 100644 --- a/typescript/cli/src/tests/warp-init.e2e-test.ts +++ b/typescript/cli/src/tests/warp-init.e2e-test.ts @@ -16,14 +16,18 @@ import { ANVIL_KEY, CHAIN_NAME_2, CHAIN_NAME_3, + CONFIRM_DETECTED_OWNER_STEP, CORE_CONFIG_PATH, DEFAULT_E2E_TEST_TIMEOUT, KeyBoardKeys, + SELECT_ANVIL_2_AND_ANVIL_3_STEPS, + SELECT_ANVIL_2_FROM_MULTICHAIN_PICKER, + SELECT_MAINNET_CHAIN_TYPE_STEP, + TestPromptAction, WARP_CONFIG_PATH_2, - asyncStreamInputWrite, deployOrUseExistingCore, deployToken, - selectAnvil2AndAnvil3, + handlePrompts, } from './commands/helpers.js'; import { hyperlaneWarpInit } from './commands/warp.js'; @@ -67,37 +71,26 @@ describe('hyperlane warp init e2e tests', async function () { } it('it should generate a warp deploy config with a single chain', async function () { - const output = hyperlaneWarpInit(WARP_CONFIG_PATH_2).stdio('pipe'); - - for await (const out of output.stdout) { - const currentLine: string = out.toString(); - - if ( - currentLine.includes('Creating a new warp route deployment config...') - ) { - // Select mainnet chains - await asyncStreamInputWrite(output.stdin, KeyBoardKeys.ENTER); - } else if (currentLine.includes('--Mainnet Chains--')) { + const steps: TestPromptAction[] = [ + SELECT_MAINNET_CHAIN_TYPE_STEP, + { + check: (currentOutput: string) => + currentOutput.includes('--Mainnet Chains--'), // Scroll down through the mainnet chains list and select anvil2 - await asyncStreamInputWrite( - output.stdin, - `${KeyBoardKeys.ARROW_DOWN.repeat(3)}${KeyBoardKeys.TAB}${ - KeyBoardKeys.ENTER - }`, - ); - } else if (currentLine.includes('token type')) { + input: `${SELECT_ANVIL_2_FROM_MULTICHAIN_PICKER}${KeyBoardKeys.ENTER}`, + }, + CONFIRM_DETECTED_OWNER_STEP, + { + check: (currentOutput: string) => + !!currentOutput.match(/Select .+?'s token type/), // Scroll up through the token type list and select native - await asyncStreamInputWrite( - output.stdin, - `${KeyBoardKeys.ARROW_UP.repeat(2)}${KeyBoardKeys.ENTER}`, - ); - } else if (currentLine.includes('Detected owner address as')) { - // Confirm owner prompts - await asyncStreamInputWrite(output.stdin, KeyBoardKeys.ENTER); - } - } - - await output; + input: `${KeyBoardKeys.ARROW_UP.repeat(2)}${KeyBoardKeys.ENTER}`, + }, + ]; + + const output = hyperlaneWarpInit(WARP_CONFIG_PATH_2).stdio('pipe'); + + await handlePrompts(output, steps); const warpConfig: WarpRouteDeployConfig = readYamlOrJson(WARP_CONFIG_PATH_2); @@ -106,31 +99,26 @@ describe('hyperlane warp init e2e tests', async function () { }); it('it should generate a warp deploy config with a 2 chains warp route (native->native)', async function () { + const steps: TestPromptAction[] = [ + SELECT_MAINNET_CHAIN_TYPE_STEP, + ...SELECT_ANVIL_2_AND_ANVIL_3_STEPS, + CONFIRM_DETECTED_OWNER_STEP, + { + check: (currentOutput: string) => + !!currentOutput.match(/Select .+?'s token type/), + input: `${KeyBoardKeys.ARROW_UP.repeat(2)}${KeyBoardKeys.ENTER}`, + }, + CONFIRM_DETECTED_OWNER_STEP, + { + check: (currentOutput: string) => + !!currentOutput.match(/Select .+?'s token type/), + input: `${KeyBoardKeys.ARROW_UP.repeat(2)}${KeyBoardKeys.ENTER}`, + }, + ]; + const output = hyperlaneWarpInit(WARP_CONFIG_PATH_2).stdio('pipe'); - for await (const out of output.stdout) { - const currentLine: string = out.toString(); - - if ( - currentLine.includes('Creating a new warp route deployment config...') - ) { - // Select mainnet chains - await asyncStreamInputWrite(output.stdin, KeyBoardKeys.ENTER); - } else if (currentLine.includes('--Mainnet Chains--')) { - await selectAnvil2AndAnvil3(output); - } else if (currentLine.match(/Select .+?'s token type/)) { - // Scroll up through the token type list and select native - await asyncStreamInputWrite( - output.stdin, - `${KeyBoardKeys.ARROW_UP.repeat(2)}${KeyBoardKeys.ENTER}`, - ); - } else if (currentLine.includes('Detected owner address as')) { - // Confirm owner prompts - await asyncStreamInputWrite(output.stdin, KeyBoardKeys.ENTER); - } - } - - await output; + await handlePrompts(output, steps); const warpConfig: WarpRouteDeployConfig = readYamlOrJson(WARP_CONFIG_PATH_2); @@ -142,46 +130,35 @@ describe('hyperlane warp init e2e tests', async function () { it('it should generate a warp deploy config with a 2 chains warp route (collateral->synthetic)', async function () { const erc20Token = await deployToken(ANVIL_KEY, CHAIN_NAME_2, 6); + const steps: TestPromptAction[] = [ + SELECT_MAINNET_CHAIN_TYPE_STEP, + ...SELECT_ANVIL_2_AND_ANVIL_3_STEPS, + // First chain token config + CONFIRM_DETECTED_OWNER_STEP, + { + check: (currentOutput: string) => + !!currentOutput.match(/Select .+?'s token type/), + // Scroll down through the token type list and select collateral + input: `${KeyBoardKeys.ARROW_DOWN.repeat(4)}${KeyBoardKeys.ENTER}`, + }, + { + check: (currentOutput: string) => + currentOutput.includes('Enter the existing token address on chain'), + input: `${erc20Token.address}${KeyBoardKeys.ENTER}`, + }, + // Other chain token config + CONFIRM_DETECTED_OWNER_STEP, + { + check: (currentOutput: string) => + !!currentOutput.match(/Select .+?'s token type/), + // Select the synthetic token type + input: `${KeyBoardKeys.ENTER}`, + }, + ]; const output = hyperlaneWarpInit(WARP_CONFIG_PATH_2).stdio('pipe'); - let tokenStep = 0; - for await (const out of output.stdout) { - const currentLine: string = out.toString(); - - if ( - currentLine.includes('Creating a new warp route deployment config...') - ) { - // Select mainnet chains - await asyncStreamInputWrite(output.stdin, KeyBoardKeys.ENTER); - } else if (currentLine.includes('--Mainnet Chains--')) { - await selectAnvil2AndAnvil3(output); - } else if ( - currentLine.includes('Enter the existing token address on chain') - ) { - await asyncStreamInputWrite( - output.stdin, - `${erc20Token.address}${KeyBoardKeys.ENTER}`, - ); - } else if (currentLine.match(/Select .+?'s token type/)) { - if (tokenStep === 0) { - // Scroll down through the token type list and select collateral - await asyncStreamInputWrite( - output.stdin, - `${KeyBoardKeys.ARROW_DOWN.repeat(4)}${KeyBoardKeys.ENTER}`, - ); - } else if (tokenStep === 1) { - // Select the synthetic token type - await asyncStreamInputWrite(output.stdin, KeyBoardKeys.ENTER); - } - tokenStep++; - } else if (currentLine.includes('Detected owner address as')) { - // Confirm owner prompts - await asyncStreamInputWrite(output.stdin, KeyBoardKeys.ENTER); - } - } - - await output; + await handlePrompts(output, steps); const warpConfig: WarpRouteDeployConfig = readYamlOrJson(WARP_CONFIG_PATH_2); From a5ece3b308afa13f60f8755b09d9000020aa30fb Mon Sep 17 00:00:00 2001 From: ljankovic-txfusion <131957285+ljankovic-txfusion@users.noreply.github.com> Date: Thu, 2 Jan 2025 18:38:14 +0100 Subject: [PATCH 34/37] feat: Add chain technical stack configuration to registry init (#5049) ### Description The PR adds support for specifying chain technical stacks during registry initialization, with special handling for Arbitrum Nitro chains. ### Drive-by changes - Add technical stack selector during chain configuration to `hyperlane registry init` CLI command - Add special handling for Arbitrum Nitro chains with` index.from `parameter - Update chain metadata schema to include technical stack information ### Related issues None ### Backward compatibility Yes ### Testing Manual testing of `registry init` --------- Co-authored-by: Morteza Shojaei <31728528+mortezashojaei@users.noreply.github.com> Co-authored-by: mshojaei-txfusion <138107084+mshojaei-txfusion@users.noreply.github.com> --- .changeset/two-jeans-sin.md | 5 +++++ typescript/cli/src/config/chain.ts | 27 +++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 .changeset/two-jeans-sin.md diff --git a/.changeset/two-jeans-sin.md b/.changeset/two-jeans-sin.md new file mode 100644 index 0000000000..2cc997f9bb --- /dev/null +++ b/.changeset/two-jeans-sin.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/cli': minor +--- + +Add chain technical stack selector with Arbitrum Nitro support to `hyperlane registry init` command diff --git a/typescript/cli/src/config/chain.ts b/typescript/cli/src/config/chain.ts index e710b6cf00..07064b5e87 100644 --- a/typescript/cli/src/config/chain.ts +++ b/typescript/cli/src/config/chain.ts @@ -5,6 +5,7 @@ import { stringify as yamlStringify } from 'yaml'; import { ChainMetadata, ChainMetadataSchema, + ChainTechnicalStack, EthJsonRpcBlockParameterTag, ExplorerFamily, ZChainName, @@ -87,14 +88,40 @@ export async function createChainConfig({ 'Is this chain a testnet (a chain used for testing & development)?', }); + const technicalStack = (await select({ + choices: Object.entries(ChainTechnicalStack).map(([_, value]) => ({ + value, + })), + message: 'Select the chain technical stack', + pageSize: 10, + })) as ChainTechnicalStack; + + const arbitrumNitroMetadata: Pick = {}; + if (technicalStack === ChainTechnicalStack.ArbitrumNitro) { + const indexFrom = await detectAndConfirmOrPrompt( + async () => { + return (await provider.getBlockNumber()).toString(); + }, + `Enter`, + 'starting block number for indexing', + 'JSON RPC provider', + ); + + arbitrumNitroMetadata.index = { + from: parseInt(indexFrom), + }; + } + const metadata: ChainMetadata = { name, displayName, chainId, domainId: chainId, protocol: ProtocolType.Ethereum, + technicalStack, rpcUrls: [{ http: rpcUrl }], isTestnet, + ...arbitrumNitroMetadata, }; await addBlockExplorerConfig(metadata); From d35502fa7c607539bd640169c9d0b7230f039c90 Mon Sep 17 00:00:00 2001 From: xeno097 Date: Thu, 2 Jan 2025 17:32:55 -0400 Subject: [PATCH 35/37] feat: better chain selection for single chain (#4824) ### Description This PR updates the single chain selection prompt to use a search prompt instead of a select one. ### Drive-by changes - Adds missing `:` where the `runSingleChainSelectionStep` is used. - Installs the `@inquirer/search` package ### Related issues - Fixes #4823 ### Backward compatibility - Yes ### Testing - Manual --------- Co-authored-by: Lee <6251863+ltyu@users.noreply.github.com> --- .changeset/yellow-icons-do.md | 5 +++ typescript/cli/package.json | 1 + typescript/cli/src/config/hooks.ts | 2 +- typescript/cli/src/deploy/agent.ts | 2 +- typescript/cli/src/send/message.ts | 4 +- typescript/cli/src/send/transfer.ts | 4 +- typescript/cli/src/status/message.ts | 2 +- typescript/cli/src/utils/chains.ts | 23 ++++++++++-- yarn.lock | 55 ++++++++++++++++++++++++++++ 9 files changed, 87 insertions(+), 11 deletions(-) create mode 100644 .changeset/yellow-icons-do.md diff --git a/.changeset/yellow-icons-do.md b/.changeset/yellow-icons-do.md new file mode 100644 index 0000000000..c8e5a583ed --- /dev/null +++ b/.changeset/yellow-icons-do.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/cli': minor +--- + +Update single chain selection to be searchable instead of a simple select diff --git a/typescript/cli/package.json b/typescript/cli/package.json index 3a249e154c..c8f53b25df 100644 --- a/typescript/cli/package.json +++ b/typescript/cli/package.json @@ -11,6 +11,7 @@ "@inquirer/core": "9.0.10", "@inquirer/figures": "1.0.5", "@inquirer/prompts": "3.3.2", + "@inquirer/search": "^3.0.1", "ansi-escapes": "^7.0.0", "asn1.js": "^5.4.1", "bignumber.js": "^9.1.1", diff --git a/typescript/cli/src/config/hooks.ts b/typescript/cli/src/config/hooks.ts index 5ad005dc24..1ca07a9a70 100644 --- a/typescript/cli/src/config/hooks.ts +++ b/typescript/cli/src/config/hooks.ts @@ -270,7 +270,7 @@ async function getOwnerAndBeneficiary( async function selectIgpChains(context: CommandContext) { const localChain = await runSingleChainSelectionStep( context.chainMetadata, - 'Select local chain for IGP hook', + 'Select local chain for IGP hook:', ); const isTestnet = context.chainMetadata[localChain].isTestnet; const remoteChains = await runMultiChainSelectionStep({ diff --git a/typescript/cli/src/deploy/agent.ts b/typescript/cli/src/deploy/agent.ts index a36955a3f3..034a2e5136 100644 --- a/typescript/cli/src/deploy/agent.ts +++ b/typescript/cli/src/deploy/agent.ts @@ -25,7 +25,7 @@ export async function runKurtosisAgentDeploy({ if (!originChain) { originChain = await runSingleChainSelectionStep( context.chainMetadata, - 'Select the origin chain', + 'Select the origin chain:', ); } if (!relayChains) { diff --git a/typescript/cli/src/send/message.ts b/typescript/cli/src/send/message.ts index f7d5eb4e15..e43d08fe2c 100644 --- a/typescript/cli/src/send/message.ts +++ b/typescript/cli/src/send/message.ts @@ -33,14 +33,14 @@ export async function sendTestMessage({ if (!origin) { origin = await runSingleChainSelectionStep( chainMetadata, - 'Select the origin chain', + 'Select the origin chain:', ); } if (!destination) { destination = await runSingleChainSelectionStep( chainMetadata, - 'Select the destination chain', + 'Select the destination chain:', ); } diff --git a/typescript/cli/src/send/transfer.ts b/typescript/cli/src/send/transfer.ts index 2929b09c6e..134eb9b3b3 100644 --- a/typescript/cli/src/send/transfer.ts +++ b/typescript/cli/src/send/transfer.ts @@ -53,14 +53,14 @@ export async function sendTestTransfer({ if (!origin) { origin = await runSingleChainSelectionStep( chainMetadata, - 'Select the origin chain', + 'Select the origin chain:', ); } if (!destination) { destination = await runSingleChainSelectionStep( chainMetadata, - 'Select the destination chain', + 'Select the destination chain:', ); } diff --git a/typescript/cli/src/status/message.ts b/typescript/cli/src/status/message.ts index b5051b4c92..a63caa4e3e 100644 --- a/typescript/cli/src/status/message.ts +++ b/typescript/cli/src/status/message.ts @@ -24,7 +24,7 @@ export async function checkMessageStatus({ if (!origin) { origin = await runSingleChainSelectionStep( context.chainMetadata, - 'Select the origin chain', + 'Select the origin chain:', ); } diff --git a/typescript/cli/src/utils/chains.ts b/typescript/cli/src/utils/chains.ts index 7e2eaccd0a..7f2f88fad8 100644 --- a/typescript/cli/src/utils/chains.ts +++ b/typescript/cli/src/utils/chains.ts @@ -1,4 +1,5 @@ import { Separator, confirm } from '@inquirer/prompts'; +import search from '@inquirer/search'; import select from '@inquirer/select'; import chalk from 'chalk'; @@ -23,9 +24,21 @@ export async function runSingleChainSelectionStep( chainMetadata, networkType, ); - const chain = (await select({ - message, - choices: [networkTypeSeparator, ...choices], + + const formattedMessage = message.endsWith(':') ? message : `${message}:`; + const options = [networkTypeSeparator, ...choices]; + const chain = (await search({ + message: formattedMessage, + source: (searchTerm) => { + if (!searchTerm) { + return options; + } + + return options.filter( + (value) => + Separator.isSeparator(value) || value.value.includes(searchTerm), + ); + }, pageSize: calculatePageSize(2), })) as string; handleNewChain([chain]); @@ -142,7 +155,9 @@ function getChainChoices( networkType: 'mainnet' | 'testnet', ) { const chainsToChoices = (chains: ChainMetadata[]) => - chains.map((c) => ({ name: c.name, value: c.name })); + chains + .map((c) => ({ name: c.name, value: c.name })) + .sort((a, b) => a.name.localeCompare(b.name)); const chains = Object.values(chainMetadata); const filteredChains = chains.filter((c) => diff --git a/yarn.lock b/yarn.lock index da261caf33..cace082811 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7326,6 +7326,7 @@ __metadata: "@inquirer/core": "npm:9.0.10" "@inquirer/figures": "npm:1.0.5" "@inquirer/prompts": "npm:3.3.2" + "@inquirer/search": "npm:^3.0.1" "@types/chai-as-promised": "npm:^8" "@types/mocha": "npm:^10.0.1" "@types/node": "npm:^18.14.5" @@ -7737,6 +7738,23 @@ __metadata: languageName: node linkType: hard +"@inquirer/core@npm:^10.1.2": + version: 10.1.2 + resolution: "@inquirer/core@npm:10.1.2" + dependencies: + "@inquirer/figures": "npm:^1.0.9" + "@inquirer/type": "npm:^3.0.2" + ansi-escapes: "npm:^4.3.2" + cli-width: "npm:^4.1.0" + mute-stream: "npm:^2.0.0" + signal-exit: "npm:^4.1.0" + strip-ansi: "npm:^6.0.1" + wrap-ansi: "npm:^6.2.0" + yoctocolors-cjs: "npm:^2.1.2" + checksum: 10/e92ade5ba7dbcfd83629db2df7fb91877ac777a7f1e03a16b0d5c08621dafe09d321c5f14b37c2dca80a3db2d68e5a478f8eaeafcb62ed42c46e7349b7276094 + languageName: node + linkType: hard + "@inquirer/core@npm:^6.0.0": version: 6.0.0 resolution: "@inquirer/core@npm:6.0.0" @@ -7790,6 +7808,13 @@ __metadata: languageName: node linkType: hard +"@inquirer/figures@npm:^1.0.9": + version: 1.0.9 + resolution: "@inquirer/figures@npm:1.0.9" + checksum: 10/7ced1275a5826cdeb61797d6c068417e7d52aa87894de18cedd259f783f42d731226c3f8b92cab27b8e7b0e31ab1dd3cd77f16935b67ebe1cbb271e5972d7758 + languageName: node + linkType: hard + "@inquirer/input@npm:^1.2.16": version: 1.2.16 resolution: "@inquirer/input@npm:1.2.16" @@ -7841,6 +7866,20 @@ __metadata: languageName: node linkType: hard +"@inquirer/search@npm:^3.0.1": + version: 3.0.4 + resolution: "@inquirer/search@npm:3.0.4" + dependencies: + "@inquirer/core": "npm:^10.1.2" + "@inquirer/figures": "npm:^1.0.9" + "@inquirer/type": "npm:^3.0.2" + yoctocolors-cjs: "npm:^2.1.2" + peerDependencies: + "@types/node": ">=18" + checksum: 10/e2363f808e32983d659049cabc21f57e8b7dd7b591677b4bd1be0c45653e3d86f98f1772f3866bc97f14f63983399b259e8956eda181fafbe7eea07f8169bd95 + languageName: node + linkType: hard + "@inquirer/select@npm:^1.3.3": version: 1.3.3 resolution: "@inquirer/select@npm:1.3.3" @@ -7872,6 +7911,15 @@ __metadata: languageName: node linkType: hard +"@inquirer/type@npm:^3.0.2": + version: 3.0.2 + resolution: "@inquirer/type@npm:3.0.2" + peerDependencies: + "@types/node": ">=18" + checksum: 10/d1a2879b1baa357421cef441fc7b43181e110243933763ae922c55c2fc9af2d459ceaca8b71ed57e3dabd5077542fa0dd1d0ff0cf362ce054e61202386b545ed + languageName: node + linkType: hard + "@interchain-ui/react@npm:^1.23.28": version: 1.26.1 resolution: "@interchain-ui/react@npm:1.26.1" @@ -29046,6 +29094,13 @@ __metadata: languageName: node linkType: hard +"mute-stream@npm:^2.0.0": + version: 2.0.0 + resolution: "mute-stream@npm:2.0.0" + checksum: 10/d2e4fd2f5aa342b89b98134a8d899d8ef9b0a6d69274c4af9df46faa2d97aeb1f2ce83d867880d6de63643c52386579b99139801e24e7526c3b9b0a6d1e18d6c + languageName: node + linkType: hard + "mz@npm:^2.7.0": version: 2.7.0 resolution: "mz@npm:2.7.0" From 99c91d8da34e417af536100cb4b726eae08b14c4 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Fri, 3 Jan 2025 19:54:14 +0000 Subject: [PATCH 36/37] feat: bump overhead when sending to zkSync chains (#5103) ### Description Context: https://discord.com/channels/935678348330434570/1319115367167033399/1319442102454849639 zkSync chains have non standard EVM gas metering, and their eth_estimateGas RPC returns abnormally high estimates compared to the actual usage. As a semi-temporary workaround, we bump the overhead to result in more gas paid for. The impact of this is actually not meaningful when it comes to cost borne by users - because the zkSync chains are cheap, our min fee logic means that applying this still results in a ~20c cost to users ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- typescript/infra/config/environments/mainnet3/igp.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/typescript/infra/config/environments/mainnet3/igp.ts b/typescript/infra/config/environments/mainnet3/igp.ts index 2b388afdd2..3dbe6c775e 100644 --- a/typescript/infra/config/environments/mainnet3/igp.ts +++ b/typescript/infra/config/environments/mainnet3/igp.ts @@ -1,6 +1,7 @@ import { ChainMap, ChainName, + ChainTechnicalStack, HookType, IgpConfig, getTokenExchangeRateFromValues, @@ -14,6 +15,7 @@ import { getOverhead, } from '../../../src/config/gas-oracle.js'; import { mustGetChainNativeToken } from '../../../src/utils/utils.js'; +import { getChain } from '../../registry.js'; import { ethereumChainNames } from './chains.js'; import gasPrices from './gasPrices.json'; @@ -25,9 +27,15 @@ const tokenPrices: ChainMap = rawTokenPrices; export function getOverheadWithOverrides(local: ChainName, remote: ChainName) { let overhead = getOverhead(local, remote, ethereumChainNames); + // Moonbeam gas usage can be up to 4x higher than vanilla EVM if (remote === 'moonbeam') { overhead *= 4; } + // ZkSync gas usage is different from the EVM and tends to give high + // estimates. We double the overhead to help account for this. + if (getChain(remote).technicalStack === ChainTechnicalStack.ZkSync) { + overhead *= 2; + } return overhead; } From c5d4bd60384062ea66f3cf23ca2df952b0b6d5b0 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 6 Jan 2025 22:43:26 +0900 Subject: [PATCH 37/37] =?UTF-8?q?feat:=20add=20the=20#=20of=20messages=20p?= =?UTF-8?q?rioritized=20in=20the=20queue=20to=20the=20http=20resp=E2=80=A6?= =?UTF-8?q?=20(#5043)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Description Add a mpsc channel between relayer and message retry handler, so message retry handler knows the retry results from the relayer. `POST /message_retry` endpoint now returns a JSON with details on the retry request: ```rust #[derive(Clone, Debug, Deserialize, Serialize)] pub struct MessageRetryResponse { /// ID of the retry request pub uuid: String, /// how many pending operations were processed pub processed: usize, /// how many of the pending operations matched the retry request pattern pub matched: u64, } ``` ### Drive-by changes ### Related issues Fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/4059 ### Backward compatibility ### Testing Updated unit tests to return a response so the handler is not blocked --- rust/main/Cargo.lock | 31 +- rust/main/Cargo.toml | 1 + rust/main/agents/relayer/Cargo.toml | 3 + rust/main/agents/relayer/src/lib.rs | 3 +- rust/main/agents/relayer/src/msg/op_queue.rs | 83 ++++- .../agents/relayer/src/msg/op_submitter.rs | 4 +- rust/main/agents/relayer/src/relayer.rs | 5 +- .../relayer/src/server/list_messages.rs | 1 + .../relayer/src/server/message_retry.rs | 286 ++++++++++++++---- rust/main/agents/relayer/src/server/mod.rs | 11 +- rust/main/utils/run-locally/Cargo.toml | 1 + rust/main/utils/run-locally/src/main.rs | 5 + rust/main/utils/run-locally/src/server.rs | 55 ++++ 13 files changed, 403 insertions(+), 86 deletions(-) create mode 100644 rust/main/utils/run-locally/src/server.rs diff --git a/rust/main/Cargo.lock b/rust/main/Cargo.lock index 4f728ab69f..47aec03422 100644 --- a/rust/main/Cargo.lock +++ b/rust/main/Cargo.lock @@ -512,6 +512,7 @@ checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" dependencies = [ "async-trait", "axum-core", + "axum-macros", "bitflags 1.3.2", "bytes", "futures-util", @@ -553,6 +554,18 @@ dependencies = [ "tower-service", ] +[[package]] +name = "axum-macros" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdca6a10ecad987bda04e95606ef85a5417dcaac1a78455242d72e031e2b6b62" +dependencies = [ + "heck 0.4.1", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + [[package]] name = "backtrace" version = "0.3.71" @@ -7015,7 +7028,9 @@ dependencies = [ "tokio-test", "tracing", "tracing-futures", + "tracing-test", "typetag", + "uuid 1.11.0", ] [[package]] @@ -7150,7 +7165,7 @@ dependencies = [ "rkyv_derive", "seahash", "tinyvec", - "uuid 1.10.0", + "uuid 1.11.0", ] [[package]] @@ -7250,6 +7265,7 @@ dependencies = [ "once_cell", "regex", "relayer", + "reqwest", "ripemd", "serde", "serde_json", @@ -7749,7 +7765,7 @@ dependencies = [ "time", "tracing", "url", - "uuid 1.10.0", + "uuid 1.11.0", ] [[package]] @@ -7810,7 +7826,7 @@ dependencies = [ "sea-query-derive", "serde_json", "time", - "uuid 1.10.0", + "uuid 1.11.0", ] [[package]] @@ -7826,7 +7842,7 @@ dependencies = [ "serde_json", "sqlx", "time", - "uuid 1.10.0", + "uuid 1.11.0", ] [[package]] @@ -9126,7 +9142,7 @@ dependencies = [ "time", "tokio-stream", "url", - "uuid 1.10.0", + "uuid 1.11.0", "whoami", ] @@ -10363,10 +10379,11 @@ dependencies = [ [[package]] name = "uuid" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" dependencies = [ + "getrandom 0.2.15", "serde", ] diff --git a/rust/main/Cargo.toml b/rust/main/Cargo.toml index 8b1a9b58e0..d9fee1663f 100644 --- a/rust/main/Cargo.toml +++ b/rust/main/Cargo.toml @@ -153,6 +153,7 @@ typetag = "0.2" uint = "0.9.5" ureq = { version = "2.4", default-features = false } url = "2.3" +uuid = { version = "1.11.0", features = ["v4"] } walkdir = "2" warp = "0.3" which = "4.3" diff --git a/rust/main/agents/relayer/Cargo.toml b/rust/main/agents/relayer/Cargo.toml index 5a891d912c..fcd89da771 100644 --- a/rust/main/agents/relayer/Cargo.toml +++ b/rust/main/agents/relayer/Cargo.toml @@ -44,6 +44,7 @@ tokio-metrics.workspace = true tracing-futures.workspace = true tracing.workspace = true typetag.workspace = true +uuid.workspace = true hyperlane-core = { path = "../../hyperlane-core", features = [ "agent", @@ -53,12 +54,14 @@ hyperlane-base = { path = "../../hyperlane-base", features = ["test-utils"] } hyperlane-ethereum = { path = "../../chains/hyperlane-ethereum" } [dev-dependencies] +axum = { workspace = true, features = ["macros"] } once_cell.workspace = true mockall.workspace = true tokio-test.workspace = true hyperlane-test = { path = "../../hyperlane-test" } hyperlane-base = { path = "../../hyperlane-base", features = ["test-utils"] } hyperlane-core = { path = "../../hyperlane-core", features = ["agent", "async", "test-utils"] } +tracing-test.workspace = true [features] default = ["color-eyre", "oneline-errors"] diff --git a/rust/main/agents/relayer/src/lib.rs b/rust/main/agents/relayer/src/lib.rs index 62b896d628..9a6e1e4147 100644 --- a/rust/main/agents/relayer/src/lib.rs +++ b/rust/main/agents/relayer/src/lib.rs @@ -3,8 +3,9 @@ mod msg; mod processor; mod prover; mod relayer; -mod server; mod settings; +pub mod server; + pub use msg::GAS_EXPENDITURE_LOG_MESSAGE; pub use relayer::*; diff --git a/rust/main/agents/relayer/src/msg/op_queue.rs b/rust/main/agents/relayer/src/msg/op_queue.rs index 99c9dde39f..d0640bc2a3 100644 --- a/rust/main/agents/relayer/src/msg/op_queue.rs +++ b/rust/main/agents/relayer/src/msg/op_queue.rs @@ -6,7 +6,7 @@ use prometheus::{IntGauge, IntGaugeVec}; use tokio::sync::{broadcast::Receiver, Mutex}; use tracing::{debug, info, instrument}; -use crate::settings::matching_list::MatchingList; +use crate::server::{MessageRetryRequest, MessageRetryResponse}; pub type OperationPriorityQueue = Arc>>>; @@ -16,7 +16,7 @@ pub type OperationPriorityQueue = Arc>> pub struct OpQueue { metrics: IntGaugeVec, queue_metrics_label: String, - retry_rx: Arc>>, + retry_receiver: Arc>>, #[new(default)] pub queue: OperationPriorityQueue, } @@ -72,27 +72,67 @@ impl OpQueue { // The other consideration is whether to put the channel receiver in the OpQueue or in a dedicated task // that also holds an Arc to the Mutex. For simplicity, we'll put it in the OpQueue for now. let mut message_retry_requests = vec![]; - while let Ok(message_id) = self.retry_rx.lock().await.try_recv() { - message_retry_requests.push(message_id); + + while let Ok(retry_request) = self.retry_receiver.lock().await.try_recv() { + let uuid = retry_request.uuid.clone(); + message_retry_requests.push(( + retry_request, + MessageRetryResponse { + uuid, + evaluated: 0, + matched: 0, + }, + )); } + if message_retry_requests.is_empty() { return; } + let mut queue = self.queue.lock().await; + let queue_length = queue.len(); + let mut reprioritized_queue: BinaryHeap<_> = queue .drain() .map(|Reverse(mut op)| { - if message_retry_requests.iter().any(|r| r.op_matches(&op)) { + let matched_requests: Vec<_> = message_retry_requests + .iter_mut() + .filter_map(|(retry_req, retry_response)| { + // update retry metrics + if retry_req.pattern.op_matches(&op) { + debug!(uuid = retry_req.uuid, "Matched request"); + retry_response.matched += 1; + Some(retry_req.uuid.clone()) + } else { + None + } + }) + .collect(); + + if !matched_requests.is_empty() { info!( operation = %op, queue_label = %self.queue_metrics_label, "Retrying OpQueue operation" ); - op.reset_attempts() + op.reset_attempts(); } Reverse(op) }) .collect(); + + for (retry_req, mut retry_response) in message_retry_requests { + retry_response.evaluated = queue_length; + tracing::debug!( + uuid = retry_response.uuid, + evaluated = retry_response.evaluated, + matched = retry_response.matched, + "Sending relayer retry response back" + ); + if let Err(err) = retry_req.transmitter.send(retry_response).await { + tracing::error!(?err, "Failed to send retry response"); + } + } queue.append(&mut reprioritized_queue); } @@ -115,7 +155,10 @@ impl OpQueue { #[cfg(test)] pub mod test { + use crate::{server::ENDPOINT_MESSAGES_QUEUE_SIZE, settings::matching_list::MatchingList}; + use super::*; + use hyperlane_core::{ HyperlaneDomain, HyperlaneDomainProtocol, HyperlaneDomainTechnicalStack, HyperlaneDomainType, HyperlaneMessage, KnownHyperlaneDomain, PendingOperationResult, @@ -127,7 +170,7 @@ pub mod test { str::FromStr, time::{Duration, Instant}, }; - use tokio::sync; + use tokio::sync::{self, mpsc}; #[derive(Debug, Clone, Serialize)] pub struct MockPendingOperation { @@ -320,6 +363,7 @@ pub mod test { async fn test_multiple_op_queues_message_id() { let (metrics, queue_metrics_label) = dummy_metrics_and_label(); let broadcaster = sync::broadcast::Sender::new(100); + let mut op_queue_1 = OpQueue::new( metrics.clone(), queue_metrics_label.clone(), @@ -364,12 +408,22 @@ pub mod test { .await; } + let (transmitter, _receiver) = mpsc::channel(ENDPOINT_MESSAGES_QUEUE_SIZE); + // Retry by message ids broadcaster - .send(MatchingList::with_message_id(op_ids[1])) + .send(MessageRetryRequest { + uuid: "0e92ace7-ba5d-4a1f-8501-51b6d9d500cf".to_string(), + pattern: MatchingList::with_message_id(op_ids[1]), + transmitter: transmitter.clone(), + }) .unwrap(); broadcaster - .send(MatchingList::with_message_id(op_ids[2])) + .send(MessageRetryRequest { + uuid: "59400966-e7fa-4fb9-9372-9a671d4392c3".to_string(), + pattern: MatchingList::with_message_id(op_ids[2]), + transmitter, + }) .unwrap(); // Pop elements from queue 1 @@ -399,6 +453,7 @@ pub mod test { async fn test_destination_domain() { let (metrics, queue_metrics_label) = dummy_metrics_and_label(); let broadcaster = sync::broadcast::Sender::new(100); + let mut op_queue = OpQueue::new( metrics.clone(), queue_metrics_label.clone(), @@ -425,11 +480,15 @@ pub mod test { .await; } + let (transmitter, _receiver) = mpsc::channel(ENDPOINT_MESSAGES_QUEUE_SIZE); + // Retry by domain broadcaster - .send(MatchingList::with_destination_domain( - destination_domain_2.id(), - )) + .send(MessageRetryRequest { + uuid: "a5b39473-7cc5-48a1-8bed-565454ba1037".to_string(), + pattern: MatchingList::with_destination_domain(destination_domain_2.id()), + transmitter, + }) .unwrap(); // Pop elements from queue diff --git a/rust/main/agents/relayer/src/msg/op_submitter.rs b/rust/main/agents/relayer/src/msg/op_submitter.rs index c1e295a24a..f35a991c45 100644 --- a/rust/main/agents/relayer/src/msg/op_submitter.rs +++ b/rust/main/agents/relayer/src/msg/op_submitter.rs @@ -32,7 +32,7 @@ use hyperlane_core::{ }; use crate::msg::pending_message::CONFIRM_DELAY; -use crate::settings::matching_list::MatchingList; +use crate::server::MessageRetryRequest; use super::op_queue::OpQueue; use super::op_queue::OperationPriorityQueue; @@ -105,7 +105,7 @@ impl SerialSubmitter { pub fn new( domain: HyperlaneDomain, rx: mpsc::UnboundedReceiver, - retry_op_transmitter: Sender, + retry_op_transmitter: Sender, metrics: SerialSubmitterMetrics, max_batch_size: u32, task_monitor: TaskMonitor, diff --git a/rust/main/agents/relayer/src/relayer.rs b/rust/main/agents/relayer/src/relayer.rs index b1f013b6ae..4c3d8d33bb 100644 --- a/rust/main/agents/relayer/src/relayer.rs +++ b/rust/main/agents/relayer/src/relayer.rs @@ -318,10 +318,11 @@ impl BaseAgent for Relayer { })); tasks.push(console_server.instrument(info_span!("Tokio console server"))); } - let sender = BroadcastSender::::new(ENDPOINT_MESSAGES_QUEUE_SIZE); + let sender = BroadcastSender::new(ENDPOINT_MESSAGES_QUEUE_SIZE); // send channels by destination chain let mut send_channels = HashMap::with_capacity(self.destination_chains.len()); let mut prep_queues = HashMap::with_capacity(self.destination_chains.len()); + for (dest_domain, dest_conf) in &self.destination_chains { let (send_channel, receive_channel) = mpsc::unbounded_channel::(); send_channels.insert(dest_domain.id(), send_channel); @@ -385,7 +386,7 @@ impl BaseAgent for Relayer { ); } // run server - let custom_routes = relayer_server::Server::new() + let custom_routes = relayer_server::Server::new(self.origin_chains.len()) .with_op_retry(sender.clone()) .with_message_queue(prep_queues) .routes(); diff --git a/rust/main/agents/relayer/src/server/list_messages.rs b/rust/main/agents/relayer/src/server/list_messages.rs index e21f39a5df..f6c92ba088 100644 --- a/rust/main/agents/relayer/src/server/list_messages.rs +++ b/rust/main/agents/relayer/src/server/list_messages.rs @@ -97,6 +97,7 @@ mod tests { fn setup_test_server() -> (SocketAddr, OperationPriorityQueue) { let (metrics, queue_metrics_label) = dummy_metrics_and_label(); let broadcaster = sync::broadcast::Sender::new(100); + let op_queue = OpQueue::new( metrics.clone(), queue_metrics_label.clone(), diff --git a/rust/main/agents/relayer/src/server/message_retry.rs b/rust/main/agents/relayer/src/server/message_retry.rs index 6d160355a5..255614b43e 100644 --- a/rust/main/agents/relayer/src/server/message_retry.rs +++ b/rust/main/agents/relayer/src/server/message_retry.rs @@ -1,32 +1,89 @@ use crate::settings::matching_list::MatchingList; + use axum::{extract::State, routing, Json, Router}; + use derive_new::new; -use tokio::sync::broadcast::Sender; +use serde::{Deserialize, Serialize}; +use tokio::sync::{broadcast::Sender, mpsc}; const MESSAGE_RETRY_API_BASE: &str = "/message_retry"; -#[derive(new, Clone)] +#[derive(Clone, Debug, new)] pub struct MessageRetryApi { - tx: Sender, + retry_request_transmitter: Sender, + relayer_chains: usize, +} + +#[derive(Clone, Debug)] +pub struct MessageRetryRequest { + pub uuid: String, + pub pattern: MatchingList, + pub transmitter: mpsc::Sender, +} + +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +pub struct MessageRetryResponse { + /// ID of the retry request + pub uuid: String, + /// how many pending operations were evaluated + pub evaluated: usize, + /// how many of the pending operations matched the retry request pattern + pub matched: u64, } async fn retry_message( - State(tx): State>, + State(state): State, Json(retry_req_payload): Json, -) -> String { - match tx.send(retry_req_payload) { - Ok(_) => "Moved message(s) to the front of the queue".to_string(), - // Technically it's bad practice to print the error message to the user, but - // this endpoint is for debugging purposes only. - Err(err) => format!("Failed to send retry request to the queue: {}", err), +) -> Result, String> { + let uuid = uuid::Uuid::new_v4(); + let uuid_string = uuid.to_string(); + + tracing::debug!(uuid = uuid_string, "Sending message retry request"); + + // This channel is only created to service this single + // retry request so we're expecting a single response + // from each transmitter end, hence we are using a channel of size 1 + let (transmitter, mut receiver) = mpsc::channel(state.relayer_chains); + state + .retry_request_transmitter + .send(MessageRetryRequest { + uuid: uuid_string.clone(), + pattern: retry_req_payload, + transmitter, + }) + .map_err(|err| { + // Technically it's bad practice to print the error message to the user, but + // this endpoint is for debugging purposes only. + format!("Failed to send retry request to the queue: {}", err) + })?; + + let mut resp = MessageRetryResponse { + uuid: uuid_string, + evaluated: 0, + matched: 0, + }; + + // Wait for responses from relayer + tracing::debug!(uuid = resp.uuid, "Waiting for response from relayer"); + while let Some(relayer_resp) = receiver.recv().await { + tracing::debug!( + uuid = resp.uuid, + evaluated = resp.evaluated, + matched = resp.matched, + "Submitter response to retry request" + ); + resp.evaluated += relayer_resp.evaluated; + resp.matched += relayer_resp.matched; } + + Ok(Json(resp)) } impl MessageRetryApi { pub fn router(&self) -> Router { Router::new() .route("/", routing::post(retry_message)) - .with_state(self.tx.clone()) + .with_state(self.clone()) } pub fn get_route(&self) -> (&'static str, Router) { @@ -41,13 +98,21 @@ mod tests { use super::*; use axum::http::StatusCode; use hyperlane_core::{HyperlaneMessage, QueueOperation}; + use serde::de::DeserializeOwned; use serde_json::json; use std::net::SocketAddr; use tokio::sync::broadcast::{Receiver, Sender}; - fn setup_test_server() -> (SocketAddr, Receiver) { - let broadcast_tx = Sender::::new(ENDPOINT_MESSAGES_QUEUE_SIZE); - let message_retry_api = MessageRetryApi::new(broadcast_tx.clone()); + #[derive(Debug)] + struct TestServerSetup { + pub socket_address: SocketAddr, + pub retry_req_rx: Receiver, + } + + fn setup_test_server() -> TestServerSetup { + let broadcast_tx = Sender::new(ENDPOINT_MESSAGES_QUEUE_SIZE); + + let message_retry_api = MessageRetryApi::new(broadcast_tx.clone(), 10); let (path, retry_router) = message_retry_api.get_route(); let app = Router::new().nest(path, retry_router); @@ -58,12 +123,51 @@ mod tests { let addr = server.local_addr(); tokio::spawn(server); - (addr, broadcast_tx.subscribe()) + let retry_req_rx = broadcast_tx.subscribe(); + + TestServerSetup { + socket_address: addr, + retry_req_rx, + } + } + + async fn send_retry_responses_future( + mut retry_request_receiver: Receiver, + pending_operations: Vec, + metrics: Vec<(usize, u64)>, + ) { + if let Ok(req) = retry_request_receiver.recv().await { + for (op, (evaluated, matched)) in pending_operations.iter().zip(metrics) { + // Check that the list received by the server matches the pending operation + assert!(req.pattern.op_matches(&op)); + let resp = MessageRetryResponse { + uuid: req.uuid.clone(), + evaluated, + matched, + }; + req.transmitter.send(resp).await.unwrap(); + } + } } + async fn parse_response_to_json(response: reqwest::Response) -> T { + let resp_body = response + .text() + .await + .expect("Failed to parse response body"); + let resp_json: T = + serde_json::from_str(&resp_body).expect("Failed to deserialize response body"); + resp_json + } + + #[tracing_test::traced_test] #[tokio::test] async fn test_message_id_retry() { - let (addr, mut rx) = setup_test_server(); + let TestServerSetup { + socket_address: addr, + retry_req_rx, + .. + } = setup_test_server(); let client = reqwest::Client::new(); // Create a random message with a random message ID @@ -75,25 +179,37 @@ mod tests { } ]); + // spawn a task to respond to message retry request + let respond_task = send_retry_responses_future( + retry_req_rx, + vec![Box::new(pending_operation.clone()) as QueueOperation], + vec![(1, 1)], + ); + // Send a POST request to the server let response = client .post(format!("http://{}{}", addr, MESSAGE_RETRY_API_BASE)) .json(&matching_list_body) // Set the request body - .send() - .await - .unwrap(); + .send(); + + let (_t1, response_res) = tokio::join!(respond_task, response); + let response = response_res.unwrap(); // Check that the response status code is OK assert_eq!(response.status(), StatusCode::OK); - let list = rx.try_recv().unwrap(); - // Check that the list received by the server matches the pending operation - assert!(list.op_matches(&(Box::new(pending_operation) as QueueOperation))); + let resp_json: MessageRetryResponse = parse_response_to_json(response).await; + assert_eq!(resp_json.evaluated, 1); + assert_eq!(resp_json.matched, 1); } #[tokio::test] async fn test_destination_domain_retry() { - let (addr, mut rx) = setup_test_server(); + let TestServerSetup { + socket_address: addr, + retry_req_rx, + .. + } = setup_test_server(); let client = reqwest::Client::new(); let message = HyperlaneMessage { @@ -108,25 +224,37 @@ mod tests { } ]); + // spawn a task to respond to message retry request + let respond_task = send_retry_responses_future( + retry_req_rx, + vec![Box::new(pending_operation.clone()) as QueueOperation], + vec![(1, 1)], + ); + // Send a POST request to the server let response = client .post(format!("http://{}{}", addr, MESSAGE_RETRY_API_BASE)) .json(&matching_list_body) // Set the request body - .send() - .await - .unwrap(); + .send(); + + let (_t1, response_res) = tokio::join!(respond_task, response); + let response = response_res.unwrap(); // Check that the response status code is OK assert_eq!(response.status(), StatusCode::OK); - let list = rx.try_recv().unwrap(); - // Check that the list received by the server matches the pending operation - assert!(list.op_matches(&(Box::new(pending_operation) as QueueOperation))); + let resp_json: MessageRetryResponse = parse_response_to_json(response).await; + assert_eq!(resp_json.evaluated, 1); + assert_eq!(resp_json.matched, 1); } #[tokio::test] async fn test_origin_domain_retry() { - let (addr, mut rx) = setup_test_server(); + let TestServerSetup { + socket_address: addr, + retry_req_rx, + .. + } = setup_test_server(); let client = reqwest::Client::new(); let message = HyperlaneMessage { @@ -141,25 +269,37 @@ mod tests { } ]); + // spawn a task to respond to message retry request + let respond_task = send_retry_responses_future( + retry_req_rx, + vec![Box::new(pending_operation.clone()) as QueueOperation], + vec![(1, 1)], + ); + // Send a POST request to the server let response = client .post(format!("http://{}{}", addr, MESSAGE_RETRY_API_BASE)) .json(&matching_list_body) // Set the request body - .send() - .await - .unwrap(); + .send(); + let (_t1, response_res) = tokio::join!(respond_task, response); + + let response = response_res.unwrap(); // Check that the response status code is OK assert_eq!(response.status(), StatusCode::OK); - let list = rx.try_recv().unwrap(); - // Check that the list received by the server matches the pending operation - assert!(list.op_matches(&(Box::new(pending_operation) as QueueOperation))); + let resp_json: MessageRetryResponse = parse_response_to_json(response).await; + assert_eq!(resp_json.evaluated, 1); + assert_eq!(resp_json.matched, 1); } #[tokio::test] async fn test_sender_address_retry() { - let (addr, mut rx) = setup_test_server(); + let TestServerSetup { + socket_address: addr, + retry_req_rx, + .. + } = setup_test_server(); let client = reqwest::Client::new(); let message = HyperlaneMessage::default(); @@ -170,25 +310,37 @@ mod tests { } ]); + // spawn a task to respond to message retry request + let respond_task = send_retry_responses_future( + retry_req_rx, + vec![Box::new(pending_operation.clone()) as QueueOperation], + vec![(1, 1)], + ); + // Send a POST request to the server let response = client .post(format!("http://{}{}", addr, MESSAGE_RETRY_API_BASE)) .json(&matching_list_body) // Set the request body - .send() - .await - .unwrap(); + .send(); + + let (_t1, response_res) = tokio::join!(respond_task, response); + let response = response_res.unwrap(); // Check that the response status code is OK assert_eq!(response.status(), StatusCode::OK); - let list = rx.try_recv().unwrap(); - // Check that the list received by the server matches the pending operation - assert!(list.op_matches(&(Box::new(pending_operation) as QueueOperation))); + let resp_json: MessageRetryResponse = parse_response_to_json(response).await; + assert_eq!(resp_json.evaluated, 1); + assert_eq!(resp_json.matched, 1); } #[tokio::test] async fn test_recipient_address_retry() { - let (addr, mut rx) = setup_test_server(); + let TestServerSetup { + socket_address: addr, + retry_req_rx, + .. + } = setup_test_server(); let client = reqwest::Client::new(); let message = HyperlaneMessage::default(); @@ -199,25 +351,37 @@ mod tests { } ]); + // spawn a task to respond to message retry request + let respond_task = send_retry_responses_future( + retry_req_rx, + vec![Box::new(pending_operation.clone()) as QueueOperation], + vec![(1, 1)], + ); + // Send a POST request to the server let response = client .post(format!("http://{}{}", addr, MESSAGE_RETRY_API_BASE)) .json(&matching_list_body) // Set the request body - .send() - .await - .unwrap(); + .send(); + let (_t1, response_res) = tokio::join!(respond_task, response); + + let response = response_res.unwrap(); // Check that the response status code is OK assert_eq!(response.status(), StatusCode::OK); - let list = rx.try_recv().unwrap(); - // Check that the list received by the server matches the pending operation - assert!(list.op_matches(&(Box::new(pending_operation) as QueueOperation))); + let resp_json: MessageRetryResponse = parse_response_to_json(response).await; + assert_eq!(resp_json.evaluated, 1); + assert_eq!(resp_json.matched, 1); } #[tokio::test] async fn test_multiple_retry() { - let (addr, mut rx) = setup_test_server(); + let TestServerSetup { + socket_address: addr, + retry_req_rx, + .. + } = setup_test_server(); let client = reqwest::Client::new(); let message = HyperlaneMessage { @@ -238,19 +402,27 @@ mod tests { } ]); + // spawn a task to respond to message retry request + let respond_task = send_retry_responses_future( + retry_req_rx, + vec![Box::new(pending_operation.clone()) as QueueOperation], + vec![(1, 1)], + ); + // Send a POST request to the server let response = client .post(format!("http://{}{}", addr, MESSAGE_RETRY_API_BASE)) .json(&matching_list_body) // Set the request body - .send() - .await - .unwrap(); + .send(); + + let (_t1, response_res) = tokio::join!(respond_task, response); + let response = response_res.unwrap(); // Check that the response status code is OK assert_eq!(response.status(), StatusCode::OK); - let list = rx.try_recv().unwrap(); - // Check that the list received by the server matches the pending operation - assert!(list.op_matches(&(Box::new(pending_operation) as QueueOperation))); + let resp_json: MessageRetryResponse = parse_response_to_json(response).await; + assert_eq!(resp_json.evaluated, 1); + assert_eq!(resp_json.matched, 1); } } diff --git a/rust/main/agents/relayer/src/server/mod.rs b/rust/main/agents/relayer/src/server/mod.rs index 083f8d94d2..8cc49a3f25 100644 --- a/rust/main/agents/relayer/src/server/mod.rs +++ b/rust/main/agents/relayer/src/server/mod.rs @@ -3,7 +3,7 @@ use derive_new::new; use std::collections::HashMap; use tokio::sync::broadcast::Sender; -use crate::{msg::op_queue::OperationPriorityQueue, settings::matching_list::MatchingList}; +use crate::msg::op_queue::OperationPriorityQueue; pub const ENDPOINT_MESSAGES_QUEUE_SIZE: usize = 100; @@ -15,14 +15,15 @@ mod message_retry; #[derive(new)] pub struct Server { + relayer_chains: usize, #[new(default)] - retry_transmitter: Option>, + retry_transmitter: Option>, #[new(default)] op_queues: Option>, } impl Server { - pub fn with_op_retry(mut self, transmitter: Sender) -> Self { + pub fn with_op_retry(mut self, transmitter: Sender) -> Self { self.retry_transmitter = Some(transmitter); self } @@ -36,8 +37,8 @@ impl Server { /// Can be extended with additional routes and feature flags to enable/disable individually. pub fn routes(self) -> Vec<(&'static str, Router)> { let mut routes = vec![]; - if let Some(retry_transmitter) = self.retry_transmitter { - routes.push(MessageRetryApi::new(retry_transmitter).get_route()); + if let Some(tx) = self.retry_transmitter { + routes.push(MessageRetryApi::new(tx, self.relayer_chains).get_route()); } if let Some(op_queues) = self.op_queues { routes.push(ListOperationsApi::new(op_queues).get_route()); diff --git a/rust/main/utils/run-locally/Cargo.toml b/rust/main/utils/run-locally/Cargo.toml index 9dedae9cea..a994324687 100644 --- a/rust/main/utils/run-locally/Cargo.toml +++ b/rust/main/utils/run-locally/Cargo.toml @@ -14,6 +14,7 @@ hyperlane-cosmos = { path = "../../chains/hyperlane-cosmos" } toml_edit.workspace = true k256.workspace = true jobserver.workspace = true +reqwest.workspace = true ripemd.workspace = true sha2.workspace = true serde.workspace = true diff --git a/rust/main/utils/run-locally/src/main.rs b/rust/main/utils/run-locally/src/main.rs index 4686c15446..8855a08d86 100644 --- a/rust/main/utils/run-locally/src/main.rs +++ b/rust/main/utils/run-locally/src/main.rs @@ -51,6 +51,7 @@ mod invariants; mod logging; mod metrics; mod program; +mod server; mod solana; mod utils; @@ -483,6 +484,10 @@ fn main() -> ExitCode { // give things a chance to fully start. sleep(Duration::from_secs(10)); + // test retry request + let resp = server::run_retry_request().expect("Failed to process retry request"); + assert!(resp.matched > 0); + if !post_startup_invariants(&checkpoints_dirs) { log!("Failure: Post startup invariants are not met"); return report_test_result(true); diff --git a/rust/main/utils/run-locally/src/server.rs b/rust/main/utils/run-locally/src/server.rs new file mode 100644 index 0000000000..4df7df78f0 --- /dev/null +++ b/rust/main/utils/run-locally/src/server.rs @@ -0,0 +1,55 @@ +use std::io; + +use reqwest::Url; + +use relayer::server::MessageRetryResponse; + +use crate::RELAYER_METRICS_PORT; + +/// create tokio runtime to send a retry request to +/// relayer to retry all existing messages in the queues +pub fn run_retry_request() -> io::Result { + let runtime = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build(); + runtime + .unwrap() + .block_on(async { call_retry_request().await }) +} + +/// sends a request to relayer to retry all existing messages +/// in the queues +async fn call_retry_request() -> io::Result { + let client = reqwest::Client::new(); + + let url = Url::parse(&format!( + "http://0.0.0.0:{RELAYER_METRICS_PORT}/message_retry" + )) + .map_err(|err| { + eprintln!("Failed to parse url: {err}"); + io::Error::new(io::ErrorKind::InvalidInput, err.to_string()) + })?; + + let body = vec![serde_json::json!({ + "message_id": "*" + })]; + let retry_response = client.post(url).json(&body).send().await.map_err(|err| { + eprintln!("Failed to send request: {err}"); + io::Error::new(io::ErrorKind::InvalidData, err.to_string()) + })?; + + let response_text = retry_response.text().await.map_err(|err| { + eprintln!("Failed to parse response body: {err}"); + io::Error::new(io::ErrorKind::InvalidData, err.to_string()) + })?; + + println!("Retry Request Response: {:?}", response_text); + + let response_json: MessageRetryResponse = + serde_json::from_str(&response_text).map_err(|err| { + eprintln!("Failed to parse response body to json: {err}"); + io::Error::new(io::ErrorKind::InvalidData, err.to_string()) + })?; + + Ok(response_json) +}