Skip to content

Commit

Permalink
respect the pouch. Respect it
Browse files Browse the repository at this point in the history
  • Loading branch information
adamewozniak committed Nov 14, 2023
1 parent 2c523d9 commit 9f6a5d0
Show file tree
Hide file tree
Showing 10 changed files with 406 additions and 119 deletions.
12 changes: 12 additions & 0 deletions proto/ojo/gmp/v1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,18 @@ message MsgRelayPrice {
(gogoproto.nullable) = false,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];

// contract_address defines the contract address to call.
string contract_address = 6;

// command_selector defines the command to call.
bytes command_selector = 7;

// command_params defines the command parameters to call.
bytes command_params = 8;

// timestamp defines the timestamp of the message.
int64 timestamp = 9;
}

// MsgRelay defines the Relay response type.
Expand Down
51 changes: 38 additions & 13 deletions x/gmp/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"time"

"github.com/cometbft/cometbft/libs/log"
"github.com/ethereum/go-ethereum/common"

sdk "github.com/cosmos/cosmos-sdk/types"
ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"
Expand Down Expand Up @@ -58,42 +59,66 @@ func (k Keeper) RelayPrice(
ctx := sdk.UnwrapSDKContext(goCtx)
params := k.GetParams(ctx)

// encode oracle data
rates := []types.PriceFeedData{}
prices := []types.PriceData{}
for _, denom := range msg.Denoms {
// get exchange rate
rate, err := k.oracleKeeper.GetExchangeRate(ctx, denom)
if err != nil {
return &types.MsgRelayPriceResponse{}, err
}

priceFeed, err := types.NewPriceFeedData(
// get any available median and standard deviation data
medians := k.oracleKeeper.HistoricMedians(
ctx,
denom,
k.oracleKeeper.MaximumMedianStamps(ctx),
)
deviations := k.oracleKeeper.HistoricDeviations(
ctx,
denom,
k.oracleKeeper.MaximumMedianStamps(ctx),
)
// convert them to a medianData slice
medianData, err := types.NewMediansSlice(medians, deviations)
if err != nil {
return &types.MsgRelayPriceResponse{}, err
}

priceFeed, err := types.NewPriceData(
denom,
rate,
// TODO: replace with actual resolve time & id
// Ref: https://github.com/ojo-network/ojo/issues/309
big.NewInt(1),
big.NewInt(1),
big.NewInt(msg.Timestamp),
medianData,
)
if err != nil {
k.Logger(ctx).With(err).Error("unable to relay price to gmp")
continue
}

rates = append(rates, priceFeed)
prices = append(prices, priceFeed)
}

// TODO: fill with actual disableResolve option
// Ref: https://github.com/ojo-network/ojo/issues/309
/*payload, err := types.EncodeABI("postPrices", rates, false)
// convert commandSelector to [4]byte
var commandSelector [4]byte
copy(commandSelector[:], msg.CommandSelector)

encoder := types.NewGMPEncoder(
prices,
msg.Denoms,
common.HexToAddress(msg.ContractAddress),
commandSelector,
msg.CommandParams,
)
payload, err := encoder.GMPEncode()
if err != nil {
return nil, err
}*/
}

// package GMP
message := types.GmpMessage{
DestinationChain: msg.DestinationChain,
DestinationAddress: msg.DestinationAddress,
Payload: []byte{},
Payload: payload,
Type: types.TypeGeneralMessage,
}
bz, err := message.Marshal()
Expand Down
1 change: 0 additions & 1 deletion x/gmp/types/abi_decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ type GmpDecoder struct {
CommandSelector [4]byte
CommandParams []byte
Timestamp *big.Int
AbiEncodedData []byte
}

// abiSpec is the ABI specification for the GMP data.
Expand Down
102 changes: 100 additions & 2 deletions x/gmp/types/abi_encode.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
package types

import (
fmt "fmt"
"math/big"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
oracletypes "github.com/ojo-network/ojo/x/oracle/types"
)

const (
// TypeUnrecognized means coin type is unrecognized
TypeUnrecognized = iota
// TypeGeneralMessage is a pure message
TypeGeneralMessage
// TypeGeneralMessageWithToken is a general message with token
TypeGeneralMessageWithToken
// TypeSendToken is a direct token transfer
TypeSendToken
)

// 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
AssetNames [][32]byte
ContractAddress common.Address
CommandSelector [4]byte
CommandParams []byte
Expand All @@ -37,7 +51,7 @@ var encoderSpec = abi.Arguments{
Type: priceDataType,
},
{
Type: assetNameType,
Type: assetNamesType,
},
{
Type: contractAddressType,
Expand All @@ -54,3 +68,87 @@ var encoderSpec = abi.Arguments{
func (g GmpEncoder) GMPEncode() ([]byte, error) {
return encoderSpec.Pack(g.PriceData, g.AssetNames, g.ContractAddress, g.CommandSelector, g.CommandParams)
}

func NewGMPEncoder(
priceData []PriceData,
assetName []string,
contractAddress common.Address,
commandSelector [4]byte,
commandParams []byte,
) GmpEncoder {
return GmpEncoder{
PriceData: priceData,
AssetNames: namesToBytes(assetName),
ContractAddress: contractAddress,
CommandSelector: commandSelector,
CommandParams: commandParams,
}
}

func nameToBytes32(name string) [32]byte {
var nameBytes [32]byte
copy(nameBytes[:], []byte(name))
return nameBytes
}

func namesToBytes(assetNames []string) [][32]byte {
assetNamesBytes := make([][32]byte, len(assetNames))
for i, name := range assetNames {
assetNamesBytes[i] = nameToBytes32(name)
}
return assetNamesBytes
}

func NewPriceData(
assetName string,
price sdk.Dec,
resolveTime *big.Int,
medianData []MedianData,
) (PriceData, error) {
assetSlice := []byte(assetName)
if len(assetSlice) > 32 {
return PriceData{}, fmt.Errorf(
"asset name is too long to convert to array: %s", assetName,
)
}
var assetArray [32]byte
copy(assetArray[:], assetSlice)
return PriceData{
AssetName: assetArray,
Price: decToInt(price),
ResolveTime: resolveTime,
MedianData: medianData,
}, nil
}

// DecToInt multiplies amount by rate factor to make it compatible with contracts.
func decToInt(amount sdk.Dec) *big.Int {
return amount.Mul(rateFactor).TruncateInt().BigInt()
}

var rateFactor = sdk.NewDec(10).Power(9)

// NewMediansSlice creates a slice of MedianData from slices of medians and deviations.
func NewMediansSlice(medians oracletypes.PriceStamps, deviations oracletypes.PriceStamps) ([]MedianData, error) {
if len(medians) != len(deviations) {
return nil, fmt.Errorf("length of medians and deviations must be equal")
}
// First, sort them so we'll be able to find matching blockNums.
sortedMedians := *medians.Sort()
sortedDeviations := *deviations.Sort()

// Then, create the MedianData slice.
medianData := make([]MedianData, 0, len(medians))
for i, median := range sortedMedians {
// If the median and deviation are not from the same block, skip.
if median.BlockNum != sortedDeviations[i].BlockNum {
continue
}
medianData = append(medianData, MedianData{
BlockNums: []*big.Int{big.NewInt(int64(median.BlockNum))},
Medians: []*big.Int{decToInt(median.ExchangeRate.Amount)},
Deviations: []*big.Int{decToInt(sortedDeviations[i].ExchangeRate.Amount)},
})
}
return medianData, nil
}
4 changes: 2 additions & 2 deletions x/gmp/types/abi_encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func TestEncode(t *testing.T) {
},
},
},
AssetNames: [32]byte{},
AssetNames: [][32]byte{},
ContractAddress: common.Address{},
CommandSelector: [4]byte{},
CommandParams: []byte{},
Expand All @@ -35,7 +35,7 @@ func TestEncode(t *testing.T) {
vals, err := encoderSpec.Unpack(bz)
require.NoError(t, err)

require.Equal(t, message.AssetNames, vals[1].([32]byte))
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))
Expand Down
1 change: 0 additions & 1 deletion x/gmp/types/abi_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ 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)
Expand Down
4 changes: 4 additions & 0 deletions x/gmp/types/expected_keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ package types

import (
sdk "github.com/cosmos/cosmos-sdk/types"
oracletypes "github.com/ojo-network/ojo/x/oracle/types"
)

// OracleKeeper defines the expected Oracle interface that is needed by the gmp module.
type OracleKeeper interface {
GetExchangeRate(ctx sdk.Context, symbol string) (sdk.Dec, error)
GetExponent(ctx sdk.Context, denom string) (uint32, error)
MaximumMedianStamps(ctx sdk.Context) uint64
HistoricMedians(ctx sdk.Context, denom string, numStamps uint64) oracletypes.PriceStamps
HistoricDeviations(ctx sdk.Context, denom string, numStamps uint64) oracletypes.PriceStamps
}
65 changes: 0 additions & 65 deletions x/gmp/types/gmp_messages.go

This file was deleted.

Loading

0 comments on commit 9f6a5d0

Please sign in to comment.