0g-chain/x/swap/handler_test.go
Nick DeLuca 20437a91fb
Add E2E Swap Support (#959)
* add message types for swaps

* add tx client commands

* add test coverage for swap message deadlines

* start handler swap tests, export handler result message event into
private method, add stubbed keeper methods

* add initial swap implementation to get handler tests passing; adds event
specific for trades

* add handler acceptance test for slippage in exact input and exact output
swaps

* implement slippage limit for swap keeper methods

* add tests to ensure a user can only swap spendable coins

* test pool not found, panic on invalid pool, and panic when module
account does not have enough funds

* validate that the exact output when using for exact swaps is less than
the pool liquidity

* nit: long line

* add validation that swap output is greater than zero

* add rest txs for swap messages

* nit: lints

* dry up swap keeper methods

* from pr feedback - spelling and increase clairty around the output
amount of a swap rounding to zero
2021-07-13 17:44:05 -05:00

581 lines
20 KiB
Go

package swap_test
import (
"fmt"
"testing"
"time"
"github.com/kava-labs/kava/x/swap"
"github.com/kava-labs/kava/x/swap/testutil"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/stretchr/testify/suite"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
tmtime "github.com/tendermint/tendermint/types/time"
)
var swapModuleAccountAddress = sdk.AccAddress(crypto.AddressHash([]byte(swap.ModuleAccountName)))
type handlerTestSuite struct {
testutil.Suite
handler sdk.Handler
}
func (suite *handlerTestSuite) SetupTest() {
suite.Suite.SetupTest()
suite.handler = swap.NewHandler(suite.Keeper)
}
func (suite *handlerTestSuite) TestDeposit_CreatePool() {
pool := swap.NewAllowedPool("ukava", "usdx")
suite.Require().NoError(pool.Validate())
suite.Keeper.SetParams(suite.Ctx, swap.NewParams(swap.NewAllowedPools(pool), swap.DefaultSwapFee))
balance := sdk.NewCoins(
sdk.NewCoin(pool.TokenA, sdk.NewInt(10e6)),
sdk.NewCoin(pool.TokenB, sdk.NewInt(50e6)),
)
depositor := suite.CreateAccount(balance)
deposit := swap.NewMsgDeposit(
depositor.GetAddress(),
sdk.NewCoin(pool.TokenA, depositor.GetCoins().AmountOf(pool.TokenA)),
sdk.NewCoin(pool.TokenB, depositor.GetCoins().AmountOf(pool.TokenB)),
sdk.MustNewDecFromStr("0.01"),
time.Now().Add(10*time.Minute).Unix(),
)
res, err := suite.handler(suite.Ctx, deposit)
suite.Require().NoError(err)
suite.AccountBalanceEqual(depositor, sdk.Coins(nil))
suite.ModuleAccountBalanceEqual(balance)
suite.PoolLiquidityEqual(balance)
suite.PoolShareValueEqual(depositor, pool, balance)
suite.EventsContains(res.Events, sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, swap.AttributeValueCategory),
sdk.NewAttribute(sdk.AttributeKeySender, depositor.GetAddress().String()),
))
suite.EventsContains(res.Events, sdk.NewEvent(
bank.EventTypeTransfer,
sdk.NewAttribute(bank.AttributeKeyRecipient, swapModuleAccountAddress.String()),
sdk.NewAttribute(bank.AttributeKeySender, depositor.GetAddress().String()),
sdk.NewAttribute(sdk.AttributeKeyAmount, balance.String()),
))
suite.EventsContains(res.Events, sdk.NewEvent(
swap.EventTypeSwapDeposit,
sdk.NewAttribute(swap.AttributeKeyPoolID, swap.PoolID(pool.TokenA, pool.TokenB)),
sdk.NewAttribute(swap.AttributeKeyDepositor, depositor.GetAddress().String()),
sdk.NewAttribute(sdk.AttributeKeyAmount, balance.String()),
sdk.NewAttribute(swap.AttributeKeyShares, "22360679"),
))
}
func (suite *handlerTestSuite) TestDeposit_DeadlineExceeded() {
pool := swap.NewAllowedPool("ukava", "usdx")
suite.Require().NoError(pool.Validate())
suite.Keeper.SetParams(suite.Ctx, swap.NewParams(swap.NewAllowedPools(pool), swap.DefaultSwapFee))
balance := sdk.NewCoins(
sdk.NewCoin(pool.TokenA, sdk.NewInt(10e6)),
sdk.NewCoin(pool.TokenB, sdk.NewInt(50e6)),
)
depositor := suite.CreateAccount(balance)
deposit := swap.NewMsgDeposit(
depositor.GetAddress(),
sdk.NewCoin(pool.TokenA, depositor.GetCoins().AmountOf(pool.TokenA)),
sdk.NewCoin(pool.TokenB, depositor.GetCoins().AmountOf(pool.TokenB)),
sdk.MustNewDecFromStr("0.01"),
suite.Ctx.BlockTime().Add(-1*time.Second).Unix(),
)
res, err := suite.handler(suite.Ctx, deposit)
suite.EqualError(err, fmt.Sprintf("deadline exceeded: block time %d >= deadline %d", suite.Ctx.BlockTime().Unix(), deposit.GetDeadline().Unix()))
suite.Nil(res)
}
func (suite *handlerTestSuite) TestDeposit_ExistingPool() {
pool := swap.NewAllowedPool("ukava", "usdx")
reserves := sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(10e6)),
sdk.NewCoin("usdx", sdk.NewInt(50e6)),
)
err := suite.CreatePool(reserves)
suite.Require().NoError(err)
balance := sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(1e6)),
sdk.NewCoin("usdx", sdk.NewInt(5e6)),
)
depositor := suite.NewAccountFromAddr(sdk.AccAddress("new depositor"), balance)
deposit := swap.NewMsgDeposit(
depositor.GetAddress(),
sdk.NewCoin("usdx", depositor.GetCoins().AmountOf("usdx")),
sdk.NewCoin("ukava", depositor.GetCoins().AmountOf("ukava")),
sdk.MustNewDecFromStr("0.01"),
time.Now().Add(10*time.Minute).Unix(),
)
res, err := suite.handler(suite.Ctx, deposit)
suite.Require().NoError(err)
expectedDeposit := sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(1e6)),
sdk.NewCoin("usdx", sdk.NewInt(5e6)),
)
expectedShareValue := sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(999999)),
sdk.NewCoin("usdx", sdk.NewInt(4999998)),
)
suite.AccountBalanceEqual(depositor, balance.Sub(expectedDeposit))
suite.ModuleAccountBalanceEqual(reserves.Add(expectedDeposit...))
suite.PoolLiquidityEqual(reserves.Add(expectedDeposit...))
suite.PoolShareValueEqual(depositor, pool, expectedShareValue)
suite.EventsContains(res.Events, sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, swap.AttributeValueCategory),
sdk.NewAttribute(sdk.AttributeKeySender, depositor.GetAddress().String()),
))
suite.EventsContains(res.Events, sdk.NewEvent(
bank.EventTypeTransfer,
sdk.NewAttribute(bank.AttributeKeyRecipient, swapModuleAccountAddress.String()),
sdk.NewAttribute(bank.AttributeKeySender, depositor.GetAddress().String()),
sdk.NewAttribute(sdk.AttributeKeyAmount, expectedDeposit.String()),
))
suite.EventsContains(res.Events, sdk.NewEvent(
swap.EventTypeSwapDeposit,
sdk.NewAttribute(swap.AttributeKeyPoolID, swap.PoolID(pool.TokenA, pool.TokenB)),
sdk.NewAttribute(swap.AttributeKeyDepositor, depositor.GetAddress().String()),
sdk.NewAttribute(sdk.AttributeKeyAmount, expectedDeposit.String()),
sdk.NewAttribute(swap.AttributeKeyShares, "2236067"),
))
}
func (suite *handlerTestSuite) TestDeposit_ExistingPool_SlippageFailure() {
reserves := sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(10e6)),
sdk.NewCoin("usdx", sdk.NewInt(50e6)),
)
err := suite.CreatePool(reserves)
suite.Require().NoError(err)
balance := sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(5e6)),
sdk.NewCoin("usdx", sdk.NewInt(5e6)),
)
depositor := suite.CreateAccount(balance)
deposit := swap.NewMsgDeposit(
depositor.GetAddress(),
sdk.NewCoin("usdx", depositor.GetCoins().AmountOf("usdx")),
sdk.NewCoin("ukava", depositor.GetCoins().AmountOf("ukava")),
sdk.MustNewDecFromStr("0.01"),
time.Now().Add(10*time.Minute).Unix(),
)
res, err := suite.handler(suite.Ctx, deposit)
suite.EqualError(err, "slippage exceeded: slippage 4.000000000000000000 > limit 0.010000000000000000")
suite.Nil(res)
}
func (suite *handlerTestSuite) TestWithdraw_AllShares() {
reserves := sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(10e6)),
sdk.NewCoin("usdx", sdk.NewInt(50e6)),
)
depositor := suite.CreateAccount(reserves)
pool := swap.NewAllowedPool(reserves[0].Denom, reserves[1].Denom)
suite.Require().NoError(pool.Validate())
suite.Keeper.SetParams(suite.Ctx, swap.NewParams(swap.NewAllowedPools(pool), swap.DefaultSwapFee))
err := suite.Keeper.Deposit(suite.Ctx, depositor.GetAddress(), reserves[0], reserves[1], sdk.MustNewDecFromStr("1"))
suite.Require().NoError(err)
withdraw := swap.NewMsgWithdraw(
depositor.GetAddress(),
sdk.NewInt(22360679),
reserves[0],
reserves[1],
time.Now().Add(10*time.Minute).Unix(),
)
ctx := suite.App.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
res, err := suite.handler(ctx, withdraw)
suite.Require().NoError(err)
suite.AccountBalanceEqual(depositor, reserves)
suite.ModuleAccountBalanceEqual(sdk.Coins(nil))
suite.PoolDeleted("ukava", "usdx")
suite.PoolSharesDeleted(depositor.GetAddress(), "ukava", "usdx")
suite.EventsContains(res.Events, sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, swap.AttributeValueCategory),
sdk.NewAttribute(sdk.AttributeKeySender, depositor.GetAddress().String()),
))
suite.EventsContains(res.Events, sdk.NewEvent(
bank.EventTypeTransfer,
sdk.NewAttribute(bank.AttributeKeyRecipient, depositor.GetAddress().String()),
sdk.NewAttribute(bank.AttributeKeySender, swapModuleAccountAddress.String()),
sdk.NewAttribute(sdk.AttributeKeyAmount, reserves.String()),
))
suite.EventsContains(res.Events, sdk.NewEvent(
swap.EventTypeSwapWithdraw,
sdk.NewAttribute(swap.AttributeKeyPoolID, swap.PoolID(pool.TokenA, pool.TokenB)),
sdk.NewAttribute(swap.AttributeKeyOwner, depositor.GetAddress().String()),
sdk.NewAttribute(sdk.AttributeKeyAmount, reserves.String()),
sdk.NewAttribute(swap.AttributeKeyShares, "22360679"),
))
}
func (suite *handlerTestSuite) TestWithdraw_PartialShares() {
reserves := sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(10e6)),
sdk.NewCoin("usdx", sdk.NewInt(50e6)),
)
depositor := suite.CreateAccount(reserves)
pool := swap.NewAllowedPool(reserves[0].Denom, reserves[1].Denom)
suite.Require().NoError(pool.Validate())
suite.Keeper.SetParams(suite.Ctx, swap.NewParams(swap.NewAllowedPools(pool), swap.DefaultSwapFee))
err := suite.Keeper.Deposit(suite.Ctx, depositor.GetAddress(), reserves[0], reserves[1], sdk.MustNewDecFromStr("1"))
suite.Require().NoError(err)
minTokenA := sdk.NewCoin("ukava", sdk.NewInt(4999999))
minTokenB := sdk.NewCoin("usdx", sdk.NewInt(24999998))
withdraw := swap.NewMsgWithdraw(
depositor.GetAddress(),
sdk.NewInt(11180339),
minTokenA,
minTokenB,
time.Now().Add(10*time.Minute).Unix(),
)
ctx := suite.App.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
res, err := suite.handler(ctx, withdraw)
suite.Require().NoError(err)
expectedCoinsReceived := sdk.NewCoins(minTokenA, minTokenB)
suite.AccountBalanceEqual(depositor, expectedCoinsReceived)
suite.ModuleAccountBalanceEqual(reserves.Sub(expectedCoinsReceived))
suite.PoolLiquidityEqual(reserves.Sub(expectedCoinsReceived))
suite.PoolShareValueEqual(depositor, swap.NewAllowedPool("ukava", "usdx"), reserves.Sub(expectedCoinsReceived))
suite.EventsContains(res.Events, sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, swap.AttributeValueCategory),
sdk.NewAttribute(sdk.AttributeKeySender, depositor.GetAddress().String()),
))
suite.EventsContains(res.Events, sdk.NewEvent(
bank.EventTypeTransfer,
sdk.NewAttribute(bank.AttributeKeyRecipient, depositor.GetAddress().String()),
sdk.NewAttribute(bank.AttributeKeySender, swapModuleAccountAddress.String()),
sdk.NewAttribute(sdk.AttributeKeyAmount, expectedCoinsReceived.String()),
))
suite.EventsContains(res.Events, sdk.NewEvent(
swap.EventTypeSwapWithdraw,
sdk.NewAttribute(swap.AttributeKeyPoolID, swap.PoolID(pool.TokenA, pool.TokenB)),
sdk.NewAttribute(swap.AttributeKeyOwner, depositor.GetAddress().String()),
sdk.NewAttribute(sdk.AttributeKeyAmount, expectedCoinsReceived.String()),
sdk.NewAttribute(swap.AttributeKeyShares, "11180339"),
))
}
func (suite *handlerTestSuite) TestWithdraw_SlippageFailure() {
reserves := sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(10e6)),
sdk.NewCoin("usdx", sdk.NewInt(50e6)),
)
depositor := suite.CreateAccount(reserves)
pool := swap.NewAllowedPool(reserves[0].Denom, reserves[1].Denom)
suite.Require().NoError(pool.Validate())
suite.Keeper.SetParams(suite.Ctx, swap.NewParams(swap.NewAllowedPools(pool), swap.DefaultSwapFee))
err := suite.Keeper.Deposit(suite.Ctx, depositor.GetAddress(), reserves[0], reserves[1], sdk.MustNewDecFromStr("1"))
suite.Require().NoError(err)
minTokenA := sdk.NewCoin("ukava", sdk.NewInt(5e6))
minTokenB := sdk.NewCoin("usdx", sdk.NewInt(25e6))
withdraw := swap.NewMsgWithdraw(
depositor.GetAddress(),
sdk.NewInt(11180339),
minTokenA,
minTokenB,
time.Now().Add(10*time.Minute).Unix(),
)
res, err := suite.handler(suite.Ctx, withdraw)
suite.EqualError(err, "slippage exceeded: minimum withdraw not met")
suite.Nil(res)
}
func (suite *handlerTestSuite) TestWithdraw_DeadlineExceeded() {
balance := sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(10e6)),
sdk.NewCoin("usdx", sdk.NewInt(50e6)),
)
from := suite.CreateAccount(balance)
withdraw := swap.NewMsgWithdraw(
from.GetAddress(),
sdk.NewInt(2e6),
sdk.NewCoin("ukava", sdk.NewInt(1e6)),
sdk.NewCoin("usdx", sdk.NewInt(5e6)),
suite.Ctx.BlockTime().Add(-1*time.Second).Unix(),
)
res, err := suite.handler(suite.Ctx, withdraw)
suite.EqualError(err, fmt.Sprintf("deadline exceeded: block time %d >= deadline %d", suite.Ctx.BlockTime().Unix(), withdraw.GetDeadline().Unix()))
suite.Nil(res)
}
func (suite *handlerTestSuite) TestSwapExactForTokens() {
reserves := sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(1000e6)),
sdk.NewCoin("usdx", sdk.NewInt(5000e6)),
)
err := suite.CreatePool(reserves)
suite.Require().NoError(err)
balance := sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(10e6)),
)
requester := suite.NewAccountFromAddr(sdk.AccAddress("requester"), balance)
swapInput := sdk.NewCoin("ukava", sdk.NewInt(1e6))
swapMsg := swap.NewMsgSwapExactForTokens(
requester.GetAddress(),
swapInput,
sdk.NewCoin("usdx", sdk.NewInt(5e6)),
sdk.MustNewDecFromStr("0.01"),
time.Now().Add(10*time.Minute).Unix(),
)
ctx := suite.App.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
res, err := suite.handler(ctx, swapMsg)
suite.Require().NoError(err)
expectedSwapOutput := sdk.NewCoin("usdx", sdk.NewInt(4980034))
suite.AccountBalanceEqual(requester, balance.Sub(sdk.NewCoins(swapInput)).Add(expectedSwapOutput))
suite.ModuleAccountBalanceEqual(reserves.Add(swapInput).Sub(sdk.NewCoins(expectedSwapOutput)))
suite.PoolLiquidityEqual(reserves.Add(swapInput).Sub(sdk.NewCoins(expectedSwapOutput)))
suite.EventsContains(res.Events, sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, swap.AttributeValueCategory),
sdk.NewAttribute(sdk.AttributeKeySender, requester.GetAddress().String()),
))
suite.EventsContains(res.Events, sdk.NewEvent(
bank.EventTypeTransfer,
sdk.NewAttribute(bank.AttributeKeyRecipient, swapModuleAccountAddress.String()),
sdk.NewAttribute(bank.AttributeKeySender, requester.GetAddress().String()),
sdk.NewAttribute(sdk.AttributeKeyAmount, swapInput.String()),
))
suite.EventsContains(res.Events, sdk.NewEvent(
bank.EventTypeTransfer,
sdk.NewAttribute(bank.AttributeKeyRecipient, requester.GetAddress().String()),
sdk.NewAttribute(bank.AttributeKeySender, swapModuleAccountAddress.String()),
sdk.NewAttribute(sdk.AttributeKeyAmount, expectedSwapOutput.String()),
))
suite.EventsContains(res.Events, sdk.NewEvent(
swap.EventTypeSwapTrade,
sdk.NewAttribute(swap.AttributeKeyPoolID, swap.PoolID("ukava", "usdx")),
sdk.NewAttribute(swap.AttributeKeyRequester, requester.GetAddress().String()),
sdk.NewAttribute(swap.AttributeKeySwapInput, swapInput.String()),
sdk.NewAttribute(swap.AttributeKeySwapOutput, expectedSwapOutput.String()),
sdk.NewAttribute(swap.AttributeKeyFeePaid, "3000ukava"),
sdk.NewAttribute(swap.AttributeKeyExactDirection, "input"),
))
}
func (suite *handlerTestSuite) TestSwapExactForTokens_SlippageFailure() {
reserves := sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(1000e6)),
sdk.NewCoin("usdx", sdk.NewInt(5000e6)),
)
err := suite.CreatePool(reserves)
suite.Require().NoError(err)
balance := sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(100e6)),
)
requester := suite.NewAccountFromAddr(sdk.AccAddress("requester"), balance)
swapInput := sdk.NewCoin("ukava", sdk.NewInt(1e6))
swapMsg := swap.NewMsgSwapExactForTokens(
requester.GetAddress(),
swapInput,
sdk.NewCoin("usdx", sdk.NewInt(5030338)),
sdk.MustNewDecFromStr("0.01"),
time.Now().Add(10*time.Minute).Unix(),
)
ctx := suite.App.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
res, err := suite.handler(ctx, swapMsg)
suite.EqualError(err, "slippage exceeded: slippage 0.010000123252155223 > limit 0.010000000000000000")
suite.Nil(res)
}
func (suite *handlerTestSuite) TestSwapExactForTokens_DeadlineExceeded() {
balance := sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(10e6)),
)
requester := suite.CreateAccount(balance)
swapMsg := swap.NewMsgSwapExactForTokens(
requester.GetAddress(),
sdk.NewCoin("ukava", sdk.NewInt(5e6)),
sdk.NewCoin("usdx", sdk.NewInt(25e5)),
sdk.MustNewDecFromStr("0.01"),
suite.Ctx.BlockTime().Add(-1*time.Second).Unix(),
)
res, err := suite.handler(suite.Ctx, swapMsg)
suite.EqualError(err, fmt.Sprintf("deadline exceeded: block time %d >= deadline %d", suite.Ctx.BlockTime().Unix(), swapMsg.GetDeadline().Unix()))
suite.Nil(res)
}
func (suite *handlerTestSuite) TestSwapForExactTokens() {
reserves := sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(1000e6)),
sdk.NewCoin("usdx", sdk.NewInt(5000e6)),
)
err := suite.CreatePool(reserves)
suite.Require().NoError(err)
balance := sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(10e6)),
)
requester := suite.NewAccountFromAddr(sdk.AccAddress("requester"), balance)
swapOutput := sdk.NewCoin("usdx", sdk.NewInt(5e6))
swapMsg := swap.NewMsgSwapForExactTokens(
requester.GetAddress(),
sdk.NewCoin("ukava", sdk.NewInt(1e6)),
swapOutput,
sdk.MustNewDecFromStr("0.01"),
time.Now().Add(10*time.Minute).Unix(),
)
ctx := suite.App.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
res, err := suite.handler(ctx, swapMsg)
suite.Require().NoError(err)
expectedSwapInput := sdk.NewCoin("ukava", sdk.NewInt(1004015))
suite.AccountBalanceEqual(requester, balance.Sub(sdk.NewCoins(expectedSwapInput)).Add(swapOutput))
suite.ModuleAccountBalanceEqual(reserves.Add(expectedSwapInput).Sub(sdk.NewCoins(swapOutput)))
suite.PoolLiquidityEqual(reserves.Add(expectedSwapInput).Sub(sdk.NewCoins(swapOutput)))
suite.EventsContains(res.Events, sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, swap.AttributeValueCategory),
sdk.NewAttribute(sdk.AttributeKeySender, requester.GetAddress().String()),
))
suite.EventsContains(res.Events, sdk.NewEvent(
bank.EventTypeTransfer,
sdk.NewAttribute(bank.AttributeKeyRecipient, swapModuleAccountAddress.String()),
sdk.NewAttribute(bank.AttributeKeySender, requester.GetAddress().String()),
sdk.NewAttribute(sdk.AttributeKeyAmount, expectedSwapInput.String()),
))
suite.EventsContains(res.Events, sdk.NewEvent(
bank.EventTypeTransfer,
sdk.NewAttribute(bank.AttributeKeyRecipient, requester.GetAddress().String()),
sdk.NewAttribute(bank.AttributeKeySender, swapModuleAccountAddress.String()),
sdk.NewAttribute(sdk.AttributeKeyAmount, swapOutput.String()),
))
suite.EventsContains(res.Events, sdk.NewEvent(
swap.EventTypeSwapTrade,
sdk.NewAttribute(swap.AttributeKeyPoolID, swap.PoolID("ukava", "usdx")),
sdk.NewAttribute(swap.AttributeKeyRequester, requester.GetAddress().String()),
sdk.NewAttribute(swap.AttributeKeySwapInput, expectedSwapInput.String()),
sdk.NewAttribute(swap.AttributeKeySwapOutput, swapOutput.String()),
sdk.NewAttribute(swap.AttributeKeyFeePaid, "3013ukava"),
sdk.NewAttribute(swap.AttributeKeyExactDirection, "output"),
))
}
func (suite *handlerTestSuite) TestSwapForExactTokens_SlippageFailure() {
reserves := sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(1000e6)),
sdk.NewCoin("usdx", sdk.NewInt(5000e6)),
)
err := suite.CreatePool(reserves)
suite.Require().NoError(err)
balance := sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(10e6)),
)
requester := suite.NewAccountFromAddr(sdk.AccAddress("requester"), balance)
swapOutput := sdk.NewCoin("usdx", sdk.NewInt(5e6))
swapMsg := swap.NewMsgSwapForExactTokens(
requester.GetAddress(),
sdk.NewCoin("ukava", sdk.NewInt(990991)),
swapOutput,
sdk.MustNewDecFromStr("0.01"),
time.Now().Add(10*time.Minute).Unix(),
)
ctx := suite.App.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
res, err := suite.handler(ctx, swapMsg)
suite.EqualError(err, "slippage exceeded: slippage 0.010000979019022939 > limit 0.010000000000000000")
suite.Nil(res)
}
func (suite *handlerTestSuite) TestSwapForExactTokens_DeadlineExceeded() {
balance := sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(10e6)),
)
requester := suite.CreateAccount(balance)
swapMsg := swap.NewMsgSwapForExactTokens(
requester.GetAddress(),
sdk.NewCoin("ukava", sdk.NewInt(5e6)),
sdk.NewCoin("usdx", sdk.NewInt(25e5)),
sdk.MustNewDecFromStr("0.01"),
suite.Ctx.BlockTime().Add(-1*time.Second).Unix(),
)
res, err := suite.handler(suite.Ctx, swapMsg)
suite.EqualError(err, fmt.Sprintf("deadline exceeded: block time %d >= deadline %d", suite.Ctx.BlockTime().Unix(), swapMsg.GetDeadline().Unix()))
suite.Nil(res)
}
func (suite *handlerTestSuite) TestInvalidMsg() {
res, err := suite.handler(suite.Ctx, sdk.NewTestMsg())
suite.Nil(res)
suite.EqualError(err, "unknown request: unrecognized swap message type: *types.TestMsg")
}
func TestHandlerTestSuite(t *testing.T) {
suite.Run(t, new(handlerTestSuite))
}