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

updated rebate to use native denom instead of reward denom #1162

Merged
merged 10 commits into from
Mar 27, 2024
Merged
2 changes: 2 additions & 0 deletions dockernet/config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ GAIA_DENOM=$ATOM_DENOM
GAIA_RPC_PORT=26557
GAIA_MAIN_CMD="$GAIA_BINARY --home $DOCKERNET_HOME/state/${GAIA_NODE_PREFIX}1"
GAIA_RECEIVER_ADDRESS='cosmos1g6qdx6kdhpf000afvvpte7hp0vnpzapuyxp8uf'
GAIA_TREASURY_ADDRESS='cosmos1vm4z7wmrfdakyh9vd3l9ghepang2jvddjtkqds'

# JUNO
JUNO_CHAIN_ID=JUNO
Expand Down Expand Up @@ -292,6 +293,7 @@ DYDX_DENOM=$DYDX_DENOM
DYDX_RPC_PORT=25957
DYDX_MAIN_CMD="$DYDX_BINARY --home $DOCKERNET_HOME/state/${DYDX_NODE_PREFIX}1"
DYDX_RECEIVER_ADDRESS='dydx1q9caajs6wrfu2yhytvkqd2csxycx6revdcme9y'
DYDX_TREASURY_ADDRESS='dydx15ztc7xy42tn2ukkc0qjthkucw9ac63pgp70urn'
# The micro denom is actually the same as default cosmos chains but there's a
# minimum stake amount so this effectively gets the validator over the minimum
DYDX_MICRO_DENOM_UNITS="000000000000000"
Expand Down
26 changes: 17 additions & 9 deletions dockernet/scripts/community-pool-staking/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,27 @@ bash dockernet/scripts/community-pool-staking/create_pool.sh
```bash
bash dockernet/scripts/community-pool-staking/add_trade_route.sh
```
* Liquid stake to create TVL
* Finally, test the reinvestment flow by sending USDC to the withdrawal address. View `logs/balances.log` to watch the funds traverse the different accounts
```bash
bash dockernet/scripts/community-pool-staking/stake.sh
bash dockernet/scripts/community-pool-staking/reinvest_reward.sh
```
* Finally, test the reinvestment flow by sending USDC to the withdrawal address. View `logs/balances.log` to watch the funds traverse the different accounts

### Rebate
* Use the default host zone setup of just `GAIA`
* To test sending a rebate to the treasury, there is no need to change anything.
* If you want to send a rebate to the community pool instead, comment out the `GAIA_TREASURY_ADDRESS` in `config.sh`
* Start the network
```bash
bash dockernet/scripts/community-pool-staking/reinvest.sh
make start-docker
```
* To register a rebate, run the following script.
* Liquid stake to create TVL
```bash
bash dockernet/scripts/community-pool-staking/rebate.sh
bash dockernet/scripts/community-pool-staking/stake.sh
```
* Then trigger reinvestment again. This time, you should notice USDC goes straight from the withdrawal account to the relevant community pool account. For Gaia, this account is the standard community pool, and for dYdX, the account is the community pool treasury.
* Register a rebate
```bash
bash dockernet/scripts/community-pool-staking/reinvest.sh
```
bash dockernet/scripts/community-pool-staking/rebate.sh
```
* Watch `balances.log` to verify the rewards were distributed correctly. The reinvestment cycle will kick off automatically.
* Notice the rewards start in the withdrawal account, then 0.25% get sent to the community pool, 9.75% get sent to the fee account and 90% get sent to the delegation account.
* Depending on the setup from above, the community pool portion will either go to the main community pool or the treasury. Note: if it goes to the main community pool, it is difficult to distinguish the rebate tokens from the tokens that were already in the account.
13 changes: 13 additions & 0 deletions dockernet/scripts/community-pool-staking/reinvest_native.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash
set -eu
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
source ${SCRIPT_DIR}/../../config.sh

HOST_CHAIN=$REWARD_CONVERTER_HOST_ZONE
HOST_MAIN_CMD=$(GET_VAR_VALUE ${HOST_CHAIN}_MAIN_CMD)
HOST_VAL_PREFIX=$(GET_VAR_VALUE ${HOST_CHAIN}_VAL_PREFIX)
HOST_DENOM=$(GET_VAR_VALUE ${HOST_CHAIN}_DENOM)

echo ">>> Sending native tokens to withdrawal ICA to simulate rewards..."
$HOST_MAIN_CMD tx bank send ${HOST_VAL_PREFIX}1 $(GET_ICA_ADDR $REWARD_CONVERTER_HOST_ZONE withdrawal) 1000000${HOST_DENOM} \
--from ${HOST_VAL_PREFIX}1 -y | TRIM_TX
2 changes: 1 addition & 1 deletion dockernet/scripts/community-pool-staking/trade_authz.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set -eu
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
source ${SCRIPT_DIR}/../../config.sh

TRADE_AMOUNT=997500
TRADE_AMOUNT=1000000

trade_account=$($STRIDE_MAIN_CMD q stakeibc list-trade-routes | grep trade_account -A 3 | grep address | awk '{print $2}')
host_denom_on_trade=$($STRIDE_MAIN_CMD q stakeibc list-trade-routes | grep host_denom_on_trade | awk '{print $2}')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set -eu
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
source ${SCRIPT_DIR}/../../config.sh

TRADE_AMOUNT=997500
TRADE_AMOUNT=1000000

# Simulates a trade by sending the native token to the trade account
# We'll send the amount that should have been sent from the ICA, which has the rebate excluded
Expand Down
10 changes: 6 additions & 4 deletions dockernet/src/register_host.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,16 @@ if [[ "$CHAIN" == "GAIA" ]]; then
LSM_ENABLED="true"
fi

COMMUNITY_POOL_TREASURY_ADDRESS=""
if [[ "$CHAIN" == "DYDX" ]]; then
COMMUNITY_POOL_TREASURY_ADDRESS="--community-pool-treasury-address dydx15ztc7xy42tn2ukkc0qjthkucw9ac63pgp70urn"
COMMUNITY_POOL_TREASURY_ADDRESS_FLAG=""
TREASURY_ADDRESS_VAR="${CHAIN}_TREASURY_ADDRESS"
TREASURY_ADDRESS="${!TREASURY_ADDRESS_VAR:-}"
if [[ "${TREASURY_ADDRESS}" != "" ]]; then
COMMUNITY_POOL_TREASURY_ADDRESS_FLAG="--community-pool-treasury-address ${TREASURY_ADDRESS}"
fi

echo "$CHAIN - Registering host zone..."
$STRIDE_MAIN_CMD tx stakeibc register-host-zone \
$CONNECTION $HOST_DENOM $ADDRESS_PREFIX $IBC_DENOM $CHANNEL 1 $LSM_ENABLED $COMMUNITY_POOL_TREASURY_ADDRESS \
$CONNECTION $HOST_DENOM $ADDRESS_PREFIX $IBC_DENOM $CHANNEL 1 $LSM_ENABLED $COMMUNITY_POOL_TREASURY_ADDRESS_FLAG \
--gas 2000000 --from $STRIDE_ADMIN_ACCT --home $DOCKERNET_HOME/state/stride1 -y | TRIM_TX
sleep 10

Expand Down
7 changes: 7 additions & 0 deletions x/stakeibc/keeper/community_pool.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package keeper

import (
"fmt"
"strings"
"time"

errorsmod "cosmossdk.io/errors"
Expand Down Expand Up @@ -244,6 +246,7 @@ func (k Keeper) BuildFundCommunityPoolMsg(
return nil, errorsmod.Wrapf(types.ErrICATxFailed,
"fund community pool ICA can only be initiated from either the community pool return or withdrawal ICA account")
}
senderAccountString := strings.ToLower(senderAccountType.String())

// If the community pool treasury address is specified, bank send there
if hostZone.CommunityPoolTreasuryAddress != "" {
Expand All @@ -252,12 +255,16 @@ func (k Keeper) BuildFundCommunityPoolMsg(
ToAddress: hostZone.CommunityPoolTreasuryAddress,
Amount: tokens,
}}
k.Logger(ctx).Info(fmt.Sprintf("Preparing MsgBankSend of %v from the %s ICA account to the community pool treasury",
tokens.String(), senderAccountString))
} else {
// Otherwise, call MsgFundCommunityPool
fundMsg = []proto.Message{&disttypes.MsgFundCommunityPool{
Amount: tokens,
Depositor: sender,
}}
k.Logger(ctx).Info(fmt.Sprintf("Preparing MsgFundCommunityPool of %v from the %s ICA account",
tokens.String(), senderAccountString))
}

return fundMsg, nil
Expand Down
18 changes: 13 additions & 5 deletions x/stakeibc/keeper/icqcallbacks_withdrawal_host_balance.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,18 @@ func WithdrawalHostBalanceCallback(k Keeper, ctx sdk.Context, args []byte, query
}

// Split the withdrawal amount into the stride fee and reinvest portion
feeAmount, reinvestAmount, err := k.CalculateRewardsSplitAfterRebate(ctx, hostZone, withdrawalBalanceAmount)
rewardsSplit, err := k.CalculateRewardsSplit(ctx, hostZone, withdrawalBalanceAmount)
if err != nil {
return errorsmod.Wrapf(err, "unable to split reward amount into fee and reinvest amounts")
}

// Prepare MsgSends from the withdrawal account
feeCoin := sdk.NewCoin(hostZone.HostDenom, feeAmount)
reinvestCoin := sdk.NewCoin(hostZone.HostDenom, reinvestAmount)
feeCoin := sdk.NewCoin(hostZone.HostDenom, rewardsSplit.StrideFeeAmount)
reinvestCoin := sdk.NewCoin(hostZone.HostDenom, rewardsSplit.ReinvestAmount)
rebateCoin := sdk.NewCoin(hostZone.HostDenom, rewardsSplit.RebateAmount)

var msgs []proto.Message
if feeCoin.Amount.GT(sdk.ZeroInt()) {
if feeCoin.Amount.GT(sdkmath.ZeroInt()) {
sampocs marked this conversation as resolved.
Show resolved Hide resolved
msgs = append(msgs, &banktypes.MsgSend{
FromAddress: hostZone.WithdrawalIcaAddress,
ToAddress: hostZone.FeeIcaAddress,
Expand All @@ -78,7 +79,7 @@ func WithdrawalHostBalanceCallback(k Keeper, ctx sdk.Context, args []byte, query
k.Logger(ctx).Info(utils.LogICQCallbackWithHostZone(chainId, ICQCallbackID_WithdrawalHostBalance,
"Preparing MsgSends of %v from the withdrawal account to the fee account (for commission)", feeCoin.String()))
}
if reinvestCoin.Amount.GT(sdk.ZeroInt()) {
if reinvestCoin.Amount.GT(sdkmath.ZeroInt()) {
msgs = append(msgs, &banktypes.MsgSend{
FromAddress: hostZone.WithdrawalIcaAddress,
ToAddress: hostZone.DelegationIcaAddress,
Expand All @@ -87,6 +88,13 @@ func WithdrawalHostBalanceCallback(k Keeper, ctx sdk.Context, args []byte, query
k.Logger(ctx).Info(utils.LogICQCallbackWithHostZone(chainId, ICQCallbackID_WithdrawalHostBalance,
"Preparing MsgSends of %v from the withdrawal account to the delegation account (for reinvestment)", reinvestCoin.String()))
}
if rebateCoin.Amount.GT(sdkmath.ZeroInt()) {
fundMsg, err := k.BuildFundCommunityPoolMsg(ctx, hostZone, sdk.NewCoins(rebateCoin), types.ICAAccountType_WITHDRAWAL)
if err != nil {
return err
}
msgs = append(msgs, fundMsg...)
}

// add callback data before calling reinvestment ICA
reinvestCallback := types.ReinvestCallback{
Expand Down
29 changes: 3 additions & 26 deletions x/stakeibc/keeper/icqcallbacks_withdrawal_reward_balance.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@ func WithdrawalRewardBalanceCallback(k Keeper, ctx sdk.Context, args []byte, que
"Starting withdrawal reward balance callback, QueryId: %vs, QueryType: %s, Connection: %s", query.Id, query.QueryType, query.ConnectionId))

chainId := query.ChainId
hostZone, err := k.GetActiveHostZone(ctx, chainId)
sampocs marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return err
}

// Unmarshal the query response args to determine the balance
withdrawalRewardBalanceAmount, err := icqkeeper.UnmarshalAmountFromBalanceQuery(k.cdc, args)
Expand Down Expand Up @@ -59,33 +55,14 @@ func WithdrawalRewardBalanceCallback(k Keeper, ctx sdk.Context, args []byte, que
k.Logger(ctx).Info(utils.LogICQCallbackWithHostZone(chainId, ICQCallbackID_WithdrawalRewardBalance,
"Query response - Withdrawal Reward Balance: %v %s", withdrawalRewardBalanceAmount, tradeRoute.RewardDenomOnHostZone))

// Split the withdrawal amount into a rebate, stride fee, and reinvest portion
rebateAmount, tradeAmount, err := k.CalculateRewardsSplitBeforeRebate(ctx, hostZone, withdrawalRewardBalanceAmount)
if err != nil {
return errorsmod.Wrapf(err, "unable to check for rebate amount")
}

// If there's a rebate portion, fund the community pool with that amount
if rebateAmount.GT(sdkmath.ZeroInt()) {
rebateToken := sdk.NewCoin(tradeRoute.RewardDenomOnHostZone, rebateAmount)
if err := k.FundCommunityPool(ctx, hostZone, rebateToken, types.ICAAccountType_WITHDRAWAL); err != nil {
return errorsmod.Wrapf(err, "unable to submit fund community pool ICA")
}

k.Logger(ctx).Info(utils.LogICQCallbackWithHostZone(chainId, ICQCallbackID_WithdrawalRewardBalance,
"Sending rebate tokens %v %s to community pool",
rebateAmount, tradeRoute.RewardDenomOnRewardZone))
}

// Transfer the amount leftover after to the rebate to the trade zone so it can be swapped for the native token
// We transfer both the amount to be reinvested, and the amount for the stride fee
if err := k.TransferRewardTokensHostToTrade(ctx, tradeAmount, tradeRoute); err != nil {
// Transfer the reward amount to the trade zone so it can be swapped for the native token
riley-stride marked this conversation as resolved.
Show resolved Hide resolved
if err := k.TransferRewardTokensHostToTrade(ctx, withdrawalRewardBalanceAmount, tradeRoute); err != nil {
return errorsmod.Wrapf(err, "initiating transfer of reward tokens to trade ICA failed")
}

k.Logger(ctx).Info(utils.LogICQCallbackWithHostZone(chainId, ICQCallbackID_WithdrawalRewardBalance,
"Sending discovered reward tokens %v %s from hostZone to tradeZone",
tradeAmount, tradeRoute.RewardDenomOnRewardZone))
withdrawalRewardBalanceAmount, tradeRoute.RewardDenomOnRewardZone))

return nil
}
39 changes: 0 additions & 39 deletions x/stakeibc/keeper/icqcallbacks_withdrawal_reward_balance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,29 +111,6 @@ func (s *KeeperTestSuite) TestWithdrawalRewardBalanceCallback_SuccessfulNoTransf
})
}

// Verify that if the amount returned by the ICQ response is less than the min_swap_amount, no transfer happens
func (s *KeeperTestSuite) TestWithdrawalRewardBalanceCallback_SuccessfulWithRebate() {
tc := s.SetupWithdrawalRewardBalanceCallbackTestCase()

// Update the host zone to have a rebate
stTokenSupply := sdkmath.NewInt(1_000_000)
hostZone := s.MustGetHostZone(HostChainId)
hostZone.TotalDelegations = sdkmath.NewInt(10_000_000)
hostZone.CommunityPoolRebate = &types.CommunityPoolRebate{
RebateRate: sdk.MustNewDecFromStr("0.5"),
LiquidStakedStTokenAmount: stTokenSupply,
}
s.App.StakeibcKeeper.SetHostZone(s.Ctx, hostZone)

// Mint stTokens so that the supply matches the liquid staked amount
s.FundAccount(s.TestAccs[0], sdk.NewCoin(types.StAssetDenomFromHostZoneDenom(hostZone.HostDenom), stTokenSupply))

// ICA inside of TransferRewardTokensHostToTrade should not actually execute because of min_swap_amount
s.CheckMultipleICATxSubmitted(tc.PortID, tc.ChannelID, func() error {
return keeper.WithdrawalRewardBalanceCallback(s.App.StakeibcKeeper, s.Ctx, tc.Response.CallbackArgs, tc.Response.Query)
})
}

func (s *KeeperTestSuite) TestWithdrawalRewardBalanceCallback_ZeroBalance() {
tc := s.SetupWithdrawalRewardBalanceCallbackTestCase()

Expand Down Expand Up @@ -182,22 +159,6 @@ func (s *KeeperTestSuite) TestWithdrawalRewardBalanceCallback_TradeRouteNotFound
s.Require().ErrorContains(err, "trade route not found")
}

func (s *KeeperTestSuite) TestWithdrawalRewardBalanceCallback_FailedToCheckForRebate() {
tc := s.SetupWithdrawalRewardBalanceCallbackTestCase()

// Add a rebate to the host zone and set the total delegations to 0 so the check fails
hostZone := s.MustGetHostZone(HostChainId)
hostZone.CommunityPoolRebate = &types.CommunityPoolRebate{
RebateRate: sdk.MustNewDecFromStr("0.5"),
LiquidStakedStTokenAmount: sdkmath.NewInt(1),
}
hostZone.TotalDelegations = sdkmath.ZeroInt()
s.App.StakeibcKeeper.SetHostZone(s.Ctx, hostZone)

err := keeper.WithdrawalRewardBalanceCallback(s.App.StakeibcKeeper, s.Ctx, tc.Response.CallbackArgs, tc.Response.Query)
s.Require().ErrorContains(err, "unable to check for rebate amount")
}

func (s *KeeperTestSuite) TestWithdrawalRewardBalanceCallback_FailedSubmitTx() {
tc := s.SetupWithdrawalRewardBalanceCallbackTestCase()

Expand Down
Loading
Loading