Skip to content

Commit

Permalink
feat(x/ecocredit): add buy/sell expiration (#671)
Browse files Browse the repository at this point in the history
* feat(x/ecocredit): add buy/sell expiration

* feat(x/ecocredit): add buy/sell expiration

* feat(x/ecocredit): add buy/sell expiration

* prune orders unit tests v1

* update expiration test

* fix expiration index

* address review comments

* address review comment

* adding parseAndSetDate

* organize tests

* address review comments

* cleanup

Co-authored-by: Robert Zaremba <robert@zaremba.ch>
  • Loading branch information
ryanchristo and robert-zaremba authored Jan 25, 2022
1 parent ca73c88 commit 26cb432
Show file tree
Hide file tree
Showing 24 changed files with 1,472 additions and 417 deletions.
2 changes: 1 addition & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ func NewRegenApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest
// NOTE: capability module's beginblocker must come before any modules using capabilities (e.g. IBC)
app.mm.SetOrderBeginBlockers(
upgradetypes.ModuleName, capabilitytypes.ModuleName, minttypes.ModuleName, distrtypes.ModuleName, slashingtypes.ModuleName,
evidencetypes.ModuleName, stakingtypes.ModuleName, ibchost.ModuleName,
evidencetypes.ModuleName, stakingtypes.ModuleName, ibchost.ModuleName, /* ecocredit.ModuleName, */
)
app.mm.SetOrderEndBlockers(crisistypes.ModuleName, govtypes.ModuleName, stakingtypes.ModuleName)
// NOTE: The genutils module must occur after staking so that pools are
Expand Down
14 changes: 14 additions & 0 deletions proto/regen/ecocredit/v1alpha2/events.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ syntax = "proto3";

package regen.ecocredit.v1alpha2;

import "gogoproto/gogo.proto";
import "cosmos/base/v1beta1/coin.proto";
import "google/protobuf/timestamp.proto";

option go_package = "github.com/regen-network/regen-ledger/x/ecocredit";

Expand Down Expand Up @@ -147,6 +149,10 @@ message EventSell {
// buyer to disable auto-retirement in their buy order enabling them to
// resell the credits to another buyer.
bool disable_auto_retire = 5;

// expiration is an optional timestamp when the sell order expires. When the
// expiration time is reached, the sell order is removed from state.
google.protobuf.Timestamp expiration = 6 [ (gogoproto.stdtime) = true ];
}

// EventUpdateSellOrder is an event emitted when a sell order is updated.
Expand All @@ -170,6 +176,10 @@ message EventUpdateSellOrder {

// disable_auto_retire updates the disable_auto_retire field in the sell order.
bool disable_auto_retire = 6;

// new_expiration is an optional timestamp when the sell order expires. When the
// expiration time is reached, the sell order is removed from state.
google.protobuf.Timestamp new_expiration = 7 [ (gogoproto.stdtime) = true ];
}

// EventBuyOrderCreated is an event emitted when a buy order is created.
Expand Down Expand Up @@ -206,6 +216,10 @@ message EventBuyOrderCreated {
// retirement_location is the optional retirement location for the credits
// which will be used only if disable_auto_retire is false.
string retirement_location = 7;

// expiration is the optional timestamp when the buy order expires. When the
// expiration time is reached, the buy order is removed from state.
google.protobuf.Timestamp expiration = 8 [ (gogoproto.stdtime) = true ];
}

// EventBuyOrderFilled is an event emitted when a buy order is filled.
Expand Down
12 changes: 12 additions & 0 deletions proto/regen/ecocredit/v1alpha2/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,10 @@ message MsgSell {
// buyer to disable auto-retirement in their buy order enabling them to
// resell the credits to another buyer.
bool disable_auto_retire = 4;

// expiration is an optional timestamp when the sell order expires. When the
// expiration time is reached, the sell order is removed from state.
google.protobuf.Timestamp expiration = 5 [ (gogoproto.stdtime) = true ];
}
}

Expand Down Expand Up @@ -415,6 +419,10 @@ message MsgUpdateSellOrders {

// disable_auto_retire updates the disable_auto_retire field in the sell order.
bool disable_auto_retire = 4;

// new_expiration is an optional timestamp when the sell order expires. When the
// expiration time is reached, the sell order is removed from state.
google.protobuf.Timestamp new_expiration = 5 [ (gogoproto.stdtime) = true ];
}
}

Expand Down Expand Up @@ -479,6 +487,10 @@ message MsgBuy {
// 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;

// expiration is the optional timestamp when the buy order expires. When the
// expiration time is reached, the buy order is removed from state.
google.protobuf.Timestamp expiration = 7 [ (gogoproto.stdtime) = true ];
}
}

Expand Down
8 changes: 8 additions & 0 deletions proto/regen/ecocredit/v1alpha2/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ message SellOrder {
// buyer to disable auto-retirement in their buy order enabling them to
// resell the credits to another buyer.
bool disable_auto_retire = 6;

// expiration is an optional timestamp when the sell order expires. When the
// expiration time is reached, the sell order is removed from state.
google.protobuf.Timestamp expiration = 7 [ (gogoproto.stdtime) = true ];
}

// BuyOrder represents the information for a buy order.
Expand Down Expand Up @@ -209,6 +213,10 @@ message BuyOrder {
// disable_partial_fill disables the default behavior of partially filling
// buy orders if the requested quantity is not available.
bool disable_partial_fill = 7;

// expiration is the optional timestamp when the buy order expires. When the
// expiration time is reached, the buy order is removed from state.
google.protobuf.Timestamp expiration = 8 [ (gogoproto.stdtime) = true ];
}

// AskDenom represents the information for an ask denom.
Expand Down
19 changes: 19 additions & 0 deletions x/ecocredit/abci.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package ecocredit

import (
"time"

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

// BeginBlocker checks if there are any expired sell or buy orders and removes them from state.
func BeginBlocker(ctx sdk.Context, k Keeper) error {
defer telemetry.ModuleMeasureSince(ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker)

if err := k.PruneOrders(ctx); err != nil {
return err
}

return nil
}
14 changes: 4 additions & 10 deletions x/ecocredit/client/testsuite/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"fmt"

"github.com/cosmos/cosmos-sdk/client/flags"
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/regen-network/regen-ledger/types/testutil/cli"
"github.com/regen-network/regen-ledger/x/ecocredit"
"github.com/regen-network/regen-ledger/x/ecocredit/client"
Expand Down Expand Up @@ -482,7 +482,8 @@ func (s *IntegrationTestSuite) TestQueryParams() {
require.NoError(err)

var params ecocredit.QueryParamsResponse
json.Unmarshal(out.Bytes(), &params)
err = json.Unmarshal(out.Bytes(), &params)
require.NoError(err)

require.Equal(ecocredit.DefaultParams(), *params.Params)
}
Expand Down Expand Up @@ -521,14 +522,7 @@ func (s *IntegrationTestSuite) TestQuerySellOrder() {
args: []string{"1"},
expErr: false,
expErrMsg: "",
expOrder: &ecocredit.SellOrder{
OrderId: 1,
Owner: val.Address.String(),
BatchDenom: batchDenom,
Quantity: "1",
AskPrice: &sdk.Coin{Denom: "regen", Amount: sdk.NewInt(100)},
DisableAutoRetire: false,
},
expOrder: s.sellOrders[0],
},
}

Expand Down
125 changes: 101 additions & 24 deletions x/ecocredit/client/testsuite/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
banktestutil "github.com/cosmos/cosmos-sdk/x/bank/client/testutil"
"github.com/gogo/protobuf/proto"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
tmcli "github.com/tendermint/tendermint/libs/cli"

"github.com/regen-network/regen-ledger/types/testutil/cli"
"github.com/regen-network/regen-ledger/types/testutil/network"
"github.com/regen-network/regen-ledger/x/ecocredit"
"github.com/regen-network/regen-ledger/x/ecocredit/client"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
tmcli "github.com/tendermint/tendermint/libs/cli"
)

type IntegrationTestSuite struct {
Expand Down Expand Up @@ -1418,12 +1419,16 @@ func (s *IntegrationTestSuite) TestTxSell() {
val0 := s.network.Validators[0]
clientCtx := val0.ClientCtx

expiration, err := client.ParseDate("expiration", "2024-01-01")
s.Require().NoError(err)

testCases := []struct {
name string
args []string
expErr bool
expErrMsg string
expOrder *ecocredit.SellOrder
name string
args []string
sellOrderId string
expErr bool
expErrMsg string
expOrder *ecocredit.SellOrder
}{
{
name: "missing args",
Expand Down Expand Up @@ -1518,7 +1523,8 @@ func (s *IntegrationTestSuite) TestTxSell() {
},
s.commonTxFlags()...,
),
expErr: false,
sellOrderId: "4",
expErr: false,
expOrder: &ecocredit.SellOrder{
OrderId: 4,
Owner: val0.Address.String(),
Expand All @@ -1528,6 +1534,27 @@ func (s *IntegrationTestSuite) TestTxSell() {
DisableAutoRetire: false,
},
},
{
name: "valid with expiration",
args: append(
[]string{
"[{batch_denom: \"C01-20210101-20210201-001\", quantity: \"5\", ask_price: \"100regen\", disable_auto_retire: false, expiration: \"2024-01-01\"}]",
makeFlagFrom(val0.Address.String()),
},
s.commonTxFlags()...,
),
sellOrderId: "5",
expErr: false,
expOrder: &ecocredit.SellOrder{
OrderId: 5,
Owner: val0.Address.String(),
BatchDenom: batchDenom,
Quantity: "5",
AskPrice: &sdk.Coin{Denom: "regen", Amount: sdk.NewInt(100)},
DisableAutoRetire: false,
Expiration: &expiration,
},
},
}

for _, tc := range testCases {
Expand All @@ -1542,7 +1569,10 @@ func (s *IntegrationTestSuite) TestTxSell() {

// query sell order
query := client.QuerySellOrderCmd()
out, err := cli.ExecTestCLICmd(clientCtx, query, []string{"4", flagOutputJSON})
out, err := cli.ExecTestCLICmd(clientCtx, query, []string{
tc.sellOrderId,
flagOutputJSON,
})
s.Require().NoError(err, out.String())

// unmarshal query response
Expand All @@ -1561,12 +1591,16 @@ func (s *IntegrationTestSuite) TestTxUpdateSellOrders() {
val0 := s.network.Validators[0]
clientCtx := val0.ClientCtx

expiration, err := client.ParseDate("expiration", "2026-01-01")
s.Require().NoError(err)

testCases := []struct {
name string
args []string
expErr bool
expErrMsg string
expOrder *ecocredit.SellOrder
name string
args []string
sellOrderId string
expErr bool
expErrMsg string
expOrder *ecocredit.SellOrder
}{
{
name: "missing args",
Expand Down Expand Up @@ -1661,7 +1695,8 @@ func (s *IntegrationTestSuite) TestTxUpdateSellOrders() {
},
s.commonTxFlags()...,
),
expErr: false,
sellOrderId: "4",
expErr: false,
expOrder: &ecocredit.SellOrder{
OrderId: 4,
Owner: val0.Address.String(),
Expand All @@ -1671,6 +1706,27 @@ func (s *IntegrationTestSuite) TestTxUpdateSellOrders() {
DisableAutoRetire: false,
},
},
{
name: "valid with expiration",
args: append(
[]string{
"[{sell_order_id: \"5\", new_quantity: \"5\", new_ask_price: \"200regen\", disable_auto_retire: false, new_expiration: \"2026-01-01\"}]",
makeFlagFrom(val0.Address.String()),
},
s.commonTxFlags()...,
),
sellOrderId: "5",
expErr: false,
expOrder: &ecocredit.SellOrder{
OrderId: 5,
Owner: val0.Address.String(),
BatchDenom: batchDenom,
Quantity: "5",
AskPrice: &sdk.Coin{Denom: "regen", Amount: sdk.NewInt(200)},
DisableAutoRetire: false,
Expiration: &expiration,
},
},
}

for _, tc := range testCases {
Expand All @@ -1685,7 +1741,10 @@ func (s *IntegrationTestSuite) TestTxUpdateSellOrders() {

// query sell order
query := client.QuerySellOrderCmd()
out, err := cli.ExecTestCLICmd(clientCtx, query, []string{"4", flagOutputJSON})
out, err := cli.ExecTestCLICmd(clientCtx, query, []string{
tc.sellOrderId,
flagOutputJSON,
})
s.Require().NoError(err, out.String())

// unmarshal query response
Expand All @@ -1705,10 +1764,11 @@ func (s *IntegrationTestSuite) TestTxBuy() {
clientCtx := val0.ClientCtx

testCases := []struct {
name string
args []string
expErr bool
expErrMsg string
name string
args []string
sellOrderId string
expErr bool
expErrMsg string
}{
{
name: "missing args",
Expand Down Expand Up @@ -1803,8 +1863,22 @@ func (s *IntegrationTestSuite) TestTxBuy() {
},
s.commonTxFlags()...,
),
expErr: false,
expErrMsg: "",
sellOrderId: "4",
expErr: false,
expErrMsg: "",
},
{
name: "valid with expiration",
args: append(
[]string{
"[{sell_order_id: \"5\", quantity: \"5\", bid_price: \"100regen\", disable_auto_retire: false, expiration: \"2024-01-01\"}]",
makeFlagFrom(val0.Address.String()),
},
s.commonTxFlags()...,
),
sellOrderId: "5",
expErr: false,
expErrMsg: "",
},
}

Expand All @@ -1820,7 +1894,10 @@ func (s *IntegrationTestSuite) TestTxBuy() {

// query sell order (should no longer exist)
query := client.QuerySellOrderCmd()
_, err := cli.ExecTestCLICmd(clientCtx, query, []string{"4", flagOutputJSON})
_, err := cli.ExecTestCLICmd(clientCtx, query, []string{
tc.sellOrderId,
flagOutputJSON,
})
s.Require().Error(err)
s.Require().Contains(err.Error(), "not found")
}
Expand Down
Loading

0 comments on commit 26cb432

Please sign in to comment.