229 lines
7.6 KiB
Go
229 lines
7.6 KiB
Go
package domain
|
|
|
|
import (
|
|
"encoding/base32"
|
|
"encoding/hex"
|
|
"fmt"
|
|
|
|
algorand_types "github.com/algorand/go-algorand-sdk/types"
|
|
"github.com/cosmos/btcutil/bech32"
|
|
"github.com/mr-tron/base58"
|
|
sdk "github.com/wormhole-foundation/wormhole/sdk/vaa"
|
|
)
|
|
|
|
var (
|
|
// nearKnownEmitters maps NEAR emitter addresses to NEAR accounts.
|
|
nearKnownEmitters = map[string]string{
|
|
"148410499d3fcda4dcfd68a1ebfcdddda16ab28326448d4aae4d2f0465cdfcb7": "contract.portalbridge.near",
|
|
}
|
|
|
|
// suiKnownEmitters maps Sui emitter addresses to Sui accounts.
|
|
suiKnownEmitters = map[string]string{
|
|
"ccceeb29348f71bdd22ffef43a2a19c1f5b5e17c5cca5411529120182672ade5": "0xc57508ee0d4595e5a8728974a4a93a787d38f339757230d441e895422c07aba9",
|
|
}
|
|
|
|
// aptosKnownEmitters maps Aptos emitter addresses to Aptos accounts.
|
|
aptosKnownEmitters = map[string]string{
|
|
// Token Bridge
|
|
"0000000000000000000000000000000000000000000000000000000000000001": "0x576410486a2da45eee6c949c995670112ddf2fbeedab20350d506328eefc9d4f",
|
|
// NFT Bridge
|
|
"0000000000000000000000000000000000000000000000000000000000000005": "0x1bdffae984043833ed7fe223f7af7a3f8902d04129b14f801823e64827da7130",
|
|
}
|
|
)
|
|
|
|
// TranslateEmitterAddress converts an emitter address into the corresponding native address for the given chain.
|
|
func TranslateEmitterAddress(chainID sdk.ChainID, address string) (string, error) {
|
|
|
|
// Decode the address from hex
|
|
addressBytes, err := hex.DecodeString(address)
|
|
if err != nil {
|
|
return "", fmt.Errorf(`failed to decode emitter address "%s" from hex: %w`, address, err)
|
|
}
|
|
if len(addressBytes) != 32 {
|
|
return "", fmt.Errorf("expected emitter address length to be 32: %s", address)
|
|
}
|
|
|
|
// Translation rules are based on the chain ID
|
|
switch chainID {
|
|
|
|
// Solana emitter addresses use base58 encoding.
|
|
case sdk.ChainIDSolana:
|
|
return base58.Encode(addressBytes), nil
|
|
|
|
// EVM chains use the classic hex, 0x-prefixed encoding.
|
|
// Also, Karura and Acala support EVM-compatible addresses, so they're handled here as well.
|
|
case sdk.ChainIDEthereum,
|
|
sdk.ChainIDBSC,
|
|
sdk.ChainIDPolygon,
|
|
sdk.ChainIDAvalanche,
|
|
sdk.ChainIDOasis,
|
|
sdk.ChainIDAurora,
|
|
sdk.ChainIDFantom,
|
|
sdk.ChainIDKarura,
|
|
sdk.ChainIDAcala,
|
|
sdk.ChainIDKlaytn,
|
|
sdk.ChainIDCelo,
|
|
sdk.ChainIDMoonbeam,
|
|
sdk.ChainIDArbitrum,
|
|
sdk.ChainIDOptimism:
|
|
|
|
return "0x" + hex.EncodeToString(addressBytes[12:]), nil
|
|
|
|
// Terra addresses use bench32 encoding
|
|
case sdk.ChainIDTerra:
|
|
return encodeBench32("terra", addressBytes[12:])
|
|
|
|
// Terra2 addresses use bench32 encoding
|
|
case sdk.ChainIDTerra2:
|
|
return encodeBench32("terra", addressBytes)
|
|
|
|
// Injective addresses use bench32 encoding
|
|
case sdk.ChainIDInjective:
|
|
return encodeBench32("inj", addressBytes[12:])
|
|
|
|
// Xpla addresses use bench32 encoding
|
|
case sdk.ChainIDXpla:
|
|
return encodeBench32("xpla", addressBytes)
|
|
|
|
// Sei addresses use bench32 encoding
|
|
case sdk.ChainIDSei:
|
|
return encodeBench32("sei", addressBytes)
|
|
|
|
// Algorand addresses use base32 encoding with a trailing checksum.
|
|
// We're using the SDK to handle the checksum logic.
|
|
case sdk.ChainIDAlgorand:
|
|
|
|
var addr algorand_types.Address
|
|
if len(addr) != len(addressBytes) {
|
|
return "", fmt.Errorf("expected Algorand address to be %d bytes long, but got: %d", len(addr), len(addressBytes))
|
|
}
|
|
copy(addr[:], addressBytes[:])
|
|
|
|
return addr.String(), nil
|
|
|
|
// Near addresses are arbitrary-length strings. The emitter is the sha256 digest of the program address string.
|
|
//
|
|
// We're using a hashmap of known emitters to avoid querying external APIs.
|
|
case sdk.ChainIDNear:
|
|
if nativeAddress, ok := nearKnownEmitters[address]; ok {
|
|
return nativeAddress, nil
|
|
} else {
|
|
return "", fmt.Errorf(`no mapping found for NEAR emitter address "%s"`, address)
|
|
}
|
|
|
|
// For Sui emitters, an emitter capacity is taken from the core bridge. The capability object ID is used.
|
|
//
|
|
// We're using a hashmap of known emitters to avoid querying the contract's state.
|
|
case sdk.ChainIDSui:
|
|
if nativeAddress, ok := suiKnownEmitters[address]; ok {
|
|
return nativeAddress, nil
|
|
} else {
|
|
return "", fmt.Errorf(`no mapping found for Sui emitter address "%s"`, address)
|
|
}
|
|
|
|
// For Aptos, an emitter capability is taken from the core bridge. The capability object ID is used.
|
|
// The core bridge generates capabilities in a sequence and the capability object ID is its index in the sequence.
|
|
//
|
|
// We're using a hashmap of known emitters to avoid querying the contract's state.
|
|
case sdk.ChainIDAptos:
|
|
if nativeAddress, ok := aptosKnownEmitters[address]; ok {
|
|
return nativeAddress, nil
|
|
} else {
|
|
return "", fmt.Errorf(`no mapping found for Aptos emitter address "%s"`, address)
|
|
}
|
|
|
|
default:
|
|
return "", fmt.Errorf("can't translate emitter address: ChainID=%d not supported", chainID)
|
|
}
|
|
}
|
|
|
|
// encodeBench32 is a helper function to encode bench32 addresses.
|
|
func encodeBench32(hrp string, data []byte) (string, error) {
|
|
|
|
aligned, err := bech32.ConvertBits(data, 8, 5, true)
|
|
if err != nil {
|
|
return "", fmt.Errorf("bech32 encoding failed: %w", err)
|
|
}
|
|
|
|
return bech32.Encode(hrp, aligned)
|
|
}
|
|
|
|
// GetSupportedChainIDs returns a map of all supported chain IDs to their respective names.
|
|
func GetSupportedChainIDs() map[sdk.ChainID]string {
|
|
chainIDs := sdk.GetAllNetworkIDs()
|
|
supportedChaindIDs := make(map[sdk.ChainID]string, len(chainIDs))
|
|
for _, chainID := range chainIDs {
|
|
supportedChaindIDs[chainID] = chainID.String()
|
|
}
|
|
return supportedChaindIDs
|
|
}
|
|
|
|
// EncodeTrxHashByChainID encodes the transaction hash by chain id with different encoding methods.
|
|
func EncodeTrxHashByChainID(chainID sdk.ChainID, txHash []byte) (string, error) {
|
|
switch chainID {
|
|
case sdk.ChainIDSolana:
|
|
return base58.Encode(txHash), nil
|
|
case sdk.ChainIDEthereum:
|
|
return hex.EncodeToString(txHash), nil
|
|
case sdk.ChainIDTerra:
|
|
return hex.EncodeToString(txHash), nil
|
|
case sdk.ChainIDBSC:
|
|
return hex.EncodeToString(txHash), nil
|
|
case sdk.ChainIDPolygon:
|
|
return hex.EncodeToString(txHash), nil
|
|
case sdk.ChainIDAvalanche:
|
|
return hex.EncodeToString(txHash), nil
|
|
case sdk.ChainIDOasis:
|
|
return hex.EncodeToString(txHash), nil
|
|
case sdk.ChainIDAlgorand:
|
|
return base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(txHash), nil
|
|
case sdk.ChainIDAurora:
|
|
return hex.EncodeToString(txHash), nil
|
|
case sdk.ChainIDFantom:
|
|
return hex.EncodeToString(txHash), nil
|
|
case sdk.ChainIDKarura:
|
|
return hex.EncodeToString(txHash), nil
|
|
case sdk.ChainIDAcala:
|
|
return hex.EncodeToString(txHash), nil
|
|
case sdk.ChainIDKlaytn:
|
|
return hex.EncodeToString(txHash), nil
|
|
case sdk.ChainIDCelo:
|
|
return hex.EncodeToString(txHash), nil
|
|
case sdk.ChainIDNear:
|
|
return base58.Encode(txHash), nil
|
|
case sdk.ChainIDMoonbeam:
|
|
return hex.EncodeToString(txHash), nil
|
|
case sdk.ChainIDNeon:
|
|
return hex.EncodeToString(txHash), nil
|
|
case sdk.ChainIDTerra2:
|
|
return hex.EncodeToString(txHash), nil
|
|
case sdk.ChainIDInjective:
|
|
return hex.EncodeToString(txHash), nil
|
|
case sdk.ChainIDSui:
|
|
return base58.Encode(txHash), nil
|
|
case sdk.ChainIDAptos:
|
|
return hex.EncodeToString(txHash), nil
|
|
case sdk.ChainIDArbitrum:
|
|
return hex.EncodeToString(txHash), nil
|
|
case sdk.ChainIDOptimism:
|
|
return hex.EncodeToString(txHash), nil
|
|
case sdk.ChainIDXpla:
|
|
return hex.EncodeToString(txHash), nil
|
|
case sdk.ChainIDBtc:
|
|
//TODO: check if this is correct
|
|
return hex.EncodeToString(txHash), nil
|
|
case sdk.ChainIDBase:
|
|
//TODO: check if this is correct
|
|
return hex.EncodeToString(txHash), nil
|
|
case sdk.ChainIDSei:
|
|
return hex.EncodeToString(txHash), nil
|
|
case sdk.ChainIDWormchain:
|
|
//TODO: check if this is correct
|
|
return hex.EncodeToString(txHash), nil
|
|
case sdk.ChainIDSepolia:
|
|
return hex.EncodeToString(txHash), nil
|
|
default:
|
|
return hex.EncodeToString(txHash), fmt.Errorf("unknown chain id: %d", chainID)
|
|
}
|
|
}
|