Skip to content

Commit

Permalink
Fixes and add unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
zale144 committed Oct 21, 2024
1 parent 462784f commit 093772d
Show file tree
Hide file tree
Showing 5 changed files with 243 additions and 41 deletions.
20 changes: 4 additions & 16 deletions x/eibc/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func (m msgServer) FulfillOrderAuthorized(goCtx context.Context, msg *types.MsgF

lpAccount := m.ak.GetAccount(ctx, msg.GetLPBech32Address())
if lpAccount == nil {
return nil, types.ErrGranterAddressDoesNotExist
return nil, types.ErrLPAccountDoesNotExist
}

// Send the funds from the lpAccount to the eibc packet original recipient
Expand All @@ -102,29 +102,17 @@ func (m msgServer) FulfillOrderAuthorized(goCtx context.Context, msg *types.MsgF
return nil, err
}

// TODO: will this work for Policy address?
operatorAccount := m.ak.GetAccount(ctx, msg.GetOperatorBech32Address())
operatorAccount := m.ak.GetAccount(ctx, msg.GetOperatorFeeBech32Address())
if operatorAccount == nil {
return nil, types.ErrFulfillerAddressDoesNotExist
}

// by default, the operator account receives the operator share
feePartReceiver := operatorAccount
// if the operator fee address is provided, the operator fee share is sent to that address
if msg.OperatorFeeAddress != "" {
operatorFeeAccount := m.ak.GetAccount(ctx, msg.GetOperatorFeeBech32Address())
if operatorFeeAccount == nil {
return nil, types.ErrOperatorAddressDoesNotExist
}
feePartReceiver = operatorFeeAccount
return nil, types.ErrOperatorFeeAccountDoesNotExist
}

fee := sdk.NewDecFromInt(demandOrder.GetFeeAmount())
operatorFee := fee.Mul(msg.OperatorFeeShare.Dec).TruncateInt()

if operatorFee.IsPositive() {
// Send the fee part to the fulfiller/operator
err = m.bk.SendCoins(ctx, lpAccount.GetAddress(), feePartReceiver.GetAddress(), sdk.NewCoins(sdk.NewCoin(demandOrder.Price[0].Denom, operatorFee)))
err = m.bk.SendCoins(ctx, lpAccount.GetAddress(), operatorAccount.GetAddress(), sdk.NewCoins(sdk.NewCoin(demandOrder.Price[0].Denom, operatorFee)))
if err != nil {
logger.Error("Failed to send fee part to operator", "error", err)
return nil, err
Expand Down
217 changes: 217 additions & 0 deletions x/eibc/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ import (
"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
bankutil "github.com/cosmos/cosmos-sdk/x/bank/testutil"
"github.com/cosmos/gogoproto/proto"
"github.com/dymensionxyz/sdk-utils/utils/uevent"
"github.com/stretchr/testify/require"

"github.com/dymensionxyz/dymension/v3/app/apptesting"
"github.com/dymensionxyz/dymension/v3/testutil/sample"
commontypes "github.com/dymensionxyz/dymension/v3/x/common/types"
dacktypes "github.com/dymensionxyz/dymension/v3/x/delayedack/types"
"github.com/dymensionxyz/dymension/v3/x/eibc/types"
Expand Down Expand Up @@ -145,6 +148,220 @@ func (suite *KeeperTestSuite) TestMsgFulfillOrder() {
}
}

func (suite *KeeperTestSuite) TestMsgFulfillOrderAuthorized() {
tests := []struct {
name string
orderPrice sdk.Coin
orderFee sdk.Int
orderRecipient string
msg *types.MsgFulfillOrderAuthorized
lpAccountBalance sdk.Coins
operatorFeeAccountBalance sdk.Coins
expectError error
expectOrderFulfilled bool
expectedLPAccountBalance sdk.Coins
expectedOperatorFeeAccountBalance sdk.Coins
}{
{
name: "Successful fulfillment",
orderPrice: sdk.NewInt64Coin("adym", 90),
orderFee: sdk.NewInt(10),
orderRecipient: sample.AccAddress(),
msg: &types.MsgFulfillOrderAuthorized{
RollappId: rollappPacket.RollappId,
Price: sdk.NewCoins(sdk.NewInt64Coin("adym", 90)),
ExpectedFee: "10",
OperatorFeeShare: sdk.DecProto{Dec: sdk.NewDecWithPrec(2, 1)}, // 0.2
OperatorFeeAddress: sample.AccAddress(),
LpAddress: sample.AccAddress(),
SettlementValidated: false,
},
lpAccountBalance: sdk.NewCoins(sdk.NewInt64Coin("adym", 200)),
operatorFeeAccountBalance: sdk.NewCoins(sdk.NewInt64Coin("adym", 50)),
expectError: nil,
expectOrderFulfilled: true,
expectedLPAccountBalance: sdk.NewCoins(sdk.NewInt64Coin("adym", 108)), // 200 - 90 (price) - 2 (operator fee)
expectedOperatorFeeAccountBalance: sdk.NewCoins(sdk.NewInt64Coin("adym", 52)), // 50 + 2 (operator fee)
},
{
name: "Failure due to mismatched rollapp ID",
orderPrice: sdk.NewInt64Coin("adym", 100),
orderFee: sdk.NewInt(10),
orderRecipient: sample.AccAddress(),
msg: &types.MsgFulfillOrderAuthorized{
RollappId: "rollapp2", // Mismatched Rollapp ID
Price: sdk.NewCoins(sdk.NewInt64Coin("adym", 100)),
ExpectedFee: "10",
OperatorFeeShare: sdk.DecProto{Dec: sdk.NewDecWithPrec(2, 1)}, // 0.2
OperatorFeeAddress: sample.AccAddress(),
LpAddress: sample.AccAddress(),
SettlementValidated: false,
},
lpAccountBalance: sdk.NewCoins(sdk.NewInt64Coin("adym", 200)),
operatorFeeAccountBalance: sdk.NewCoins(sdk.NewInt64Coin("adym", 50)),
expectError: types.ErrRollappIdMismatch,
expectOrderFulfilled: false,
expectedLPAccountBalance: sdk.NewCoins(sdk.NewInt64Coin("adym", 200)), // Unchanged
},
{
name: "Failure due to mismatched price",
orderPrice: sdk.NewInt64Coin("adym", 100),
orderFee: sdk.NewInt(10),
orderRecipient: sample.AccAddress(),
msg: &types.MsgFulfillOrderAuthorized{
RollappId: rollappPacket.RollappId,
Price: sdk.NewCoins(sdk.NewInt64Coin("adym", 110)), // Mismatched Price
ExpectedFee: "10",
OperatorFeeShare: sdk.DecProto{Dec: sdk.NewDecWithPrec(2, 1)}, // 0.2
OperatorFeeAddress: sample.AccAddress(),
LpAddress: sample.AccAddress(),
SettlementValidated: false,
},
lpAccountBalance: sdk.NewCoins(sdk.NewInt64Coin("adym", 200)),
operatorFeeAccountBalance: sdk.NewCoins(sdk.NewInt64Coin("adym", 50)),
expectError: types.ErrPriceMismatch,
expectOrderFulfilled: false,
expectedLPAccountBalance: sdk.NewCoins(sdk.NewInt64Coin("adym", 200)), // Unchanged
},
{
name: "Failure due to mismatched expected fee",
orderPrice: sdk.NewInt64Coin("adym", 100),
orderFee: sdk.NewInt(10),
orderRecipient: sample.AccAddress(),
msg: &types.MsgFulfillOrderAuthorized{
RollappId: rollappPacket.RollappId,
Price: sdk.NewCoins(sdk.NewInt64Coin("adym", 100)),
ExpectedFee: "15", // Mismatched Expected Fee
OperatorFeeShare: sdk.DecProto{Dec: sdk.NewDecWithPrec(2, 1)}, // 0.2
OperatorFeeAddress: sample.AccAddress(),
LpAddress: sample.AccAddress(),
SettlementValidated: false,
},
lpAccountBalance: sdk.NewCoins(sdk.NewInt64Coin("adym", 200)),
operatorFeeAccountBalance: sdk.NewCoins(sdk.NewInt64Coin("adym", 50)),
expectError: types.ErrExpectedFeeNotMet,
expectOrderFulfilled: false,
expectedLPAccountBalance: sdk.NewCoins(sdk.NewInt64Coin("adym", 200)), // Unchanged
},
{
name: "Failure due to LP account not existing",
orderPrice: sdk.NewInt64Coin("adym", 100),
orderFee: sdk.NewInt(10),
orderRecipient: sample.AccAddress(),
msg: &types.MsgFulfillOrderAuthorized{
RollappId: rollappPacket.RollappId,
Price: sdk.NewCoins(sdk.NewInt64Coin("adym", 100)),
ExpectedFee: "10",
OperatorFeeShare: sdk.DecProto{Dec: sdk.NewDecWithPrec(2, 1)}, // 0.2
OperatorFeeAddress: sample.AccAddress(),
LpAddress: sample.AccAddress(), // Non-existent LP account
SettlementValidated: false,
},
lpAccountBalance: nil, // Account does not exist
operatorFeeAccountBalance: sdk.NewCoins(sdk.NewInt64Coin("adym", 50)),
expectError: types.ErrLPAccountDoesNotExist,
expectOrderFulfilled: false,
},
{
name: "Failure due to operator fee account not existing",
orderPrice: sdk.NewInt64Coin("adym", 100),
orderFee: sdk.NewInt(10),
orderRecipient: sample.AccAddress(),
msg: &types.MsgFulfillOrderAuthorized{
RollappId: rollappPacket.RollappId,
Price: sdk.NewCoins(sdk.NewInt64Coin("adym", 100)),
ExpectedFee: "10",
OperatorFeeShare: sdk.DecProto{Dec: sdk.NewDecWithPrec(2, 1)}, // 0.2
OperatorFeeAddress: sample.AccAddress(), // Non-existent operator account
LpAddress: sample.AccAddress(),
SettlementValidated: false,
},
lpAccountBalance: sdk.NewCoins(sdk.NewInt64Coin("adym", 200)),
operatorFeeAccountBalance: nil, // Account does not exist
expectError: types.ErrOperatorFeeAccountDoesNotExist,
expectOrderFulfilled: false,
expectedLPAccountBalance: sdk.NewCoins(sdk.NewInt64Coin("adym", 200)), // Unchanged
},
{
name: "Failure due to insufficient funds in LP account",
orderPrice: sdk.NewInt64Coin("adym", 100),
orderFee: sdk.NewInt(10),
orderRecipient: sample.AccAddress(),
msg: &types.MsgFulfillOrderAuthorized{
RollappId: rollappPacket.RollappId,
Price: sdk.NewCoins(sdk.NewInt64Coin("adym", 100)),
ExpectedFee: "10",
OperatorFeeShare: sdk.DecProto{Dec: sdk.NewDecWithPrec(2, 1)}, // 0.2
OperatorFeeAddress: sample.AccAddress(),
LpAddress: sample.AccAddress(),
SettlementValidated: false,
},
lpAccountBalance: sdk.NewCoins(sdk.NewInt64Coin("adym", 90)), // Insufficient funds
operatorFeeAccountBalance: sdk.NewCoins(sdk.NewInt64Coin("adym", 50)),
expectError: sdkerrors.ErrInsufficientFunds,
expectOrderFulfilled: false,
expectedLPAccountBalance: sdk.NewCoins(sdk.NewInt64Coin("adym", 90)), // Unchanged
},
}

for _, tc := range tests {
suite.Run(tc.name, func() {
// Set up initial state
suite.SetupTest() // Reset the context and keepers before each test

// Create accounts
var lpAccount, operatorFeeAccount sdk.AccAddress

// LP Account
if tc.lpAccountBalance != nil {
lpAccount = sdk.MustAccAddressFromBech32(tc.msg.LpAddress)
err := bankutil.FundAccount(suite.App.BankKeeper, suite.Ctx, lpAccount, tc.lpAccountBalance)
require.NoError(suite.T(), err, "Failed to fund LP account")
}

// Operator Account
if tc.operatorFeeAccountBalance != nil {
operatorFeeAccount = sdk.MustAccAddressFromBech32(tc.msg.OperatorFeeAddress)
err := bankutil.FundAccount(suite.App.BankKeeper, suite.Ctx, operatorFeeAccount, tc.operatorFeeAccountBalance)
require.NoError(suite.T(), err, "Failed to fund operator account")
}

suite.App.DelayedAckKeeper.SetRollappPacket(suite.Ctx, *rollappPacket)
demandOrder := types.NewDemandOrder(*rollappPacket, tc.orderPrice.Amount, tc.orderFee, tc.orderPrice.Denom, tc.orderRecipient)
err := suite.App.EIBCKeeper.SetDemandOrder(suite.Ctx, demandOrder)
suite.Require().NoError(err)

tc.msg.OrderId = demandOrder.Id

// Execute the handler
_, err = suite.msgServer.FulfillOrderAuthorized(sdk.WrapSDKContext(suite.Ctx), tc.msg)

// Check for expected errors
if tc.expectError != nil {
suite.Require().Error(err, tc.name)
suite.Require().ErrorContains(err, tc.expectError.Error(), tc.name)
} else {
suite.Require().NoError(err, tc.name)
}

// Check if the demand order is fulfilled
gotOrder, _ := suite.App.EIBCKeeper.GetDemandOrder(suite.Ctx, commontypes.Status_PENDING, demandOrder.Id)
suite.Require().Equal(tc.expectOrderFulfilled, gotOrder.IsFulfilled(), tc.name)

// Check account balances if no error expected
if tc.expectError == nil {
// LP Account Balance
lpBalance := suite.App.BankKeeper.GetAllBalances(suite.Ctx, lpAccount)
suite.Require().Equal(tc.expectedLPAccountBalance, lpBalance, "LP account balance mismatch")

// Operator Fee Account Balance (if applicable)
operatorFeeBalance := suite.App.BankKeeper.GetAllBalances(suite.Ctx, operatorFeeAccount)
suite.Require().Equal(tc.expectedOperatorFeeAccountBalance, operatorFeeBalance, "Operator fee account balance mismatch")
}
})
}
}

// TestFulfillOrderEvent tests the event upon fulfilling a demand order
func (suite *KeeperTestSuite) TestFulfillOrderEvent() {
// Create and fund the account
Expand Down
38 changes: 19 additions & 19 deletions x/eibc/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,23 @@ import (

// x/eibc module sentinel errors
var (
ErrInvalidOrderID = errorsmod.Register(ModuleName, 3, "Invalid order ID")
ErrDemandOrderAlreadyExist = errorsmod.Register(ModuleName, 4, "Demand order already exists")
ErrDemandOrderDoesNotExist = errorsmod.Register(ModuleName, 5, "Demand order does not exist")
ErrDemandOrderInactive = errorsmod.Register(ModuleName, 6, "Demand order inactive")
ErrFulfillerAddressDoesNotExist = errorsmod.Register(ModuleName, 7, "Fulfiller address does not exist")
ErrInvalidRecipientAddress = errorsmod.Register(ModuleName, 8, "Invalid recipient address")
ErrBlockedAddress = errorsmod.Register(ModuleName, 9, "Can't purchase demand order for recipient with blocked address")
ErrDemandAlreadyFulfilled = errorsmod.Register(ModuleName, 10, "Demand order already fulfilled")
ErrFeeTooHigh = errorsmod.Register(ModuleName, 11, "Fee must be less than or equal to the total amount")
ErrExpectedFeeNotMet = errorsmod.Register(ModuleName, 12, "Expected fee not met")
ErrNegativeFee = errorsmod.Register(ModuleName, 13, "Fee must be greater than or equal to 0")
ErrMultipleDenoms = errorsmod.Register(ModuleName, 15, "Multiple denoms not allowed")
ErrEmptyPrice = errorsmod.Register(ModuleName, 16, "Price must be greater than 0")
ErrOperatorAddressDoesNotExist = errorsmod.Register(ModuleName, 17, "Operator address does not exist")
ErrGranterAddressDoesNotExist = errorsmod.Register(ModuleName, 18, "Granter address does not exist")
ErrRollappStateInfoNotFound = errorsmod.Register(ModuleName, 19, "Rollapp state info not found")
ErrOrderNotSettlementValidated = errorsmod.Register(ModuleName, 20, "Demand order not settlement validated")
ErrRollappIdMismatch = errorsmod.Register(ModuleName, 21, "Rollapp ID mismatch")
ErrPriceMismatch = errorsmod.Register(ModuleName, 22, "Price mismatch")
ErrInvalidOrderID = errorsmod.Register(ModuleName, 3, "Invalid order ID")
ErrDemandOrderAlreadyExist = errorsmod.Register(ModuleName, 4, "Demand order already exists")
ErrDemandOrderDoesNotExist = errorsmod.Register(ModuleName, 5, "Demand order does not exist")
ErrDemandOrderInactive = errorsmod.Register(ModuleName, 6, "Demand order inactive")
ErrFulfillerAddressDoesNotExist = errorsmod.Register(ModuleName, 7, "Fulfiller address does not exist")
ErrInvalidRecipientAddress = errorsmod.Register(ModuleName, 8, "Invalid recipient address")
ErrBlockedAddress = errorsmod.Register(ModuleName, 9, "Can't purchase demand order for recipient with blocked address")
ErrDemandAlreadyFulfilled = errorsmod.Register(ModuleName, 10, "Demand order already fulfilled")
ErrFeeTooHigh = errorsmod.Register(ModuleName, 11, "Fee must be less than or equal to the total amount")
ErrExpectedFeeNotMet = errorsmod.Register(ModuleName, 12, "Expected fee not met")
ErrNegativeFee = errorsmod.Register(ModuleName, 13, "Fee must be greater than or equal to 0")
ErrMultipleDenoms = errorsmod.Register(ModuleName, 15, "Multiple denoms not allowed")
ErrEmptyPrice = errorsmod.Register(ModuleName, 16, "Price must be greater than 0")
ErrOperatorFeeAccountDoesNotExist = errorsmod.Register(ModuleName, 17, "Operator fee account does not exist")
ErrLPAccountDoesNotExist = errorsmod.Register(ModuleName, 18, "LP account does not exist")
ErrRollappStateInfoNotFound = errorsmod.Register(ModuleName, 19, "Rollapp state info not found")
ErrOrderNotSettlementValidated = errorsmod.Register(ModuleName, 20, "Demand order not settlement validated")
ErrRollappIdMismatch = errorsmod.Register(ModuleName, 21, "Rollapp ID mismatch")
ErrPriceMismatch = errorsmod.Register(ModuleName, 22, "Price mismatch")
)
3 changes: 2 additions & 1 deletion x/eibc/types/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"

"github.com/dymensionxyz/dymension/v3/testutil/sample"
"github.com/dymensionxyz/dymension/v3/x/eibc/types"
)

Expand Down Expand Up @@ -63,7 +64,7 @@ var validDemandOrder = types.DemandOrder{
Id: "1",
Price: sdk.Coins{sdk.NewInt64Coin("denom", 2)},
Fee: sdk.Coins{sdk.NewInt64Coin("denom", 1)},
Recipient: "cosmos18wvvwfmq77a6d8tza4h5sfuy2yj3jj88yqg82a",
Recipient: sample.AccAddress(),
}

var validParams = types.Params{
Expand Down
6 changes: 1 addition & 5 deletions x/eibc/types/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func (msg *MsgFulfillOrderAuthorized) ValidateBasic() error {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "rollapp id cannot be empty")
}

err := validateCommon(msg.OrderId, msg.ExpectedFee, msg.OperatorAddress, msg.LpAddress, msg.OperatorAddress)
err := validateCommon(msg.OrderId, msg.ExpectedFee, msg.OperatorFeeAddress, msg.LpAddress)
if err != nil {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, err.Error())
}
Expand All @@ -126,10 +126,6 @@ func (msg *MsgFulfillOrderAuthorized) ValidateBasic() error {
return nil
}

func (msg *MsgFulfillOrderAuthorized) GetOperatorBech32Address() []byte {
return sdk.MustAccAddressFromBech32(msg.OperatorAddress)
}

func (msg *MsgFulfillOrderAuthorized) GetLPBech32Address() []byte {
return sdk.MustAccAddressFromBech32(msg.LpAddress)
}
Expand Down

0 comments on commit 093772d

Please sign in to comment.