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

refactor(ecocredit/marketplace): BuyDirect #967

Merged
merged 36 commits into from
Apr 11, 2022
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
ba1e747
refactor: separate buy and buy direct
technicallyty Mar 31, 2022
d361d33
chore: slim down test
technicallyty Mar 31, 2022
97fd205
fix: comment
technicallyty Mar 31, 2022
741ce80
chore: cleanup
technicallyty Mar 31, 2022
c3b4601
chore: add decimal test case'
technicallyty Mar 31, 2022
7e201bb
chore: make tidy
technicallyty Mar 31, 2022
fac0d41
chore: remove print
technicallyty Mar 31, 2022
824d473
Merge branch 'master' into ty/905-buy_direct
technicallyty Apr 1, 2022
1462953
chore: Int64() -> String()
technicallyty Apr 4, 2022
ee2a61e
Merge branch 'master' into ty/905-buy_direct
technicallyty Apr 4, 2022
db010af
chore: apply suggestions from code review
technicallyty Apr 4, 2022
e1a327e
fix: import order
technicallyty Apr 4, 2022
5f8029c
chore: make buy fail in validate basic
technicallyty Apr 4, 2022
487fcf6
chore: proto-gen
technicallyty Apr 4, 2022
d71dc02
chore: add comment explaining div by 1
technicallyty Apr 4, 2022
e152968
refactor: price per credit -> bidprice
technicallyty Apr 4, 2022
2e27c64
refactor: add orderOptions struct
technicallyty Apr 4, 2022
fe36dd2
refactor: batch direct buy orders
technicallyty Apr 5, 2022
bbf0fd4
Merge branch 'master' into ty/905-buy_direct
technicallyty Apr 6, 2022
d9dd4c9
Merge branch 'master' into ty/905-buy_direct
technicallyty Apr 6, 2022
4af5c30
chore: address review
technicallyty Apr 6, 2022
5dac0bb
chore: remove buy.go
technicallyty Apr 6, 2022
ae9ef59
refactor: string -> big int
technicallyty Apr 6, 2022
b0a45d3
Merge branch 'master' into ty/905-buy_direct
technicallyty Apr 6, 2022
a4b91a9
chore: remove all buy code
technicallyty Apr 7, 2022
346e52d
Merge branch 'ty/905-buy_direct' of https://github.com/regen-network/…
technicallyty Apr 7, 2022
4e4083b
Update x/ecocredit/server/marketplace/buy_direct.go
technicallyty Apr 8, 2022
8996422
Update x/ecocredit/server/marketplace/buy_direct.go
technicallyty Apr 8, 2022
c81cd5d
Update x/ecocredit/server/marketplace/utils.go
technicallyty Apr 8, 2022
0aa0a90
Update x/ecocredit/server/marketplace/utils.go
technicallyty Apr 8, 2022
b917a9f
Merge branch 'master' into ty/905-buy_direct
technicallyty Apr 9, 2022
df038d4
chore: expand test fields
technicallyty Apr 9, 2022
a57a4c2
Merge branch 'master' into ty/905-buy_direct
technicallyty Apr 11, 2022
a9fa299
refactor: use SdkIntTrim
technicallyty Apr 11, 2022
1214351
chore: spacing
technicallyty Apr 11, 2022
1c05433
chore: comment out buyorder reliant code
technicallyty Apr 11, 2022
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
2,464 changes: 1,814 additions & 650 deletions api/regen/ecocredit/marketplace/v1/tx.pulsar.go

Large diffs are not rendered by default.

38 changes: 38 additions & 0 deletions api/regen/ecocredit/marketplace/v1/tx_grpc.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

61 changes: 43 additions & 18 deletions proto/regen/ecocredit/marketplace/v1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ service Msg {
// Buy creates credit buy orders.
rpc Buy(MsgBuy) returns (MsgBuyResponse);
technicallyty marked this conversation as resolved.
Show resolved Hide resolved
technicallyty marked this conversation as resolved.
Show resolved Hide resolved

// BuyDirect purchases credits directly from the specified sell order.
rpc BuyDirect(MsgBuyDirect) returns (MsgBuyDirectResponse);

// AllowAskDenom is a governance operation which authorizes a new ask denom to
// be used in sell orders
rpc AllowAskDenom(MsgAllowAskDenom) returns (MsgAllowAskDenomResponse);
Expand Down Expand Up @@ -135,24 +138,9 @@ message MsgBuy {
// Order is a buy order.
message Order {

// Selection defines a buy order selection.
message Selection {

// sum defines the type of selection.
oneof sum {
// sell_order_id is the sell order ID against which the buyer is trying
// to buy. When sell_order_id is set, this is known as a direct buy
// order because it is placed directly against a specific sell order.
uint64 sell_order_id = 1;

// filter selects credits to buy based upon the specified filter
// criteria.
Filter filter = 2;
}
}

// selection is the buy order selection.
Selection selection = 1;
// filter selects credits to buy based upon the specified filter
// criteria.
Filter filter = 1;

// quantity is the quantity of credits to buy. If the quantity of credits
// available is less than this amount the order will be partially filled
Expand Down Expand Up @@ -195,6 +183,43 @@ message MsgBuyResponse {
repeated uint64 buy_order_ids = 1;
}

// MsgBuyDirect is the Msg/BuyDirect request type.
message MsgBuyDirect {

// buyer is the address of the credit buyer.
string buyer = 1;

// orders is a list of orders for ecocredits.
repeated Order orders = 2;

// Order contains the information needed to purchase an ecocredit.
message Order {
// sell_order_id is the sell order ID against which the buyer is trying
// to buy.
uint64 sell_order_id = 2;

// quantity is the quantity of credits to buy.
string quantity = 3;

// bid_price is the price the buyer is willing to pay per credit.
cosmos.base.v1beta1.Coin bid_price = 4;

// disable_auto_retire allows auto-retirement to be disabled. If it is set
// to true the credits will not auto-retire and can be resold assuming that
// the corresponding sell order has auto-retirement disabled. If the sell
// order hasn't disabled auto-retirement and the buy order tries to disable
// it, that buy order will fail.
bool disable_auto_retire = 5;

// retirement_location is the optional retirement location for the credits
// which will be used only if disable_auto_retire is false.
string retirement_location = 6;
}
}

// MsgBuyDirectResponse is the Msg/BuyDirect response type.
message MsgBuyDirectResponse {}

// MsgAllowAskDenom is the Msg/AllowAskDenom request type.
message MsgAllowAskDenom {
// root_address is the address of the governance account which can authorize
Expand Down
1 change: 1 addition & 0 deletions types/math/dec.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"math/big"

"github.com/cockroachdb/apd/v2"

"github.com/cosmos/cosmos-sdk/types/errors"
)

Expand Down
3 changes: 2 additions & 1 deletion x/ecocredit/marketplace/msg_buy.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func (m MsgBuy) GetSignBytes() []byte {

// ValidateBasic does a sanity check on the provided data.
func (m *MsgBuy) ValidateBasic() error {
return sdkerrors.ErrNotSupported.Wrap("filtered buy orders are not suported at this time")
if _, err := sdk.AccAddressFromBech32(m.Buyer); err != nil {
return sdkerrors.ErrInvalidAddress
}
Expand All @@ -49,7 +50,7 @@ func (m *MsgBuy) ValidateBasic() error {

if !order.DisableAutoRetire {
if err := core.ValidateLocation(order.RetirementLocation); err != nil {
// ValidateLocation returns an sdkerrors.ErrInvalidRequest, so we can just wrap it here
// ValidateLocation returns a sdkerrors.ErrInvalidRequest, so we can just wrap it here
return sdkerrors.Wrap(err, "a valid retirement location is required when DisableAutoRetire is false")
}
}
Expand Down
53 changes: 53 additions & 0 deletions x/ecocredit/marketplace/msg_buy_direct.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package marketplace

import (
"github.com/regen-network/regen-ledger/types/math"
"github.com/regen-network/regen-ledger/x/ecocredit"
"github.com/regen-network/regen-ledger/x/ecocredit/core"

"github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx"
)

var _ legacytx.LegacyMsg = &MsgBuyDirect{}

func (m MsgBuyDirect) ValidateBasic() error {
if _, err := types.AccAddressFromBech32(m.Buyer); err != nil {
return sdkerrors.ErrInvalidAddress.Wrap(err.Error())
}
for _, order := range m.Orders {
if order.SellOrderId == 0 {
return sdkerrors.ErrInvalidRequest.Wrap("0 is not a valid sell order id")
}
if _, err := math.NewDecFromString(order.Quantity); err != nil {
return sdkerrors.ErrInvalidRequest.Wrap(err.Error())
}
if !order.DisableAutoRetire {
if err := core.ValidateLocation(order.RetirementLocation); err != nil {
return sdkerrors.Wrapf(err, "when DisableAutoRetire is false, a valid retirement location must be provided")
}
}
if order.BidPrice == nil {
return sdkerrors.ErrInvalidRequest.Wrap("must specify price per credit")
}
if err := order.BidPrice.Validate(); err != nil {
return err
}
}

return nil
}

func (m MsgBuyDirect) GetSigners() []types.AccAddress {
addr, _ := types.AccAddressFromBech32(m.Buyer)
return []types.AccAddress{addr}
}

func (m MsgBuyDirect) GetSignBytes() []byte {
return types.MustSortJSON(ecocredit.ModuleCdc.MustMarshalJSON(&m))
}

func (m MsgBuyDirect) Route() string { return types.MsgTypeURL(&m) }

func (m MsgBuyDirect) Type() string { return types.MsgTypeURL(&m) }
101 changes: 101 additions & 0 deletions x/ecocredit/marketplace/msg_buy_direct_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package marketplace

import (
"testing"

"gotest.tools/v3/assert"

"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"

"github.com/regen-network/regen-ledger/types/math"
)

func TestMsgBuyDirect_ValidateBasic(t *testing.T) {
type fields struct {
Buyer string
SellOrderId uint64
Quantity string
PricePerCredit *sdk.Coin
DisableAutoRetire bool
RetirementLocation string
}
validCoin := sdk.NewInt64Coin("ufoo", 31)
_, _, addr := testdata.KeyTestPubAddr()
tests := []struct {
name string
fields fields
errMsg string
}{
{
name: "valid",
fields: fields{
Buyer: addr.String(),
SellOrderId: 1,
Quantity: "45.32",
PricePerCredit: &validCoin,
DisableAutoRetire: true,
},
},
{
name: "valid retirement location",
fields: fields{Buyer: addr.String(), SellOrderId: 1, Quantity: "45", PricePerCredit: &validCoin,
DisableAutoRetire: false, RetirementLocation: "US-NY"},
technicallyty marked this conversation as resolved.
Show resolved Hide resolved
},
{
name: "invalid addr",
fields: fields{Buyer: "foobar"},
errMsg: sdkerrors.ErrInvalidAddress.Error(),
},
{
name: "invalid order id",
fields: fields{Buyer: addr.String(), SellOrderId: 0},
errMsg: "0 is not a valid sell order id",
},
{
name: "invalid quantity",
fields: fields{Buyer: addr.String(), SellOrderId: 1, Quantity: "45.3xyz"},
errMsg: math.ErrInvalidDecString.Error(),
},
{
name: "no price per credit",
fields: fields{Buyer: addr.String(), SellOrderId: 1, Quantity: "45", DisableAutoRetire: true, PricePerCredit: nil},
errMsg: "must specify price per credit",
},
{
name: "invalid coin",
fields: fields{Buyer: addr.String(), SellOrderId: 1, Quantity: "45", DisableAutoRetire: true,
PricePerCredit: &sdk.Coin{Denom: "foo3=21.", Amount: sdk.NewInt(3)}},
errMsg: "invalid denom",
},
{
name: "no retirement location when AutoRetiring",
fields: fields{Buyer: addr.String(), SellOrderId: 1, Quantity: "45", PricePerCredit: &validCoin, DisableAutoRetire: false},
errMsg: "when DisableAutoRetire is false, a valid retirement location must be provided",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
m := MsgBuyDirect{
Buyer: tt.fields.Buyer,
Orders: []*MsgBuyDirect_Order{
{
SellOrderId: tt.fields.SellOrderId,
Quantity: tt.fields.Quantity,
BidPrice: tt.fields.PricePerCredit,
DisableAutoRetire: tt.fields.DisableAutoRetire,
RetirementLocation: tt.fields.RetirementLocation,
},
},
}
err := m.ValidateBasic()
if len(tt.errMsg) == 0 {
assert.NilError(t, err)
} else {
assert.Check(t, err != nil)
assert.ErrorContains(t, err, tt.errMsg)
Copy link
Member

Choose a reason for hiding this comment

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

Might be nice to provide the whole error and use equals rather than contains but ok leave as is.

}
})
}
}
Loading