Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

List on main market #831

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion integration_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ function prepare_node() {
$(cd "./${home}/config" && sed -i -e "s/BEP70Height = 9223372036854775807/BEP70Height = 1/g" app.toml)
$(cd "./${home}/config" && sed -i -e "s/ListRefactorHeight = 9223372036854775807/ListRefactorHeight = 150/g" app.toml)


# stop and start node
ps -ef | grep bnbchaind | grep testnoded | awk '{print $2}' | xargs kill -9
./bnbchaind start --home ${home} > ./testnoded/node.log 2>&1 &
Expand Down Expand Up @@ -400,4 +399,31 @@ sleep 2s
result=$(expect ./list_growth_market.exp ${gbc_symbol} BNB 100000000 bob ${chain_id} ${cli_home} 1)
check_operation "List Trading Pair" "${result}" "${chain_operation_words}"

## ROUND 7 ##

sleep 1s
# issue token
result=$(expect ./issue.exp LBC Bitcoin 1000000000000000 true bob ${chain_id} ${cli_home})
lbc_symbol=$(echo "${result}" | tail -n 1 | grep -o "LBC-[0-9A-Z]*")
check_operation "Issue Token" "${result}" "${chain_operation_words}"

sleep 1s
# propose list
((expire_time=$(date '+%s')+1000))
lower_case_lbc_symbol=$(echo ${lbc_symbol} | tr 'A-Z' 'a-z')
result=$(expect ./propose_list.exp ${chain_id} alice 200000000000:BNB ${lower_case_lbc_symbol} bnb 100000000 "list LBC/BNB" "list LBC/BNB" ${cli_home} ${expire_time} 5)
check_operation "Propose list" "${result}" "${chain_operation_words}"

sleep 2s
# vote for propose
result=$(expect ./vote.exp alice ${chain_id} 2 Yes ${cli_home})
check_operation "Vote" "${result}" "${chain_operation_words}"

sleep 3s
# place buy order
result=$(expect ./order.exp ${lbc_symbol}_BNB 1 100000000 1000000000 alice ${chain_id} gte ${cli_home})
check_operation "Place Order" "${result}" "${chain_operation_words}"
order_id=$(echo "${result}" | tail -n 1 | grep -o "[0-9A-Z]\{4,\}-[0-9]*") # capture order id, not symbol
printf "Order ID: $order_id\n"

exit_test 0
112 changes: 106 additions & 6 deletions plugins/dex/list/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ import (
"encoding/json"
"errors"
"fmt"
"strings"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/gov"

"github.com/binance-chain/node/common/log"
"github.com/binance-chain/node/common/types"
"github.com/binance-chain/node/common/upgrade"
"github.com/binance-chain/node/plugins/dex/order"
dextypes "github.com/binance-chain/node/plugins/dex/types"
"github.com/binance-chain/node/plugins/tokens"
)

Expand All @@ -25,7 +29,49 @@ func NewListHooks(orderKeeper *order.DexKeeper, tokenMapper tokens.Mapper) ListH
}
}

var _ gov.GovHooks = ListHooks{}
var _ gov.ExtGovHooks = ListHooks{}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we just use the GovHooks

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we use the GovHooks, then we need implement the OnProposalPassed method for all the places where implement this hook.


func (hooks ListHooks) OnProposalPassed(ctx sdk.Context, proposal gov.Proposal) error {
if !sdk.IsUpgrade(upgrade.ListRefactor) {
return nil
}

if proposal.GetProposalType() != gov.ProposalTypeListTradingPair {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need this check if this Listhook is invoked.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Theoretically we don't, as the invoker won't invoke this function when proposal type is not ProposalTypeListTradingPair. But is that better to add this check as the function itself to prevent the unexpected invocation?

return fmt.Errorf("proposal type(%s) should be %s",
proposal.GetProposalType(), gov.ProposalTypeListTradingPair)
}

if proposal.GetStatus() != gov.StatusPassed {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as the above, we may not need this check

return fmt.Errorf("proposal status(%s) should be Passed before you can list your token",
proposal.GetStatus())
}

listParams := gov.ListTradingPairParams{}
err := json.Unmarshal([]byte(proposal.GetDescription()), &listParams)
if err != nil {
return fmt.Errorf("illegal list params in proposal, params=%s", proposal.GetDescription())
}

baseAssetSymbol := strings.ToUpper(listParams.BaseAssetSymbol)
quoteAssetSymbol := strings.ToUpper(listParams.QuoteAssetSymbol)

if err := checkListingPairOnMainMarket(hooks, ctx, baseAssetSymbol, quoteAssetSymbol); err != nil {
return err
}

lotSize := hooks.orderKeeper.DetermineLotSize(baseAssetSymbol, quoteAssetSymbol, listParams.InitPrice)

pair := dextypes.NewTradingPairWithLotSize(baseAssetSymbol, quoteAssetSymbol, listParams.InitPrice, lotSize)
err = hooks.orderKeeper.PairMapper.AddTradingPair(ctx, pair)
if err != nil {
return err
}

hooks.orderKeeper.AddEngine(pair)
log.With("module", "dex").Info("List new Pair and created new match engine", "pair", pair)

return nil
}

func (hooks ListHooks) OnProposalSubmitted(ctx sdk.Context, proposal gov.Proposal) error {
if proposal.GetProposalType() != gov.ProposalTypeListTradingPair {
Expand Down Expand Up @@ -58,12 +104,18 @@ func (hooks ListHooks) OnProposalSubmitted(ctx sdk.Context, proposal gov.Proposa
return errors.New("expire time should after now")
}

if !hooks.tokenMapper.ExistsBEP2(ctx, listParams.BaseAssetSymbol) {
return errors.New("base token does not exist")
}
if sdk.IsUpgrade(upgrade.ListRefactor) {
if err := checkListingPairOnMainMarket(hooks, ctx, strings.ToUpper(listParams.BaseAssetSymbol), strings.ToUpper(listParams.QuoteAssetSymbol)); err != nil {
return err
}
} else {
if !hooks.tokenMapper.ExistsBEP2(ctx, listParams.BaseAssetSymbol) {
return errors.New("base token does not exist")
}

if !hooks.tokenMapper.ExistsBEP2(ctx, listParams.QuoteAssetSymbol) {
return errors.New("quote token does not exist")
if !hooks.tokenMapper.ExistsBEP2(ctx, listParams.QuoteAssetSymbol) {
return errors.New("quote token does not exist")
}
}

if err := hooks.orderKeeper.CanListTradingPair(ctx, listParams.BaseAssetSymbol, listParams.QuoteAssetSymbol); err != nil {
Expand All @@ -73,6 +125,54 @@ func (hooks ListHooks) OnProposalSubmitted(ctx sdk.Context, proposal gov.Proposa
return nil
}

/**
* 1. Existence check
* 2. Mini asset can only be Base Asset against BNB/BUSD
* 3. Asset can only listed on one market:
* a. if quote is BNB, check if exists Base/BUSD listed in new market already
* b. if quote is BUSD, check if exists Base/BNB listed in new market already
* c. else, check if exists Quote/BNB listed in new market already
*/
func checkListingPairOnMainMarket(hooks ListHooks, ctx sdk.Context, BaseAssetSymbol string, QuoteAssetSymbol string) error {
if types.IsMiniTokenSymbol(BaseAssetSymbol) {
if !hooks.tokenMapper.ExistsMini(ctx, BaseAssetSymbol) {
return errors.New("base token does not exist")
}
if QuoteAssetSymbol != types.NativeTokenSymbol && QuoteAssetSymbol != order.BUSDSymbol {
return errors.New("mini token can only be base symbol against BNB or BUSD")
}
} else {
if types.IsMiniTokenSymbol(QuoteAssetSymbol) {
return errors.New("mini token can not be listed as quote symbol")
}
if !hooks.tokenMapper.ExistsBEP2(ctx, BaseAssetSymbol) {
return errors.New("base token does not exist")
}

if !hooks.tokenMapper.ExistsBEP2(ctx, QuoteAssetSymbol) {
return errors.New("quote token does not exist")
}
}
if types.NativeTokenSymbol == QuoteAssetSymbol {
if pair, err := hooks.orderKeeper.PairMapper.GetTradingPair(ctx, BaseAssetSymbol, order.BUSDSymbol); err == nil {
// TODO check if pair type is new market, return err: one token can only be listed in one market
log.Info(fmt.Sprintf("%s", pair)) // remove this log
}
} else if order.BUSDSymbol == QuoteAssetSymbol {
if pair, err := hooks.orderKeeper.PairMapper.GetTradingPair(ctx, BaseAssetSymbol, types.NativeTokenSymbol); err == nil {
// TODO check if pair type is new market, return err: one token can only be listed in one market
log.Info(fmt.Sprintf("%s", pair)) // remove this log
}
} else {
if pair, err := hooks.orderKeeper.PairMapper.GetTradingPair(ctx, QuoteAssetSymbol, types.NativeTokenSymbol); err == nil {
// TODO check if pair type is new market, return err: one token can only be listed in one market
log.Info(fmt.Sprintf("%s", pair)) // remove this log
}
}

return nil
}

type DelistHooks struct {
orderKeeper *order.DexKeeper
}
Expand Down
140 changes: 140 additions & 0 deletions plugins/dex/list/hooks_proposal_passed_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package list

import (
"encoding/json"
"github.com/binance-chain/node/common/upgrade"
"testing"
"time"

"github.com/stretchr/testify/require"

abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/log"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/gov"

"github.com/binance-chain/node/common/types"
dexTypes "github.com/binance-chain/node/plugins/dex/types"
)

func prepare() {
sdk.UpgradeMgr.AddUpgradeHeight(upgrade.ListRefactor, 100)
sdk.UpgradeMgr.SetHeight(200)
}

func TestIncorrectProposalType(t *testing.T) {
prepare()
hooks := NewListHooks(nil, nil)
proposal := gov.TextProposal{
ProposalType: gov.ProposalTypeText,
}
err := hooks.OnProposalPassed(sdk.Context{}, &proposal)
require.NotNil(t, err)
require.Contains(t, err.Error(), "proposal type")
}

func TestIncorrectProposalStatus(t *testing.T) {
prepare()
hooks := NewListHooks(nil, nil)
proposal := gov.TextProposal{
ProposalType: gov.ProposalTypeListTradingPair,
Status: gov.StatusVotingPeriod,
}
err := hooks.OnProposalPassed(sdk.Context{}, &proposal)
require.NotNil(t, err)
require.Contains(t, err.Error(), "proposal status")
}

func TestDuplicatedTradingPair(t *testing.T) {
listParams := gov.ListTradingPairParams{
BaseAssetSymbol: "BNB",
QuoteAssetSymbol: "BTC-ABC",
InitPrice: 1,
ExpireTime: time.Now(),
}

listParamsBz, err := json.Marshal(listParams)
require.Nil(t, err, "marshal list params error")
proposal := gov.TextProposal{
ProposalType: gov.ProposalTypeListTradingPair,
Description: string(listParamsBz),
}

cdc := MakeCodec()
ms, orderKeeper, tokenMapper, _ := MakeKeepers(cdc)
hooks := NewListHooks(orderKeeper, tokenMapper)

ctx := sdk.NewContext(ms, abci.Header{}, sdk.RunTxModeDeliver, log.NewNopLogger())

err = tokenMapper.NewToken(ctx, &types.Token{
Name: "Native Token",
Symbol: listParams.BaseAssetSymbol,
OrigSymbol: listParams.BaseAssetSymbol,
TotalSupply: 10000,
Owner: sdk.AccAddress("testacc"),
})
require.Nil(t, err, "new token error")

err = tokenMapper.NewToken(ctx, &types.Token{
Name: "Native Token",
Symbol: listParams.QuoteAssetSymbol,
OrigSymbol: "BTC",
TotalSupply: 10000,
Owner: sdk.AccAddress("testacc"),
})
require.Nil(t, err, "new token error")

pair := dexTypes.NewTradingPair(listParams.BaseAssetSymbol, listParams.QuoteAssetSymbol, listParams.InitPrice)
err = orderKeeper.PairMapper.AddTradingPair(ctx, pair)
require.Nil(t, err, "add trading pair error")

err = hooks.OnProposalSubmitted(ctx, &proposal)
require.NotNil(t, err, "err should not be nil")

require.Contains(t, err.Error(), "trading pair exists")
}

func TestPassed(t *testing.T) {
listParams := gov.ListTradingPairParams{
BaseAssetSymbol: "BNB",
QuoteAssetSymbol: "BTC-ABC",
InitPrice: 1,
ExpireTime: time.Now(),
}

listParamsBz, err := json.Marshal(listParams)
require.Nil(t, err, "marshal list params error")
proposal := gov.TextProposal{
ProposalType: gov.ProposalTypeListTradingPair,
Status: gov.StatusPassed,
Description: string(listParamsBz),
}

cdc := MakeCodec()
ms, orderKeeper, tokenMapper, _ := MakeKeepers(cdc)
hooks := NewListHooks(orderKeeper, tokenMapper)

ctx := sdk.NewContext(ms, abci.Header{}, sdk.RunTxModeDeliver, log.NewNopLogger())

err = tokenMapper.NewToken(ctx, &types.Token{
Name: "Native Token",
Symbol: listParams.BaseAssetSymbol,
OrigSymbol: listParams.BaseAssetSymbol,
TotalSupply: 10000,
Owner: sdk.AccAddress("testacc"),
})
require.Nil(t, err, "new token error")

err = tokenMapper.NewToken(ctx, &types.Token{
Name: "Native Token",
Symbol: listParams.QuoteAssetSymbol,
OrigSymbol: "BTC",
TotalSupply: 10000,
Owner: sdk.AccAddress("testacc"),
})
require.Nil(t, err, "new token error")

err = hooks.OnProposalSubmitted(ctx, &proposal)
require.Nil(t, err, "err should be nil")
}
8 changes: 5 additions & 3 deletions plugins/dex/types/msg_growth_market.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/binance-chain/node/common/types"
"github.com/binance-chain/node/plugins/dex/order"
)

const ListGrowthMarketMsgType = "dexListGrowthMarket"
Expand Down Expand Up @@ -47,8 +46,11 @@ func (msg ListGrowthMarketMsg) ValidateBasic() sdk.Error {
}
}

if msg.QuoteAssetSymbol != types.NativeTokenSymbol && msg.QuoteAssetSymbol != order.BUSDSymbol {
return sdk.ErrInvalidCoins("quote token must be " + types.NativeTokenSymbol + " or " + order.BUSDSymbol)
if !types.IsValidMiniTokenSymbol(msg.QuoteAssetSymbol) {
err := types.ValidateTokenSymbol(msg.QuoteAssetSymbol)
if err != nil {
return sdk.ErrInvalidCoins("quote token: " + err.Error())
}
}

if msg.InitPrice <= 0 {
Expand Down