-
Notifications
You must be signed in to change notification settings - Fork 42
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
List on main market #831
Changes from all commits
56ed2a6
f9b413b
14d62d9
1645834
a5ff9ba
e56f339
28726ae
ece8df8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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" | ||
) | ||
|
||
|
@@ -25,7 +29,49 @@ func NewListHooks(orderKeeper *order.DexKeeper, tokenMapper tokens.Mapper) ListH | |
} | ||
} | ||
|
||
var _ gov.GovHooks = ListHooks{} | ||
var _ gov.ExtGovHooks = ListHooks{} | ||
|
||
func (hooks ListHooks) OnProposalPassed(ctx sdk.Context, proposal gov.Proposal) error { | ||
if !sdk.IsUpgrade(upgrade.ListRefactor) { | ||
return nil | ||
} | ||
|
||
if proposal.GetProposalType() != gov.ProposalTypeListTradingPair { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we need this check if this Listhook is invoked. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
return fmt.Errorf("proposal type(%s) should be %s", | ||
proposal.GetProposalType(), gov.ProposalTypeListTradingPair) | ||
} | ||
|
||
if proposal.GetStatus() != gov.StatusPassed { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 { | ||
|
@@ -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 { | ||
|
@@ -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 | ||
} | ||
|
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") | ||
} |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.