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

[Tunnel] Add withdraw fee payer funds #522

Merged
merged 8 commits into from
Dec 17, 2024
Merged
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
1,585 changes: 1,343 additions & 242 deletions api/band/tunnel/v1beta1/tx.pulsar.go

Large diffs are not rendered by default.

39 changes: 39 additions & 0 deletions api/band/tunnel/v1beta1/tx_grpc.pb.go

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

2 changes: 1 addition & 1 deletion app/upgrades/v3/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
tunneltypes "github.com/bandprotocol/chain/v3/x/tunnel/types"
)

// UpgradeName defines the on-chain upgrade name for the Osmosis v26 upgrade.
// UpgradeName defines the on-chain upgrade name.
const (
UpgradeName = "v3"

Expand Down
23 changes: 23 additions & 0 deletions proto/band/tunnel/v1beta1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ service Msg {
// UpdateSignalsAndInterval is a RPC method to update a signals and interval of the tunnel.
rpc UpdateSignalsAndInterval(MsgUpdateSignalsAndInterval) returns (MsgUpdateSignalsAndIntervalResponse);

// WithdrawFeePayerFunds is a RPC method to withdraw fee payer funds to creator.
rpc WithdrawFeePayerFunds(MsgWithdrawFeePayerFunds) returns (MsgWithdrawFeePayerFundsResponse);

// Activate is a RPC method to activate a tunnel.
rpc Activate(MsgActivate) returns (MsgActivateResponse);

Expand Down Expand Up @@ -105,6 +108,26 @@ message MsgUpdateSignalsAndInterval {
// MsgUpdateSignalsAndIntervalResponse is the response type for the Msg/UpdateSignalsAndInterval RPC method.
message MsgUpdateSignalsAndIntervalResponse {}

// MsgWithdrawFeePayerFunds is the transaction message to withdraw fee payer funds to creator.
message MsgWithdrawFeePayerFunds {
option (cosmos.msg.v1.signer) = "creator";
option (amino.name) = "tunnel/MsgWithdrawFeePayerFunds";

// tunnel_id is the ID of the tunnel to withdraw fee payer coins.
uint64 tunnel_id = 1 [(gogoproto.customname) = "TunnelID"];
// amount is the coins to withdraw.
repeated cosmos.base.v1beta1.Coin amount = 2 [
(gogoproto.nullable) = false,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins",
(amino.dont_omitempty) = true
];
// creator is the address of the creator.
string creator = 3 [(cosmos_proto.scalar) = "cosmos.AddressString"];
}

// MsgWithdrawFeePayerFundsResponse is the response type for the Msg/WithdrawFeePayerFunds RPC method.
message MsgWithdrawFeePayerFundsResponse {}

// Activate is the transaction message to activate a tunnel.
message MsgActivate {
option (cosmos.msg.v1.signer) = "creator";
Expand Down
1 change: 1 addition & 0 deletions scripts/tunnel/withdraw_fee_payer_funds.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bandd tx tunnel withdraw-fee-payer-funds 1 1000000uband --from requester --keyring-backend test --gas-prices 0.0025uband -y --chain-id bandchain
26 changes: 26 additions & 0 deletions x/tunnel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ The Tunnel module is designed to decentralize the creation of push-based price d
- [MsgCreateTunnel](#msgcreatetunnel)
- [MsgUpdateRoute](#msgupdateroute)
- [MsgUpdateSignalsAndInterval](#msgupdatesignalsandinterval)
- [MsgWithdrawFeePayerFunds](#msgwithdrawfeepayerfunds)
- [MsgActivate](#msgactivate)
- [MsgDeactivate](#msgdeactivate)
- [MsgTriggerTunnel](#msgtriggertunnel)
Expand Down Expand Up @@ -289,6 +290,8 @@ message MsgUpdateRoute {

### MsgUpdateSignalsAndInterval

Allows the creator of a tunnel to update the list of signal deviations and the interval for the tunnel.

```protobuf
// MsgUpdateSignalsAndInterval is the transaction message to update signals and interval of the tunnel.
message MsgUpdateSignalsAndInterval {
Expand All @@ -306,6 +309,29 @@ message MsgUpdateSignalsAndInterval {
}
```

### MsgWithdrawFeePayerFunds

Allows the creator of a tunnel to withdraw funds from the fee payer to the creator.

```protobuf
// MsgWithdrawFeePayerFunds is the transaction message to withdraw fee payer funds to creator.
message MsgWithdrawFeePayerFunds {
option (cosmos.msg.v1.signer) = "creator";
option (amino.name) = "tunnel/MsgWithdrawFeePayerFunds";

// tunnel_id is the ID of the tunnel to withdraw fee payer coins.
uint64 tunnel_id = 1 [(gogoproto.customname) = "TunnelID"];
// amount is the coins to withdraw.
repeated cosmos.base.v1beta1.Coin amount = 2 [
(gogoproto.nullable) = false,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins",
(amino.dont_omitempty) = true
];
// creator is the address of the creator.
string creator = 3 [(cosmos_proto.scalar) = "cosmos.AddressString"];
}
```

### MsgActivate

To activate the tunnel for processing at the EndBlock, the following conditions must be met:
Expand Down
32 changes: 32 additions & 0 deletions x/tunnel/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func GetTxCmd() *cobra.Command {
GetTxCmdCreateTunnel(),
GetTxCmdUpdateRoute(),
GetTxCmdUpdateSignalsAndInterval(),
GetTxCmdWithdrawFeePayerFunds(),
GetTxCmdActivate(),
GetTxCmdDeactivate(),
GetTxCmdTriggerTunnel(),
Expand Down Expand Up @@ -246,6 +247,37 @@ func GetTxCmdUpdateSignalsAndInterval() *cobra.Command {
return cmd
}

func GetTxCmdWithdrawFeePayerFunds() *cobra.Command {
cmd := &cobra.Command{
Use: "withdraw-fee-payer-funds [tunnel-id] [amount]",
Short: "Withdraw fee payer funds from a tunnel to the creator",
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

id, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
return err
}

amount, err := sdk.ParseCoinsNormalized(args[1])
if err != nil {
return err
}

msg := types.NewMsgWithdrawFeePayerFunds(id, amount, clientCtx.GetFromAddress().String())
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}

flags.AddTxFlagsToCmd(cmd)

return cmd
}

func GetTxCmdActivate() *cobra.Command {
cmd := &cobra.Command{
Use: "activate [tunnel-id]",
Expand Down
4 changes: 3 additions & 1 deletion x/tunnel/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func (s *KeeperTestSuite) reset() {
s.Require().NoError(err)
}

func (s *KeeperTestSuite) AddSampleTunnel(isActive bool) {
func (s *KeeperTestSuite) AddSampleTunnel(isActive bool) *types.Tunnel {
ctx, k := s.ctx, s.keeper

s.accountKeeper.EXPECT().
Expand Down Expand Up @@ -148,6 +148,8 @@ func (s *KeeperTestSuite) AddSampleTunnel(isActive bool) {
err = k.ActivateTunnel(ctx, tunnel.ID)
s.Require().NoError(err)
}

return tunnel
}

func (s *KeeperTestSuite) AddSampleIBCTunnel(isActive bool) {
Expand Down
39 changes: 39 additions & 0 deletions x/tunnel/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,45 @@ func (k msgServer) UpdateSignalsAndInterval(
return &types.MsgUpdateSignalsAndIntervalResponse{}, nil
}

// WithdrawFeePayerFunds withdraws the fee payer's funds from the tunnel to the creator.
func (k msgServer) WithdrawFeePayerFunds(
goCtx context.Context,
msg *types.MsgWithdrawFeePayerFunds,
) (*types.MsgWithdrawFeePayerFundsResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)

tunnel, err := k.Keeper.GetTunnel(ctx, msg.TunnelID)
if err != nil {
return nil, err
}

if msg.Creator != tunnel.Creator {
return nil, types.ErrInvalidTunnelCreator.Wrapf("creator %s, tunnelID %d", msg.Creator, msg.TunnelID)
}

feePayer, err := sdk.AccAddressFromBech32(tunnel.FeePayer)
if err != nil {
return nil, err
}

creator, err := sdk.AccAddressFromBech32(msg.Creator)
if err != nil {
return nil, err
}

// send coins from the fee payer to the creator
if err := k.Keeper.bankKeeper.SendCoins(
ctx,
feePayer,
creator,
msg.Amount,
); err != nil {
return nil, err
}

return &types.MsgWithdrawFeePayerFundsResponse{}, nil
}

// Activate activates a tunnel.
func (k msgServer) Activate(
goCtx context.Context,
Expand Down
87 changes: 87 additions & 0 deletions x/tunnel/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,93 @@ func (s *KeeperTestSuite) TestMsgUpdateSignalsAndInterval() {
}
}

func (s *KeeperTestSuite) TestWithdrawFeePayerFunds() {
creator := sdk.AccAddress([]byte("creator_address")).String()

cases := map[string]struct {
preRun func() *types.MsgWithdrawFeePayerFunds
expErr bool
expErrMsg string
}{
"tunnel not found": {
preRun: func() *types.MsgWithdrawFeePayerFunds {
return &types.MsgWithdrawFeePayerFunds{
Creator: creator,
TunnelID: 1,
Amount: sdk.NewCoins(sdk.NewInt64Coin("uband", 500)),
}
},
expErr: true,
expErrMsg: "tunnel not found",
},
"invalid creator": {
preRun: func() *types.MsgWithdrawFeePayerFunds {
s.AddSampleTunnel(false)

return &types.MsgWithdrawFeePayerFunds{
Creator: "invalid_creator",
TunnelID: 1,
Amount: sdk.NewCoins(sdk.NewInt64Coin("uband", 500)),
}
},
expErr: true,
expErrMsg: "creator invalid_creator, tunnelID 1",
},
"insufficient funds": {
preRun: func() *types.MsgWithdrawFeePayerFunds {
tunnel := s.AddSampleTunnel(false)

amount := sdk.NewCoins(sdk.NewInt64Coin("uband", 9999999999))

s.bankKeeper.EXPECT().
SendCoins(s.ctx, sdk.MustAccAddressFromBech32(tunnel.FeePayer), sdk.MustAccAddressFromBech32(creator), amount).
Return(sdkerrors.ErrInsufficientFunds).Times(1)

return &types.MsgWithdrawFeePayerFunds{
Creator: creator,
TunnelID: tunnel.ID,
Amount: amount,
}
},
expErr: true,
expErrMsg: "insufficient funds",
},
"all good": {
preRun: func() *types.MsgWithdrawFeePayerFunds {
tunnel := s.AddSampleTunnel(false)

amount := sdk.NewCoins(sdk.NewInt64Coin("uband", 500))

s.bankKeeper.EXPECT().
SendCoins(s.ctx, sdk.MustAccAddressFromBech32(tunnel.FeePayer), sdk.MustAccAddressFromBech32(creator), amount).
Return(nil).Times(1)

return &types.MsgWithdrawFeePayerFunds{
Creator: creator,
TunnelID: 1,
Amount: amount,
}
},
expErr: false,
},
}

for name, tc := range cases {
s.Run(name, func() {
s.reset()
msg := tc.preRun()

_, err := s.msgServer.WithdrawFeePayerFunds(s.ctx, msg)
if tc.expErr {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.expErrMsg)
} else {
s.Require().NoError(err)
}
})
}
}

func (s *KeeperTestSuite) TestMsgActivate() {
cases := map[string]struct {
preRun func() *types.MsgActivate
Expand Down
Loading
Loading