Compare commits
13 Commits
98f6c2e8d7
...
a1ff81002b
Author | SHA1 | Date |
---|---|---|
Nikhil Suri | a1ff81002b | |
Dirk Brink | d63f2ca532 | |
Evan Gray | e6dfb9115e | |
bruce-riley | 034c570b33 | |
Dirk Brink | a566c3d3bf | |
Csongor Kiss | 9620fca895 | |
Bruce Riley | 35f0b343ed | |
Jeff Schroeder | bda43b2ac7 | |
Jeff Schroeder | 14d73dafe0 | |
Michael Nguyen | ddf5ba159c | |
bruce-riley | 9af1fac9e1 | |
Nikhil Suri | 16e5b65175 | |
Nikhil Suri | 85fdc5c83b |
|
@ -44,6 +44,7 @@ As these 3rd party audits are completed and issues are sufficiently addressed, w
|
|||
- **[Jan 2024 - OtterSec](https://github.com/wormhole-foundation/wormhole-audits/blob/main/2024-01-ottersec-terra.pdf)**: _Terra Classic Contract Upgrades_
|
||||
- **[Feb 2024 - Cyfrin](https://github.com/wormhole-foundation/wormhole-audits/blob/main/2024-04-09-cyfrin-wormhole-evm-cctp-v2-1.pdf)**: _CCTP EVM Contracts_
|
||||
- **[Mar 2024 - Cyfrin](https://github.com/wormhole-foundation/wormhole-audits/blob/main/2024-04-11-cyfrin-wormhole-evm-ntt.pdf)**: _NTT EVM Contracts_
|
||||
- **[Mar 2024 - Cantina](https://github.com/wormhole-foundation/wormhole-audits/blob/main/2024-04-cantina-wormhole-evm-ntt.pdf)**: _NTT EVM Contracts_
|
||||
- **[Mar 2024 - OtterSec](https://github.com/wormhole-foundation/wormhole-audits/blob/main/2024-03-28-ottersec-solana-ntt.pdf)**: _NTT Solana Contracts_
|
||||
- **[Mar 2024 - Neodyme](https://github.com/wormhole-foundation/wormhole-audits/blob/main/2024-04-12-neodyme-solana-ntt.pdf)**: _NTT Solana Contracts_
|
||||
|
||||
|
|
|
@ -253,9 +253,10 @@ Options:
|
|||
"avalanche", "oasis", "algorand", "aurora", "fantom", "karura", "acala",
|
||||
"klaytn", "celo", "near", "moonbeam", "neon", "terra2", "injective",
|
||||
"osmosis", "sui", "aptos", "arbitrum", "optimism", "gnosis", "pythnet",
|
||||
"xpla", "btc", "base", "sei", "rootstock", "scroll", "mantle", "wormchain",
|
||||
"cosmoshub", "evmos", "kujira", "neutron", "celestia", "stargaze", "seda",
|
||||
"dymension", "sepolia", "arbitrum_sepolia", "base_sepolia",
|
||||
"xpla", "btc", "base", "sei", "rootstock", "scroll", "mantle", "blast",
|
||||
"xlayer", "linea", "berachain", "seievm", "wormchain", "cosmoshub", "evmos",
|
||||
"kujira", "neutron", "celestia", "stargaze", "seda", "dymension",
|
||||
"provenance", "sepolia", "arbitrum_sepolia", "base_sepolia",
|
||||
"optimism_sepolia", "holesky", "polygon_sepolia"]
|
||||
-n, --network Network
|
||||
[required] [choices: "mainnet", "testnet", "devnet"]
|
||||
|
@ -311,18 +312,20 @@ Options:
|
|||
"avalanche", "oasis", "algorand", "aurora", "fantom", "karura", "acala",
|
||||
"klaytn", "celo", "near", "moonbeam", "neon", "terra2", "injective",
|
||||
"osmosis", "sui", "aptos", "arbitrum", "optimism", "gnosis", "pythnet",
|
||||
"xpla", "btc", "base", "sei", "rootstock", "scroll", "mantle", "wormchain",
|
||||
"cosmoshub", "evmos", "kujira", "neutron", "celestia", "stargaze", "seda",
|
||||
"dymension", "sepolia", "arbitrum_sepolia", "base_sepolia",
|
||||
"xpla", "btc", "base", "sei", "rootstock", "scroll", "mantle", "blast",
|
||||
"xlayer", "linea", "berachain", "seievm", "wormchain", "cosmoshub", "evmos",
|
||||
"kujira", "neutron", "celestia", "stargaze", "seda", "dymension",
|
||||
"provenance", "sepolia", "arbitrum_sepolia", "base_sepolia",
|
||||
"optimism_sepolia", "holesky", "polygon_sepolia"]
|
||||
--dst-chain destination chain
|
||||
[required] [choices: "solana", "ethereum", "terra", "bsc", "polygon",
|
||||
"avalanche", "oasis", "algorand", "aurora", "fantom", "karura", "acala",
|
||||
"klaytn", "celo", "near", "moonbeam", "neon", "terra2", "injective",
|
||||
"osmosis", "sui", "aptos", "arbitrum", "optimism", "gnosis", "pythnet",
|
||||
"xpla", "btc", "base", "sei", "rootstock", "scroll", "mantle", "wormchain",
|
||||
"cosmoshub", "evmos", "kujira", "neutron", "celestia", "stargaze", "seda",
|
||||
"dymension", "sepolia", "arbitrum_sepolia", "base_sepolia",
|
||||
"xpla", "btc", "base", "sei", "rootstock", "scroll", "mantle", "blast",
|
||||
"xlayer", "linea", "berachain", "seievm", "wormchain", "cosmoshub", "evmos",
|
||||
"kujira", "neutron", "celestia", "stargaze", "seda", "dymension",
|
||||
"provenance", "sepolia", "arbitrum_sepolia", "base_sepolia",
|
||||
"optimism_sepolia", "holesky", "polygon_sepolia"]
|
||||
--dst-addr destination address [string] [required]
|
||||
--token-addr token address [string] [default: native token]
|
||||
|
@ -355,9 +358,10 @@ Positionals:
|
|||
"avalanche", "oasis", "algorand", "aurora", "fantom", "karura", "acala",
|
||||
"klaytn", "celo", "near", "moonbeam", "neon", "terra2", "injective",
|
||||
"osmosis", "sui", "aptos", "arbitrum", "optimism", "gnosis", "pythnet",
|
||||
"xpla", "btc", "base", "sei", "rootstock", "scroll", "mantle", "wormchain",
|
||||
"cosmoshub", "evmos", "kujira", "neutron", "celestia", "stargaze", "seda",
|
||||
"dymension", "sepolia", "arbitrum_sepolia", "base_sepolia",
|
||||
"xpla", "btc", "base", "sei", "rootstock", "scroll", "mantle", "blast",
|
||||
"xlayer", "linea", "berachain", "seievm", "wormchain", "cosmoshub", "evmos",
|
||||
"kujira", "neutron", "celestia", "stargaze", "seda", "dymension",
|
||||
"provenance", "sepolia", "arbitrum_sepolia", "base_sepolia",
|
||||
"optimism_sepolia", "holesky", "polygon_sepolia"]
|
||||
tx Source transaction hash [string]
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@celo-tools/celo-ethers-wrapper": "^0.1.0",
|
||||
"@certusone/wormhole-sdk": "^0.10.13",
|
||||
"@certusone/wormhole-sdk": "^0.10.14",
|
||||
"@cosmjs/encoding": "^0.26.2",
|
||||
"@improbable-eng/grpc-web-node-http-transport": "^0.15.0",
|
||||
"@injectivelabs/networks": "^1.10.7",
|
||||
|
@ -683,9 +683,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@certusone/wormhole-sdk": {
|
||||
"version": "0.10.13",
|
||||
"resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk/-/wormhole-sdk-0.10.13.tgz",
|
||||
"integrity": "sha512-04OCGoJUylTFNu4+g96Ax7jvn3M7FwVbTTcPNIjgta8QcC046kZBYAEd/yrK5hkDGqejQyJwO0ieaJfxL/kv1w==",
|
||||
"version": "0.10.14",
|
||||
"resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk/-/wormhole-sdk-0.10.14.tgz",
|
||||
"integrity": "sha512-36pEcLZbG+DLFKL2N7khLLnSYBMPoMpJrUs7IFqps3i+PhCZ6e8xb2ohNcIKX58G9Ibj+xRmc5ilo+D37WodVg==",
|
||||
"dependencies": {
|
||||
"@certusone/wormhole-sdk-proto-web": "0.0.7",
|
||||
"@certusone/wormhole-sdk-wasm": "^0.0.1",
|
||||
|
@ -11917,9 +11917,9 @@
|
|||
"requires": {}
|
||||
},
|
||||
"@certusone/wormhole-sdk": {
|
||||
"version": "0.10.13",
|
||||
"resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk/-/wormhole-sdk-0.10.13.tgz",
|
||||
"integrity": "sha512-04OCGoJUylTFNu4+g96Ax7jvn3M7FwVbTTcPNIjgta8QcC046kZBYAEd/yrK5hkDGqejQyJwO0ieaJfxL/kv1w==",
|
||||
"version": "0.10.14",
|
||||
"resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk/-/wormhole-sdk-0.10.14.tgz",
|
||||
"integrity": "sha512-36pEcLZbG+DLFKL2N7khLLnSYBMPoMpJrUs7IFqps3i+PhCZ6e8xb2ohNcIKX58G9Ibj+xRmc5ilo+D37WodVg==",
|
||||
"requires": {
|
||||
"@certusone/wormhole-sdk-proto-web": "0.0.7",
|
||||
"@certusone/wormhole-sdk-wasm": "^0.0.1",
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
],
|
||||
"dependencies": {
|
||||
"@celo-tools/celo-ethers-wrapper": "^0.1.0",
|
||||
"@certusone/wormhole-sdk": "^0.10.13",
|
||||
"@certusone/wormhole-sdk": "^0.10.14",
|
||||
"@cosmjs/encoding": "^0.26.2",
|
||||
"@improbable-eng/grpc-web-node-http-transport": "^0.15.0",
|
||||
"@injectivelabs/networks": "^1.10.7",
|
||||
|
|
|
@ -62,6 +62,11 @@ export const getOriginalAsset = async (
|
|||
// case "rootstock":
|
||||
case "scroll":
|
||||
case "mantle":
|
||||
case "blast":
|
||||
case "xlayer":
|
||||
case "linea":
|
||||
case "berachain":
|
||||
case "seievm":
|
||||
case "sepolia":
|
||||
case "arbitrum_sepolia":
|
||||
case "base_sepolia":
|
||||
|
@ -125,6 +130,7 @@ export const getOriginalAsset = async (
|
|||
case "stargaze":
|
||||
case "seda":
|
||||
case "dymension":
|
||||
case "provenance":
|
||||
case "rootstock":
|
||||
throw new Error(`${chainName} not supported`);
|
||||
default:
|
||||
|
|
|
@ -72,6 +72,11 @@ export const getWrappedAssetAddress = async (
|
|||
// case "rootstock":
|
||||
case "scroll":
|
||||
case "mantle":
|
||||
case "blast":
|
||||
case "xlayer":
|
||||
case "linea":
|
||||
case "berachain":
|
||||
case "seievm":
|
||||
case "sepolia":
|
||||
case "arbitrum_sepolia":
|
||||
case "base_sepolia":
|
||||
|
@ -172,6 +177,7 @@ export const getWrappedAssetAddress = async (
|
|||
case "stargaze":
|
||||
case "seda":
|
||||
case "dymension":
|
||||
case "provenance":
|
||||
throw new Error(`${chainName} not supported`);
|
||||
default:
|
||||
impossible(chainName);
|
||||
|
|
|
@ -99,6 +99,11 @@ export const getProviderForChain = <T extends ChainId | ChainName>(
|
|||
// case "rootstock":
|
||||
case "scroll":
|
||||
case "mantle":
|
||||
case "blast":
|
||||
case "xlayer":
|
||||
case "linea":
|
||||
case "berachain":
|
||||
case "seievm":
|
||||
case "sepolia":
|
||||
case "arbitrum_sepolia":
|
||||
case "base_sepolia":
|
||||
|
@ -169,6 +174,7 @@ export const getProviderForChain = <T extends ChainId | ChainName>(
|
|||
case "stargaze":
|
||||
case "seda":
|
||||
case "dymension":
|
||||
case "provenance":
|
||||
case "rootstock":
|
||||
throw new Error(`${chainName} not supported`);
|
||||
default:
|
||||
|
|
|
@ -200,6 +200,8 @@ async function executeSubmit(
|
|||
throw Error("seda is not supported yet");
|
||||
} else if (chain === "dymension") {
|
||||
throw Error("dymension is not supported yet");
|
||||
} else if (chain === "provenance") {
|
||||
throw Error("provenance is not supported yet");
|
||||
} else if (chain === "rootstock") {
|
||||
throw Error("rootstock is not supported yet");
|
||||
} else {
|
||||
|
|
|
@ -154,6 +154,8 @@ export const handler = async (
|
|||
throw Error("seda is not supported yet");
|
||||
} else if (srcChain === "dymension") {
|
||||
throw Error("dymension is not supported yet");
|
||||
} else if (srcChain === "provenance") {
|
||||
throw Error("provenance is not supported yet");
|
||||
} else if (srcChain === "rootstock") {
|
||||
throw Error("rootstock is not supported yet");
|
||||
} else {
|
||||
|
|
|
@ -171,6 +171,41 @@ const MAINNET = {
|
|||
key: getEnvVar("ETH_KEY"),
|
||||
chain_id: 30,
|
||||
},
|
||||
scroll: {
|
||||
rpc: undefined,
|
||||
key: undefined,
|
||||
chain_id: undefined,
|
||||
},
|
||||
mantle: {
|
||||
rpc: undefined,
|
||||
key: undefined,
|
||||
chain_id: undefined,
|
||||
},
|
||||
blast: {
|
||||
rpc: undefined,
|
||||
key: undefined,
|
||||
chain_id: undefined,
|
||||
},
|
||||
xlayer: {
|
||||
rpc: undefined,
|
||||
key: undefined,
|
||||
chain_id: undefined,
|
||||
},
|
||||
linea: {
|
||||
rpc: undefined,
|
||||
key: undefined,
|
||||
chain_id: undefined,
|
||||
},
|
||||
berachain: {
|
||||
rpc: undefined,
|
||||
key: undefined,
|
||||
chain_id: undefined,
|
||||
},
|
||||
seievm: {
|
||||
rpc: undefined,
|
||||
key: undefined,
|
||||
chain_id: undefined,
|
||||
},
|
||||
sepolia: {
|
||||
rpc: undefined,
|
||||
key: undefined,
|
||||
|
@ -206,16 +241,6 @@ const MAINNET = {
|
|||
key: undefined,
|
||||
chain_id: undefined,
|
||||
},
|
||||
scroll: {
|
||||
rpc: undefined,
|
||||
key: undefined,
|
||||
chain_id: undefined,
|
||||
},
|
||||
mantle: {
|
||||
rpc: undefined,
|
||||
key: undefined,
|
||||
chain_id: undefined,
|
||||
},
|
||||
arbitrum_sepolia: {
|
||||
rpc: undefined,
|
||||
key: undefined,
|
||||
|
@ -251,6 +276,11 @@ const MAINNET = {
|
|||
key: undefined,
|
||||
chain_id: undefined,
|
||||
},
|
||||
provenance: {
|
||||
rpc: undefined,
|
||||
key: undefined,
|
||||
chain_id: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
const TESTNET = {
|
||||
|
@ -362,6 +392,41 @@ const TESTNET = {
|
|||
rpc: "https://rpc.atlantic-2.seinetwork.io",
|
||||
key: getEnvVar("SEI_KEY_TESTNET"),
|
||||
},
|
||||
scroll: {
|
||||
rpc: "https://rpc.ankr.com/scroll_sepolia_testnet",
|
||||
key: getEnvVar("ETH_KEY_TESTNET"),
|
||||
chain_id: 534353,
|
||||
},
|
||||
mantle: {
|
||||
rpc: "https://mantle-sepolia.drpc.org",
|
||||
key: getEnvVar("ETH_KEY_TESTNET"),
|
||||
chain_id: 5003,
|
||||
},
|
||||
blast: {
|
||||
rpc: "https://blast-sepolia.drpc.org",
|
||||
key: getEnvVar("ETH_KEY_TESTNET"),
|
||||
chain_id: 168587773,
|
||||
},
|
||||
xlayer: {
|
||||
rpc: "https://testrpc.xlayer.tech/",
|
||||
key: getEnvVar("ETH_KEY_TESTNET"),
|
||||
chain_id: 195,
|
||||
},
|
||||
linea: {
|
||||
rpc: "https://rpc.sepolia.linea.build",
|
||||
key: getEnvVar("ETH_KEY_TESTNET"),
|
||||
chain_id: 59141,
|
||||
},
|
||||
berachain: {
|
||||
rpc: "https://artio.rpc.berachain.com/",
|
||||
key: getEnvVar("ETH_KEY_TESTNET"),
|
||||
chain_id: 80085,
|
||||
},
|
||||
seievm: {
|
||||
rpc: "https://evm-rpc-arctic-1.sei-apis.com/",
|
||||
key: getEnvVar("ETH_KEY_TESTNET"),
|
||||
chain_id: 713715,
|
||||
},
|
||||
sepolia: {
|
||||
rpc: "https://rpc.ankr.com/eth_sepolia",
|
||||
key: getEnvVar("ETH_KEY_TESTNET"),
|
||||
|
@ -445,16 +510,6 @@ const TESTNET = {
|
|||
key: undefined,
|
||||
chain_id: undefined,
|
||||
},
|
||||
scroll: {
|
||||
rpc: "https://rpc.ankr.com/scroll_sepolia_testnet",
|
||||
key: getEnvVar("ETH_KEY_TESTNET"),
|
||||
chain_id: 534353,
|
||||
},
|
||||
mantle: {
|
||||
rpc: "https://mantle-sepolia.drpc.org",
|
||||
key: getEnvVar("ETH_KEY_TESTNET"),
|
||||
chain_id: 5003,
|
||||
},
|
||||
arbitrum_sepolia: {
|
||||
rpc: "https://arbitrum-sepolia.publicnode.com",
|
||||
key: getEnvVar("ETH_KEY_TESTNET"),
|
||||
|
@ -490,6 +545,11 @@ const TESTNET = {
|
|||
key: undefined,
|
||||
chain_id: undefined,
|
||||
},
|
||||
provenance: {
|
||||
rpc: undefined,
|
||||
key: undefined,
|
||||
chain_id: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
const DEVNET = {
|
||||
|
@ -584,6 +644,41 @@ const DEVNET = {
|
|||
rpc: undefined,
|
||||
key: undefined,
|
||||
},
|
||||
scroll: {
|
||||
rpc: undefined,
|
||||
key: undefined,
|
||||
chain_id: undefined,
|
||||
},
|
||||
mantle: {
|
||||
rpc: undefined,
|
||||
key: undefined,
|
||||
chain_id: undefined,
|
||||
},
|
||||
blast: {
|
||||
rpc: undefined,
|
||||
key: undefined,
|
||||
chain_id: undefined,
|
||||
},
|
||||
xlayer: {
|
||||
rpc: undefined,
|
||||
key: undefined,
|
||||
chain_id: undefined,
|
||||
},
|
||||
linea: {
|
||||
rpc: undefined,
|
||||
key: undefined,
|
||||
chain_id: undefined,
|
||||
},
|
||||
berachain: {
|
||||
rpc: undefined,
|
||||
key: undefined,
|
||||
chain_id: undefined,
|
||||
},
|
||||
seievm: {
|
||||
rpc: undefined,
|
||||
key: undefined,
|
||||
chain_id: undefined,
|
||||
},
|
||||
sepolia: {
|
||||
rpc: undefined,
|
||||
key: undefined,
|
||||
|
@ -660,16 +755,6 @@ const DEVNET = {
|
|||
key: undefined,
|
||||
chain_id: undefined,
|
||||
},
|
||||
scroll: {
|
||||
rpc: undefined,
|
||||
key: undefined,
|
||||
chain_id: undefined,
|
||||
},
|
||||
mantle: {
|
||||
rpc: undefined,
|
||||
key: undefined,
|
||||
chain_id: undefined,
|
||||
},
|
||||
arbitrum_sepolia: {
|
||||
rpc: undefined,
|
||||
key: undefined,
|
||||
|
@ -705,6 +790,11 @@ const DEVNET = {
|
|||
key: undefined,
|
||||
chain_id: undefined,
|
||||
},
|
||||
provenance: {
|
||||
rpc: undefined,
|
||||
key: undefined,
|
||||
chain_id: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,8 +3,8 @@ import {
|
|||
BridgeImplementation__factory,
|
||||
Implementation__factory,
|
||||
NFTBridgeImplementation__factory,
|
||||
WormholeRelayer__factory,
|
||||
} from "@certusone/wormhole-sdk/lib/esm/ethers-contracts";
|
||||
import { WormholeRelayer__factory } from "@certusone/wormhole-sdk/lib/esm/ethers-relayer-contracts";
|
||||
import { getWormholeRelayerAddress } from "@certusone/wormhole-sdk/lib/esm/relayer";
|
||||
import {
|
||||
CHAINS,
|
||||
|
|
|
@ -7,6 +7,7 @@ Algorand
|
|||
algosdk
|
||||
alist
|
||||
Aptos
|
||||
arbitrum
|
||||
authorisation
|
||||
authorise
|
||||
authorised
|
||||
|
@ -25,6 +26,7 @@ callstack
|
|||
CCTP
|
||||
celestia
|
||||
Celestia
|
||||
celo
|
||||
certusone
|
||||
Chainlink
|
||||
Coinspect
|
||||
|
@ -143,6 +145,7 @@ rustup
|
|||
satoshi
|
||||
secp
|
||||
seda
|
||||
seievm
|
||||
Sepolia
|
||||
serde
|
||||
setcap
|
||||
|
@ -192,6 +195,7 @@ wormchaind
|
|||
Wormholescan
|
||||
wormscan
|
||||
wormscanurl
|
||||
xlayer
|
||||
xpla
|
||||
XPLA
|
||||
Zellic
|
||||
|
|
|
@ -60,6 +60,11 @@ var ibcUpdateChannelChainChainId *string
|
|||
var recoverChainIdEvmChainId *string
|
||||
var recoverChainIdNewChainId *string
|
||||
|
||||
var governanceContractAddress *string
|
||||
var governanceTargetAddress *string
|
||||
var governanceTargetChain *string
|
||||
var governanceCallData *string
|
||||
|
||||
func init() {
|
||||
governanceFlagSet := pflag.NewFlagSet("governance", pflag.ExitOnError)
|
||||
chainID = governanceFlagSet.String("chain-id", "", "Chain ID")
|
||||
|
@ -171,6 +176,19 @@ func init() {
|
|||
AdminClientRecoverChainIdCmd.Flags().AddFlagSet(recoverChainIdFlagSet)
|
||||
AdminClientRecoverChainIdCmd.Flags().AddFlagSet(moduleFlagSet)
|
||||
TemplateCmd.AddCommand(AdminClientRecoverChainIdCmd)
|
||||
|
||||
// flags for general-purpose governance call command
|
||||
generalPurposeGovernanceFlagSet := pflag.NewFlagSet("general-purpose-governance", pflag.ExitOnError)
|
||||
governanceContractAddress = generalPurposeGovernanceFlagSet.String("governance-contract", "", "Governance contract address")
|
||||
governanceTargetAddress = generalPurposeGovernanceFlagSet.String("target-address", "", "Address of the governed contract")
|
||||
governanceCallData = generalPurposeGovernanceFlagSet.String("call-data", "", "calldata")
|
||||
governanceTargetChain = generalPurposeGovernanceFlagSet.String("chain-id", "", "Chain ID")
|
||||
// evm call command
|
||||
AdminClientGeneralPurposeGovernanceEvmCallCmd.Flags().AddFlagSet(generalPurposeGovernanceFlagSet)
|
||||
TemplateCmd.AddCommand(AdminClientGeneralPurposeGovernanceEvmCallCmd)
|
||||
// solana call command
|
||||
AdminClientGeneralPurposeGovernanceSolanaCallCmd.Flags().AddFlagSet(generalPurposeGovernanceFlagSet)
|
||||
TemplateCmd.AddCommand(AdminClientGeneralPurposeGovernanceSolanaCallCmd)
|
||||
}
|
||||
|
||||
var TemplateCmd = &cobra.Command{
|
||||
|
@ -292,6 +310,18 @@ var AdminClientWormholeRelayerSetDefaultDeliveryProviderCmd = &cobra.Command{
|
|||
Run: runWormholeRelayerSetDefaultDeliveryProviderTemplate,
|
||||
}
|
||||
|
||||
var AdminClientGeneralPurposeGovernanceEvmCallCmd = &cobra.Command{
|
||||
Use: "governance-evm-call",
|
||||
Short: "Generate a 'general purpose evm governance call' template for specified chain and address",
|
||||
Run: runGeneralPurposeGovernanceEvmCallTemplate,
|
||||
}
|
||||
|
||||
var AdminClientGeneralPurposeGovernanceSolanaCallCmd = &cobra.Command{
|
||||
Use: "governance-solana-call",
|
||||
Short: "Generate a 'general purpose solana governance call' template for specified chain and address",
|
||||
Run: runGeneralPurposeGovernanceSolanaCallTemplate,
|
||||
}
|
||||
|
||||
func runGuardianSetTemplate(cmd *cobra.Command, args []string) {
|
||||
// Use deterministic devnet addresses as examples in the template, such that this doubles as a test fixture.
|
||||
guardians := make([]*nodev1.GuardianSetUpdate_Guardian, *setUpdateNumGuardians)
|
||||
|
@ -932,6 +962,100 @@ func runWormholeRelayerSetDefaultDeliveryProviderTemplate(cmd *cobra.Command, ar
|
|||
fmt.Print(string(b))
|
||||
}
|
||||
|
||||
func runGeneralPurposeGovernanceEvmCallTemplate(cmd *cobra.Command, args []string) {
|
||||
if *governanceTargetAddress == "" {
|
||||
log.Fatal("--target-address must be specified")
|
||||
}
|
||||
if !common.IsHexAddress(*governanceTargetAddress) {
|
||||
log.Fatal("invalid target address")
|
||||
}
|
||||
governanceTargetAddress := common.HexToAddress(*governanceTargetAddress).Hex()
|
||||
if *governanceCallData == "" {
|
||||
log.Fatal("--call-data must be specified")
|
||||
}
|
||||
if *governanceContractAddress == "" {
|
||||
log.Fatal("--governance-contract must be specified")
|
||||
}
|
||||
if !common.IsHexAddress(*governanceContractAddress) {
|
||||
log.Fatal("invalid governance contract address")
|
||||
}
|
||||
governanceContractAddress := common.HexToAddress(*governanceContractAddress).Hex()
|
||||
if *governanceTargetChain == "" {
|
||||
log.Fatal("--chain-id must be specified")
|
||||
}
|
||||
chainID, err := parseChainID(*governanceTargetChain)
|
||||
if err != nil {
|
||||
log.Fatal("failed to parse chain id: ", err)
|
||||
}
|
||||
|
||||
m := &nodev1.InjectGovernanceVAARequest{
|
||||
CurrentSetIndex: uint32(*templateGuardianIndex),
|
||||
Messages: []*nodev1.GovernanceMessage{
|
||||
{
|
||||
Sequence: rand.Uint64(),
|
||||
Nonce: rand.Uint32(),
|
||||
Payload: &nodev1.GovernanceMessage_EvmCall{
|
||||
EvmCall: &nodev1.EvmCall{
|
||||
ChainId: uint32(chainID),
|
||||
GovernanceContract: governanceContractAddress,
|
||||
TargetContract: governanceTargetAddress,
|
||||
AbiEncodedCall: *governanceCallData,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
b, err := prototext.MarshalOptions{Multiline: true}.Marshal(m)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Print(string(b))
|
||||
}
|
||||
|
||||
func runGeneralPurposeGovernanceSolanaCallTemplate(cmd *cobra.Command, args []string) {
|
||||
if *governanceCallData == "" {
|
||||
log.Fatal("--call-data must be specified")
|
||||
}
|
||||
if *governanceContractAddress == "" {
|
||||
log.Fatal("--governance-contract must be specified")
|
||||
}
|
||||
_, err := base58.Decode(*governanceContractAddress)
|
||||
if err != nil {
|
||||
log.Fatal("invalid base58 governance contract address")
|
||||
}
|
||||
if *governanceTargetChain == "" {
|
||||
log.Fatal("--chain-id must be specified")
|
||||
}
|
||||
chainID, err := parseChainID(*governanceTargetChain)
|
||||
if err != nil {
|
||||
log.Fatal("failed to parse chain id: ", err)
|
||||
}
|
||||
|
||||
m := &nodev1.InjectGovernanceVAARequest{
|
||||
CurrentSetIndex: uint32(*templateGuardianIndex),
|
||||
Messages: []*nodev1.GovernanceMessage{
|
||||
{
|
||||
Sequence: rand.Uint64(),
|
||||
Nonce: rand.Uint32(),
|
||||
Payload: &nodev1.GovernanceMessage_SolanaCall{
|
||||
SolanaCall: &nodev1.SolanaCall{
|
||||
ChainId: uint32(chainID),
|
||||
GovernanceContract: *governanceContractAddress,
|
||||
EncodedInstruction: *governanceCallData,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
b, err := prototext.MarshalOptions{Multiline: true}.Marshal(m)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Print(string(b))
|
||||
}
|
||||
|
||||
// parseAddress parses either a hex-encoded address and returns
|
||||
// a left-padded 32 byte hex string.
|
||||
func parseAddress(s string) (string, error) {
|
||||
|
|
|
@ -182,8 +182,10 @@ var (
|
|||
xlayerRPC *string
|
||||
xlayerContract *string
|
||||
|
||||
lineaRPC *string
|
||||
lineaContract *string
|
||||
lineaRPC *string
|
||||
lineaContract *string
|
||||
lineaRollUpUrl *string
|
||||
lineaRollUpContract *string
|
||||
|
||||
berachainRPC *string
|
||||
berachainContract *string
|
||||
|
@ -386,6 +388,8 @@ func init() {
|
|||
|
||||
lineaRPC = node.RegisterFlagWithValidationOrFail(NodeCmd, "lineaRPC", "Linea RPC URL", "ws://eth-devnet:8545", []string{"ws", "wss"})
|
||||
lineaContract = NodeCmd.Flags().String("lineaContract", "", "Linea contract address")
|
||||
lineaRollUpUrl = NodeCmd.Flags().String("lineaRollUpUrl", "", "Linea roll up URL")
|
||||
lineaRollUpContract = NodeCmd.Flags().String("lineaRollUpContract", "", "Linea roll up contract address")
|
||||
|
||||
berachainRPC = node.RegisterFlagWithValidationOrFail(NodeCmd, "berachainRPC", "Berachain RPC URL", "ws://eth-devnet:8545", []string{"ws", "wss"})
|
||||
berachainContract = NodeCmd.Flags().String("berachainContract", "", "Berachain contract address")
|
||||
|
@ -802,6 +806,9 @@ func runNode(cmd *cobra.Command, args []string) {
|
|||
if (*lineaRPC == "") != (*lineaContract == "") {
|
||||
logger.Fatal("Both --lineaContract and --lineaRPC must be set together or both unset")
|
||||
}
|
||||
if (*lineaRPC != "") && (*lineaRollUpUrl == "" || *lineaRollUpContract == "") && !*unsafeDevMode {
|
||||
logger.Fatal("If --lineaRPC is specified, --lineaRollUpUrl and --lineaRollUpContract must also be specified")
|
||||
}
|
||||
|
||||
if *berachainRPC != "" && !*testnetMode && !*unsafeDevMode {
|
||||
logger.Fatal("berachain is currently only supported in devnet and testnet")
|
||||
|
@ -996,10 +1003,16 @@ func runNode(cmd *cobra.Command, args []string) {
|
|||
if idx != 0 {
|
||||
// try to connect to guardian-0
|
||||
for {
|
||||
// tilt uses this hostname format
|
||||
_, err := net.LookupIP("guardian-0.guardian")
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
// load tests use this hostname format
|
||||
_, err = net.LookupIP("guardian-0")
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
logger.Info("Error resolving guardian-0.guardian. Trying again...")
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
|
@ -1144,6 +1157,10 @@ func runNode(cmd *cobra.Command, args []string) {
|
|||
|
||||
var accountantWormchainConn, accountantNttWormchainConn *wormconn.ClientConn
|
||||
if *accountantContract != "" {
|
||||
if *wormchainURL == "" {
|
||||
logger.Fatal("if accountantContract is specified, wormchainURL is required", zap.String("component", "gacct"))
|
||||
}
|
||||
|
||||
if *accountantKeyPath == "" {
|
||||
logger.Fatal("if accountantContract is specified, accountantKeyPath is required", zap.String("component", "gacct"))
|
||||
}
|
||||
|
@ -1176,6 +1193,10 @@ func runNode(cmd *cobra.Command, args []string) {
|
|||
|
||||
// If the NTT accountant is enabled, create a wormchain connection for it.
|
||||
if *accountantNttContract != "" {
|
||||
if *wormchainURL == "" {
|
||||
logger.Fatal("if accountantNttContract is specified, wormchainURL is required", zap.String("component", "gacct"))
|
||||
}
|
||||
|
||||
if *accountantNttKeyPath == "" {
|
||||
logger.Fatal("if accountantNttContract is specified, accountantNttKeyPath is required", zap.String("component", "gacct"))
|
||||
}
|
||||
|
@ -1506,11 +1527,13 @@ func runNode(cmd *cobra.Command, args []string) {
|
|||
|
||||
if shouldStart(lineaRPC) {
|
||||
wc := &evm.WatcherConfig{
|
||||
NetworkID: "linea",
|
||||
ChainID: vaa.ChainIDLinea,
|
||||
Rpc: *lineaRPC,
|
||||
Contract: *lineaContract,
|
||||
CcqBackfillCache: *ccqBackfillCache,
|
||||
NetworkID: "linea",
|
||||
ChainID: vaa.ChainIDLinea,
|
||||
Rpc: *lineaRPC,
|
||||
Contract: *lineaContract,
|
||||
CcqBackfillCache: *ccqBackfillCache,
|
||||
LineaRollUpUrl: *lineaRollUpUrl,
|
||||
LineaRollUpContract: *lineaRollUpContract,
|
||||
}
|
||||
|
||||
watcherConfigs = append(watcherConfigs, wc)
|
||||
|
|
|
@ -106,7 +106,7 @@ require (
|
|||
github.com/cosmos/gorocksdb v1.2.0 // indirect
|
||||
github.com/cosmos/iavl v0.19.4 // indirect
|
||||
github.com/cosmos/ibc-go/v4 v4.2.2 // indirect
|
||||
github.com/cosmos/ledger-cosmos-go v0.12.1 // indirect
|
||||
github.com/cosmos/ledger-cosmos-go v0.12.4 // indirect
|
||||
github.com/creachadair/taskgroup v0.3.2 // indirect
|
||||
github.com/danieljoos/wincred v1.1.2 // indirect
|
||||
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
|
||||
|
@ -314,8 +314,8 @@ require (
|
|||
github.com/weaveworks/common v0.0.0-20230531151736-e2613bee6b73 // indirect
|
||||
github.com/weaveworks/promrus v1.2.0 // indirect
|
||||
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect
|
||||
github.com/zondax/hid v0.9.1 // indirect
|
||||
github.com/zondax/ledger-go v0.14.0 // indirect
|
||||
github.com/zondax/hid v0.9.2 // indirect
|
||||
github.com/zondax/ledger-go v0.14.3 // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.5.5 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.5 // indirect
|
||||
|
|
12
node/go.sum
12
node/go.sum
|
@ -1036,8 +1036,8 @@ github.com/cosmos/ibc-go/v4 v4.2.2/go.mod h1:EFFqkrJHQPHWUlw155QUxGuis4Ett4lvewA
|
|||
github.com/cosmos/interchain-accounts v0.2.4 h1:7UrroFQsCRSp17980mk6anx4YteveIJVkU+a0wlsHQI=
|
||||
github.com/cosmos/interchain-accounts v0.2.4/go.mod h1:jeiJEb0zg609G0oCrCG0r6Guhb7YbA1uFiwww/1YgZE=
|
||||
github.com/cosmos/ledger-cosmos-go v0.11.1/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY=
|
||||
github.com/cosmos/ledger-cosmos-go v0.12.1 h1:sMBxza5p/rNK/06nBSNmsI/WDqI0pVJFVNihy1Y984w=
|
||||
github.com/cosmos/ledger-cosmos-go v0.12.1/go.mod h1:dhO6kj+Y+AHIOgAe4L9HL/6NDdyyth4q238I9yFpD2g=
|
||||
github.com/cosmos/ledger-cosmos-go v0.12.4 h1:drvWt+GJP7Aiw550yeb3ON/zsrgW0jgh5saFCr7pDnw=
|
||||
github.com/cosmos/ledger-cosmos-go v0.12.4/go.mod h1:fjfVWRf++Xkygt9wzCsjEBdjcf7wiiY35fv3ctT+k4M=
|
||||
github.com/cosmos/ledger-go v0.9.2/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI=
|
||||
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
|
@ -3153,10 +3153,10 @@ github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX
|
|||
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
|
||||
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
|
||||
github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM=
|
||||
github.com/zondax/hid v0.9.1 h1:gQe66rtmyZ8VeGFcOpbuH3r7erYtNEAezCAYu8LdkJo=
|
||||
github.com/zondax/hid v0.9.1/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM=
|
||||
github.com/zondax/ledger-go v0.14.0 h1:dlMC7aO8Wss1CxBq2I96kZ69Nh1ligzbs8UWOtq/AsA=
|
||||
github.com/zondax/ledger-go v0.14.0/go.mod h1:fZ3Dqg6qcdXWSOJFKMG8GCTnD7slO/RL2feOQv8K320=
|
||||
github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U=
|
||||
github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM=
|
||||
github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw=
|
||||
github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI=
|
||||
gitlab.com/bosi/decorder v0.2.1/go.mod h1:6C/nhLSbF6qZbYD8bRmISBwc6vcWdNsiIBkRvjJFrH0=
|
||||
gitlab.com/bosi/decorder v0.2.3/go.mod h1:9K1RB5+VPNQYtXtTDAzd2OEftsZb1oV0IrJrzChSdGE=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
|
|
|
@ -577,6 +577,58 @@ func wormholeRelayerSetDefaultDeliveryProvider(req *nodev1.WormholeRelayerSetDef
|
|||
return v, nil
|
||||
}
|
||||
|
||||
func evmCallToVaa(evmCall *nodev1.EvmCall, timestamp time.Time, guardianSetIndex, nonce uint32, sequence uint64) (*vaa.VAA, error) {
|
||||
governanceContract := ethcommon.HexToAddress(evmCall.GovernanceContract)
|
||||
targetContract := ethcommon.HexToAddress(evmCall.TargetContract)
|
||||
|
||||
payload, err := hex.DecodeString(evmCall.AbiEncodedCall)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode ABI encoded call: %w", err)
|
||||
}
|
||||
|
||||
body, err := vaa.BodyGeneralPurposeGovernanceEvm{
|
||||
ChainID: vaa.ChainID(evmCall.ChainId),
|
||||
GovernanceContract: governanceContract,
|
||||
TargetContract: targetContract,
|
||||
Payload: payload,
|
||||
}.Serialize()
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to serialize governance body: %w", err)
|
||||
}
|
||||
|
||||
v := vaa.CreateGovernanceVAA(timestamp, nonce, sequence, guardianSetIndex, body)
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func solanaCallToVaa(solanaCall *nodev1.SolanaCall, timestamp time.Time, guardianSetIndex, nonce uint32, sequence uint64) (*vaa.VAA, error) {
|
||||
address, err := base58.Decode(solanaCall.GovernanceContract)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode base58 governance contract address: %w", err)
|
||||
}
|
||||
if len(address) != 32 {
|
||||
return nil, errors.New("invalid governance contract address length (expected 32 bytes)")
|
||||
}
|
||||
|
||||
var governanceContract [32]byte
|
||||
copy(governanceContract[:], address)
|
||||
|
||||
instruction, err := hex.DecodeString(solanaCall.EncodedInstruction)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode instruction: %w", err)
|
||||
}
|
||||
|
||||
v := vaa.CreateGovernanceVAA(timestamp, nonce, sequence, guardianSetIndex,
|
||||
vaa.BodyGeneralPurposeGovernanceSolana{
|
||||
ChainID: vaa.ChainID(solanaCall.ChainId),
|
||||
GovernanceContract: governanceContract,
|
||||
Instruction: instruction,
|
||||
}.Serialize())
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func GovMsgToVaa(message *nodev1.GovernanceMessage, currentSetIndex uint32, timestamp time.Time) (*vaa.VAA, error) {
|
||||
var (
|
||||
v *vaa.VAA
|
||||
|
@ -620,6 +672,10 @@ func GovMsgToVaa(message *nodev1.GovernanceMessage, currentSetIndex uint32, time
|
|||
v, err = ibcUpdateChannelChain(payload.IbcUpdateChannelChain, timestamp, currentSetIndex, message.Nonce, message.Sequence)
|
||||
case *nodev1.GovernanceMessage_WormholeRelayerSetDefaultDeliveryProvider:
|
||||
v, err = wormholeRelayerSetDefaultDeliveryProvider(payload.WormholeRelayerSetDefaultDeliveryProvider, timestamp, currentSetIndex, message.Nonce, message.Sequence)
|
||||
case *nodev1.GovernanceMessage_EvmCall:
|
||||
v, err = evmCallToVaa(payload.EvmCall, timestamp, currentSetIndex, message.Nonce, message.Sequence)
|
||||
case *nodev1.GovernanceMessage_SolanaCall:
|
||||
v, err = solanaCallToVaa(payload.SolanaCall, timestamp, currentSetIndex, message.Nonce, message.Sequence)
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported VAA type: %T", payload))
|
||||
}
|
||||
|
|
|
@ -14,5 +14,37 @@ func manualTokenList() []tokenConfigEntry {
|
|||
{chain: 13, addr: "0000000000000000000000005096db80b21ef45230c9e423c373f1fc9c0198dd", symbol: "WEMIX", coinGeckoId: "wemix-token", decimals: 8, price: 1.74},
|
||||
{chain: 15, addr: "0000000000000000000000000000000000000000000000000000000000000000", symbol: "NEAR", coinGeckoId: "near", decimals: 8, price: 3.85},
|
||||
{chain: 32, addr: "00881043998ff2b738519d444d2dd0da3da4545de08290c1076746538d5333df", symbol: "Sei", coinGeckoId: "sei-network", decimals: 6, price: 0.0},
|
||||
// BLAST (tokens over $50,000 24h volume)
|
||||
{chain: 36, addr: "0000000000000000000000004300000000000000000000000000000000000003", symbol: "USDB", coinGeckoId: "usdb", decimals: 18, price: 1.00},
|
||||
{chain: 36, addr: "0000000000000000000000004300000000000000000000000000000000000004", symbol: "WETH", coinGeckoId: "weth", decimals: 18, price: 3157.42},
|
||||
{chain: 36, addr: "0000000000000000000000002416092f143378750bb29b79ed961ab195cceea5", symbol: "EZETH", coinGeckoId: "renzo-restaked-eth", decimals: 18, price: 3092.32},
|
||||
{chain: 36, addr: "0000000000000000000000004fee793d435c6d2c10c135983bb9d6d4fc7b9bbd", symbol: "USD+", coinGeckoId: "usd", decimals: 18, price: 1.00},
|
||||
{chain: 36, addr: "000000000000000000000000818a92bc81aad0053d72ba753fb5bc3d0c5c0923", symbol: "JUICE", coinGeckoId: "juice-finance", decimals: 18, price: 0.1051},
|
||||
{chain: 36, addr: "0000000000000000000000009e20461bc2c4c980f62f1b279d71734207a6a356", symbol: "OMNI", coinGeckoId: "omnicat", decimals: 18, price: 0.0004575},
|
||||
{chain: 36, addr: "000000000000000000000000764933fbad8f5d04ccd088602096655c2ed9879f", symbol: "AI", coinGeckoId: "any-inu", decimals: 18, price: 0.00002742},
|
||||
{chain: 36, addr: "0000000000000000000000005ffd9ebd27f2fcab044c0f0a26a45cb62fa29c06", symbol: "PAC", coinGeckoId: "pacmoon", decimals: 18, price: 0.05459},
|
||||
{chain: 36, addr: "00000000000000000000000020fe91f17ec9080e3cac2d688b4ecb48c5ac3a9c", symbol: "YES", coinGeckoId: "yes-money", decimals: 18, price: 3.96},
|
||||
{chain: 36, addr: "00000000000000000000000076da31d7c9cbeae102aff34d3398bc450c8374c1", symbol: "MIM", coinGeckoId: "magic-internet-money", decimals: 18, price: 0.9935},
|
||||
{chain: 36, addr: "00000000000000000000000015d24de366f69b835be19f7cf9447e770315dd80", symbol: "KAP", coinGeckoId: "kapital-dao", decimals: 18, price: 0.1143},
|
||||
{chain: 36, addr: "000000000000000000000000b9dfcd4cf589bb8090569cb52fac1b88dbe4981f", symbol: "BAG", coinGeckoId: "bag", decimals: 18, price: 0.002972},
|
||||
{chain: 36, addr: "00000000000000000000000068449870eea84453044bd430822827e21fd8f101", symbol: "ZAI", coinGeckoId: "zaibot", decimals: 18, price: 0.2348},
|
||||
{chain: 36, addr: "00000000000000000000000047c337bd5b9344a6f3d6f58c474d9d8cd419d8ca", symbol: "DACKIE", coinGeckoId: "dackieswap", decimals: 18, price: 0.006554},
|
||||
{chain: 36, addr: "000000000000000000000000d43d8adac6a4c7d9aeece7c3151fca8f23752cf8", symbol: "ANDY", coinGeckoId: "andyerc", decimals: 9, price: 0.1165},
|
||||
{chain: 36, addr: "00000000000000000000000087e154e86fb691ab8a27116e93ed8d54e2b8c18c", symbol: "TES", coinGeckoId: "titan-trading-token", decimals: 18, price: 0.867},
|
||||
{chain: 36, addr: "000000000000000000000000870a8f46b62b8bdeda4c02530c1750cddf2ed32e", symbol: "USDC+", coinGeckoId: "usdc-plus-overnight", decimals: 18, price: 1.00},
|
||||
{chain: 36, addr: "00000000000000000000000042e12d42b3d6c4a74a88a61063856756ea2db357", symbol: "ORBIT", coinGeckoId: "orbit-protocol", decimals: 18, price: 0.3074},
|
||||
// SCROLL (tokens over $50,000 24h volume)
|
||||
{chain: 34, addr: "0000000000000000000000000018d96c579121a94307249d47f053e2d687b5e7", symbol: "MVX", coinGeckoId: "metavault-trade", decimals: 18, price: 2.06},
|
||||
{chain: 34, addr: "00000000000000000000000047c337bd5b9344a6f3d6f58c474d9d8cd419d8ca", symbol: "DACKIE", coinGeckoId: "dackieswap", decimals: 18, price: 0.00655},
|
||||
{chain: 34, addr: "0000000000000000000000005300000000000000000000000000000000000004", symbol: "WETH", coinGeckoId: "bridged-wrapped-ether-scroll", decimals: 18, price: 3145.98},
|
||||
{chain: 34, addr: "000000000000000000000000f55bec9cafdbe8730f096aa55dad6d22d44099df", symbol: "USDT", coinGeckoId: "bridged-tether-scroll", decimals: 6, price: 1.00},
|
||||
{chain: 34, addr: "00000000000000000000000006efdbff2a14a7c8e15944d1f4a48f9f95f663a4", symbol: "USDC", coinGeckoId: "bridged-usd-coin-scroll", decimals: 6, price: 1.00},
|
||||
{chain: 34, addr: "000000000000000000000000eb466342c4d449bc9f53a865d5cb90586f405215", symbol: "AXLUSDC", coinGeckoId: "bridged-axelar-wrapped-usd-coin-scroll", decimals: 6, price: 1.01},
|
||||
{chain: 34, addr: "0000000000000000000000003c1bca5a656e69edcd0d4e36bebb3fcdaca60cf1", symbol: "WBTC", coinGeckoId: "bridged-wrapped-bitcoin-scroll", decimals: 8, price: 64415.17},
|
||||
{chain: 34, addr: "00000000000000000000000060d01ec2d5e98ac51c8b4cf84dfcce98d527c747", symbol: "IZI", coinGeckoId: "izumi-finance", decimals: 18, price: 0.0142},
|
||||
{chain: 34, addr: "0000000000000000000000000a3bb08b3a15a19b4de82f8acfc862606fb69a2d", symbol: "IUSD", coinGeckoId: "izumi-bond-usd", decimals: 18, price: 0.9195},
|
||||
{chain: 34, addr: "000000000000000000000000f610a9dfb7c89644979b4a0f27063e9e7d7cda32", symbol: "WSTETH", coinGeckoId: "bridged-wrapped-lido-staked-ether-scroll", decimals: 18, price: 3659.28},
|
||||
{chain: 34, addr: "000000000000000000000000cA77eB3fEFe3725Dc33bccB54eDEFc3D9f764f97", symbol: "DAI", coinGeckoId: "dai", decimals: 18, price: 1.00},
|
||||
{chain: 34, addr: "00000000000000000000000053878B874283351D26d206FA512aEcE1Bef6C0dD", symbol: "RETH", coinGeckoId: "rocket-pool-eth", decimals: 18, price: 3475.55},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -147,7 +147,7 @@ func GuardianOptionAccountant(
|
|||
}
|
||||
|
||||
if websocket == "" {
|
||||
return errors.New("if accountantContract is specified, accountantWS is required")
|
||||
return errors.New("if either accountantContract or accountantNttContract is specified, accountantWS is required")
|
||||
}
|
||||
if contract != "" {
|
||||
if wormchainConn == nil {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,8 @@
|
|||
package evm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/certusone/wormhole/node/pkg/common"
|
||||
gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1"
|
||||
"github.com/certusone/wormhole/node/pkg/query"
|
||||
|
@ -20,6 +22,10 @@ type WatcherConfig struct {
|
|||
L1FinalizerRequired watchers.NetworkID // (optional)
|
||||
l1Finalizer interfaces.L1Finalizer
|
||||
CcqBackfillCache bool
|
||||
|
||||
// These parameters are currently only used for Linea and should be set via SetLineaParams()
|
||||
LineaRollUpUrl string
|
||||
LineaRollUpContract string
|
||||
}
|
||||
|
||||
func (wc *WatcherConfig) GetNetworkID() watchers.NetworkID {
|
||||
|
@ -57,5 +63,12 @@ func (wc *WatcherConfig) Create(
|
|||
|
||||
watcher := NewEthWatcher(wc.Rpc, eth_common.HexToAddress(wc.Contract), string(wc.NetworkID), wc.ChainID, msgC, setWriteC, obsvReqC, queryReqC, queryResponseC, devMode, wc.CcqBackfillCache)
|
||||
watcher.SetL1Finalizer(wc.l1Finalizer)
|
||||
if wc.ChainID == vaa.ChainIDLinea {
|
||||
if err := watcher.SetLineaParams(wc.LineaRollUpUrl, wc.LineaRollUpContract); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
} else if wc.LineaRollUpUrl != "" || wc.LineaRollUpContract != "" {
|
||||
return nil, nil, errors.New("LineaRollUpUrl and LineaRollUpContract may only be specified for Linea")
|
||||
}
|
||||
return watcher, watcher.Run, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,218 @@
|
|||
// A block is considered finalized on Linea when it is marked finalized by the LineaRollup contract on Ethereum.
|
||||
//
|
||||
// For a discussion of finality on Linea, see here:
|
||||
// https://www.notion.so/wormholefoundation/Testnet-Info-V2-633e4aa64a634d56a7ce07a103789774?pvs=4#03513c2eb3654d33aff2206a562d25b1
|
||||
//
|
||||
// The LineaRollup proxy contract on ethereum is available at the following addresses:
|
||||
// Mainnet: 0xd19d4B5d358258f05D7B411E21A1460D11B0876F
|
||||
// Testnet: 0xB218f8A4Bc926cF1cA7b3423c154a0D627Bdb7E5
|
||||
//
|
||||
// To generate the golang abi for the LineaRollup contract:
|
||||
// - Grab the ABIs from the LineaRollup contract (not the proxy) (0x934Dd4C63E285551CEceF8459103554D0096c179 on Ethereum mainnet) and put it in /tmp/LineaRollup.abi.
|
||||
// - mkdir node/pkg/watchers/evm/connectors/lineaabi
|
||||
// - Install abigen: go install github.com/ethereum/go-ethereum/cmd/abigen@latest
|
||||
// - abigen --abi /tmp/LineaRollup.abi --pkg lineaabi --out node/pkg/watchers/evm/connectors/lineaabi/LineaRollup.go
|
||||
|
||||
package connectors
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/certusone/wormhole/node/pkg/common"
|
||||
rollUpAbi "github.com/certusone/wormhole/node/pkg/watchers/evm/connectors/lineaabi"
|
||||
|
||||
ethereum "github.com/ethereum/go-ethereum"
|
||||
ethBind "github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
ethCommon "github.com/ethereum/go-ethereum/common"
|
||||
ethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
ethClient "github.com/ethereum/go-ethereum/ethclient"
|
||||
ethRpc "github.com/ethereum/go-ethereum/rpc"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// LineaConnector listens for new finalized blocks for Linea by reading the roll up contract on Ethereum.
|
||||
type LineaConnector struct {
|
||||
Connector
|
||||
logger *zap.Logger
|
||||
|
||||
// These are used for querying the roll up contract.
|
||||
rollUpRawClient *ethRpc.Client
|
||||
rollUpClient *ethClient.Client
|
||||
|
||||
// These are used to subscribe for new block finalized events from the roll up contract.
|
||||
rollUpFilterer *rollUpAbi.LineaabiFilterer
|
||||
rollUpCaller *rollUpAbi.LineaabiCaller
|
||||
|
||||
latestBlockNum uint64
|
||||
latestFinalizedBlockNum uint64
|
||||
}
|
||||
|
||||
// NewLineaConnector creates a new Linea poll connector using the specified roll up contract.
|
||||
func NewLineaConnector(
|
||||
ctx context.Context,
|
||||
logger *zap.Logger,
|
||||
baseConnector Connector,
|
||||
rollUpUrl string,
|
||||
rollUpAddress string,
|
||||
) (*LineaConnector, error) {
|
||||
|
||||
rollUpRawClient, err := ethRpc.DialContext(ctx, rollUpUrl)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create roll up raw client for url %s: %w", rollUpUrl, err)
|
||||
}
|
||||
|
||||
rollUpClient := ethClient.NewClient(rollUpRawClient)
|
||||
|
||||
addr := ethCommon.HexToAddress(rollUpAddress)
|
||||
rollUpFilterer, err := rollUpAbi.NewLineaabiFilterer(addr, rollUpClient)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create roll up filter for url %s: %w", rollUpUrl, err)
|
||||
}
|
||||
|
||||
rollUpCaller, err := rollUpAbi.NewLineaabiCaller(addr, rollUpClient)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create roll up caller for url %s: %w", rollUpUrl, err)
|
||||
}
|
||||
|
||||
logger.Info("Using roll up for Linea", zap.String("rollUpUrl", rollUpUrl), zap.String("rollUpAddress", rollUpAddress))
|
||||
|
||||
connector := &LineaConnector{
|
||||
Connector: baseConnector,
|
||||
logger: logger,
|
||||
rollUpRawClient: rollUpRawClient,
|
||||
rollUpClient: rollUpClient,
|
||||
rollUpFilterer: rollUpFilterer,
|
||||
rollUpCaller: rollUpCaller,
|
||||
}
|
||||
|
||||
return connector, nil
|
||||
}
|
||||
|
||||
// SubscribeForBlocks starts polling. It implements the standard connector interface.
|
||||
func (c *LineaConnector) SubscribeForBlocks(ctx context.Context, errC chan error, sink chan<- *NewBlock) (ethereum.Subscription, error) {
|
||||
timeout, cancel := context.WithTimeout(ctx, 15*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Use the standard geth head sink to get latest blocks.
|
||||
headSink := make(chan *ethTypes.Header, 2)
|
||||
headerSubscription, err := c.Connector.Client().SubscribeNewHead(ctx, headSink)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to subscribe for latest blocks: %w", err)
|
||||
}
|
||||
|
||||
// Subscribe to data finalized events from the roll up contract.
|
||||
dataFinalizedChan := make(chan *rollUpAbi.LineaabiDataFinalized, 2)
|
||||
dataFinalizedSub, err := c.rollUpFilterer.WatchDataFinalized(ðBind.WatchOpts{Context: timeout}, dataFinalizedChan, nil, nil, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to subscribe for events from roll up contract: %w", err)
|
||||
}
|
||||
|
||||
// Get the current latest block on Linea.
|
||||
latestBlock, err := GetBlockByFinality(timeout, c.logger, c.Connector, Latest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get current latest block: %w", err)
|
||||
}
|
||||
c.latestBlockNum = latestBlock.Number.Uint64()
|
||||
|
||||
// Get and publish the current latest finalized block.
|
||||
opts := ðBind.CallOpts{Context: timeout}
|
||||
initialBlock, err := c.rollUpCaller.CurrentL2BlockNumber(opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get initial block: %w", err)
|
||||
}
|
||||
c.latestFinalizedBlockNum = initialBlock.Uint64()
|
||||
|
||||
if c.latestFinalizedBlockNum > c.latestBlockNum {
|
||||
return nil, fmt.Errorf("latest finalized block reported by L1 (%d) is ahead of latest block reported by L2 (%d), L2 node seems to be stuck",
|
||||
c.latestFinalizedBlockNum, c.latestBlockNum)
|
||||
}
|
||||
|
||||
c.logger.Info("queried initial finalized block", zap.Uint64("initialBlock", c.latestFinalizedBlockNum), zap.Uint64("latestBlock", c.latestBlockNum))
|
||||
if err = c.postFinalizedAndSafe(ctx, c.latestFinalizedBlockNum, sink); err != nil {
|
||||
return nil, fmt.Errorf("failed to post initial block: %w", err)
|
||||
}
|
||||
|
||||
common.RunWithScissors(ctx, errC, "linea_block_poller", func(ctx context.Context) error {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
dataFinalizedSub.Unsubscribe()
|
||||
return nil
|
||||
case err := <-dataFinalizedSub.Err():
|
||||
errC <- fmt.Errorf("finalized data watcher posted an error: %w", err)
|
||||
dataFinalizedSub.Unsubscribe()
|
||||
return nil
|
||||
case evt := <-dataFinalizedChan:
|
||||
if err := c.processDataFinalizedEvent(ctx, sink, evt); err != nil {
|
||||
errC <- fmt.Errorf("failed to process block finalized event: %w", err)
|
||||
dataFinalizedSub.Unsubscribe()
|
||||
return nil
|
||||
}
|
||||
case ev := <-headSink:
|
||||
if ev == nil {
|
||||
c.logger.Error("new latest header event is nil")
|
||||
continue
|
||||
}
|
||||
if ev.Number == nil {
|
||||
c.logger.Error("new latest header block number is nil")
|
||||
continue
|
||||
}
|
||||
c.latestBlockNum = ev.Number.Uint64()
|
||||
sink <- &NewBlock{
|
||||
Number: ev.Number,
|
||||
Time: ev.Time,
|
||||
Hash: ev.Hash(),
|
||||
Finality: Latest,
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return headerSubscription, nil
|
||||
}
|
||||
|
||||
// processDataFinalizedEvent handles a DataFinalized event published by the roll up contract.
|
||||
func (c *LineaConnector) processDataFinalizedEvent(ctx context.Context, sink chan<- *NewBlock, evt *rollUpAbi.LineaabiDataFinalized) error {
|
||||
latestFinalizedBlockNum := evt.LastBlockFinalized.Uint64()
|
||||
// Leaving this log info in for now because these events come very infrequently.
|
||||
c.logger.Info("processing data finalized event",
|
||||
zap.Uint64("latestFinalizedBlockNum", latestFinalizedBlockNum),
|
||||
zap.Uint64("prevFinalizedBlockNum", c.latestFinalizedBlockNum),
|
||||
)
|
||||
|
||||
if latestFinalizedBlockNum > c.latestBlockNum {
|
||||
return fmt.Errorf("latest finalized block reported by L1 (%d) is ahead of latest block reported by L2 (%d), L2 node seems to be stuck",
|
||||
latestFinalizedBlockNum, c.latestBlockNum)
|
||||
}
|
||||
|
||||
for blockNum := c.latestFinalizedBlockNum + 1; blockNum <= latestFinalizedBlockNum; blockNum++ {
|
||||
if err := c.postFinalizedAndSafe(ctx, blockNum, sink); err != nil {
|
||||
c.latestFinalizedBlockNum = blockNum - 1
|
||||
return fmt.Errorf("failed to post block %d: %w", blockNum, err)
|
||||
}
|
||||
}
|
||||
|
||||
c.latestFinalizedBlockNum = latestFinalizedBlockNum
|
||||
return nil
|
||||
}
|
||||
|
||||
// postFinalizedAndSafe publishes a block as finalized and safe. It takes a block number and looks it up on chain to publish the current values.
|
||||
func (c *LineaConnector) postFinalizedAndSafe(ctx context.Context, blockNum uint64, sink chan<- *NewBlock) error {
|
||||
timeout, cancel := context.WithTimeout(ctx, 15*time.Second)
|
||||
defer cancel()
|
||||
|
||||
block, err := GetBlockByNumberUint64(timeout, c.logger, c.Connector, blockNum, Finalized)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get block %d: %w", blockNum, err)
|
||||
}
|
||||
|
||||
// Publish the finalized block.
|
||||
sink <- block
|
||||
|
||||
// Publish same thing for the safe block.
|
||||
sink <- block.Copy(Safe)
|
||||
return nil
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -2,6 +2,7 @@ package evm
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
|
@ -136,6 +137,10 @@ type (
|
|||
ccqBatchSize int64
|
||||
ccqBackfillCache bool
|
||||
ccqLogger *zap.Logger
|
||||
|
||||
// These parameters are currently only used for Linea and should be set via SetLineaParams()
|
||||
lineaRollUpUrl string
|
||||
lineaRollUpContract string
|
||||
}
|
||||
|
||||
pendingKey struct {
|
||||
|
@ -242,6 +247,19 @@ func (w *Watcher) Run(parentCtx context.Context) error {
|
|||
p2p.DefaultRegistry.AddErrorCount(w.chainID, 1)
|
||||
return fmt.Errorf("dialing eth client failed: %w", err)
|
||||
}
|
||||
} else if w.chainID == vaa.ChainIDLinea {
|
||||
baseConnector, err := connectors.NewEthereumBaseConnector(timeout, w.networkName, w.url, w.contract, logger)
|
||||
if err != nil {
|
||||
ethConnectionErrors.WithLabelValues(w.networkName, "dial_error").Inc()
|
||||
p2p.DefaultRegistry.AddErrorCount(w.chainID, 1)
|
||||
return fmt.Errorf("dialing eth client failed: %w", err)
|
||||
}
|
||||
w.ethConn, err = connectors.NewLineaConnector(ctx, logger, baseConnector, w.lineaRollUpUrl, w.lineaRollUpContract)
|
||||
if err != nil {
|
||||
ethConnectionErrors.WithLabelValues(w.networkName, "dial_error").Inc()
|
||||
p2p.DefaultRegistry.AddErrorCount(w.chainID, 1)
|
||||
return fmt.Errorf("failed to create Linea poller: %w", err)
|
||||
}
|
||||
} else {
|
||||
// Everything else is instant finality.
|
||||
logger.Info("assuming instant finality")
|
||||
|
@ -699,41 +717,61 @@ func fetchCurrentGuardianSet(ctx context.Context, ethConn connectors.Connector)
|
|||
// getFinality determines if the chain supports "finalized" and "safe". This is hard coded so it requires thought to change something. However, it also reads the RPC
|
||||
// to make sure the node actually supports the expected values, and returns an error if it doesn't. Note that we do not support using safe mode but not finalized mode.
|
||||
func (w *Watcher) getFinality(ctx context.Context) (bool, bool, error) {
|
||||
// TODO: Need to handle finality for Linea before it can be deployed in Mainnet.
|
||||
finalized := false
|
||||
safe := false
|
||||
|
||||
// Tilt supports polling for both finalized and safe.
|
||||
if w.unsafeDevMode {
|
||||
finalized = true
|
||||
safe = true
|
||||
|
||||
// The following chains support polling for both finalized and safe.
|
||||
} else if w.chainID == vaa.ChainIDAcala ||
|
||||
w.chainID == vaa.ChainIDArbitrum ||
|
||||
w.chainID == vaa.ChainIDArbitrumSepolia ||
|
||||
w.chainID == vaa.ChainIDBase ||
|
||||
w.chainID == vaa.ChainIDBaseSepolia ||
|
||||
w.chainID == vaa.ChainIDBlast ||
|
||||
w.chainID == vaa.ChainIDBSC ||
|
||||
w.chainID == vaa.ChainIDEthereum ||
|
||||
w.chainID == vaa.ChainIDHolesky ||
|
||||
w.chainID == vaa.ChainIDKarura ||
|
||||
w.chainID == vaa.ChainIDMantle ||
|
||||
w.chainID == vaa.ChainIDMoonbeam ||
|
||||
w.chainID == vaa.ChainIDOptimism ||
|
||||
w.chainID == vaa.ChainIDSepolia ||
|
||||
w.chainID == vaa.ChainIDHolesky ||
|
||||
w.chainID == vaa.ChainIDArbitrumSepolia ||
|
||||
w.chainID == vaa.ChainIDBaseSepolia ||
|
||||
w.chainID == vaa.ChainIDOptimismSepolia ||
|
||||
w.chainID == vaa.ChainIDSepolia ||
|
||||
w.chainID == vaa.ChainIDXLayer {
|
||||
finalized = true
|
||||
safe = true
|
||||
} else if w.chainID == vaa.ChainIDScroll {
|
||||
// As of 11/10/2023 Scroll supports polling for finalized but not safe.
|
||||
finalized = true
|
||||
} else if w.chainID == vaa.ChainIDPolygon ||
|
||||
w.chainID == vaa.ChainIDPolygonSepolia {
|
||||
|
||||
// The following chains have their own specialized finalizers.
|
||||
} else if w.chainID == vaa.ChainIDCelo ||
|
||||
w.chainID == vaa.ChainIDLinea {
|
||||
return false, false, nil
|
||||
|
||||
// Polygon now supports polling for finalized but not safe.
|
||||
// https://forum.polygon.technology/t/optimizing-decentralized-apps-ux-with-milestones-a-significantly-accelerated-finality-solution/13154
|
||||
} else if w.chainID == vaa.ChainIDPolygon ||
|
||||
w.chainID == vaa.ChainIDPolygonSepolia {
|
||||
finalized = true
|
||||
} else if w.chainID == vaa.ChainIDBerachain {
|
||||
// Berachain supports instant finality: https://docs.berachain.com/faq/
|
||||
|
||||
// As of 11/10/2023 Scroll supports polling for finalized but not safe.
|
||||
} else if w.chainID == vaa.ChainIDScroll {
|
||||
finalized = true
|
||||
|
||||
// The following chains support instant finality.
|
||||
} else if w.chainID == vaa.ChainIDAvalanche ||
|
||||
w.chainID == vaa.ChainIDBerachain || // Berachain supports instant finality: https://docs.berachain.com/faq/
|
||||
w.chainID == vaa.ChainIDOasis ||
|
||||
w.chainID == vaa.ChainIDAurora ||
|
||||
w.chainID == vaa.ChainIDFantom ||
|
||||
w.chainID == vaa.ChainIDKlaytn {
|
||||
return false, false, nil
|
||||
|
||||
// Anything else is undefined / not supported.
|
||||
} else {
|
||||
return false, false, fmt.Errorf("unsupported chain: %s", w.chainID.String())
|
||||
}
|
||||
|
||||
// If finalized / safe should be supported, read the RPC to make sure they actually are.
|
||||
|
@ -935,3 +973,22 @@ func (w *Watcher) waitForBlockTime(ctx context.Context, logger *zap.Logger, errC
|
|||
func msgIdFromLogEvent(chainID vaa.ChainID, ev *ethabi.AbiLogMessagePublished) string {
|
||||
return fmt.Sprintf("%v/%v/%v", uint16(chainID), PadAddress(ev.Sender), ev.Sequence)
|
||||
}
|
||||
|
||||
// SetLineaParams is used to enable polling on Linea using the roll up contract on Ethereum.
|
||||
func (w *Watcher) SetLineaParams(lineaRollUpUrl string, lineaRollUpContract string) error {
|
||||
if w.chainID != vaa.ChainIDLinea {
|
||||
return errors.New("function only allowed for Linea")
|
||||
}
|
||||
if w.unsafeDevMode && lineaRollUpUrl == "" && lineaRollUpContract == "" {
|
||||
return nil
|
||||
}
|
||||
if lineaRollUpUrl == "" {
|
||||
return fmt.Errorf("lineaRollUpUrl must be set")
|
||||
}
|
||||
if lineaRollUpContract == "" {
|
||||
return fmt.Errorf("lineaRollUpContract must be set")
|
||||
}
|
||||
w.lineaRollUpUrl = lineaRollUpUrl
|
||||
w.lineaRollUpContract = lineaRollUpContract
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ service NodePrivilegedService {
|
|||
|
||||
// ChainGovernorReleasePendingVAA release a VAA from the chain governor pending list, publishing it immediately.
|
||||
rpc ChainGovernorReleasePendingVAA (ChainGovernorReleasePendingVAARequest) returns (ChainGovernorReleasePendingVAAResponse);
|
||||
|
||||
|
||||
// ChainGovernorResetReleaseTimer resets the release timer for a chain governor pending VAA to the configured maximum.
|
||||
rpc ChainGovernorResetReleaseTimer (ChainGovernorResetReleaseTimerRequest) returns (ChainGovernorResetReleaseTimerResponse);
|
||||
|
||||
|
@ -51,10 +51,10 @@ service NodePrivilegedService {
|
|||
rpc SignExistingVAA (SignExistingVAARequest) returns (SignExistingVAAResponse);
|
||||
|
||||
// DumpRPCs returns the RPCs being used by the guardian
|
||||
rpc DumpRPCs (DumpRPCsRequest) returns (DumpRPCsResponse);
|
||||
rpc DumpRPCs (DumpRPCsRequest) returns (DumpRPCsResponse);
|
||||
|
||||
// GetMissingVAAs returns the VAAs from a cloud function that need to be reobserved.
|
||||
rpc GetAndObserveMissingVAAs (GetAndObserveMissingVAAsRequest) returns (GetAndObserveMissingVAAsResponse);
|
||||
rpc GetAndObserveMissingVAAs (GetAndObserveMissingVAAsRequest) returns (GetAndObserveMissingVAAsResponse);
|
||||
}
|
||||
|
||||
message InjectGovernanceVAARequest {
|
||||
|
@ -85,7 +85,7 @@ message GovernanceMessage {
|
|||
GuardianSetUpdate guardian_set = 10;
|
||||
ContractUpgrade contract_upgrade = 11;
|
||||
|
||||
// Token bridge, NFT module, and Wormhole Relayer module (for the first two)
|
||||
// Token bridge, NFT module, and Wormhole Relayer module (for the first two)
|
||||
|
||||
BridgeRegisterChain bridge_register_chain = 12;
|
||||
BridgeUpgradeContract bridge_contract_upgrade = 13;
|
||||
|
@ -117,6 +117,10 @@ message GovernanceMessage {
|
|||
IbcUpdateChannelChain ibc_update_channel_chain = 21;
|
||||
// Wormhole Relayer module
|
||||
WormholeRelayerSetDefaultDeliveryProvider wormhole_relayer_set_default_delivery_provider = 22;
|
||||
|
||||
// Generic governance
|
||||
EvmCall evm_call = 28;
|
||||
SolanaCall solana_call = 29;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -191,7 +195,7 @@ message AccountantModifyBalance {
|
|||
|
||||
// ContractUpgrade represents a Wormhole contract update to be submitted to and signed by the node.
|
||||
message ContractUpgrade {
|
||||
// ID of the chain where the Wormhole contract should be updated (uint8).
|
||||
// ID of the chain where the Wormhole contract should be updated (uint16).
|
||||
uint32 chain_id = 1;
|
||||
|
||||
// Hex-encoded address (without leading 0x) address of the new program/contract.
|
||||
|
@ -417,3 +421,30 @@ message GetAndObserveMissingVAAsRequest {
|
|||
message GetAndObserveMissingVAAsResponse {
|
||||
string response =1;
|
||||
}
|
||||
|
||||
// EvmCall represents a generic EVM call that can be executed by the generalized governance contract.
|
||||
message EvmCall {
|
||||
// ID of the chain where the action should be executed (uint16).
|
||||
uint32 chain_id = 1;
|
||||
|
||||
// Address of the governance contract (eth address starting with 0x)
|
||||
string governance_contract = 2;
|
||||
|
||||
// Address of the governed contract (eth address starting with 0x)
|
||||
string target_contract = 3;
|
||||
|
||||
// ABI-encoded calldata to be passed on to the governed contract (hex encoded)
|
||||
string abi_encoded_call = 4;
|
||||
}
|
||||
|
||||
// SolanaCall represents a generic Solana call that can be executed by the generalized governance contract.
|
||||
message SolanaCall {
|
||||
// ID of the chain where the action should be executed (uint16).
|
||||
uint32 chain_id = 1;
|
||||
|
||||
// Address of the governance contract (solana address)
|
||||
string governance_contract = 2;
|
||||
|
||||
// Encoded instruction data to be passed on to the governed contract (hex encoded)
|
||||
string encoded_instruction = 3;
|
||||
}
|
||||
|
|
|
@ -4,8 +4,9 @@ import (
|
|||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
ethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
|
@ -55,6 +56,13 @@ var WormholeRelayerModule = [32]byte{
|
|||
}
|
||||
var WormholeRelayerModuleStr = string(WormholeRelayerModule[:])
|
||||
|
||||
var GeneralPurposeGovernanceModule = [32]byte{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x65, 0x6E, 0x65, 0x72, 0x61, 0x6C,
|
||||
0x50, 0x75, 0x72, 0x70, 0x6F, 0x73, 0x65, 0x47, 0x6F, 0x76, 0x65, 0x72, 0x6E, 0x61, 0x6E,
|
||||
0x63, 0x65,
|
||||
}
|
||||
var GeneralPurposeGovernanceModuleStr = string(GeneralPurposeGovernanceModule[:])
|
||||
|
||||
type GovernanceAction uint8
|
||||
|
||||
var (
|
||||
|
@ -99,6 +107,10 @@ var (
|
|||
|
||||
// Wormhole relayer governance actions
|
||||
WormholeRelayerSetDefaultDeliveryProvider GovernanceAction = 3
|
||||
|
||||
// General purpose governance
|
||||
GeneralPurposeGovernanceEvmAction GovernanceAction = 1
|
||||
GeneralPurposeGovernanceSolanaAction GovernanceAction = 2
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -110,7 +122,7 @@ type (
|
|||
|
||||
// BodyGuardianSetUpdate is a governance message to set a new guardian set
|
||||
BodyGuardianSetUpdate struct {
|
||||
Keys []common.Address
|
||||
Keys []ethcommon.Address
|
||||
NewIndex uint32
|
||||
}
|
||||
|
||||
|
@ -216,6 +228,24 @@ type (
|
|||
ChainID ChainID
|
||||
NewDefaultDeliveryProviderAddress Address
|
||||
}
|
||||
|
||||
// BodyGeneralPurposeGovernanceEvm is a general purpose governance message for EVM chains
|
||||
BodyGeneralPurposeGovernanceEvm struct {
|
||||
ChainID ChainID
|
||||
GovernanceContract ethcommon.Address
|
||||
TargetContract ethcommon.Address
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
// BodyGeneralPurposeGovernanceSolana is a general purpose governance message for Solana chains
|
||||
BodyGeneralPurposeGovernanceSolana struct {
|
||||
ChainID ChainID
|
||||
GovernanceContract Address
|
||||
// NOTE: unlike in EVM, no target contract in the schema here, the
|
||||
// instruction encodes the target contract address (unlike in EVM, where
|
||||
// an abi encoded calldata doesn't include the target contract address)
|
||||
Instruction []byte
|
||||
}
|
||||
)
|
||||
|
||||
func (b BodyContractUpgrade) Serialize() []byte {
|
||||
|
@ -402,6 +432,31 @@ func (r BodyWormholeRelayerSetDefaultDeliveryProvider) Serialize() []byte {
|
|||
return serializeBridgeGovernanceVaa(WormholeRelayerModuleStr, WormholeRelayerSetDefaultDeliveryProvider, r.ChainID, payload.Bytes())
|
||||
}
|
||||
|
||||
func (r BodyGeneralPurposeGovernanceEvm) Serialize() ([]byte, error) {
|
||||
payload := &bytes.Buffer{}
|
||||
payload.Write(r.GovernanceContract[:])
|
||||
payload.Write(r.TargetContract[:])
|
||||
|
||||
// write payload len as uint16
|
||||
if len(r.Payload) > math.MaxUint16 {
|
||||
return nil, fmt.Errorf("payload too long; expected at most %d bytes", math.MaxUint16)
|
||||
}
|
||||
MustWrite(payload, binary.BigEndian, uint16(len(r.Payload)))
|
||||
payload.Write(r.Payload)
|
||||
return serializeBridgeGovernanceVaa(GeneralPurposeGovernanceModuleStr, GeneralPurposeGovernanceEvmAction, r.ChainID, payload.Bytes()), nil
|
||||
}
|
||||
|
||||
func (r BodyGeneralPurposeGovernanceSolana) Serialize() []byte {
|
||||
payload := &bytes.Buffer{}
|
||||
payload.Write(r.GovernanceContract[:])
|
||||
// NOTE: unlike in EVM, we don't write the payload length here, because we're using
|
||||
// a custom instruction encoding (there is no standard encoding like evm ABI
|
||||
// encoding), generated by an external tool. That tool length-prefixes all
|
||||
// the relevant dynamic fields.
|
||||
payload.Write(r.Instruction)
|
||||
return serializeBridgeGovernanceVaa(GeneralPurposeGovernanceModuleStr, GeneralPurposeGovernanceSolanaAction, r.ChainID, payload.Bytes())
|
||||
}
|
||||
|
||||
func EmptyPayloadVaa(module string, actionId GovernanceAction, chainId ChainID) []byte {
|
||||
return serializeBridgeGovernanceVaa(module, actionId, chainId, []byte{})
|
||||
}
|
||||
|
|
|
@ -669,11 +669,13 @@ func New(
|
|||
|
||||
// Wrap the standard cosmos-sdk antehandlers with additional antehandlers:
|
||||
// - wormhole allowlist antehandler
|
||||
// - wormhole ibc error antehandler
|
||||
// - default ibc antehandler
|
||||
func WrapAnteHandler(originalHandler sdk.AnteHandler, wormKeeper wormholemodulekeeper.Keeper, ibcKeeper *ibckeeper.Keeper) sdk.AnteHandler {
|
||||
whHandler := wormholemoduleante.NewWormholeAllowlistDecorator(wormKeeper)
|
||||
whIbcHandler := wormholemoduleante.NewWormholeIbcErrorDecorator()
|
||||
ibcHandler := ibcante.NewAnteDecorator(ibcKeeper)
|
||||
newHandlers := sdk.ChainAnteDecorators(whHandler, ibcHandler)
|
||||
newHandlers := sdk.ChainAnteDecorators(whHandler, whIbcHandler, ibcHandler)
|
||||
return func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
|
||||
newCtx, err := originalHandler(ctx, tx, simulate)
|
||||
if err != nil {
|
||||
|
|
|
@ -38,7 +38,7 @@ require (
|
|||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bgentry/speakeasy v0.1.0 // indirect
|
||||
github.com/btcsuite/btcd v0.22.1 // indirect
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
|
||||
github.com/cespare/xxhash v1.1.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/coinbase/rosetta-sdk-go v0.7.0 // indirect
|
||||
|
@ -49,8 +49,7 @@ require (
|
|||
github.com/cosmos/gogoproto v1.4.3 // indirect
|
||||
github.com/cosmos/gorocksdb v1.2.0 // indirect
|
||||
github.com/cosmos/iavl v0.19.4 // indirect
|
||||
github.com/cosmos/ledger-cosmos-go v0.11.1 // indirect
|
||||
github.com/cosmos/ledger-go v0.9.2 // indirect
|
||||
github.com/cosmos/ledger-cosmos-go v0.12.4 // indirect
|
||||
github.com/creachadair/taskgroup v0.3.2 // indirect
|
||||
github.com/danieljoos/wincred v1.1.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
|
@ -122,7 +121,8 @@ require (
|
|||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
|
||||
github.com/tendermint/btcd v0.1.1 // indirect
|
||||
github.com/tendermint/go-amino v0.16.0 // indirect
|
||||
github.com/zondax/hid v0.9.0 // indirect
|
||||
github.com/zondax/hid v0.9.2 // indirect
|
||||
github.com/zondax/ledger-go v0.14.3 // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
|
||||
golang.org/x/net v0.2.0 // indirect
|
||||
|
|
|
@ -332,6 +332,7 @@ github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c=
|
|||
github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
||||
|
@ -579,6 +580,8 @@ github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76 h1:DdzS1m6o/pCqeZ
|
|||
github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76/go.mod h1:0mkLWIoZuQ7uBoospo5Q9zIpqq6rYCPJDSUdeCJvPM8=
|
||||
github.com/cosmos/ledger-cosmos-go v0.11.1 h1:9JIYsGnXP613pb2vPjFeMMjBI5lEDsEaF6oYorTy6J4=
|
||||
github.com/cosmos/ledger-cosmos-go v0.11.1/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY=
|
||||
github.com/cosmos/ledger-cosmos-go v0.12.4 h1:drvWt+GJP7Aiw550yeb3ON/zsrgW0jgh5saFCr7pDnw=
|
||||
github.com/cosmos/ledger-cosmos-go v0.12.4/go.mod h1:fjfVWRf++Xkygt9wzCsjEBdjcf7wiiY35fv3ctT+k4M=
|
||||
github.com/cosmos/ledger-go v0.9.2 h1:Nnao/dLwaVTk1Q5U9THldpUMMXU94BOTWPddSmVB6pI=
|
||||
github.com/cosmos/ledger-go v0.9.2/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
|
@ -2118,6 +2121,10 @@ github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPS
|
|||
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
|
||||
github.com/zondax/hid v0.9.0 h1:eiT3P6vNxAEVxXMw66eZUAAnU2zD33JBkfG/EnfAKl8=
|
||||
github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM=
|
||||
github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U=
|
||||
github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM=
|
||||
github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw=
|
||||
github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI=
|
||||
gitlab.com/bosi/decorder v0.2.1/go.mod h1:6C/nhLSbF6qZbYD8bRmISBwc6vcWdNsiIBkRvjJFrH0=
|
||||
gitlab.com/bosi/decorder v0.2.3/go.mod h1:9K1RB5+VPNQYtXtTDAzd2OEftsZb1oV0IrJrzChSdGE=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
|
|
|
@ -1,11 +1,47 @@
|
|||
package ante
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
ibcchanneltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
|
||||
"github.com/wormhole-foundation/wormchain/x/wormhole/keeper"
|
||||
)
|
||||
|
||||
type WormholeIbcErrorDecorator struct{}
|
||||
|
||||
func NewWormholeIbcErrorDecorator() WormholeIbcErrorDecorator {
|
||||
return WormholeIbcErrorDecorator{}
|
||||
}
|
||||
|
||||
func (wh WormholeIbcErrorDecorator) AnteHandle(request sdk.Request, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Request, err error) {
|
||||
// ignore all blocks except 3_151_174
|
||||
if request.BlockHeight() != 3_151_174 {
|
||||
return next(request, tx, simulate)
|
||||
}
|
||||
|
||||
// if we're on block 3_151_174, we need to reject the IBC Channel Open Init transaction
|
||||
// the transaction should have a single message
|
||||
msgs := tx.GetMsgs()
|
||||
if len(msgs) != 1 {
|
||||
return next(request, tx, simulate)
|
||||
}
|
||||
|
||||
// ensure the single message in the tx is an IBC Channel Open Init message
|
||||
switch msgs[0].(type) {
|
||||
case *ibcchanneltypes.MsgChannelOpenInit:
|
||||
// we've verified it's the IBC Channel Open Init message.
|
||||
// fail with the proper error message
|
||||
// failing early uses 55613 gas in total. We need to use 59774 gas to get the same apphash.
|
||||
// 59774 - 55613 = 4161
|
||||
request.GasMeter().ConsumeGas(4161, "consuming extra gas so the transaction uses 59,774 gas")
|
||||
return request, errors.New("failed to execute message; message index: 0: route not found to module: wasm: route not found")
|
||||
default:
|
||||
return next(request, tx, simulate)
|
||||
}
|
||||
}
|
||||
|
||||
// Reject all messages if we're expecting a software update.
|
||||
type WormholeAllowlistDecorator struct {
|
||||
k keeper.Keeper
|
||||
|
|
Loading…
Reference in New Issue