diff --git a/app/gmpmiddleware/handler.go b/app/gmpmiddleware/handler.go index f8a88750..6cfc264a 100644 --- a/app/gmpmiddleware/handler.go +++ b/app/gmpmiddleware/handler.go @@ -48,7 +48,7 @@ func (h GmpHandler) HandleGeneralMessage( if err != nil { return err } - msg, err := NewGmpData(payload) + msg, err := types.NewGmpDecoder(payload) if err != nil { return err } @@ -58,7 +58,7 @@ func (h GmpHandler) HandleGeneralMessage( Relayer: srcAddress, DestinationChain: srcChain, DestinationAddress: destAddress, - Denoms: msg.assets(), + Denoms: msg.GetDenoms(), }, ) return err @@ -88,7 +88,7 @@ func (h GmpHandler) HandleGeneralMessageWithToken( if err != nil { return err } - msg, err := NewGmpData(payload) + msg, err := types.NewGmpDecoder(payload) if err != nil { return err } @@ -97,7 +97,7 @@ func (h GmpHandler) HandleGeneralMessageWithToken( Relayer: srcAddress, DestinationChain: srcChain, DestinationAddress: destAddress, - Denoms: msg.assets(), + Denoms: msg.GetDenoms(), Token: coin, }, ) diff --git a/app/gmpmiddleware/types.go b/app/gmpmiddleware/types.go index d03e73c4..f4888de3 100644 --- a/app/gmpmiddleware/types.go +++ b/app/gmpmiddleware/types.go @@ -2,14 +2,11 @@ package gmpmiddleware import ( "fmt" - "math/big" "strings" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" gmptypes "github.com/ojo-network/ojo/x/gmp/types" ) @@ -43,91 +40,6 @@ type Message struct { Type int64 `json:"type"` } -// GmpData is the payload sent from Axelar to IBC middleware. -// It needs to be decoded using the ABI. -type GmpData struct { - AssetNames [][32]byte // bytes32 in Solidity is represented as [32]byte in Go - ContractAddress common.Address // address in Solidity is represented as common.Address in Go - CommandSelector [4]byte // bytes4 in Solidity is represented as [4]byte in Go - CommandParams []byte // bytes in Solidity is represented as []byte in Go - Timestamp *big.Int // uint256 in Solidity is represented as *big.Int in Go - AbiEncodedData []byte // the ABI encoded data -} - -var ( - assetNamesType, _ = abi.NewType("bytes32[]", "bytes32[]", nil) - contractAddressType, _ = abi.NewType("address", "address", nil) - commandSelectorType, _ = abi.NewType("bytes4", "bytes4", nil) - commandParamsType, _ = abi.NewType("bytes", "bytes", nil) - timestampType, _ = abi.NewType("uint256", "uint256", nil) -) - -var abiSpec = abi.Arguments{ - { - Type: assetNamesType, - }, - { - Type: contractAddressType, - }, - { - Type: commandSelectorType, - }, - { - Type: commandParamsType, - }, - { - Type: timestampType, - }, -} - -// NewGmpData decodes a payload from GMP given a byte array -func NewGmpData(payload []byte) (GmpData, error) { - args, err := abiSpec.Unpack(payload) - if err != nil { - return GmpData{}, err - } - - // check to make sure each argument is the correct type - if assetNames, ok := args[0].([][32]byte); !ok { - return GmpData{}, fmt.Errorf("invalid asset names type: %T", args[0]) - } else if contractAddress, ok := args[1].(common.Address); !ok { - return GmpData{}, fmt.Errorf("invalid contract address type: %T", args[1]) - } else if commandSelector, ok := args[2].([4]byte); !ok { - return GmpData{}, fmt.Errorf("invalid command selector type: %T", args[2]) - } else if commandParams, ok := args[3].([]byte); !ok { - return GmpData{}, fmt.Errorf("invalid command params type: %T", args[3]) - } else if timestamp, ok := args[4].(*big.Int); !ok { - return GmpData{}, fmt.Errorf("invalid timestamp type: %T", args[4]) - } else { - return GmpData{ - AssetNames: assetNames, - ContractAddress: contractAddress, - CommandSelector: commandSelector, - CommandParams: commandParams, - Timestamp: timestamp, - }, nil - } -} - -// assets takes a GmpData and returns the asset names as a slice of strings -func (g GmpData) assets() []string { - var assetNames []string - for _, assetName := range g.AssetNames { - assetNames = append(assetNames, string(assetName[:])) - } - return assetNames -} - -func (g GmpData) Encode() ([]byte, error) { - return abiSpec.Pack( - g.AssetNames, - g.ContractAddress, - g.CommandSelector, - g.CommandParams, - g.Timestamp, - ) -} - func verifyParams(params gmptypes.Params, sender string, channel string) error { if !strings.EqualFold(params.GmpAddress, sender) { return fmt.Errorf("invalid sender address: %s", sender) diff --git a/app/gmpmiddleware/types_test.go b/app/gmpmiddleware/types_test.go index 22f817f8..1b9ce19c 100644 --- a/app/gmpmiddleware/types_test.go +++ b/app/gmpmiddleware/types_test.go @@ -1,11 +1,9 @@ package gmpmiddleware import ( - "math/big" "testing" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" - "github.com/ethereum/go-ethereum/common" "github.com/ojo-network/ojo/x/gmp/types" "github.com/stretchr/testify/require" ) @@ -22,23 +20,6 @@ func TestVerifyParams(t *testing.T) { require.Error(t, err) } -// TestGmpData tests the GmpData struct by encoding and decoding it. -func TestGmpData(t *testing.T) { - gmpData := GmpData{ - AssetNames: [][32]byte{{1}}, - ContractAddress: common.HexToAddress("0x0000001"), - CommandSelector: [4]byte{1}, - CommandParams: []byte{1}, - Timestamp: big.NewInt(1), - } - payload, err := gmpData.Encode() - require.NoError(t, err) - newGmpData, err := NewGmpData(payload) - require.NoError(t, err) - - require.Equal(t, gmpData, newGmpData) -} - func TestParseDenom(t *testing.T) { packet := channeltypes.Packet{ SourcePort: "ibc", diff --git a/x/gmp/keeper/keeper.go b/x/gmp/keeper/keeper.go index 40331ffe..e596ecec 100644 --- a/x/gmp/keeper/keeper.go +++ b/x/gmp/keeper/keeper.go @@ -84,16 +84,16 @@ func (k Keeper) RelayPrice( // TODO: fill with actual disableResolve option // Ref: https://github.com/ojo-network/ojo/issues/309 - payload, err := types.EncodeABI("postPrices", rates, false) + /*payload, err := types.EncodeABI("postPrices", rates, false) if err != nil { return nil, err - } + }*/ // package GMP message := types.GmpMessage{ DestinationChain: msg.DestinationChain, DestinationAddress: msg.DestinationAddress, - Payload: payload, + Payload: []byte{}, Type: types.TypeGeneralMessage, } bz, err := message.Marshal() diff --git a/x/gmp/types/abi_decode.go b/x/gmp/types/abi_decode.go new file mode 100644 index 00000000..8f68f3c8 --- /dev/null +++ b/x/gmp/types/abi_decode.go @@ -0,0 +1,77 @@ +package types + +import ( + fmt "fmt" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" +) + +// GmpDecoder is the payload sent from Axelar to IBC middleware. +// It needs to be decoded using the ABI. +type GmpDecoder struct { + AssetNames [][32]byte + ContractAddress common.Address + CommandSelector [4]byte + CommandParams []byte + Timestamp *big.Int + AbiEncodedData []byte +} + +// abiSpec is the ABI specification for the GMP data. +var decoderSpec = abi.Arguments{ + { + Type: assetNamesType, + }, + { + Type: contractAddressType, + }, + { + Type: commandSelectorType, + }, + { + Type: commandParamsType, + }, + { + Type: timestampType, + }, +} + +// NewGmpDecoder decodes a payload from GMP given a byte array +func NewGmpDecoder(payload []byte) (GmpDecoder, error) { + args, err := decoderSpec.Unpack(payload) + if err != nil { + return GmpDecoder{}, err + } + + // check to make sure each argument is the correct type + if assetNames, ok := args[0].([][32]byte); !ok { + return GmpDecoder{}, fmt.Errorf("invalid asset names type: %T", args[0]) + } else if contractAddress, ok := args[1].(common.Address); !ok { + return GmpDecoder{}, fmt.Errorf("invalid contract address type: %T", args[1]) + } else if commandSelector, ok := args[2].([4]byte); !ok { + return GmpDecoder{}, fmt.Errorf("invalid command selector type: %T", args[2]) + } else if commandParams, ok := args[3].([]byte); !ok { + return GmpDecoder{}, fmt.Errorf("invalid command params type: %T", args[3]) + } else if timestamp, ok := args[4].(*big.Int); !ok { + return GmpDecoder{}, fmt.Errorf("invalid timestamp type: %T", args[4]) + } else { + return GmpDecoder{ + AssetNames: assetNames, + ContractAddress: contractAddress, + CommandSelector: commandSelector, + CommandParams: commandParams, + Timestamp: timestamp, + }, nil + } +} + +func (g GmpDecoder) GetDenoms() []string { + denoms := make([]string, len(g.AssetNames)) + for i, name := range g.AssetNames { + denoms[i] = strings.TrimRight(string(name[:]), "\x00") + } + return denoms +} diff --git a/x/gmp/types/abi_decode_test.go b/x/gmp/types/abi_decode_test.go new file mode 100644 index 00000000..34e2ed9b --- /dev/null +++ b/x/gmp/types/abi_decode_test.go @@ -0,0 +1,53 @@ +package types + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +// TestGmpData tests the GmpData struct by encoding and decoding it. +func TestGmpData(t *testing.T) { + g := GmpDecoder{ + AssetNames: [][32]byte{{1}}, + ContractAddress: common.HexToAddress("0x0000001"), + CommandSelector: [4]byte{1}, + CommandParams: []byte{1}, + Timestamp: big.NewInt(1), + } + payload, err := decoderSpec.Pack( + g.AssetNames, + g.ContractAddress, + g.CommandSelector, + g.CommandParams, + g.Timestamp, + ) + require.NoError(t, err) + newGmpData, err := NewGmpDecoder(payload) + require.NoError(t, err) + + require.Equal(t, g, newGmpData) +} + +func TestGetDenoms(t *testing.T) { + assetNamesString := []string{ + "BTC", + "ETH", + "USDT", + "BNB", + "ADA", + "FOOBARFOOBARFOOBARFOOBARFOOBARFO", // maxmimum allowed length + } + assetNamesAsBytes := make([][32]byte, len(assetNamesString)) + for i, name := range assetNamesString { + copy(assetNamesAsBytes[i][:], name) + } + + g := GmpDecoder{ + AssetNames: assetNamesAsBytes, + } + denoms := g.GetDenoms() + require.Equal(t, assetNamesString, denoms) +} diff --git a/x/gmp/types/abi_encode.go b/x/gmp/types/abi_encode.go new file mode 100644 index 00000000..969e1596 --- /dev/null +++ b/x/gmp/types/abi_encode.go @@ -0,0 +1,56 @@ +package types + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" +) + +// GmpEncoder is the struct we use to encode the data we want to send to the GMP. +type GmpEncoder struct { + PriceData []PriceData + AssetNames [32]byte + ContractAddress common.Address + CommandSelector [4]byte + CommandParams []byte +} + +// MedianData is the struct that represents the MedianData tuple in Solidity. +type MedianData struct { + BlockNums []*big.Int + Medians []*big.Int + Deviations []*big.Int +} + +// PriceData is the struct that represents the PriceData tuple in Solidity. +type PriceData struct { + AssetName [32]byte + Price *big.Int + ResolveTime *big.Int + MedianData []MedianData +} + +// encoderSpec is the ABI specification for the GMP data. +var encoderSpec = abi.Arguments{ + { + Type: priceDataType, + }, + { + Type: assetNameType, + }, + { + Type: contractAddressType, + }, + { + Type: commandSelectorType, + }, + { + Type: commandParamsType, + }, +} + +// GMPEncode encodes the GMP data into a byte array. +func (g GmpEncoder) GMPEncode() ([]byte, error) { + return encoderSpec.Pack(g.PriceData, g.AssetNames, g.ContractAddress, g.CommandSelector, g.CommandParams) +} diff --git a/x/gmp/types/abi_encode_test.go b/x/gmp/types/abi_encode_test.go new file mode 100644 index 00000000..a224a214 --- /dev/null +++ b/x/gmp/types/abi_encode_test.go @@ -0,0 +1,42 @@ +package types + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +func TestEncode(t *testing.T) { + message := GmpEncoder{ + PriceData: []PriceData{ + { + AssetName: [32]byte{}, + Price: big.NewInt(1), + ResolveTime: big.NewInt(1), + MedianData: []MedianData{ + { + BlockNums: []*big.Int{big.NewInt(1)}, + Medians: []*big.Int{big.NewInt(1)}, + Deviations: []*big.Int{big.NewInt(1)}, + }, + }, + }, + }, + AssetNames: [32]byte{}, + ContractAddress: common.Address{}, + CommandSelector: [4]byte{}, + CommandParams: []byte{}, + } + + bz, err := message.GMPEncode() + require.NoError(t, err) + vals, err := encoderSpec.Unpack(bz) + require.NoError(t, err) + + require.Equal(t, message.AssetNames, vals[1].([32]byte)) + require.Equal(t, message.ContractAddress, vals[2].(common.Address)) + require.Equal(t, message.CommandSelector, vals[3].([4]byte)) + require.Equal(t, message.CommandParams, vals[4].([]byte)) +} diff --git a/x/gmp/types/abi_encoding.go b/x/gmp/types/abi_encoding.go deleted file mode 100644 index fabb8bd5..00000000 --- a/x/gmp/types/abi_encoding.go +++ /dev/null @@ -1,41 +0,0 @@ -package types - -import ( - "fmt" - "strings" - - "github.com/ethereum/go-ethereum/accounts/abi" -) - -type FnName string - -const ( - prices FnName = "postPrices" - medians FnName = "postMedians" - deviations FnName = "postDeviations" -) - -// EncodeABI encodes the function name and parameters into ABI encoding using the above JSON. -// It can only be used with the following functions: -// - postPrices -// - postMedians -// - postDeviations -func EncodeABI(fn string, params ...interface{}) ([]byte, error) { - switch FnName(fn) { - case prices, medians, deviations: - parsedABI, err := abi.JSON(strings.NewReader(abiJSON)) - if err != nil { - return nil, err - } - - data, err := parsedABI.Pack(fn, params...) - if err != nil { - return nil, err - } - - return data, nil - - default: - return []byte{}, fmt.Errorf("invalid function name") - } -} diff --git a/x/gmp/types/abi_encoding_test.go b/x/gmp/types/abi_encoding_test.go deleted file mode 100644 index 0072efed..00000000 --- a/x/gmp/types/abi_encoding_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package types - -import ( - "math/big" - "testing" - - "github.com/stretchr/testify/require" -) - -// TODO: Make these tests more thorough -// Ref: https://github.com/ojo-network/ojo/issues/310 -func TestEncodeABI(t *testing.T) { - assetNameArray := [32]byte{} - copy(assetNameArray[:], []byte("btc")) - rates := [1]PriceFeedData{ - { - AssetName: assetNameArray, - Value: big.NewInt(50000), - ResolveTime: big.NewInt(50000), - Id: big.NewInt(50000), - }, - } - disableResolve := false - _, err := EncodeABI("postPrices", rates, disableResolve) - require.NoError(t, err) -} diff --git a/x/gmp/types/abi_json.go b/x/gmp/types/abi_json.go deleted file mode 100644 index bd5967db..00000000 --- a/x/gmp/types/abi_json.go +++ /dev/null @@ -1,7 +0,0 @@ -package types - -// abiJson is a string value of the ABI interface for interacting with the -// Ojo EVM contract. -// -//nolint:lll -const abiJSON = "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"MedianDisabled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnAuthorized\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"relayer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"}],\"name\":\"DeviationPosted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"relayer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"}],\"name\":\"MedianPosted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"status\",\"type\":\"bool\"}],\"name\":\"MedianStatus\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"relayer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"}],\"name\":\"PricePosted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"}],\"name\":\"RemovedFromWhitelist\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"previousAdminRole\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"newAdminRole\",\"type\":\"bytes32\"}],\"name\":\"RoleAdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleGranted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleRevoked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"status\",\"type\":\"bool\"}],\"name\":\"WhitelistStatus\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"}],\"name\":\"Whitelisted\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"DEFAULT_ADMIN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"RELAYER_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"USD\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"relayer\",\"type\":\"address\"}],\"name\":\"assignRelayerRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"claimOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_assetName\",\"type\":\"bytes32\"}],\"name\":\"getDeviationData\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"assetName\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"resolveTime\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"internalType\":\"structPriceFeed.Data\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"_assetNames\",\"type\":\"bytes32[]\"}],\"name\":\"getDeviationDataBulk\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"assetName\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"resolveTime\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"internalType\":\"structPriceFeed.Data[]\",\"name\":\"deviationData\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_assetName\",\"type\":\"bytes32\"}],\"name\":\"getMedianData\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"assetName\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"resolveTime\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"}],\"internalType\":\"structPriceFeed.MedianData\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"_assetNames\",\"type\":\"bytes32[]\"}],\"name\":\"getMedianDataBulk\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"assetName\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"resolveTime\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"}],\"internalType\":\"structPriceFeed.MedianData[]\",\"name\":\"medianData\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_base\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"_qoute\",\"type\":\"bytes32\"}],\"name\":\"getPrice\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"price\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"baseResolveTime\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"quoteResolveTime\",\"type\":\"uint256\"}],\"internalType\":\"structPriceFeed.Price\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_assetName\",\"type\":\"bytes32\"}],\"name\":\"getPriceData\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"assetName\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"resolveTime\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"internalType\":\"structPriceFeed.Data\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"_assetNames\",\"type\":\"bytes32[]\"}],\"name\":\"getPriceDataBulk\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"assetName\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"resolveTime\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"internalType\":\"structPriceFeed.Data[]\",\"name\":\"priceData\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"}],\"name\":\"getRoleAdmin\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"grantRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"hasRole\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"assetName\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"resolveTime\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"internalType\":\"structPriceFeed.Data[]\",\"name\":\"_deviations\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"_disableResolve\",\"type\":\"bool\"}],\"name\":\"postDeviations\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"assetName\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"resolveTime\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"}],\"internalType\":\"structPriceFeed.MedianData[]\",\"name\":\"_medians\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"_disableResolve\",\"type\":\"bool\"}],\"name\":\"postMedians\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"assetName\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"resolveTime\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"internalType\":\"structPriceFeed.Data[]\",\"name\":\"_prices\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"_disableResolve\",\"type\":\"bool\"}],\"name\":\"postPrices\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_user\",\"type\":\"address\"}],\"name\":\"removeAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"renounceRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"relayer\",\"type\":\"address\"}],\"name\":\"revokeRelayerRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"revokeRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"_status\",\"type\":\"bool\"}],\"name\":\"setMedianStatus\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"_status\",\"type\":\"bool\"}],\"name\":\"setWhitelistStatus\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_user\",\"type\":\"address\"}],\"name\":\"whitelistAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" diff --git a/x/gmp/types/abi_types.go b/x/gmp/types/abi_types.go new file mode 100644 index 00000000..edd84157 --- /dev/null +++ b/x/gmp/types/abi_types.go @@ -0,0 +1,31 @@ +package types + +import "github.com/ethereum/go-ethereum/accounts/abi" + +// These are the types we use to encode and decode data to and from the GMP. +var ( + assetNameType, _ = abi.NewType("bytes32", "bytes32", nil) + assetNamesType, _ = abi.NewType("bytes32[]", "bytes32[]", nil) + contractAddressType, _ = abi.NewType("address", "address", nil) + commandSelectorType, _ = abi.NewType("bytes4", "bytes4", nil) + commandParamsType, _ = abi.NewType("bytes", "bytes", nil) + timestampType, _ = abi.NewType("uint256", "uint256", nil) + + // priceDataType is the ABI specification for the PriceData tuple in Solidity. + // It is a tuple of (bytes32, uint256, uint256, tuple[]). + // It includes MedianData, another tuple of (uint256[], uint256[], uint256[]). + priceDataType, _ = abi.NewType("tuple[]", "", + []abi.ArgumentMarshaling{ + {Name: "AssetName", Type: "bytes32"}, + {Name: "Price", Type: "uint256"}, + {Name: "ResolveTime", Type: "uint256"}, + { + Name: "MedianData", Type: "tuple[]", Components: []abi.ArgumentMarshaling{ + {Name: "BlockNums", Type: "uint256[]"}, + {Name: "Medians", Type: "uint256[]"}, + {Name: "Deviations", Type: "uint256[]"}, + }, + }, + }, + ) +)