Skip to content

Commit

Permalink
feat(eibc): auto create eibc demand order for rollapp packets (#944)
Browse files Browse the repository at this point in the history
  • Loading branch information
mtsitrin authored Jun 18, 2024
1 parent 6721d98 commit 0274850
Show file tree
Hide file tree
Showing 6 changed files with 212 additions and 108 deletions.
65 changes: 44 additions & 21 deletions ibctesting/eibc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
channeltypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types"
ibctesting "github.com/cosmos/ibc-go/v6/testing"

"github.com/dymensionxyz/dymension/v3/app/apptesting"
commontypes "github.com/dymensionxyz/dymension/v3/x/common/types"
eibckeeper "github.com/dymensionxyz/dymension/v3/x/eibc/keeper"
eibctypes "github.com/dymensionxyz/dymension/v3/x/eibc/types"
Expand Down Expand Up @@ -68,68 +69,77 @@ func (suite *EIBCTestSuite) TestEIBCDemandOrderCreation() {
name string
amount string
fee string
recipient string
demandOrdersCreated int
expectAck bool
extraMemoData map[string]map[string]string
skipEIBCmemo bool
}{
{
"valid demand order",
"1000000000",
"150",
suite.hubChain.SenderAccount.GetAddress().String(),
1,
false,
map[string]map[string]string{},
false,
},
{
"valid demand order - fee is 0",
"1000000000",
"0",
1,
false,
map[string]map[string]string{},
false,
},
{
"valid demand order - auto created",
"1000000000",
"0",
1,
false,
map[string]map[string]string{},
true,
},
{
"invalid demand order - negative fee",
"1000000000",
"-150",
suite.hubChain.SenderAccount.GetAddress().String(),
0,
true,
map[string]map[string]string{},
false,
},
{
"invalid demand order - fee > amount",
"1000",
"1001",
suite.hubChain.SenderAccount.GetAddress().String(),
0,
true,
map[string]map[string]string{},
},
{
"invalid demand order - fee is 0",
"1",
"0",
suite.hubChain.SenderAccount.GetAddress().String(),
0,
true,
map[string]map[string]string{},
false,
},
{
"invalid demand order - fee > max uint64",
"10000",
"100000000000000000000000000000",
suite.hubChain.SenderAccount.GetAddress().String(),
0,
true,
map[string]map[string]string{},
false,
},
{
"invalid demand order - PFM and EIBC are not supported together",
"1000000000",
"150",
suite.hubChain.SenderAccount.GetAddress().String(),
0,
true,
map[string]map[string]string{"forward": {
"receiver": suite.hubChain.SenderAccount.GetAddress().String(),
"port": "transfer",
"channel": "channel-0",
}},
false,
},
}
totalDemandOrdersCreated := 0
Expand All @@ -148,23 +158,36 @@ func (suite *EIBCTestSuite) TestEIBCDemandOrderCreation() {
}
eibcJson, _ := json.Marshal(memoObj)
memo := string(eibcJson)
_ = suite.TransferRollappToHub(path, IBCSenderAccount, tc.recipient, tc.amount, memo, tc.expectAck)

if tc.skipEIBCmemo {
memo = ""
}

recipient := apptesting.CreateRandomAccounts(1)[0]
_ = suite.TransferRollappToHub(path, IBCSenderAccount, recipient.String(), tc.amount, memo, tc.expectAck)
// Validate demand orders results
eibcKeeper := ConvertToApp(suite.hubChain).EIBCKeeper
demandOrders, err := eibcKeeper.ListAllDemandOrders(suite.hubChain.GetContext())
suite.Require().NoError(err)
suite.Require().Equal(tc.demandOrdersCreated, len(demandOrders)-totalDemandOrdersCreated)
totalDemandOrdersCreated = len(demandOrders)

amountInt, ok := sdk.NewIntFromString(tc.amount)
suite.Require().True(ok)
feeInt, ok := sdk.NewIntFromString(tc.fee)
suite.Require().True(ok)
if tc.demandOrdersCreated > 0 {
lastDemandOrder := demandOrders[len(demandOrders)-1]
suite.Require().True(ok)
suite.Require().Equal(tc.recipient, lastDemandOrder.Recipient)
suite.Require().Equal(amountInt.Sub(feeInt), lastDemandOrder.Price[0].Amount)
suite.Require().Equal(feeInt, lastDemandOrder.Fee[0].Amount)
var demandOrder *eibctypes.DemandOrder
for _, order := range demandOrders {
if order.Recipient == recipient.String() {
demandOrder = order
break
}
}
suite.Require().NotNil(demandOrder)
suite.Require().Equal(recipient.String(), demandOrder.Recipient)
suite.Require().Equal(amountInt.Sub(feeInt), demandOrder.Price[0].Amount)
suite.Require().Equal(feeInt, demandOrder.Fee.AmountOf(demandOrder.Price[0].Denom))
}
})
}
Expand Down
3 changes: 1 addition & 2 deletions x/delayedack/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ import (
var (
ErrCanOnlyUpdatePendingPacket = errorsmod.Register(ModuleName, 1, "can only update pending packet")
ErrRollappPacketDoesNotExist = errorsmod.Register(ModuleName, 2, "rollapp packet does not exist")
ErrEmptyEpochIdentifier = errorsmod.Register(ModuleName, 4, "empty epoch identifier")
ErrMismatchedStateRoots = errorsmod.Register(ModuleName, 5, "mismatched state roots")
ErrMismatchedSequencer = errorsmod.Register(ModuleName, 6, "mismatched sequencer")
ErrMissingEIBCMetadata = errorsmod.Register(ModuleName, 7, "missing eibc metadata")
ErrUnknownRequest = errorsmod.Register(ModuleName, 8, "unknown request")
ErrInvalidType = errorsmod.Register(ModuleName, 9, "invalid type")
ErrBadEIBCFee = errorsmod.Register(ModuleName, 10, "provided eibc fee is invalid")
)
12 changes: 5 additions & 7 deletions x/delayedack/types/packet_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,10 @@ func (e EIBCMetadata) ValidateBasic() error {
return nil
}

var ErrEIBCFeeNotPositiveInt = fmt.Errorf("eibc fee is not a positive integer")

func (e EIBCMetadata) FeeInt() (math.Int, error) {
i, ok := sdk.NewIntFromString(e.Fee)
if !ok || !i.IsPositive() {
return math.Int{}, ErrEIBCFeeNotPositiveInt
if !ok || i.IsNegative() {
return math.Int{}, ErrBadEIBCFee
}
return i, nil
}
Expand All @@ -59,13 +57,13 @@ func ParsePacketMetadata(input string) (*PacketMetadata, error) {
if err != nil {
return nil, ErrMemoUnmarshal
}
if memo[memoObjectKeyEIBC] == nil {
return nil, ErrMemoEibcEmpty
}
if memo[memoObjectKeyPFM] != nil {
// Currently not supporting eibc with PFM: https://github.com/dymensionxyz/dymension/issues/599
return nil, ErrMemoHashPFMandEIBC
}
if memo[memoObjectKeyEIBC] == nil {
return nil, ErrMemoEibcEmpty
}
var metadata PacketMetadata
err = json.Unmarshal(bz, &metadata)
if err != nil {
Expand Down
21 changes: 11 additions & 10 deletions x/eibc/keeper/handler.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package keeper

import (
"errors"
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"
transfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
channeltypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types"
"github.com/pkg/errors"

"github.com/dymensionxyz/dymension/v3/utils"
commontypes "github.com/dymensionxyz/dymension/v3/x/common/types"
Expand Down Expand Up @@ -61,16 +61,17 @@ func (k Keeper) EIBCDemandOrderHandler(ctx sdk.Context, rollappPacket commontype
func (k *Keeper) CreateDemandOrderOnRecv(ctx sdk.Context, fungibleTokenPacketData transfertypes.FungibleTokenPacketData,
rollappPacket *commontypes.RollappPacket,
) (*types.DemandOrder, error) {
packetMetaData, err := dacktypes.ParsePacketMetadata(fungibleTokenPacketData.Memo)
if errors.Is(err, dacktypes.ErrMemoUnmarshal) || errors.Is(err, dacktypes.ErrMemoEibcEmpty) {
ctx.Logger().Debug("skipping demand order creation - no eibc memo provided")
return nil, nil
}
if err != nil {
return nil, err
// zero fee demand order by default
eibcMetaData := dacktypes.EIBCMetadata{Fee: "0"}

if fungibleTokenPacketData.Memo != "" {
packetMetaData, err := dacktypes.ParsePacketMetadata(fungibleTokenPacketData.Memo)
if err == nil {
eibcMetaData = *packetMetaData.EIBC
} else if !errors.Is(err, dacktypes.ErrMemoEibcEmpty) {
return nil, fmt.Errorf("parse packet metadata: %w", err)
}
}

eibcMetaData := packetMetaData.EIBC
if err := eibcMetaData.ValidateBasic(); err != nil {
return nil, fmt.Errorf("validate eibc metadata: %w", err)
}
Expand Down
82 changes: 82 additions & 0 deletions x/eibc/keeper/handler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package keeper_test

import (
sdk "github.com/cosmos/cosmos-sdk/types"
channeltypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types"
dacktypes "github.com/dymensionxyz/dymension/v3/x/delayedack/types"
)

func (suite *KeeperTestSuite) TestCreateDemandOrderOnRecv() {
tests := []struct {
name string
memo string
expectedErr bool
expectedFee string
expectedPrice string // considering bridging fee of 1%
}{
{
name: "fee by memo - create demand order",
memo: `{"eibc":{"fee":"100"}}`,
expectedErr: false,
expectedFee: "100",
expectedPrice: "890",
},
{
name: "empty memo - create demand order",
memo: "",
expectedErr: false,
expectedPrice: "990",
},
{
name: "memo w/o eibc - create demand order",
memo: `{"notEIBC":{}}`,
expectedErr: false,
expectedPrice: "990",
},
{
name: "bad memo - fail",
memo: "bad",
expectedErr: true,
},
{
name: "PFM memo - fail",
memo: `{"forward":{}}`,
expectedErr: true,
},
}

// set 1% bridging fee
dackParams := dacktypes.NewParams("hour", sdk.NewDecWithPrec(1, 2)) // 1%
suite.App.DelayedAckKeeper.SetParams(suite.Ctx, dackParams)

amt, _ := sdk.NewIntFromString(transferPacketData.Amount)
bridgeFee := suite.App.DelayedAckKeeper.BridgingFeeFromAmt(suite.Ctx, amt)
suite.Require().True(bridgeFee.IsPositive())

for _, tt := range tests {
suite.Run(tt.name, func() {
// modify the memo and set rollapp packet
transferPacketData.Memo = tt.memo
packet = channeltypes.NewPacket(transferPacketData.GetBytes(), 1, portid, chanid, cpportid, cpchanid, timeoutHeight, timeoutTimestamp)
rollappPacket.Packet = &packet
suite.App.DelayedAckKeeper.SetRollappPacket(suite.Ctx, *rollappPacket)

// Create new demand order
order, err := suite.App.EIBCKeeper.CreateDemandOrderOnRecv(suite.Ctx, transferPacketData, rollappPacket)
if tt.expectedErr {
suite.Require().Error(err)
return
}

suite.Require().NoError(err)
suite.Require().NotNil(order)
if tt.expectedFee != "" {
suite.Require().Equal(tt.expectedFee, order.Fee[0].Amount.String())
suite.Require().Equal(tt.expectedPrice, order.Price[0].Amount.String())
} else {
suite.Require().Len(order.Fee, 0)
suite.Require().Equal(tt.expectedPrice, order.Price[0].Amount.String())
}
})
}
}
Loading

0 comments on commit 0274850

Please sign in to comment.