package guardiand import ( "encoding/hex" "fmt" "log" "strconv" "strings" "github.com/btcsuite/btcutil/bech32" "github.com/ethereum/go-ethereum/common" "github.com/mr-tron/base58" "github.com/spf13/pflag" "github.com/tendermint/tendermint/libs/rand" "github.com/wormhole-foundation/wormhole/sdk/vaa" "github.com/ethereum/go-ethereum/crypto" "github.com/spf13/cobra" "google.golang.org/protobuf/encoding/prototext" "github.com/certusone/wormhole/node/pkg/devnet" nodev1 "github.com/certusone/wormhole/node/pkg/proto/node/v1" ) var setUpdateNumGuardians *int var templateGuardianIndex *int var chainID *string var address *string var module *string var circleIntegrationChainID *string var circleIntegrationFinality *string var circleIntegrationForeignEmitterChainID *string var circleIntegrationForeignEmitterAddress *string var circleIntegrationCircleDomain *string var circleIntegrationNewImplementationAddress *string var wormchainStoreCodeWasmHash *string var wormchainInstantiateContractCodeId *string var wormchainInstantiateContractInstantiationMsg *string var wormchainInstantiateContractLabel *string var wormchainMigrateContractCodeId *string var wormchainMigrateContractContractAddress *string var wormchainMigrateContractInstantiationMsg *string var ibcReceiverUpdateChannelChainTargetChainId *string var ibcReceiverUpdateChannelChainChannelId *string var ibcReceiverUpdateChannelChainChainId *string func init() { governanceFlagSet := pflag.NewFlagSet("governance", pflag.ExitOnError) chainID = governanceFlagSet.String("chain-id", "", "Chain ID") address = governanceFlagSet.String("new-address", "", "New address (hex, base58 or bech32)") moduleFlagSet := pflag.NewFlagSet("module", pflag.ExitOnError) module = moduleFlagSet.String("module", "", "Module name") templateGuardianIndex = TemplateCmd.PersistentFlags().Int("idx", 3, "Default current guardian set index") setUpdateNumGuardians = AdminClientGuardianSetTemplateCmd.Flags().Int("num", 1, "Number of devnet guardians in example file") TemplateCmd.AddCommand(AdminClientGuardianSetTemplateCmd) AdminClientContractUpgradeTemplateCmd.Flags().AddFlagSet(governanceFlagSet) TemplateCmd.AddCommand(AdminClientContractUpgradeTemplateCmd) AdminClientTokenBridgeRegisterChainCmd.Flags().AddFlagSet(governanceFlagSet) AdminClientTokenBridgeRegisterChainCmd.Flags().AddFlagSet(moduleFlagSet) TemplateCmd.AddCommand(AdminClientTokenBridgeRegisterChainCmd) AdminClientTokenBridgeUpgradeContractCmd.Flags().AddFlagSet(governanceFlagSet) AdminClientTokenBridgeUpgradeContractCmd.Flags().AddFlagSet(moduleFlagSet) TemplateCmd.AddCommand(AdminClientTokenBridgeUpgradeContractCmd) circleIntegrationChainIDFlagSet := pflag.NewFlagSet("circle-integ", pflag.ExitOnError) circleIntegrationChainID = circleIntegrationChainIDFlagSet.String("chain-id", "", "Target chain ID") circleIntegrationFinalityFlagSet := pflag.NewFlagSet("finality", pflag.ExitOnError) circleIntegrationFinality = circleIntegrationFinalityFlagSet.String("finality", "", "Desired wormhole finality") AdminClientCircleIntegrationUpdateWormholeFinalityCmd.Flags().AddFlagSet(circleIntegrationChainIDFlagSet) AdminClientCircleIntegrationUpdateWormholeFinalityCmd.Flags().AddFlagSet(circleIntegrationFinalityFlagSet) TemplateCmd.AddCommand(AdminClientCircleIntegrationUpdateWormholeFinalityCmd) circleIntegrationRegisterEmitterFlagSet := pflag.NewFlagSet("register", pflag.ExitOnError) circleIntegrationForeignEmitterChainID = circleIntegrationRegisterEmitterFlagSet.String("foreign-emitter-chain-id", "", "Foreign emitter chain ID") circleIntegrationForeignEmitterAddress = circleIntegrationRegisterEmitterFlagSet.String("foreign-emitter-address", "", "Foreign emitter address (hex, base58 or bech32)") circleIntegrationCircleDomain = circleIntegrationRegisterEmitterFlagSet.String("circle-domain", "", "Circle domain") AdminClientCircleIntegrationRegisterEmitterAndDomainCmd.Flags().AddFlagSet(circleIntegrationChainIDFlagSet) AdminClientCircleIntegrationRegisterEmitterAndDomainCmd.Flags().AddFlagSet(circleIntegrationRegisterEmitterFlagSet) TemplateCmd.AddCommand(AdminClientCircleIntegrationRegisterEmitterAndDomainCmd) circleIntegrationUpgradeContractImplementationFlagSet := pflag.NewFlagSet("upgrade", pflag.ExitOnError) circleIntegrationNewImplementationAddress = circleIntegrationUpgradeContractImplementationFlagSet.String("new-implementation-address", "", "New implementation address (hex, base58 or bech32)") AdminClientCircleIntegrationUpgradeContractImplementationCmd.Flags().AddFlagSet(circleIntegrationChainIDFlagSet) AdminClientCircleIntegrationUpgradeContractImplementationCmd.Flags().AddFlagSet(circleIntegrationUpgradeContractImplementationFlagSet) TemplateCmd.AddCommand(AdminClientCircleIntegrationUpgradeContractImplementationCmd) wormchainStoreCodeFlagSet := pflag.NewFlagSet("wormchain-store-code", pflag.ExitOnError) wormchainStoreCodeWasmHash = wormchainStoreCodeFlagSet.String("wasm-hash", "", "WASM Hash of the stored code") AdminClientWormchainStoreCodeCmd.Flags().AddFlagSet(wormchainStoreCodeFlagSet) TemplateCmd.AddCommand(AdminClientWormchainStoreCodeCmd) wormchainInstantiateContractFlagSet := pflag.NewFlagSet("wormchain-instantiate-contract", pflag.ExitOnError) wormchainInstantiateContractCodeId = wormchainInstantiateContractFlagSet.String("code-id", "", "code ID of the stored code") wormchainInstantiateContractLabel = wormchainInstantiateContractFlagSet.String("label", "", "label") wormchainInstantiateContractInstantiationMsg = wormchainInstantiateContractFlagSet.String("instantiation-msg", "", "instantiate message") AdminClientWormchainInstantiateContractCmd.Flags().AddFlagSet(wormchainInstantiateContractFlagSet) TemplateCmd.AddCommand(AdminClientWormchainInstantiateContractCmd) wormchainMigrateContractFlagSet := pflag.NewFlagSet("wormchain-migrate-contract", pflag.ExitOnError) wormchainMigrateContractCodeId = wormchainMigrateContractFlagSet.String("code-id", "", "code ID of the stored code") wormchainMigrateContractContractAddress = wormchainMigrateContractFlagSet.String("contract-address", "", "contract address") wormchainMigrateContractInstantiationMsg = wormchainMigrateContractFlagSet.String("instantiation-msg", "", "instantiate message") AdminClientWormchainMigrateContractCmd.Flags().AddFlagSet(wormchainMigrateContractFlagSet) TemplateCmd.AddCommand(AdminClientWormchainMigrateContractCmd) // flags for the ibc-receiver-update-channel-chain command ibcReceiverUpdateChannelChainFlagSet := pflag.NewFlagSet("ibc-mapping", pflag.ExitOnError) ibcReceiverUpdateChannelChainTargetChainId = ibcReceiverUpdateChannelChainFlagSet.String("target-chain-id", "", "Target Chain ID for the governance VAA") ibcReceiverUpdateChannelChainChannelId = ibcReceiverUpdateChannelChainFlagSet.String("channel-id", "", "IBC Channel ID on Wormchain") ibcReceiverUpdateChannelChainChainId = ibcReceiverUpdateChannelChainFlagSet.String("chain-id", "", "IBC Chain ID that the channel ID corresponds to") AdminClientIbcReceiverUpdateChannelChainCmd.Flags().AddFlagSet(ibcReceiverUpdateChannelChainFlagSet) TemplateCmd.AddCommand(AdminClientIbcReceiverUpdateChannelChainCmd) } var TemplateCmd = &cobra.Command{ Use: "template", Short: "Guardian governance VAA template commands ", } var AdminClientGuardianSetTemplateCmd = &cobra.Command{ Use: "guardian-set-update", Short: "Generate an empty guardian set template", Run: runGuardianSetTemplate, } var AdminClientContractUpgradeTemplateCmd = &cobra.Command{ Use: "contract-upgrade", Short: "Generate an empty contract upgrade template", Run: runContractUpgradeTemplate, } var AdminClientTokenBridgeRegisterChainCmd = &cobra.Command{ Use: "token-bridge-register-chain", Short: "Generate an empty token bridge chain registration template at specified path", Run: runTokenBridgeRegisterChainTemplate, } var AdminClientTokenBridgeUpgradeContractCmd = &cobra.Command{ Use: "token-bridge-upgrade-contract", Short: "Generate an empty token bridge contract upgrade template at specified path", Run: runTokenBridgeUpgradeContractTemplate, } var AdminClientCircleIntegrationUpdateWormholeFinalityCmd = &cobra.Command{ Use: "circle-integration-update-wormhole-finality", Short: "Generate an empty circle integration update wormhole finality template at specified path", Run: runCircleIntegrationUpdateWormholeFinalityTemplate, } var AdminClientCircleIntegrationRegisterEmitterAndDomainCmd = &cobra.Command{ Use: "circle-integration-register-emitter-and-domain", Short: "Generate an empty circle integration register emitter and domain template at specified path", Run: runCircleIntegrationRegisterEmitterAndDomainTemplate, } var AdminClientCircleIntegrationUpgradeContractImplementationCmd = &cobra.Command{ Use: "circle-integration-upgrade-contract-implementation", Short: "Generate an empty circle integration upgrade contract implementation template at specified path", Run: runCircleIntegrationUpgradeContractImplementationTemplate, } var AdminClientWormchainStoreCodeCmd = &cobra.Command{ Use: "wormchain-store-code", Short: "Generate an empty wormchain store code template at specified path", Run: runWormchainStoreCodeTemplate, } var AdminClientWormchainInstantiateContractCmd = &cobra.Command{ Use: "wormchain-instantiate-contract", Short: "Generate an empty wormchain instantiate contract template at specified path", Run: runWormchainInstantiateContractTemplate, } var AdminClientWormchainMigrateContractCmd = &cobra.Command{ Use: "wormchain-migrate-contract", Short: "Generate an empty wormchain migrate contract template at specified path", Run: runWormchainMigrateContractTemplate, } var AdminClientIbcReceiverUpdateChannelChainCmd = &cobra.Command{ Use: "ibc-receiver-update-channel-chain", Short: "Generate an empty ibc receiver channelId to chainId mapping update template at specified path", Run: runIbcReceiverUpdateChannelChainTemplate, } 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) for i := 0; i < *setUpdateNumGuardians; i++ { k := devnet.InsecureDeterministicEcdsaKeyByIndex(crypto.S256(), uint64(i)) guardians[i] = &nodev1.GuardianSetUpdate_Guardian{ Pubkey: crypto.PubkeyToAddress(k.PublicKey).Hex(), Name: fmt.Sprintf("Example validator %d", i), } } m := &nodev1.InjectGovernanceVAARequest{ CurrentSetIndex: uint32(*templateGuardianIndex), Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), Nonce: rand.Uint32(), Payload: &nodev1.GovernanceMessage_GuardianSet{ GuardianSet: &nodev1.GuardianSetUpdate{Guardians: guardians}, }, }, }, } b, err := prototext.MarshalOptions{Multiline: true}.Marshal(m) if err != nil { panic(err) } fmt.Print(string(b)) } func runContractUpgradeTemplate(cmd *cobra.Command, args []string) { address, err := parseAddress(*address) if err != nil { log.Fatal(err) } chainID, err := parseChainID(*chainID) if err != nil { log.Fatal(err) } m := &nodev1.InjectGovernanceVAARequest{ CurrentSetIndex: uint32(*templateGuardianIndex), Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), Nonce: rand.Uint32(), Payload: &nodev1.GovernanceMessage_ContractUpgrade{ ContractUpgrade: &nodev1.ContractUpgrade{ ChainId: uint32(chainID), NewContract: address, }, }, }, }, } b, err := prototext.MarshalOptions{Multiline: true}.Marshal(m) if err != nil { panic(err) } fmt.Print(string(b)) } func runTokenBridgeRegisterChainTemplate(cmd *cobra.Command, args []string) { address, err := parseAddress(*address) if err != nil { log.Fatal(err) } chainID, err := parseChainID(*chainID) if err != nil { log.Fatal(err) } m := &nodev1.InjectGovernanceVAARequest{ CurrentSetIndex: uint32(*templateGuardianIndex), Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), Nonce: rand.Uint32(), Payload: &nodev1.GovernanceMessage_BridgeRegisterChain{ BridgeRegisterChain: &nodev1.BridgeRegisterChain{ Module: *module, ChainId: uint32(chainID), EmitterAddress: address, }, }, }, }, } b, err := prototext.MarshalOptions{Multiline: true}.Marshal(m) if err != nil { panic(err) } fmt.Print(string(b)) } func runTokenBridgeUpgradeContractTemplate(cmd *cobra.Command, args []string) { address, err := parseAddress(*address) if err != nil { log.Fatal(err) } chainID, err := parseChainID(*chainID) if err != nil { log.Fatal(err) } m := &nodev1.InjectGovernanceVAARequest{ CurrentSetIndex: uint32(*templateGuardianIndex), Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), Nonce: rand.Uint32(), Payload: &nodev1.GovernanceMessage_BridgeContractUpgrade{ BridgeContractUpgrade: &nodev1.BridgeUpgradeContract{ Module: *module, TargetChainId: uint32(chainID), NewContract: address, }, }, }, }, } b, err := prototext.MarshalOptions{Multiline: true}.Marshal(m) if err != nil { panic(err) } fmt.Print(string(b)) } func runCircleIntegrationUpdateWormholeFinalityTemplate(cmd *cobra.Command, args []string) { if *circleIntegrationChainID == "" { log.Fatal("--chain-id must be specified.") } chainID, err := parseChainID(*circleIntegrationChainID) if err != nil { log.Fatal("failed to parse chain id:", err) } if *circleIntegrationFinality == "" { log.Fatal("--finality must be specified.") } finality, err := strconv.ParseUint(*circleIntegrationFinality, 10, 8) if err != nil { log.Fatal("failed to parse finality as uint8: ", err) } m := &nodev1.InjectGovernanceVAARequest{ CurrentSetIndex: uint32(*templateGuardianIndex), Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), Nonce: rand.Uint32(), Payload: &nodev1.GovernanceMessage_CircleIntegrationUpdateWormholeFinality{ CircleIntegrationUpdateWormholeFinality: &nodev1.CircleIntegrationUpdateWormholeFinality{ TargetChainId: uint32(chainID), Finality: uint32(finality), }, }, }, }, } b, err := prototext.MarshalOptions{Multiline: true}.Marshal(m) if err != nil { panic(err) } fmt.Print(string(b)) } func runCircleIntegrationRegisterEmitterAndDomainTemplate(cmd *cobra.Command, args []string) { if *circleIntegrationChainID == "" { log.Fatal("--chain-id must be specified.") } chainID, err := parseChainID(*circleIntegrationChainID) if err != nil { log.Fatal("failed to parse chain id:", err) } if *circleIntegrationForeignEmitterChainID == "" { log.Fatal("--foreign-emitter-chain-id must be specified.") } foreignEmitterChainId, err := parseChainID(*circleIntegrationForeignEmitterChainID) if err != nil { log.Fatal("failed to parse foreign emitter chain id as uint8:", err) } if *circleIntegrationForeignEmitterAddress == "" { log.Fatal("--foreign-emitter-address must be specified.") } foreignEmitterAddress, err := parseAddress(*circleIntegrationForeignEmitterAddress) if err != nil { log.Fatal("failed to parse foreign emitter address: ", err) } if *circleIntegrationCircleDomain == "" { log.Fatal("--circle-domain must be specified.") } circleDomain, err := strconv.ParseUint(*circleIntegrationCircleDomain, 10, 32) if err != nil { log.Fatal("failed to parse circle domain as uint32:", err) } m := &nodev1.InjectGovernanceVAARequest{ CurrentSetIndex: uint32(*templateGuardianIndex), Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), Nonce: rand.Uint32(), Payload: &nodev1.GovernanceMessage_CircleIntegrationRegisterEmitterAndDomain{ CircleIntegrationRegisterEmitterAndDomain: &nodev1.CircleIntegrationRegisterEmitterAndDomain{ TargetChainId: uint32(chainID), ForeignEmitterChainId: uint32(foreignEmitterChainId), ForeignEmitterAddress: foreignEmitterAddress, CircleDomain: uint32(circleDomain), }, }, }, }, } b, err := prototext.MarshalOptions{Multiline: true}.Marshal(m) if err != nil { panic(err) } fmt.Print(string(b)) } func runCircleIntegrationUpgradeContractImplementationTemplate(cmd *cobra.Command, args []string) { if *circleIntegrationChainID == "" { log.Fatal("--chain-id must be specified.") } chainID, err := parseChainID(*circleIntegrationChainID) if err != nil { log.Fatal("failed to parse chain id:", err) } if *circleIntegrationNewImplementationAddress == "" { log.Fatal("--new-implementation-address must be specified.") } newImplementationAddress, err := parseAddress(*circleIntegrationNewImplementationAddress) if err != nil { log.Fatal("failed to parse new implementation address: ", err) } m := &nodev1.InjectGovernanceVAARequest{ CurrentSetIndex: uint32(*templateGuardianIndex), Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), Nonce: rand.Uint32(), Payload: &nodev1.GovernanceMessage_CircleIntegrationUpgradeContractImplementation{ CircleIntegrationUpgradeContractImplementation: &nodev1.CircleIntegrationUpgradeContractImplementation{ TargetChainId: uint32(chainID), NewImplementationAddress: newImplementationAddress, }, }, }, }, } b, err := prototext.MarshalOptions{Multiline: true}.Marshal(m) if err != nil { panic(err) } fmt.Print(string(b)) } func runWormchainStoreCodeTemplate(cmd *cobra.Command, args []string) { if *wormchainStoreCodeWasmHash == "" { log.Fatal("--wasm-hash must be specified.") } // Validate the string is valid hex. buf, err := hex.DecodeString(*wormchainStoreCodeWasmHash) if err != nil { log.Fatal("invalid wasm-hash (expected hex): %w", err) } // Validate the string is the correct length. if len(buf) != 32 { log.Fatalf("wasm-hash (expected 32 bytes but received %d bytes)", len(buf)) } m := &nodev1.InjectGovernanceVAARequest{ CurrentSetIndex: uint32(*templateGuardianIndex), Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), Nonce: rand.Uint32(), Payload: &nodev1.GovernanceMessage_WormchainStoreCode{ WormchainStoreCode: &nodev1.WormchainStoreCode{ WasmHash: string(*wormchainStoreCodeWasmHash), }, }, }, }, } b, err := prototext.MarshalOptions{Multiline: true}.Marshal(m) if err != nil { panic(err) } fmt.Print(string(b)) } func runWormchainInstantiateContractTemplate(cmd *cobra.Command, args []string) { if *wormchainInstantiateContractCodeId == "" { log.Fatal("--code-id must be specified.") } codeId, err := strconv.ParseUint(*wormchainInstantiateContractCodeId, 10, 64) if err != nil { log.Fatal("failed to parse code-id as uint64: ", err) } if *wormchainInstantiateContractLabel == "" { log.Fatal("--label must be specified.") } if *wormchainInstantiateContractInstantiationMsg == "" { log.Fatal("--instantiate-msg must be specified.") } m := &nodev1.InjectGovernanceVAARequest{ CurrentSetIndex: uint32(*templateGuardianIndex), Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), Nonce: rand.Uint32(), Payload: &nodev1.GovernanceMessage_WormchainInstantiateContract{ WormchainInstantiateContract: &nodev1.WormchainInstantiateContract{ CodeId: codeId, Label: *wormchainInstantiateContractLabel, InstantiationMsg: *wormchainInstantiateContractInstantiationMsg, }, }, }, }, } b, err := prototext.MarshalOptions{Multiline: true}.Marshal(m) if err != nil { panic(err) } fmt.Print(string(b)) } func runWormchainMigrateContractTemplate(cmd *cobra.Command, args []string) { if *wormchainMigrateContractCodeId == "" { log.Fatal("--code-id must be specified.") } codeId, err := strconv.ParseUint(*wormchainMigrateContractCodeId, 10, 64) if err != nil { log.Fatal("failed to parse code-id as uint64: ", err) } if *wormchainMigrateContractContractAddress == "" { log.Fatal("--contract-address must be specified.") } if *wormchainMigrateContractInstantiationMsg == "" { log.Fatal("--instantiate-msg must be specified.") } m := &nodev1.InjectGovernanceVAARequest{ CurrentSetIndex: uint32(*templateGuardianIndex), Messages: []*nodev1.GovernanceMessage{ { Sequence: rand.Uint64(), Nonce: rand.Uint32(), Payload: &nodev1.GovernanceMessage_WormchainMigrateContract{ WormchainMigrateContract: &nodev1.WormchainMigrateContract{ CodeId: codeId, Contract: *wormchainMigrateContractContractAddress, InstantiationMsg: *wormchainMigrateContractInstantiationMsg, }, }, }, }, } b, err := prototext.MarshalOptions{Multiline: true}.Marshal(m) if err != nil { panic(err) } fmt.Print(string(b)) } func runIbcReceiverUpdateChannelChainTemplate(cmd *cobra.Command, args []string) { if *ibcReceiverUpdateChannelChainTargetChainId == "" { log.Fatal("--target-chain-id must be specified") } targetChainId, err := parseChainID(*ibcReceiverUpdateChannelChainTargetChainId) if err != nil { log.Fatal("failed to parse chain id: ", err) } if *ibcReceiverUpdateChannelChainChannelId == "" { log.Fatal("--channel-id must be specified") } if len(*ibcReceiverUpdateChannelChainChannelId) > 64 { log.Fatal("invalid channel id length, must be <= 64") } if *ibcReceiverUpdateChannelChainChainId == "" { log.Fatal("--chain-id must be specified") } chainId, err := parseChainID(*ibcReceiverUpdateChannelChainChainId) 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_IbcReceiverUpdateChannelChain{ IbcReceiverUpdateChannelChain: &nodev1.IbcReceiverUpdateChannelChain{ TargetChainId: uint32(targetChainId), ChannelId: *ibcReceiverUpdateChannelChainChannelId, ChainId: uint32(chainId), }, }, }, }, } 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) { // try base58 b, err := base58.Decode(s) if err == nil { return leftPadAddress(b) } // try bech32 _, b, err = bech32.Decode(s) if err == nil { return leftPadAddress(b) } // try hex if len(s) > 2 && strings.ToLower(s[:2]) == "0x" { s = s[2:] } a, err := hex.DecodeString(s) if err != nil { return "", fmt.Errorf("invalid hex address: %v", err) } return leftPadAddress(a) } func leftPadAddress(a []byte) (string, error) { if len(a) > 32 { return "", fmt.Errorf("address longer than 32 bytes") } return hex.EncodeToString(common.LeftPadBytes(a, 32)), nil } // parseChainID parses a human-readable chain name or a chain ID. func parseChainID(name string) (vaa.ChainID, error) { s, err := vaa.ChainIDFromString(name) if err == nil { return s, nil } // parse as uint32 i, err := strconv.ParseUint(name, 10, 32) if err != nil { return 0, fmt.Errorf("failed to parse as name or uint32: %v", err) } return vaa.ChainID(i), nil }