Skip to content

Commit

Permalink
chore: change bridge-tax to be per token (#1212)
Browse files Browse the repository at this point in the history
# Related Github tickets

- Closes VolumeFi#1815

# Background

We want bridge taxes to be per token, instead of global.

- Change bridge tax setter and getter to include token
- Change bridge tax proposal to include token
- Change query RPC to return all bridge taxes, same as bridge transfer
limits
- Update genesis
- Removed `excluded_token` setting as it doesn't make sense now

# Testing completed

- [x] test coverage exists or has been added/updated
- [x] tested in a private testnet

# Breaking changes

- [x] I have checked my code for breaking changes
- [x] If there are breaking changes, there is a supporting migration.
  • Loading branch information
maharifu authored Jul 5, 2024
1 parent d4a54f7 commit a2b7628
Show file tree
Hide file tree
Showing 21 changed files with 478 additions and 701 deletions.
13 changes: 4 additions & 9 deletions docs/Skyway-Bridge-Tax.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,21 @@

All outbound transactions from the bridge to the target EVM and other chains pay
a tax on the skyway bridge. This tax is added to the cost of the transfer.
A governance vote is needed to define the tax rate, as well as a list of tokens
and addresses that are exempt from the bridge tax.
A governance vote is needed to define the tax rate for a specific token, as well
as a list of addresses that are exempt from the bridge tax.

## Tax Rate

The tax rate must be defined as a non-negative value, with 0 meaning no tax is
applied.
applied. This is the ratio of the tax applied on top of the transfer, so a tax
rate value of 0.2 means a 20% tax.

The tax is added to the cost of the transfer and will stay locked until the
transfer is finished.
If the transfer is successful, the taxed amount is burned on the Paloma side.
If a transfer is canceled before being executed, the full initial amount, plus
tax, is refunded.

## Excluded Tokens

The governance vote can define a list of tokens that are excluded from the
bridge tax. Transfers of these tokens will never pay bridge tax and will be
transferred in the full amount.

## Exempt Addresses

Similarly, the governance vote can define a list of addresses that are exempt
Expand Down
7 changes: 5 additions & 2 deletions proto/palomachain/paloma/skyway/bridge_tax.proto
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
syntax = "proto3";
package palomachain.paloma.skyway;

import "gogoproto/gogo.proto";
import "cosmos_proto/cosmos.proto";
import "gogoproto/gogo.proto";

option go_package = "github.com/palomachain/paloma/x/skyway/types";

message BridgeTax {
reserved 2;
reserved "excluded_tokens";

string rate = 1 [ (cosmos_proto.scalar) = "cosmos.Dec" ];
repeated string excluded_tokens = 2;
repeated bytes exempt_addresses = 3
[ (gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress" ];
string token = 4;
}
5 changes: 4 additions & 1 deletion proto/palomachain/paloma/skyway/bridge_tax_proposal.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ import "cosmos_proto/cosmos.proto";
option go_package = "github.com/palomachain/paloma/x/skyway/types";

message SetBridgeTaxProposal {
reserved 4;
reserved "excluded_tokens";

string title = 1;
string description = 2;
string rate = 3 [ (cosmos_proto.scalar) = "cosmos.Dec" ];
repeated string excluded_tokens = 4;
repeated string exempt_addresses = 5;
string token = 6;
}
5 changes: 4 additions & 1 deletion proto/palomachain/paloma/skyway/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ option go_package = "github.com/palomachain/paloma/x/skyway/types";
// GenesisState struct, containing all persistant data required by the Skyway
// module
message GenesisState {
reserved 11;
reserved "bridge_tax";

Params params = 1;
repeated SkywayNonces skyway_nonces = 2 [ (gogoproto.nullable) = false ];
repeated OutgoingTxBatch batches = 3 [ (gogoproto.nullable) = false ];
Expand All @@ -23,8 +26,8 @@ message GenesisState {
repeated ERC20ToDenom erc20_to_denoms = 9 [ (gogoproto.nullable) = false ];
repeated OutgoingTransferTx unbatched_transfers = 10
[ (gogoproto.nullable) = false ];
BridgeTax bridge_tax = 11;
repeated BridgeTransferLimit bridge_transfer_limits = 12;
repeated BridgeTax bridge_taxes = 13;
}

// SkywayCounters contains the many noces and counters required to maintain the
Expand Down
8 changes: 4 additions & 4 deletions proto/palomachain/paloma/skyway/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ service Query {
rpc GetPendingSendToEth(QueryPendingSendToEth)
returns (QueryPendingSendToEthResponse) {}

rpc GetBridgeTax(google.protobuf.Empty) returns (QueryBridgeTaxResponse) {
option (google.api.http).get = "/palomachain/paloma/skyway/bridge_tax";
rpc GetBridgeTaxes(google.protobuf.Empty) returns (QueryBridgeTaxesResponse) {
option (google.api.http).get = "/palomachain/paloma/skyway/bridge_taxes";
}

rpc GetBridgeTransferLimits(google.protobuf.Empty)
Expand Down Expand Up @@ -161,8 +161,8 @@ message QueryPendingSendToEthResponse {
[ (gogoproto.nullable) = false ];
}

message QueryBridgeTaxResponse {
BridgeTax bridge_tax = 1;
message QueryBridgeTaxesResponse {
repeated BridgeTax bridge_taxes = 1;
}

message QueryBridgeTransferLimitsResponse {
Expand Down
6 changes: 3 additions & 3 deletions x/skyway/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,8 +336,8 @@ func GetCmdQueryParams() *cobra.Command {
// GetCmdQueryBridgeTax fetches the current Skyway module bridge tax settings
func GetCmdQueryBridgeTax() *cobra.Command {
cmd := &cobra.Command{
Use: "bridge-tax",
Short: "Query BridgeTax Settings",
Use: "bridge-taxes",
Short: "Query bridge tax settings for all tokens",
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) (err error) {
clientCtx, err := client.GetClientTxContext(cmd)
Expand All @@ -349,7 +349,7 @@ func GetCmdQueryBridgeTax() *cobra.Command {

params := &emptypb.Empty{}

res, err := queryClient.GetBridgeTax(cmd.Context(), params)
res, err := queryClient.GetBridgeTaxes(cmd.Context(), params)
if err != nil {
return err
}
Expand Down
18 changes: 7 additions & 11 deletions x/skyway/client/cli/tx_proposal.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,18 @@ func CmdSetErc20ToDenom() *cobra.Command {

func CmdSetBridgeTax() *cobra.Command {
cmd := &cobra.Command{
Use: "set-bridge-tax [tax-rate]",
Short: "Sets the bridge tax rate, and optionally token exceptions and exempt addresses",
Long: "Each outgoing transfer from Paloma will pay a tax. Tax amount is calculated using [tax-rate], which must be non-negative.",
Args: cobra.ExactArgs(1),
Use: "set-bridge-tax [token] [tax-rate]",
Short: "Sets the bridge tax rate for a token, and optional exempt addresses",
Long: "Each outgoing transfer from Paloma will pay a tax. Tax amount is calculated using [tax-rate], which must be non-negative. [tax-rate] is the ratio of tax collected, so for a 20%% tax it must be set to 0.2.",
Example: "set-bridge-tax ugrain 0.2",
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

rateRaw := args[0]
token, rateRaw := args[0], args[1]

rate, ok := new(big.Rat).SetString(rateRaw)
if !ok || rate.Sign() < 0 {
Expand All @@ -119,11 +120,6 @@ func CmdSetBridgeTax() *cobra.Command {
return err
}

excludedTokens, err := cmd.Flags().GetStringSlice(flagExcludedTokens)
if err != nil {
return err
}

exemptAddresses, err := cmd.Flags().GetStringSlice(flagExemptAddresses)
if err != nil {
return err
Expand All @@ -132,8 +128,8 @@ func CmdSetBridgeTax() *cobra.Command {
prop := &types.SetBridgeTaxProposal{
Title: title,
Description: description,
Token: token,
Rate: rateRaw,
ExcludedTokens: excludedTokens,
ExemptAddresses: exemptAddresses,
}

Expand Down
2 changes: 1 addition & 1 deletion x/skyway/keeper/batch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -777,7 +777,7 @@ func TestBridgeTax(t *testing.T) {
require.NoError(t, e4)

// set bridge tax
input.SkywayKeeper.SetBridgeTax(ctx, &types.BridgeTax{Rate: "0.02"})
input.SkywayKeeper.SetBridgeTax(ctx, &types.BridgeTax{Token: testDenom, Rate: "0.02"})

// mint some voucher first
require.NoError(t, input.BankKeeper.MintCoins(ctx, types.ModuleName, allVouchers))
Expand Down
16 changes: 7 additions & 9 deletions x/skyway/keeper/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,17 +137,15 @@ func InitGenesis(ctx context.Context, k Keeper, data types.GenesisState) {
}
}

if data.BridgeTax != nil {
if err := k.SetBridgeTax(ctx, data.BridgeTax); err != nil {
for _, tax := range data.BridgeTaxes {
if err := k.SetBridgeTax(ctx, tax); err != nil {
panic(err)
}
}

if data.BridgeTransferLimits != nil {
for _, limit := range data.BridgeTransferLimits {
if err := k.SetBridgeTransferLimit(ctx, limit); err != nil {
panic(err)
}
for _, limit := range data.BridgeTransferLimits {
if err := k.SetBridgeTransferLimit(ctx, limit); err != nil {
panic(err)
}
}
}
Expand Down Expand Up @@ -239,7 +237,7 @@ func ExportGenesis(ctx context.Context, k Keeper) types.GenesisState {
unbatchedTxs[i] = v.ToExternal()
}

tax, err := k.BridgeTax(ctx)
taxes, err := k.AllBridgeTaxes(ctx)
if err != nil && !errors.Is(err, keeperutil.ErrNotFound) {
panic(err)
}
Expand All @@ -257,7 +255,7 @@ func ExportGenesis(ctx context.Context, k Keeper) types.GenesisState {
Attestations: attestations,
Erc20ToDenoms: erc20ToDenoms,
UnbatchedTransfers: unbatchedTxs,
BridgeTax: tax,
BridgeTaxes: taxes,
BridgeTransferLimits: limits,
}
}
14 changes: 8 additions & 6 deletions x/skyway/keeper/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,12 @@ func TestGenesis(t *testing.T) {

genesisState := types.GenesisState{
Params: types.DefaultParams(),
BridgeTax: &types.BridgeTax{
Rate: "0.02",
ExcludedTokens: []string{"test"},
ExemptAddresses: addresses,
BridgeTaxes: []*types.BridgeTax{
{
Rate: "0.02",
Token: "test",
ExemptAddresses: addresses,
},
},
BridgeTransferLimits: []*types.BridgeTransferLimit{
{
Expand All @@ -167,7 +169,7 @@ func TestGenesis(t *testing.T) {
got := ExportGenesis(input.Context, input.SkywayKeeper)
require.NotNil(t, got)

require.Equal(t, genesisState.BridgeTax, got.BridgeTax)
require.Equal(t, genesisState.BridgeTaxes, got.BridgeTaxes)
require.Equal(t, genesisState.BridgeTransferLimits, got.BridgeTransferLimits)
}

Expand All @@ -182,6 +184,6 @@ func TestGenesisEmptyOptionalValues(t *testing.T) {
got := ExportGenesis(input.Context, input.SkywayKeeper)
require.NotNil(t, got)

require.Nil(t, got.BridgeTax)
require.Empty(t, got.BridgeTaxes)
require.Empty(t, got.BridgeTransferLimits)
}
2 changes: 1 addition & 1 deletion x/skyway/keeper/governance_proposals.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func NewSkywayProposalHandler(k Keeper) govv1beta1types.Handler {

bridgeTax := &types.BridgeTax{
Rate: c.Rate,
ExcludedTokens: c.ExcludedTokens,
Token: c.Token,
ExemptAddresses: addresses,
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,18 @@ import (
"google.golang.org/protobuf/types/known/emptypb"
)

func (k Keeper) GetBridgeTax(
func (k Keeper) GetBridgeTaxes(
goCtx context.Context,
_ *emptypb.Empty,
) (*types.QueryBridgeTaxResponse, error) {
) (*types.QueryBridgeTaxesResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)

tax, err := k.BridgeTax(ctx)
taxes, err := k.AllBridgeTaxes(ctx)
if err != nil {
return nil, err
}

return &types.QueryBridgeTaxResponse{
BridgeTax: tax,
return &types.QueryBridgeTaxesResponse{
BridgeTaxes: taxes,
}, nil
}
Loading

0 comments on commit a2b7628

Please sign in to comment.