chore(auction): Moving bid extraction to from msg handler to ante handler (#135)
* moving bid extraction to antehandler * lint * fix * no zero timeout * reset integration tests
This commit is contained in:
parent
d495b38954
commit
3374203c39
@ -162,7 +162,10 @@ func (handler *CheckTxHandler) CheckTx() CheckTx {
|
||||
handler.baseApp.Logger().Info(
|
||||
"invalid bid tx",
|
||||
"err", err,
|
||||
"tx", req.Tx,
|
||||
"height", ctx.BlockHeight(),
|
||||
"bid_height", bidInfo.Timeout,
|
||||
"bidder", bidInfo.Bidder,
|
||||
"bid", bidInfo.Bid,
|
||||
"removing tx from mempool", true,
|
||||
)
|
||||
|
||||
@ -187,7 +190,10 @@ func (handler *CheckTxHandler) CheckTx() CheckTx {
|
||||
|
||||
handler.baseApp.Logger().Info(
|
||||
"valid bid tx",
|
||||
"tx", req.Tx,
|
||||
"height", ctx.BlockHeight(),
|
||||
"bid_height", bidInfo.Timeout,
|
||||
"bidder", bidInfo.Bidder,
|
||||
"bid", bidInfo.Bid,
|
||||
"inserting tx into mempool", true,
|
||||
)
|
||||
|
||||
|
||||
@ -11,7 +11,6 @@ import (
|
||||
|
||||
type BSDKHandlerOptions struct {
|
||||
BaseOptions ante.HandlerOptions
|
||||
Mempool block.Mempool
|
||||
MEVLane auctionante.MEVLane
|
||||
TxDecoder sdk.TxDecoder
|
||||
TxEncoder sdk.TxEncoder
|
||||
@ -55,7 +54,7 @@ func NewBSDKAnteHandler(options BSDKHandlerOptions) sdk.AnteHandler {
|
||||
ante.NewSigGasConsumeDecorator(options.BaseOptions.AccountKeeper, options.BaseOptions.SigGasConsumer),
|
||||
ante.NewSigVerificationDecorator(options.BaseOptions.AccountKeeper, options.BaseOptions.SignModeHandler),
|
||||
ante.NewIncrementSequenceDecorator(options.BaseOptions.AccountKeeper),
|
||||
auctionante.NewAuctionDecorator(options.auctionkeeper, options.TxEncoder, options.MEVLane, options.Mempool),
|
||||
auctionante.NewAuctionDecorator(options.auctionkeeper, options.TxEncoder, options.MEVLane),
|
||||
}
|
||||
|
||||
return sdk.ChainAnteDecorators(anteDecorators...)
|
||||
|
||||
@ -328,7 +328,6 @@ func New(
|
||||
TxEncoder: app.txConfig.TxEncoder(),
|
||||
FreeLane: freeLane,
|
||||
MEVLane: mevLane,
|
||||
Mempool: mempool,
|
||||
}
|
||||
anteHandler := NewBSDKAnteHandler(options)
|
||||
|
||||
|
||||
@ -1,145 +0,0 @@
|
||||
package testutils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
"github.com/golang/mock/gomock"
|
||||
)
|
||||
|
||||
type MockAccountKeeper struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockAccountKeeperMockRecorder
|
||||
}
|
||||
|
||||
type MockAccountKeeperMockRecorder struct {
|
||||
mock *MockAccountKeeper
|
||||
}
|
||||
|
||||
func NewMockAccountKeeper(ctrl *gomock.Controller) *MockAccountKeeper {
|
||||
mock := &MockAccountKeeper{ctrl: ctrl}
|
||||
mock.recorder = &MockAccountKeeperMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
func (m *MockAccountKeeper) EXPECT() *MockAccountKeeperMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
func (m *MockAccountKeeper) GetModuleAddress(name string) sdk.AccAddress {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetModuleAddress", name)
|
||||
ret0, _ := ret[0].(sdk.AccAddress)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (mr *MockAccountKeeperMockRecorder) GetModuleAddress(name interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetModuleAddress", reflect.TypeOf((*MockAccountKeeper)(nil).GetModuleAddress), name)
|
||||
}
|
||||
|
||||
type MockBankKeeper struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockBankKeeperMockRecorder
|
||||
}
|
||||
|
||||
type MockBankKeeperMockRecorder struct {
|
||||
mock *MockBankKeeper
|
||||
}
|
||||
|
||||
func NewMockBankKeeper(ctrl *gomock.Controller) *MockBankKeeper {
|
||||
mock := &MockBankKeeper{ctrl: ctrl}
|
||||
mock.recorder = &MockBankKeeperMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
func (m *MockBankKeeper) EXPECT() *MockBankKeeperMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
func (m *MockBankKeeper) GetBalance(ctx context.Context, addr sdk.AccAddress, denom string) sdk.Coin {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetBalance", ctx, addr, denom)
|
||||
ret0 := ret[0].(sdk.Coin)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (mr *MockBankKeeperMockRecorder) GetBalance(ctx, addr, denom interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBalance", reflect.TypeOf((*MockBankKeeper)(nil).GetBalance), ctx, addr, denom)
|
||||
}
|
||||
|
||||
func (m *MockBankKeeper) SendCoins(ctx context.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error {
|
||||
m.ctrl.T.Helper()
|
||||
m.ctrl.Call(m, "SendCoins", ctx, fromAddr, toAddr, amt)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mr *MockBankKeeperMockRecorder) SendCoins(ctx, fromAddr, toAddr, amt interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoins", reflect.TypeOf((*MockBankKeeper)(nil).SendCoins), ctx, fromAddr, toAddr, amt)
|
||||
}
|
||||
|
||||
type MockDistributionKeeperRecorder struct {
|
||||
mock *MockDistributionKeeper
|
||||
}
|
||||
|
||||
type MockDistributionKeeper struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockDistributionKeeperRecorder
|
||||
}
|
||||
|
||||
func NewMockDistributionKeeper(ctrl *gomock.Controller) *MockDistributionKeeper {
|
||||
mock := &MockDistributionKeeper{ctrl: ctrl}
|
||||
mock.recorder = &MockDistributionKeeperRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
func (m *MockDistributionKeeper) EXPECT() *MockDistributionKeeperRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
func (m *MockDistributionKeeper) GetPreviousProposerConsAddr(ctx context.Context) (sdk.ConsAddress, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetPreviousProposerConsAddr", ctx)
|
||||
ret0 := ret[0].(sdk.ConsAddress)
|
||||
return ret0, nil
|
||||
}
|
||||
|
||||
func (mr *MockDistributionKeeperRecorder) GetPreviousProposerConsAddr(ctx any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPreviousProposerConsAddr", reflect.TypeOf((*MockDistributionKeeper)(nil).GetPreviousProposerConsAddr), ctx)
|
||||
}
|
||||
|
||||
type MockStakingKeeperRecorder struct {
|
||||
mock *MockStakingKeeper
|
||||
}
|
||||
|
||||
type MockStakingKeeper struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockStakingKeeperRecorder
|
||||
}
|
||||
|
||||
func NewMockStakingKeeper(ctrl *gomock.Controller) *MockStakingKeeper {
|
||||
mock := &MockStakingKeeper{ctrl: ctrl}
|
||||
mock.recorder = &MockStakingKeeperRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
func (m *MockStakingKeeper) EXPECT() *MockStakingKeeperRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
func (m *MockStakingKeeper) GetValidatorByConsAddr(ctx context.Context, consAddr sdk.ConsAddress) (stakingtypes.Validator, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetValidatorByConsAddr", ctx, consAddr)
|
||||
ret0 := ret[0].(stakingtypes.Validator)
|
||||
return ret0, nil
|
||||
}
|
||||
|
||||
func (mr *MockStakingKeeperRecorder) GetValidatorByConsAddr(ctx, consAddr any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidatorByConsAddr", reflect.TypeOf((*MockStakingKeeper)(nil).GetValidatorByConsAddr), ctx, consAddr)
|
||||
}
|
||||
@ -2,46 +2,28 @@ package ante
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"cosmossdk.io/errors"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
"github.com/skip-mev/block-sdk/x/auction/keeper"
|
||||
"github.com/skip-mev/block-sdk/x/auction/types"
|
||||
)
|
||||
|
||||
var _ sdk.AnteDecorator = AuctionDecorator{}
|
||||
|
||||
type (
|
||||
// MEVLane is an interface that defines the methods required to interact with the MEV
|
||||
// lane.
|
||||
MEVLane interface {
|
||||
GetAuctionBidInfo(tx sdk.Tx) (*types.BidInfo, error)
|
||||
GetTopAuctionTx(ctx context.Context) sdk.Tx
|
||||
}
|
||||
|
||||
// Mempool is an interface that defines the methods required to interact with the application-side mempool.
|
||||
Mempool interface {
|
||||
Contains(tx sdk.Tx) bool
|
||||
}
|
||||
|
||||
// AuctionDecorator is an AnteDecorator that validates the auction bid and bundled transactions.
|
||||
AuctionDecorator struct {
|
||||
auctionkeeper keeper.Keeper
|
||||
txEncoder sdk.TxEncoder
|
||||
lane MEVLane
|
||||
mempool Mempool
|
||||
auctionkeeper AuctionKeeper
|
||||
txEncoder sdk.TxEncoder
|
||||
}
|
||||
)
|
||||
|
||||
func NewAuctionDecorator(ak keeper.Keeper, txEncoder sdk.TxEncoder, lane MEVLane, mempool Mempool) AuctionDecorator {
|
||||
// NewAuctionDecorator returns a new AuctionDecorator.
|
||||
func NewAuctionDecorator(ak AuctionKeeper, txEncoder sdk.TxEncoder, lane MEVLane) AuctionDecorator {
|
||||
return AuctionDecorator{
|
||||
auctionkeeper: ak,
|
||||
txEncoder: txEncoder,
|
||||
lane: lane,
|
||||
mempool: mempool,
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,25 +37,14 @@ func (ad AuctionDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool,
|
||||
|
||||
// Validate the auction bid if one exists.
|
||||
if bidInfo != nil {
|
||||
// If comet is re-checking a transaction, we only need to check if the transaction is in the application-side mempool.
|
||||
if ctx.IsReCheckTx() {
|
||||
if !ad.mempool.Contains(tx) {
|
||||
return ctx, fmt.Errorf("transaction not found in application-side mempool")
|
||||
}
|
||||
}
|
||||
|
||||
// Auction transactions must have a timeout set to a valid block height.
|
||||
if err := ad.ValidateTimeout(ctx, int64(bidInfo.Timeout)); err != nil {
|
||||
if err := ValidateTimeout(ctx, int64(bidInfo.Timeout)); err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
|
||||
// We only need to verify the auction bid relative to the local validator's mempool if the mode
|
||||
// is checkTx or recheckTx. Otherwise, the ABCI handlers (VerifyVoteExtension, ExtendVoteExtension, etc.)
|
||||
// will always compare the auction bid to the highest bidding transaction in the mempool leading to
|
||||
// poor liveness guarantees.
|
||||
// TODO(nikhil/david): refactor this logic (is this necessary?)
|
||||
// Only compare the bid to the top bid if necessary.
|
||||
topBid := sdk.Coin{}
|
||||
if ctx.IsCheckTx() || ctx.IsReCheckTx() {
|
||||
if _, ok := nextHeightExecModes[ctx.ExecMode()]; ok {
|
||||
if topBidTx := ad.lane.GetTopAuctionTx(ctx); topBidTx != nil {
|
||||
topBidBz, err := ad.txEncoder(topBidTx)
|
||||
if err != nil {
|
||||
@ -86,6 +57,8 @@ func (ad AuctionDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool,
|
||||
}
|
||||
|
||||
// Compare the bytes to see if the current transaction is the highest bidding transaction.
|
||||
// If it is the same transaction, we do not need to compare the bids as the bid check will
|
||||
// fail.
|
||||
if !bytes.Equal(topBidBz, currentTxBz) {
|
||||
topBidInfo, err := ad.lane.GetAuctionBidInfo(topBidTx)
|
||||
if err != nil {
|
||||
@ -104,25 +77,3 @@ func (ad AuctionDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool,
|
||||
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
||||
// ValidateTimeout validates that the timeout is greater than or equal to the expected block height
|
||||
// the bid transaction will be executed in.
|
||||
func (ad AuctionDecorator) ValidateTimeout(ctx sdk.Context, timeout int64) error {
|
||||
currentBlockHeight := ctx.BlockHeight()
|
||||
|
||||
// If the mode is CheckTx or ReCheckTx, we increment the current block height by one to
|
||||
// account for the fact that the transaction will be executed in the next block.
|
||||
if ctx.IsCheckTx() || ctx.IsReCheckTx() {
|
||||
currentBlockHeight++
|
||||
}
|
||||
|
||||
if timeout < currentBlockHeight {
|
||||
return fmt.Errorf(
|
||||
"timeout height cannot be less than the current block height (timeout: %d, current block height: %d)",
|
||||
timeout,
|
||||
currentBlockHeight,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1,311 +0,0 @@
|
||||
package ante_test
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"cosmossdk.io/log"
|
||||
"cosmossdk.io/math"
|
||||
storetypes "cosmossdk.io/store/types"
|
||||
"github.com/cosmos/cosmos-sdk/testutil"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
signer_extraction "github.com/skip-mev/block-sdk/adapters/signer_extraction_adapter"
|
||||
"github.com/skip-mev/block-sdk/block"
|
||||
"github.com/skip-mev/block-sdk/block/base"
|
||||
defaultlane "github.com/skip-mev/block-sdk/lanes/base"
|
||||
"github.com/skip-mev/block-sdk/lanes/mev"
|
||||
testutils "github.com/skip-mev/block-sdk/testutils"
|
||||
"github.com/skip-mev/block-sdk/x/auction/ante"
|
||||
"github.com/skip-mev/block-sdk/x/auction/keeper"
|
||||
auctiontypes "github.com/skip-mev/block-sdk/x/auction/types"
|
||||
)
|
||||
|
||||
type AnteTestSuite struct {
|
||||
suite.Suite
|
||||
ctx sdk.Context
|
||||
|
||||
encodingConfig testutils.EncodingConfig
|
||||
random *rand.Rand
|
||||
|
||||
// auction setup
|
||||
auctionkeeper keeper.Keeper
|
||||
bankKeeper *testutils.MockBankKeeper
|
||||
accountKeeper *testutils.MockAccountKeeper
|
||||
distrKeeper *testutils.MockDistributionKeeper
|
||||
stakingKeeper *testutils.MockStakingKeeper
|
||||
auctionDecorator ante.AuctionDecorator
|
||||
key *storetypes.KVStoreKey
|
||||
authorityAccount sdk.AccAddress
|
||||
|
||||
// mempool and lane set up
|
||||
mempool block.Mempool
|
||||
mevLane *mev.MEVLane
|
||||
baseLane *defaultlane.DefaultLane
|
||||
lanes []block.Lane
|
||||
|
||||
// Account set up
|
||||
balance sdk.Coin
|
||||
}
|
||||
|
||||
func TestAnteTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(AnteTestSuite))
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) SetupTest() {
|
||||
// General config
|
||||
suite.encodingConfig = testutils.CreateTestEncodingConfig()
|
||||
suite.random = rand.New(rand.NewSource(time.Now().Unix()))
|
||||
suite.key = storetypes.NewKVStoreKey(auctiontypes.StoreKey)
|
||||
testCtx := testutil.DefaultContextWithDB(suite.T(), suite.key, storetypes.NewTransientStoreKey("transient_test"))
|
||||
suite.ctx = testCtx.Ctx.WithIsCheckTx(true)
|
||||
|
||||
// Keepers set up
|
||||
ctrl := gomock.NewController(suite.T())
|
||||
suite.accountKeeper = testutils.NewMockAccountKeeper(ctrl)
|
||||
suite.accountKeeper.EXPECT().GetModuleAddress(auctiontypes.ModuleName).Return(sdk.AccAddress{}).AnyTimes()
|
||||
suite.bankKeeper = testutils.NewMockBankKeeper(ctrl)
|
||||
suite.distrKeeper = testutils.NewMockDistributionKeeper(ctrl)
|
||||
suite.stakingKeeper = testutils.NewMockStakingKeeper(ctrl)
|
||||
suite.authorityAccount = sdk.AccAddress([]byte("authority"))
|
||||
suite.auctionkeeper = keeper.NewKeeper(
|
||||
suite.encodingConfig.Codec,
|
||||
suite.key,
|
||||
suite.accountKeeper,
|
||||
suite.bankKeeper,
|
||||
suite.distrKeeper,
|
||||
suite.stakingKeeper,
|
||||
suite.authorityAccount.String(),
|
||||
)
|
||||
err := suite.auctionkeeper.SetParams(suite.ctx, auctiontypes.DefaultParams())
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Lanes configuration
|
||||
//
|
||||
// TOB lane set up
|
||||
mevConfig := base.LaneConfig{
|
||||
Logger: suite.ctx.Logger(),
|
||||
TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(),
|
||||
TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(),
|
||||
SignerExtractor: signer_extraction.NewDefaultAdapter(),
|
||||
AnteHandler: suite.anteHandler,
|
||||
MaxBlockSpace: math.LegacyZeroDec(),
|
||||
}
|
||||
suite.mevLane = mev.NewMEVLane(
|
||||
mevConfig,
|
||||
mev.NewDefaultAuctionFactory(suite.encodingConfig.TxConfig.TxDecoder(), signer_extraction.NewDefaultAdapter()),
|
||||
)
|
||||
|
||||
// Base lane set up
|
||||
baseConfig := base.LaneConfig{
|
||||
Logger: suite.ctx.Logger(),
|
||||
TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(),
|
||||
TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(),
|
||||
AnteHandler: suite.anteHandler,
|
||||
SignerExtractor: signer_extraction.NewDefaultAdapter(),
|
||||
MaxBlockSpace: math.LegacyZeroDec(),
|
||||
IgnoreList: []block.Lane{suite.mevLane},
|
||||
}
|
||||
suite.baseLane = defaultlane.NewDefaultLane(baseConfig)
|
||||
|
||||
// Mempool set up
|
||||
suite.lanes = []block.Lane{suite.mevLane, suite.baseLane}
|
||||
suite.mempool = block.NewLanedMempool(log.NewTestLogger(suite.T()), true, suite.lanes...)
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) anteHandler(ctx sdk.Context, tx sdk.Tx, _ bool) (sdk.Context, error) {
|
||||
suite.bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), suite.balance.Denom).AnyTimes().Return(suite.balance)
|
||||
|
||||
next := func(ctx sdk.Context, tx sdk.Tx, _ bool) (sdk.Context, error) {
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
return suite.auctionDecorator.AnteHandle(ctx, tx, false, next)
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) TestAnteHandler() {
|
||||
var (
|
||||
// Bid set up
|
||||
bidder = testutils.RandomAccounts(suite.random, 1)[0]
|
||||
bid = sdk.NewCoin("stake", math.NewInt(1000))
|
||||
balance = sdk.NewCoin("stake", math.NewInt(10000))
|
||||
signers = []testutils.Account{bidder}
|
||||
|
||||
// Top bidding auction tx set up
|
||||
topBidder = testutils.RandomAccounts(suite.random, 1)[0]
|
||||
topBid = sdk.NewCoin("stake", math.NewInt(100))
|
||||
insertTopBid = true
|
||||
timeout = uint64(1000)
|
||||
|
||||
// Auction setup
|
||||
maxBundleSize uint32 = 5
|
||||
reserveFee = sdk.NewCoin("stake", math.NewInt(100))
|
||||
minBidIncrement = sdk.NewCoin("stake", math.NewInt(100))
|
||||
frontRunningProtection = true
|
||||
)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
malleate func()
|
||||
pass bool
|
||||
}{
|
||||
{
|
||||
"empty mempool, valid bid",
|
||||
func() {
|
||||
insertTopBid = false
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"smaller bid than winning bid, invalid auction tx",
|
||||
func() {
|
||||
insertTopBid = true
|
||||
topBid = sdk.NewCoin("stake", math.NewInt(100000))
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"bidder has insufficient balance, invalid auction tx",
|
||||
func() {
|
||||
insertTopBid = false
|
||||
balance = sdk.NewCoin("stake", math.NewInt(10))
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"bid is smaller than reserve fee, invalid auction tx",
|
||||
func() {
|
||||
balance = sdk.NewCoin("stake", math.NewInt(10000))
|
||||
bid = sdk.NewCoin("stake", math.NewInt(101))
|
||||
reserveFee = sdk.NewCoin("stake", math.NewInt(1000))
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"valid mev bid tx",
|
||||
func() {
|
||||
balance = sdk.NewCoin("stake", math.NewInt(10000))
|
||||
bid = sdk.NewCoin("stake", math.NewInt(1000))
|
||||
reserveFee = sdk.NewCoin("stake", math.NewInt(100))
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"invalid mev bid tx with no timeout",
|
||||
func() {
|
||||
timeout = 0
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"mev tx is the top bidding tx",
|
||||
func() {
|
||||
timeout = 1000
|
||||
balance = sdk.NewCoin("stake", math.NewInt(10000))
|
||||
bid = sdk.NewCoin("stake", math.NewInt(1000))
|
||||
reserveFee = sdk.NewCoin("stake", math.NewInt(100))
|
||||
|
||||
insertTopBid = true
|
||||
topBidder = bidder
|
||||
topBid = bid
|
||||
signers = []testutils.Account{}
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"invalid frontrunning mev bid tx",
|
||||
func() {
|
||||
randomAccount := testutils.RandomAccounts(suite.random, 2)
|
||||
bidder := randomAccount[0]
|
||||
otherUser := randomAccount[1]
|
||||
insertTopBid = false
|
||||
|
||||
signers = []testutils.Account{bidder, otherUser}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"valid frontrunning mev bid tx",
|
||||
func() {
|
||||
randomAccount := testutils.RandomAccounts(suite.random, 2)
|
||||
bidder := randomAccount[0]
|
||||
otherUser := randomAccount[1]
|
||||
|
||||
signers = []testutils.Account{bidder, otherUser}
|
||||
frontRunningProtection = false
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"invalid sandwiching mev bid tx",
|
||||
func() {
|
||||
randomAccount := testutils.RandomAccounts(suite.random, 2)
|
||||
bidder := randomAccount[0]
|
||||
otherUser := randomAccount[1]
|
||||
|
||||
signers = []testutils.Account{bidder, otherUser, bidder}
|
||||
frontRunningProtection = true
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"invalid mev bid tx with many signers",
|
||||
func() {
|
||||
signers = testutils.RandomAccounts(suite.random, 10)
|
||||
frontRunningProtection = true
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
suite.Run(tc.name, func() {
|
||||
suite.SetupTest()
|
||||
tc.malleate()
|
||||
|
||||
suite.ctx = suite.ctx.WithBlockHeight(1)
|
||||
|
||||
// Set the mev params
|
||||
err := suite.auctionkeeper.SetParams(suite.ctx, auctiontypes.Params{
|
||||
MaxBundleSize: maxBundleSize,
|
||||
ReserveFee: reserveFee,
|
||||
MinBidIncrement: minBidIncrement,
|
||||
FrontRunningProtection: frontRunningProtection,
|
||||
})
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Insert the top bid into the mempool
|
||||
if insertTopBid {
|
||||
topAuctionTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, topBidder, topBid, 0, timeout, []testutils.Account{})
|
||||
suite.Require().NoError(err)
|
||||
|
||||
distribution := suite.mempool.GetTxDistribution()
|
||||
suite.Require().Equal(0, distribution[mev.LaneName])
|
||||
suite.Require().Equal(0, distribution[defaultlane.LaneName])
|
||||
|
||||
suite.Require().NoError(suite.mempool.Insert(suite.ctx, topAuctionTx))
|
||||
|
||||
distribution = suite.mempool.GetTxDistribution()
|
||||
suite.Require().Equal(1, distribution[mev.LaneName])
|
||||
suite.Require().Equal(0, distribution[defaultlane.LaneName])
|
||||
}
|
||||
|
||||
// Create the actual mev tx and insert into the mempool
|
||||
mevTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, 0, timeout, signers)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Execute the ante handler
|
||||
suite.balance = balance
|
||||
suite.auctionDecorator = ante.NewAuctionDecorator(suite.auctionkeeper, suite.encodingConfig.TxConfig.TxEncoder(), suite.mevLane, suite.mempool)
|
||||
_, err = suite.anteHandler(suite.ctx, mevTx, false)
|
||||
if tc.pass {
|
||||
suite.Require().NoError(err)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
53
x/auction/ante/utils.go
Normal file
53
x/auction/ante/utils.go
Normal file
@ -0,0 +1,53 @@
|
||||
package ante
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/skip-mev/block-sdk/x/auction/types"
|
||||
)
|
||||
|
||||
// nextHeightExecModes is the subset of execution modes that need special treatment. In
|
||||
// particular, these modes are used to signal that the transaction is being executed in the
|
||||
// next block. This is used to determine if the current transaction should be compared
|
||||
// against the top bid and what the expected timeout height should be.
|
||||
var nextHeightExecModes = map[sdk.ExecMode]struct{}{
|
||||
sdk.ExecModeCheck: {},
|
||||
sdk.ExecModeReCheck: {},
|
||||
sdk.ExecModeSimulate: {},
|
||||
}
|
||||
|
||||
// MEVLane is an interface that defines the methods required to interact with the MEV
|
||||
// lane.
|
||||
type MEVLane interface {
|
||||
GetAuctionBidInfo(tx sdk.Tx) (*types.BidInfo, error)
|
||||
GetTopAuctionTx(ctx context.Context) sdk.Tx
|
||||
}
|
||||
|
||||
// AuctionKeeper is an interface that defines the methods required to interact with the
|
||||
// auction keeper.
|
||||
type AuctionKeeper interface {
|
||||
ValidateBidInfo(ctx sdk.Context, highestBid sdk.Coin, bidInfo *types.BidInfo) error
|
||||
}
|
||||
|
||||
// ValidateTimeout validates that the timeout is greater than or equal to the expected block height
|
||||
// the bid transaction will be executed in.
|
||||
func ValidateTimeout(ctx sdk.Context, timeout int64) error {
|
||||
// Every transaction must have a timeout height greater than or equal to the height at which
|
||||
// the bid transaction will be executed.
|
||||
height := ctx.BlockHeight()
|
||||
if _, ok := nextHeightExecModes[ctx.ExecMode()]; ok {
|
||||
height++
|
||||
}
|
||||
|
||||
if height > timeout {
|
||||
return fmt.Errorf(
|
||||
"timeout height cannot be less than the current block height (timeout: %d, current block height: %d)",
|
||||
timeout,
|
||||
height,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -86,10 +86,49 @@ func (k Keeper) ValidateAuctionBid(ctx sdk.Context, bidder sdk.AccAddress, bid,
|
||||
}
|
||||
}
|
||||
|
||||
// ensure the bidder has enough funds to cover all the inclusion fees
|
||||
balances := k.bankKeeper.GetBalance(ctx, bidder, bid.Denom)
|
||||
if !balances.IsGTE(bid) {
|
||||
return fmt.Errorf("insufficient funds to bid %s with balance %s", bid, balances)
|
||||
// Extract the bid from the bidder.
|
||||
return k.ExtractBid(ctx, bidder, bid)
|
||||
}
|
||||
|
||||
// ExtractBid extracts the bid amount from the transaction.
|
||||
func (k Keeper) ExtractBid(ctx sdk.Context, bidder sdk.AccAddress, bid sdk.Coin) error {
|
||||
params, err := k.GetParams(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
escrowAddress := sdk.AccAddress(params.EscrowAccountAddress)
|
||||
|
||||
var proposerReward sdk.Coins
|
||||
if params.ProposerFee.IsZero() {
|
||||
// send the entire bid to the escrow account when no proposer fee is set
|
||||
if err := k.bankKeeper.SendCoins(ctx, bidder, escrowAddress, sdk.NewCoins(bid)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
rewardsAddress, err := k.rewardsAddressProvider.GetRewardsAddress(ctx)
|
||||
if err != nil {
|
||||
// In the case where the rewards address provider returns an error, the
|
||||
// escrow account will receive the entire bid.
|
||||
rewardsAddress = escrowAddress
|
||||
}
|
||||
|
||||
// determine the amount of the bid that goes to the (previous) proposer
|
||||
bid := sdk.NewDecCoinsFromCoins(bid)
|
||||
proposerReward, _ = bid.MulDecTruncate(params.ProposerFee).TruncateDecimal()
|
||||
|
||||
if err := k.bankKeeper.SendCoins(ctx, bidder, rewardsAddress, proposerReward); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Determine the amount of the remaining bid that goes to the escrow account.
|
||||
// If a decimal remainder exists, it'll stay with the bidding account.
|
||||
escrowTotal := bid.Sub(sdk.NewDecCoinsFromCoins(proposerReward...))
|
||||
escrowReward, _ := escrowTotal.TruncateDecimal()
|
||||
|
||||
if err := k.bankKeeper.SendCoins(ctx, bidder, escrowAddress, escrowReward); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
@ -10,215 +11,114 @@ import (
|
||||
testutils "github.com/skip-mev/block-sdk/testutils"
|
||||
"github.com/skip-mev/block-sdk/x/auction/keeper"
|
||||
"github.com/skip-mev/block-sdk/x/auction/types"
|
||||
"github.com/skip-mev/block-sdk/x/auction/types/mocks"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
func (suite *KeeperTestSuite) TestValidateBidInfo() {
|
||||
var (
|
||||
// Tx building variables
|
||||
accounts = []testutils.Account{} // tracks the order of signers in the bundle
|
||||
balance = sdk.NewCoin("stake", math.NewInt(10000))
|
||||
bid = sdk.NewCoin("stake", math.NewInt(1000))
|
||||
func (s *KeeperTestSuite) TestValidateAuctionBid() {
|
||||
rng := rand.New(rand.NewSource(time.Now().Unix()))
|
||||
bidder := testutils.RandomAccounts(rng, 1)[0].Address
|
||||
bankSendErr := fmt.Errorf("bank send error")
|
||||
|
||||
// Auction params
|
||||
maxBundleSize uint32 = 10
|
||||
reserveFee = sdk.NewCoin("stake", math.NewInt(1000))
|
||||
minBidIncrement = sdk.NewCoin("stake", math.NewInt(1000))
|
||||
escrowAddress = sdk.AccAddress([]byte("escrow"))
|
||||
frontRunningProtection = true
|
||||
|
||||
// mempool variables
|
||||
highestBid = sdk.Coin{}
|
||||
)
|
||||
|
||||
rnd := rand.New(rand.NewSource(time.Now().Unix()))
|
||||
bidder := testutils.RandomAccounts(rnd, 1)[0]
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
malleate func()
|
||||
pass bool
|
||||
}{
|
||||
{
|
||||
"insufficient bid amount",
|
||||
func() {
|
||||
bid = sdk.Coin{}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"insufficient balance",
|
||||
func() {
|
||||
bid = sdk.NewCoin("stake", math.NewInt(1000))
|
||||
balance = sdk.NewCoin("stake", math.NewInt(100))
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"too many transactions in the bundle",
|
||||
func() {
|
||||
// reset the balance and bid to their original values
|
||||
bid = sdk.NewCoin("stake", math.NewInt(1000))
|
||||
balance = sdk.NewCoin("stake", math.NewInt(10000))
|
||||
accounts = testutils.RandomAccounts(rnd, int(maxBundleSize+1))
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"frontrunning bundle",
|
||||
func() {
|
||||
randomAccount := testutils.RandomAccounts(rnd, 1)[0]
|
||||
accounts = []testutils.Account{bidder, randomAccount}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"sandwiching bundle",
|
||||
func() {
|
||||
randomAccount := testutils.RandomAccounts(rnd, 1)[0]
|
||||
accounts = []testutils.Account{bidder, randomAccount, bidder}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"valid bundle",
|
||||
func() {
|
||||
randomAccount := testutils.RandomAccounts(rnd, 1)[0]
|
||||
accounts = []testutils.Account{randomAccount, randomAccount, bidder, bidder, bidder}
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"valid bundle with only bidder txs",
|
||||
func() {
|
||||
accounts = []testutils.Account{bidder, bidder, bidder, bidder}
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"valid bundle with only random txs from single same user",
|
||||
func() {
|
||||
randomAccount := testutils.RandomAccounts(rnd, 1)[0]
|
||||
accounts = []testutils.Account{randomAccount, randomAccount, randomAccount, randomAccount}
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"invalid bundle with random accounts",
|
||||
func() {
|
||||
accounts = testutils.RandomAccounts(rnd, 2)
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"disabled front-running protection",
|
||||
func() {
|
||||
accounts = testutils.RandomAccounts(rnd, 10)
|
||||
frontRunningProtection = false
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"invalid bundle that does not outbid the highest bid",
|
||||
func() {
|
||||
accounts = []testutils.Account{bidder, bidder, bidder}
|
||||
highestBid = sdk.NewCoin("stake", math.NewInt(500))
|
||||
bid = sdk.NewCoin("stake", math.NewInt(500))
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"valid bundle that outbids the highest bid",
|
||||
func() {
|
||||
highestBid = sdk.NewCoin("stake", math.NewInt(500))
|
||||
bid = sdk.NewCoin("stake", math.NewInt(1500))
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"attempting to bid with a different denom",
|
||||
func() {
|
||||
highestBid = sdk.NewCoin("stake", math.NewInt(500))
|
||||
bid = sdk.NewCoin("stake2", math.NewInt(1500))
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"min bid increment is different from bid denom", // THIS SHOULD NEVER HAPPEN
|
||||
func() {
|
||||
highestBid = sdk.NewCoin("stake", math.NewInt(500))
|
||||
bid = sdk.NewCoin("stake", math.NewInt(1500))
|
||||
minBidIncrement = sdk.NewCoin("stake2", math.NewInt(1000))
|
||||
},
|
||||
false,
|
||||
},
|
||||
params := types.Params{
|
||||
ReserveFee: sdk.NewCoin("stake", math.NewInt(1000)),
|
||||
EscrowAccountAddress: sdk.AccAddress([]byte("escrow")),
|
||||
MinBidIncrement: sdk.NewCoin("stake", math.NewInt(1000)),
|
||||
ProposerFee: math.LegacyZeroDec(),
|
||||
}
|
||||
s.Require().NoError(s.auctionkeeper.SetParams(s.ctx, params))
|
||||
|
||||
for _, tc := range cases {
|
||||
suite.Run(tc.name, func() {
|
||||
suite.SetupTest() // reset
|
||||
s.Run("nil bid", func() {
|
||||
highestBid := sdk.NewCoin("stake", math.NewInt(1000))
|
||||
s.Require().Error(s.auctionkeeper.ValidateAuctionBid(s.ctx, bidder, sdk.Coin{}, highestBid))
|
||||
})
|
||||
|
||||
tc.malleate()
|
||||
s.Run("reserve fee and bid denom mismatch", func() {
|
||||
highestBid := sdk.NewCoin("stake", math.NewInt(1000))
|
||||
bid := sdk.NewCoin("stake2", math.NewInt(1000))
|
||||
s.Require().Error(s.auctionkeeper.ValidateAuctionBid(s.ctx, bidder, bid, highestBid))
|
||||
})
|
||||
|
||||
// Set up the new auction keeper with mocks customized for this test case
|
||||
suite.bankKeeper.EXPECT().GetBalance(suite.ctx, bidder.Address, minBidIncrement.Denom).Return(balance).AnyTimes()
|
||||
suite.bankKeeper.EXPECT().SendCoins(suite.ctx, bidder.Address, escrowAddress, reserveFee).Return(nil).AnyTimes()
|
||||
s.Run("bid less than reserve fee", func() {
|
||||
highestBid := sdk.NewCoin("stake", math.NewInt(1000))
|
||||
bid := sdk.NewCoin("stake", math.NewInt(500))
|
||||
s.Require().Error(s.auctionkeeper.ValidateAuctionBid(s.ctx, bidder, bid, highestBid))
|
||||
})
|
||||
|
||||
suite.auctionkeeper = keeper.NewKeeper(
|
||||
suite.encCfg.Codec,
|
||||
suite.key,
|
||||
suite.accountKeeper,
|
||||
suite.bankKeeper,
|
||||
suite.distrKeeper,
|
||||
suite.stakingKeeper,
|
||||
suite.authorityAccount.String(),
|
||||
)
|
||||
params := types.Params{
|
||||
MaxBundleSize: maxBundleSize,
|
||||
ReserveFee: reserveFee,
|
||||
EscrowAccountAddress: escrowAddress,
|
||||
FrontRunningProtection: frontRunningProtection,
|
||||
MinBidIncrement: minBidIncrement,
|
||||
}
|
||||
suite.auctionkeeper.SetParams(suite.ctx, params)
|
||||
s.Run("bid less than highest bid + min bid increment", func() {
|
||||
highestBid := sdk.NewCoin("stake", math.NewInt(1000))
|
||||
bid := sdk.NewCoin("stake", math.NewInt(1500))
|
||||
s.Require().Error(s.auctionkeeper.ValidateAuctionBid(s.ctx, bidder, bid, highestBid))
|
||||
})
|
||||
|
||||
// Create the bundle of transactions ordered by accounts
|
||||
bundle := make([][]byte, 0)
|
||||
for _, acc := range accounts {
|
||||
tx, err := testutils.CreateRandomTx(suite.encCfg.TxConfig, acc, 0, 1, 100, 0)
|
||||
suite.Require().NoError(err)
|
||||
s.Run("valid bid", func() {
|
||||
highestBid := sdk.Coin{}
|
||||
bid := sdk.NewCoin("stake", math.NewInt(1500))
|
||||
|
||||
txBz, err := suite.encCfg.TxConfig.TxEncoder()(tx)
|
||||
suite.Require().NoError(err)
|
||||
bundle = append(bundle, txBz)
|
||||
}
|
||||
s.bankKeeper.On("SendCoins", mock.Anything, mock.Anything, mock.Anything, sdk.NewCoins(bid)).Return(nil)
|
||||
|
||||
signers := make([]map[string]struct{}, len(accounts))
|
||||
for index, acc := range accounts {
|
||||
txSigners := map[string]struct{}{
|
||||
acc.Address.String(): {},
|
||||
}
|
||||
err := s.auctionkeeper.ValidateAuctionBid(s.ctx, bidder, bid, highestBid)
|
||||
s.Require().NoError(err)
|
||||
})
|
||||
|
||||
signers[index] = txSigners
|
||||
}
|
||||
s.Run("insufficient funds", func() {
|
||||
highestBid := sdk.Coin{}
|
||||
bid := sdk.NewCoin("stake", math.NewInt(1500))
|
||||
|
||||
bidInfo := &types.BidInfo{
|
||||
Bidder: bidder.Address,
|
||||
Bid: bid,
|
||||
Transactions: bundle,
|
||||
Signers: signers,
|
||||
}
|
||||
s.bankKeeper = mocks.NewBankKeeper(s.T())
|
||||
s.auctionkeeper = keeper.NewKeeper(
|
||||
s.encCfg.Codec,
|
||||
s.key,
|
||||
s.accountKeeper,
|
||||
s.bankKeeper,
|
||||
s.distrKeeper,
|
||||
s.stakingKeeper,
|
||||
s.authorityAccount.String(),
|
||||
)
|
||||
|
||||
err := suite.auctionkeeper.ValidateBidInfo(suite.ctx, highestBid, bidInfo)
|
||||
if tc.pass {
|
||||
suite.Require().NoError(err)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
s.bankKeeper.On("SendCoins", mock.Anything, mock.Anything, mock.Anything, sdk.NewCoins(bid)).Return(bankSendErr)
|
||||
|
||||
err := s.auctionkeeper.ValidateAuctionBid(s.ctx, bidder, bid, highestBid)
|
||||
s.Require().Error(err)
|
||||
})
|
||||
|
||||
s.Run("valid bid with proposer split", func() {
|
||||
highestBid := sdk.Coin{}
|
||||
bid := sdk.NewCoin("stake", math.NewInt(1000))
|
||||
|
||||
s.bankKeeper = mocks.NewBankKeeper(s.T())
|
||||
rewardsProvider := mocks.NewRewardsAddressProvider(s.T())
|
||||
rewardsAddr := sdk.AccAddress([]byte("rewards"))
|
||||
rewardsProvider.On("GetRewardsAddress", mock.Anything).Return(rewardsAddr, nil)
|
||||
|
||||
s.auctionkeeper = keeper.NewKeeperWithRewardsAddressProvider(
|
||||
s.encCfg.Codec,
|
||||
s.key,
|
||||
s.accountKeeper,
|
||||
s.bankKeeper,
|
||||
rewardsProvider,
|
||||
s.authorityAccount.String(),
|
||||
)
|
||||
|
||||
params := types.Params{
|
||||
ProposerFee: math.LegacyMustNewDecFromStr("0.1"),
|
||||
ReserveFee: sdk.NewCoin("stake", math.NewInt(1000)),
|
||||
EscrowAccountAddress: sdk.AccAddress([]byte("escrow")),
|
||||
MinBidIncrement: sdk.NewCoin("stake", math.NewInt(1000)),
|
||||
}
|
||||
s.Require().NoError(s.auctionkeeper.SetParams(s.ctx, params))
|
||||
|
||||
proposalSplit := sdk.NewCoin("stake", math.NewInt(100))
|
||||
escrowSplit := sdk.NewCoin("stake", math.NewInt(900))
|
||||
s.bankKeeper.On("SendCoins", mock.Anything, mock.Anything, mock.Anything, sdk.NewCoins(proposalSplit)).Return(nil)
|
||||
s.bankKeeper.On("SendCoins", mock.Anything, mock.Anything, mock.Anything, sdk.NewCoins(escrowSplit)).Return(nil)
|
||||
|
||||
err := s.auctionkeeper.ValidateAuctionBid(s.ctx, bidder, bid, highestBid)
|
||||
s.Require().NoError(err)
|
||||
})
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestValidateBundle() {
|
||||
func (s *KeeperTestSuite) TestValidateBundle() {
|
||||
var accounts []testutils.Account // tracks the order of signers in the bundle
|
||||
|
||||
rng := rand.New(rand.NewSource(time.Now().Unix()))
|
||||
@ -301,8 +201,8 @@ func (suite *KeeperTestSuite) TestValidateBundle() {
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
suite.Run(tc.name, func() {
|
||||
suite.SetupTest() // reset
|
||||
s.Run(tc.name, func() {
|
||||
s.SetupTest() // reset
|
||||
|
||||
// Malleate the test case
|
||||
tc.malleate()
|
||||
@ -317,11 +217,11 @@ func (suite *KeeperTestSuite) TestValidateBundle() {
|
||||
}
|
||||
|
||||
// Validate the bundle
|
||||
err := suite.auctionkeeper.ValidateAuctionBundle(bidder.Address, signers)
|
||||
err := s.auctionkeeper.ValidateAuctionBundle(bidder.Address, signers)
|
||||
if tc.pass {
|
||||
suite.Require().NoError(err)
|
||||
s.Require().NoError(err)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
s.Require().Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -6,11 +6,11 @@ import (
|
||||
storetypes "cosmossdk.io/store/types"
|
||||
"github.com/cosmos/cosmos-sdk/testutil"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/golang/mock/gomock"
|
||||
|
||||
testutils "github.com/skip-mev/block-sdk/testutils"
|
||||
"github.com/skip-mev/block-sdk/x/auction/keeper"
|
||||
"github.com/skip-mev/block-sdk/x/auction/types"
|
||||
"github.com/skip-mev/block-sdk/x/auction/types/mocks"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
@ -19,10 +19,10 @@ type KeeperTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
auctionkeeper keeper.Keeper
|
||||
bankKeeper *testutils.MockBankKeeper
|
||||
accountKeeper *testutils.MockAccountKeeper
|
||||
distrKeeper *testutils.MockDistributionKeeper
|
||||
stakingKeeper *testutils.MockStakingKeeper
|
||||
bankKeeper *mocks.BankKeeper
|
||||
accountKeeper *mocks.AccountKeeper
|
||||
distrKeeper *mocks.DistributionKeeper
|
||||
stakingKeeper *mocks.StakingKeeper
|
||||
encCfg testutils.EncodingConfig
|
||||
ctx sdk.Context
|
||||
msgServer types.MsgServer
|
||||
@ -34,33 +34,31 @@ func TestKeeperTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(KeeperTestSuite))
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) SetupTest() {
|
||||
suite.encCfg = testutils.CreateTestEncodingConfig()
|
||||
suite.key = storetypes.NewKVStoreKey(types.StoreKey)
|
||||
testCtx := testutil.DefaultContextWithDB(suite.T(), suite.key, storetypes.NewTransientStoreKey("transient_test"))
|
||||
suite.ctx = testCtx.Ctx
|
||||
func (s *KeeperTestSuite) SetupTest() {
|
||||
s.encCfg = testutils.CreateTestEncodingConfig()
|
||||
s.key = storetypes.NewKVStoreKey(types.StoreKey)
|
||||
testCtx := testutil.DefaultContextWithDB(s.T(), s.key, storetypes.NewTransientStoreKey("transient_test"))
|
||||
s.ctx = testCtx.Ctx
|
||||
|
||||
ctrl := gomock.NewController(suite.T())
|
||||
s.accountKeeper = mocks.NewAccountKeeper(s.T())
|
||||
s.accountKeeper.On("GetModuleAddress", types.ModuleName).Return(sdk.AccAddress{}).Maybe()
|
||||
|
||||
suite.accountKeeper = testutils.NewMockAccountKeeper(ctrl)
|
||||
suite.accountKeeper.EXPECT().GetModuleAddress(types.ModuleName).Return(sdk.AccAddress{}).AnyTimes()
|
||||
|
||||
suite.bankKeeper = testutils.NewMockBankKeeper(ctrl)
|
||||
suite.distrKeeper = testutils.NewMockDistributionKeeper(ctrl)
|
||||
suite.stakingKeeper = testutils.NewMockStakingKeeper(ctrl)
|
||||
suite.authorityAccount = sdk.AccAddress([]byte("authority"))
|
||||
suite.auctionkeeper = keeper.NewKeeper(
|
||||
suite.encCfg.Codec,
|
||||
suite.key,
|
||||
suite.accountKeeper,
|
||||
suite.bankKeeper,
|
||||
suite.distrKeeper,
|
||||
suite.stakingKeeper,
|
||||
suite.authorityAccount.String(),
|
||||
s.bankKeeper = mocks.NewBankKeeper(s.T())
|
||||
s.distrKeeper = mocks.NewDistributionKeeper(s.T())
|
||||
s.stakingKeeper = mocks.NewStakingKeeper(s.T())
|
||||
s.authorityAccount = sdk.AccAddress([]byte("authority"))
|
||||
s.auctionkeeper = keeper.NewKeeper(
|
||||
s.encCfg.Codec,
|
||||
s.key,
|
||||
s.accountKeeper,
|
||||
s.bankKeeper,
|
||||
s.distrKeeper,
|
||||
s.stakingKeeper,
|
||||
s.authorityAccount.String(),
|
||||
)
|
||||
|
||||
err := suite.auctionkeeper.SetParams(suite.ctx, types.DefaultParams())
|
||||
suite.Require().NoError(err)
|
||||
err := s.auctionkeeper.SetParams(s.ctx, types.DefaultParams())
|
||||
s.Require().NoError(err)
|
||||
|
||||
suite.msgServer = keeper.NewMsgServerImpl(suite.auctionkeeper)
|
||||
s.msgServer = keeper.NewMsgServerImpl(s.auctionkeeper)
|
||||
}
|
||||
|
||||
@ -24,59 +24,11 @@ func NewMsgServerImpl(keeper Keeper) *MsgServer {
|
||||
return &MsgServer{Keeper: keeper}
|
||||
}
|
||||
|
||||
// AuctionBid is a no-op function that emits an event for the auction bid. The bid is extracted
|
||||
// in the antehandler.
|
||||
func (m MsgServer) AuctionBid(goCtx context.Context, msg *types.MsgAuctionBid) (*types.MsgAuctionBidResponse, error) {
|
||||
ctx := sdk.UnwrapSDKContext(goCtx)
|
||||
|
||||
// This should never return an error because the address was validated when
|
||||
// the message was ingressed.
|
||||
bidder, err := sdk.AccAddressFromBech32(msg.Bidder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params, err := m.GetParams(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if uint32(len(msg.Transactions)) > params.MaxBundleSize {
|
||||
return nil, fmt.Errorf("the number of transactions in the bid is greater than the maximum allowed; expected <= %d, got %d", params.MaxBundleSize, len(msg.Transactions))
|
||||
}
|
||||
|
||||
escrowAddress := params.EscrowAccountAddress
|
||||
|
||||
var proposerReward sdk.Coins
|
||||
if params.ProposerFee.IsZero() {
|
||||
// send the entire bid to the escrow account when no proposer fee is set
|
||||
if err := m.bankKeeper.SendCoins(ctx, bidder, escrowAddress, sdk.NewCoins(msg.Bid)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
rewardsAddress, err := m.rewardsAddressProvider.GetRewardsAddress(ctx)
|
||||
if err != nil {
|
||||
// In the case where the rewards address provider returns an error, the
|
||||
// escrow account will receive the entire bid.
|
||||
rewardsAddress = escrowAddress
|
||||
}
|
||||
|
||||
// determine the amount of the bid that goes to the (previous) proposer
|
||||
bid := sdk.NewDecCoinsFromCoins(msg.Bid)
|
||||
proposerReward, _ = bid.MulDecTruncate(params.ProposerFee).TruncateDecimal()
|
||||
|
||||
if err := m.bankKeeper.SendCoins(ctx, bidder, rewardsAddress, proposerReward); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Determine the amount of the remaining bid that goes to the escrow account.
|
||||
// If a decimal remainder exists, it'll stay with the bidding account.
|
||||
escrowTotal := bid.Sub(sdk.NewDecCoinsFromCoins(proposerReward...))
|
||||
escrowReward, _ := escrowTotal.TruncateDecimal()
|
||||
|
||||
if err := m.bankKeeper.SendCoins(ctx, bidder, escrowAddress, escrowReward); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
bundledTxHashes := make([]string, len(msg.Transactions))
|
||||
for i, refTxRaw := range msg.Transactions {
|
||||
hash := sha256.Sum256(refTxRaw)
|
||||
@ -88,7 +40,6 @@ func (m MsgServer) AuctionBid(goCtx context.Context, msg *types.MsgAuctionBid) (
|
||||
types.EventTypeAuctionBid,
|
||||
sdk.NewAttribute(types.EventAttrBidder, msg.Bidder),
|
||||
sdk.NewAttribute(types.EventAttrBid, msg.Bid.String()),
|
||||
sdk.NewAttribute(types.EventAttrProposerReward, proposerReward.String()),
|
||||
sdk.NewAttribute(types.EventAttrBundledTxs, strings.Join(bundledTxHashes, ",")),
|
||||
),
|
||||
)
|
||||
|
||||
@ -6,126 +6,12 @@ import (
|
||||
|
||||
"cosmossdk.io/math"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
|
||||
testutils "github.com/skip-mev/block-sdk/testutils"
|
||||
"github.com/skip-mev/block-sdk/x/auction/types"
|
||||
)
|
||||
|
||||
func (suite *KeeperTestSuite) TestMsgAuctionBid() {
|
||||
rng := rand.New(rand.NewSource(time.Now().Unix()))
|
||||
accounts := testutils.RandomAccounts(rng, 4)
|
||||
|
||||
bidder := accounts[0]
|
||||
escrow := accounts[1]
|
||||
|
||||
proposerCons := accounts[2]
|
||||
proposerOperator := accounts[3]
|
||||
proposer := stakingtypes.Validator{
|
||||
OperatorAddress: sdk.ValAddress(proposerOperator.Address).String(),
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
msg *types.MsgAuctionBid
|
||||
malleate func()
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "invalid bidder address",
|
||||
msg: &types.MsgAuctionBid{
|
||||
Bidder: "stake",
|
||||
},
|
||||
malleate: func() {},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "too many bundled transactions",
|
||||
msg: &types.MsgAuctionBid{
|
||||
Bidder: bidder.Address.String(),
|
||||
Transactions: [][]byte{{0xFF}, {0xFF}, {0xFF}},
|
||||
},
|
||||
malleate: func() {
|
||||
params := types.DefaultParams()
|
||||
params.MaxBundleSize = 2
|
||||
suite.auctionkeeper.SetParams(suite.ctx, params)
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "valid bundle with no proposer fee",
|
||||
msg: &types.MsgAuctionBid{
|
||||
Bidder: bidder.Address.String(),
|
||||
Bid: sdk.NewInt64Coin("stake", 1024),
|
||||
Transactions: [][]byte{{0xFF}, {0xFF}},
|
||||
},
|
||||
malleate: func() {
|
||||
params := types.DefaultParams()
|
||||
params.ProposerFee = math.LegacyZeroDec()
|
||||
params.EscrowAccountAddress = escrow.Address
|
||||
suite.auctionkeeper.SetParams(suite.ctx, params)
|
||||
|
||||
suite.bankKeeper.EXPECT().
|
||||
SendCoins(
|
||||
suite.ctx,
|
||||
bidder.Address,
|
||||
escrow.Address,
|
||||
sdk.NewCoins(sdk.NewInt64Coin("stake", 1024)),
|
||||
).
|
||||
Return(nil).
|
||||
AnyTimes()
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid bundle with proposer fee",
|
||||
msg: &types.MsgAuctionBid{
|
||||
Bidder: bidder.Address.String(),
|
||||
Bid: sdk.NewInt64Coin("stake", 3416),
|
||||
Transactions: [][]byte{{0xFF}, {0xFF}},
|
||||
},
|
||||
malleate: func() {
|
||||
params := types.DefaultParams()
|
||||
params.ProposerFee = math.LegacyMustNewDecFromStr("0.30")
|
||||
params.EscrowAccountAddress = escrow.Address
|
||||
suite.auctionkeeper.SetParams(suite.ctx, params)
|
||||
|
||||
suite.distrKeeper.EXPECT().
|
||||
GetPreviousProposerConsAddr(suite.ctx).
|
||||
Return(proposerCons.ConsKey.PubKey().Address().Bytes(), nil)
|
||||
|
||||
suite.stakingKeeper.EXPECT().
|
||||
GetValidatorByConsAddr(suite.ctx, sdk.ConsAddress(proposerCons.ConsKey.PubKey().Address().Bytes())).
|
||||
Return(proposer, nil).
|
||||
AnyTimes()
|
||||
|
||||
suite.bankKeeper.EXPECT().
|
||||
SendCoins(suite.ctx, bidder.Address, proposerOperator.Address, sdk.NewCoins(sdk.NewInt64Coin("stake", 1024))).
|
||||
Return(nil).AnyTimes()
|
||||
|
||||
suite.bankKeeper.EXPECT().
|
||||
SendCoins(suite.ctx, bidder.Address, escrow.Address, sdk.NewCoins(sdk.NewInt64Coin("stake", 2392))).
|
||||
Return(nil).AnyTimes()
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
tc.malleate()
|
||||
|
||||
_, err := suite.msgServer.AuctionBid(suite.ctx, tc.msg)
|
||||
if tc.expectErr {
|
||||
suite.Require().Error(err)
|
||||
} else {
|
||||
suite.Require().NoError(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestMsgUpdateParams() {
|
||||
func (s *KeeperTestSuite) TestMsgUpdateParams() {
|
||||
rng := rand.New(rand.NewSource(time.Now().Unix()))
|
||||
account := testutils.RandomAccounts(rng, 1)[0]
|
||||
|
||||
@ -139,7 +25,7 @@ func (suite *KeeperTestSuite) TestMsgUpdateParams() {
|
||||
{
|
||||
name: "invalid proposer fee",
|
||||
msg: &types.MsgUpdateParams{
|
||||
Authority: suite.authorityAccount.String(),
|
||||
Authority: s.authorityAccount.String(),
|
||||
Params: types.Params{
|
||||
ProposerFee: math.LegacyMustNewDecFromStr("1.1"),
|
||||
},
|
||||
@ -150,7 +36,7 @@ func (suite *KeeperTestSuite) TestMsgUpdateParams() {
|
||||
{
|
||||
name: "invalid auction fees",
|
||||
msg: &types.MsgUpdateParams{
|
||||
Authority: suite.authorityAccount.String(),
|
||||
Authority: s.authorityAccount.String(),
|
||||
Params: types.Params{
|
||||
ProposerFee: math.LegacyMustNewDecFromStr("0.1"),
|
||||
},
|
||||
@ -165,7 +51,7 @@ func (suite *KeeperTestSuite) TestMsgUpdateParams() {
|
||||
Params: types.Params{
|
||||
ProposerFee: math.LegacyMustNewDecFromStr("0.1"),
|
||||
MaxBundleSize: 2,
|
||||
EscrowAccountAddress: suite.authorityAccount,
|
||||
EscrowAccountAddress: s.authorityAccount,
|
||||
MinBidIncrement: sdk.NewInt64Coin("stake", 100),
|
||||
ReserveFee: sdk.NewInt64Coin("stake", 100),
|
||||
},
|
||||
@ -176,16 +62,16 @@ func (suite *KeeperTestSuite) TestMsgUpdateParams() {
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
s.Run(tc.name, func() {
|
||||
if !tc.passBasic {
|
||||
suite.Require().Error(tc.msg.ValidateBasic())
|
||||
s.Require().Error(tc.msg.ValidateBasic())
|
||||
}
|
||||
|
||||
_, err := suite.msgServer.UpdateParams(suite.ctx, tc.msg)
|
||||
_, err := s.msgServer.UpdateParams(s.ctx, tc.msg)
|
||||
if tc.pass {
|
||||
suite.Require().NoError(err)
|
||||
s.Require().NoError(err)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
s.Require().Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -7,12 +7,16 @@ import (
|
||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
)
|
||||
|
||||
// BankKeeper defines the expected API contract for the x/auth module.
|
||||
// BankKeeper defines the expected API contract for the x/account module.
|
||||
//
|
||||
//go:generate mockery --name AccountKeeper --output ./mocks --outpkg mocks --case underscore
|
||||
type AccountKeeper interface {
|
||||
GetModuleAddress(moduleName string) sdk.AccAddress
|
||||
}
|
||||
|
||||
// BankKeeper defines the expected API contract for the x/bank module.
|
||||
//
|
||||
//go:generate mockery --name BankKeeper --output ./mocks --outpkg mocks --case underscore
|
||||
type BankKeeper interface {
|
||||
SendCoins(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) error
|
||||
GetBalance(ctx context.Context, addr sdk.AccAddress, denom string) sdk.Coin
|
||||
@ -20,16 +24,22 @@ type BankKeeper interface {
|
||||
|
||||
// DistributionKeeper defines the expected API contract for the x/distribution
|
||||
// module.
|
||||
//
|
||||
//go:generate mockery --name DistributionKeeper --output ./mocks --outpkg mocks --case underscore
|
||||
type DistributionKeeper interface {
|
||||
GetPreviousProposerConsAddr(ctx context.Context) (sdk.ConsAddress, error)
|
||||
}
|
||||
|
||||
// StakingKeeper defines the expected API contract for the x/staking module.
|
||||
//
|
||||
//go:generate mockery --name StakingKeeper --output ./mocks --outpkg mocks --case underscore
|
||||
type StakingKeeper interface {
|
||||
GetValidatorByConsAddr(context.Context, sdk.ConsAddress) (stakingtypes.Validator, error)
|
||||
}
|
||||
|
||||
// RewardsAddressProvider is an interface that provides an address where proposer/subset of auction profits are sent.
|
||||
//
|
||||
//go:generate mockery --name RewardsAddressProvider --output ./mocks --outpkg mocks --case underscore
|
||||
type RewardsAddressProvider interface {
|
||||
GetRewardsAddress(context sdk.Context) (sdk.AccAddress, error)
|
||||
}
|
||||
|
||||
43
x/auction/types/mocks/account_keeper.go
Normal file
43
x/auction/types/mocks/account_keeper.go
Normal file
@ -0,0 +1,43 @@
|
||||
// Code generated by mockery v2.30.1. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
types "github.com/cosmos/cosmos-sdk/types"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// AccountKeeper is an autogenerated mock type for the AccountKeeper type
|
||||
type AccountKeeper struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// GetModuleAddress provides a mock function with given fields: moduleName
|
||||
func (_m *AccountKeeper) GetModuleAddress(moduleName string) types.AccAddress {
|
||||
ret := _m.Called(moduleName)
|
||||
|
||||
var r0 types.AccAddress
|
||||
if rf, ok := ret.Get(0).(func(string) types.AccAddress); ok {
|
||||
r0 = rf(moduleName)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(types.AccAddress)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// NewAccountKeeper creates a new instance of AccountKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewAccountKeeper(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *AccountKeeper {
|
||||
mock := &AccountKeeper{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
57
x/auction/types/mocks/bank_keeper.go
Normal file
57
x/auction/types/mocks/bank_keeper.go
Normal file
@ -0,0 +1,57 @@
|
||||
// Code generated by mockery v2.30.1. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
types "github.com/cosmos/cosmos-sdk/types"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// BankKeeper is an autogenerated mock type for the BankKeeper type
|
||||
type BankKeeper struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// GetBalance provides a mock function with given fields: ctx, addr, denom
|
||||
func (_m *BankKeeper) GetBalance(ctx context.Context, addr types.AccAddress, denom string) types.Coin {
|
||||
ret := _m.Called(ctx, addr, denom)
|
||||
|
||||
var r0 types.Coin
|
||||
if rf, ok := ret.Get(0).(func(context.Context, types.AccAddress, string) types.Coin); ok {
|
||||
r0 = rf(ctx, addr, denom)
|
||||
} else {
|
||||
r0 = ret.Get(0).(types.Coin)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// SendCoins provides a mock function with given fields: ctx, fromAddr, toAddr, amt
|
||||
func (_m *BankKeeper) SendCoins(ctx context.Context, fromAddr types.AccAddress, toAddr types.AccAddress, amt types.Coins) error {
|
||||
ret := _m.Called(ctx, fromAddr, toAddr, amt)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, types.AccAddress, types.AccAddress, types.Coins) error); ok {
|
||||
r0 = rf(ctx, fromAddr, toAddr, amt)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// NewBankKeeper creates a new instance of BankKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewBankKeeper(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *BankKeeper {
|
||||
mock := &BankKeeper{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
55
x/auction/types/mocks/distribution_keeper.go
Normal file
55
x/auction/types/mocks/distribution_keeper.go
Normal file
@ -0,0 +1,55 @@
|
||||
// Code generated by mockery v2.30.1. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
types "github.com/cosmos/cosmos-sdk/types"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// DistributionKeeper is an autogenerated mock type for the DistributionKeeper type
|
||||
type DistributionKeeper struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// GetPreviousProposerConsAddr provides a mock function with given fields: ctx
|
||||
func (_m *DistributionKeeper) GetPreviousProposerConsAddr(ctx context.Context) (types.ConsAddress, error) {
|
||||
ret := _m.Called(ctx)
|
||||
|
||||
var r0 types.ConsAddress
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context) (types.ConsAddress, error)); ok {
|
||||
return rf(ctx)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context) types.ConsAddress); ok {
|
||||
r0 = rf(ctx)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(types.ConsAddress)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
|
||||
r1 = rf(ctx)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// NewDistributionKeeper creates a new instance of DistributionKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewDistributionKeeper(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *DistributionKeeper {
|
||||
mock := &DistributionKeeper{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
53
x/auction/types/mocks/rewards_address_provider.go
Normal file
53
x/auction/types/mocks/rewards_address_provider.go
Normal file
@ -0,0 +1,53 @@
|
||||
// Code generated by mockery v2.30.1. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
types "github.com/cosmos/cosmos-sdk/types"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// RewardsAddressProvider is an autogenerated mock type for the RewardsAddressProvider type
|
||||
type RewardsAddressProvider struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// GetRewardsAddress provides a mock function with given fields: context
|
||||
func (_m *RewardsAddressProvider) GetRewardsAddress(context types.Context) (types.AccAddress, error) {
|
||||
ret := _m.Called(context)
|
||||
|
||||
var r0 types.AccAddress
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(types.Context) (types.AccAddress, error)); ok {
|
||||
return rf(context)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(types.Context) types.AccAddress); ok {
|
||||
r0 = rf(context)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(types.AccAddress)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(types.Context) error); ok {
|
||||
r1 = rf(context)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// NewRewardsAddressProvider creates a new instance of RewardsAddressProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewRewardsAddressProvider(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *RewardsAddressProvider {
|
||||
mock := &RewardsAddressProvider{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
55
x/auction/types/mocks/staking_keeper.go
Normal file
55
x/auction/types/mocks/staking_keeper.go
Normal file
@ -0,0 +1,55 @@
|
||||
// Code generated by mockery v2.30.1. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
types "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// StakingKeeper is an autogenerated mock type for the StakingKeeper type
|
||||
type StakingKeeper struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// GetValidatorByConsAddr provides a mock function with given fields: _a0, _a1
|
||||
func (_m *StakingKeeper) GetValidatorByConsAddr(_a0 context.Context, _a1 types.ConsAddress) (stakingtypes.Validator, error) {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
var r0 stakingtypes.Validator
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, types.ConsAddress) (stakingtypes.Validator, error)); ok {
|
||||
return rf(_a0, _a1)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, types.ConsAddress) stakingtypes.Validator); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
} else {
|
||||
r0 = ret.Get(0).(stakingtypes.Validator)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, types.ConsAddress) error); ok {
|
||||
r1 = rf(_a0, _a1)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// NewStakingKeeper creates a new instance of StakingKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewStakingKeeper(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *StakingKeeper {
|
||||
mock := &StakingKeeper{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user