diff --git a/app/upgrades/v3/upgrades.go b/app/upgrades/v3/upgrades.go index fcc1e4efa1..4fc0905083 100644 --- a/app/upgrades/v3/upgrades.go +++ b/app/upgrades/v3/upgrades.go @@ -8,6 +8,7 @@ import ( upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" claimkeeper "github.com/Stride-Labs/stride/v8/x/claim/keeper" + "github.com/Stride-Labs/stride/v8/x/claim/types" claimtypes "github.com/Stride-Labs/stride/v8/x/claim/types" ) @@ -22,6 +23,7 @@ var ( "stride12pum4adk5dhp32d90f8g8gfwujm0gwxqnrdlum", } airdropIdentifiers = []string{"stride", "gaia", "osmosis", "juno", "stars"} + airdropChainIds = []string{"stride-1", "cosmoshub-4", "osmosis-1", "juno-1", "stargaze-1"} airdropDuration = time.Hour * 24 * 30 * 12 * 3 // 3 years ) @@ -40,7 +42,14 @@ func CreateUpgradeHandler( // total number of airdrop distributors must be equal to identifiers if len(airdropDistributors) == len(airdropIdentifiers) { for idx, airdropDistributor := range airdropDistributors { - err = ck.CreateAirdropAndEpoch(ctx, airdropDistributor, claimtypes.DefaultClaimDenom, uint64(ctx.BlockTime().Unix()), uint64(airdropDuration.Seconds()), airdropIdentifiers[idx]) + err = ck.CreateAirdropAndEpoch(ctx, types.MsgCreateAirdrop{ + Distributor: airdropDistributor, + Identifier: airdropIdentifiers[idx], + ChainId: airdropChainIds[idx], + Denom: claimtypes.DefaultClaimDenom, + StartTime: uint64(ctx.BlockTime().Unix()), + Duration: uint64(airdropDuration.Seconds()), + }) if err != nil { return newVm, err } diff --git a/app/upgrades/v8/upgrades.go b/app/upgrades/v8/upgrades.go index b1bbd44bc6..81f277e497 100644 --- a/app/upgrades/v8/upgrades.go +++ b/app/upgrades/v8/upgrades.go @@ -24,6 +24,7 @@ var ( UpgradeName = "v8" EvmosAirdropDistributor = "stride10dy5pmc2fq7fnmufjfschkfrxaqnpykl6ezy5j" EvmosAirdropIdentifier = "evmos" + EvmosChainId = "evmos_9001-2" AirdropDuration = time.Hour * 24 * 30 * 12 * 3 // 3 years ResetAirdropIdentifiers = []string{"stride", "gaia", "osmosis", "juno", "stars"} AirdropStartTime = time.Date(2023, 4, 3, 16, 0, 0, 0, time.UTC) // April 3, 2023 @ 16:00 UTC (12:00 EST) @@ -71,7 +72,15 @@ func CreateUpgradeHandler( // Add the evmos airdrop ctx.Logger().Info("Adding evmos airdrop...") duration := uint64(AirdropDuration.Seconds()) - if err := claimKeeper.CreateAirdropAndEpoch(ctx, EvmosAirdropDistributor, claimtypes.DefaultClaimDenom, uint64(AirdropStartTime.Unix()), duration, EvmosAirdropIdentifier); err != nil { + if err := claimKeeper.CreateAirdropAndEpoch(ctx, types.MsgCreateAirdrop{ + Distributor: EvmosAirdropDistributor, + Identifier: EvmosAirdropIdentifier, + ChainId: EvmosChainId, + Denom: claimtypes.DefaultClaimDenom, + StartTime: uint64(AirdropStartTime.Unix()), + Duration: duration, + AutopilotEnabled: true, + }); err != nil { return vm, err } diff --git a/dockernet/config.sh b/dockernet/config.sh index 213c051cfa..56327ac513 100644 --- a/dockernet/config.sh +++ b/dockernet/config.sh @@ -16,7 +16,7 @@ TX_LOGS=$DOCKERNET_HOME/logs/tx.log KEYS_LOGS=$DOCKERNET_HOME/logs/keys.log # List of hosts enabled -HOST_CHAINS=() +HOST_CHAINS=(EVMOS) # If no host zones are specified above: # `start-docker` defaults to just GAIA if HOST_CHAINS is empty @@ -228,9 +228,9 @@ EVMOS_CHAIN_ID=evmos_9001-2 EVMOS_NODE_PREFIX=evmos EVMOS_NUM_NODES=1 EVMOS_BINARY="$DOCKERNET_HOME/../build/evmosd" -EVMOS_VAL_PREFIX=nval +EVMOS_VAL_PREFIX=eval EVMOS_ADDRESS_PREFIX=evmos -EVMOS_REV_ACCT=nrev1 +EVMOS_REV_ACCT=erev1 EVMOS_DENOM=$EVMOS_DENOM EVMOS_RPC_PORT=26057 EVMOS_MAIN_CMD="$EVMOS_BINARY --home $DOCKERNET_HOME/state/${EVMOS_NODE_PREFIX}1" diff --git a/dockernet/scripts/airdrop.sh b/dockernet/scripts/airdrop.sh deleted file mode 100644 index cf45caa846..0000000000 --- a/dockernet/scripts/airdrop.sh +++ /dev/null @@ -1,195 +0,0 @@ -### AIRDROP TESTING FLOW -SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) -source ${SCRIPT_DIR}/../config.sh - -# CLEANUP if running tests twice, clear out and re-fund accounts -$STRIDE_MAIN_CMD keys delete distributor-test -y &> /dev/null || true -$GAIA_MAIN_CMD keys delete hosttest -y &> /dev/null || true -$STRIDE_MAIN_CMD keys delete airdrop-test -y &> /dev/null || true -$OSMO_MAIN_CMD keys delete host-address-test -y &> /dev/null || true - -# First, start the network with `make start-docker` -# Then, run this script with `bash dockernet/scripts/airdrop.sh` - -# NOTE: First, store the keys using the following mnemonics -# distributor address: stride1z835j3j65nqr6ng257q0xkkc9gta72gf48txwl -# distributor mnemonic: barrel salmon half click confirm crunch sense defy salute process cart fiscal sport clump weasel render private manage picture spell wreck hill frozen before -echo "barrel salmon half click confirm crunch sense defy salute process cart fiscal sport clump weasel render private manage picture spell wreck hill frozen before" | \ - $STRIDE_MAIN_CMD keys add distributor-test --recover - -# airdrop-test address: stride1nf6v2paty9m22l3ecm7dpakq2c92ueyununayr -# airdrop claimer mnemonic: royal auction state december october hip monster hotel south help bulk supreme history give deliver pigeon license gold carpet rabbit raw wool fatigue donate -echo "royal auction state december october hip monster hotel south help bulk supreme history give deliver pigeon license gold carpet rabbit raw wool fatigue donate" | \ - $STRIDE_MAIN_CMD keys add airdrop-test --recover - -## AIRDROP SETUP -echo "Funding accounts..." -# Transfer uatom from gaia to stride, so that we can liquid stake later -$GAIA_MAIN_CMD tx ibc-transfer transfer transfer channel-0 stride1nf6v2paty9m22l3ecm7dpakq2c92ueyununayr 1000000uatom --from ${GAIA_VAL_PREFIX}1 -y -sleep 5 -# Fund the distributor account -$STRIDE_MAIN_CMD tx bank send val1 stride1z835j3j65nqr6ng257q0xkkc9gta72gf48txwl 600000ustrd --from val1 -y -sleep 5 -# Fund the airdrop account -$STRIDE_MAIN_CMD tx bank send val1 stride1nf6v2paty9m22l3ecm7dpakq2c92ueyununayr 1000000000ustrd --from val1 -y -sleep 5 -# Create the airdrop, so that the airdrop account can claim tokens -$STRIDE_MAIN_CMD tx claim create-airdrop stride 1666792900 40000000 ustrd --from distributor-test -y -sleep 5 -# Set airdrop allocations -$STRIDE_MAIN_CMD tx claim set-airdrop-allocations stride stride1nf6v2paty9m22l3ecm7dpakq2c92ueyununayr 1 --from distributor-test -y -sleep 5 - -# AIRDROP CLAIMS -# Check balances before claims -echo "Initial balance before claim:" -$STRIDE_MAIN_CMD query bank balances stride1nf6v2paty9m22l3ecm7dpakq2c92ueyununayr -# NOTE: You can claim here using the CLI, or from the frontend! -# Claim 20% of the free tokens -echo "Claiming fee amount..." -$STRIDE_MAIN_CMD tx claim claim-free-amount --from airdrop-test --gas 400000 -y -sleep 5 -echo "Balance after claim:" -$STRIDE_MAIN_CMD query bank balances stride1nf6v2paty9m22l3ecm7dpakq2c92ueyununayr -# Stake, to claim another 20% -echo "Staking..." -$STRIDE_MAIN_CMD tx staking delegate stridevaloper1nnurja9zt97huqvsfuartetyjx63tc5zrj5x9f 100ustrd --from airdrop-test --gas 400000 -y -sleep 5 -echo "Balance after stake:" -$STRIDE_MAIN_CMD query bank balances stride1nf6v2paty9m22l3ecm7dpakq2c92ueyununayr -# Liquid stake, to claim the final 60% of tokens -echo "Liquid staking..." -$STRIDE_MAIN_CMD tx stakeibc liquid-stake 1000 uatom --from airdrop-test --gas 400000 -y -sleep 5 -echo "Balance after liquid stake:" -$STRIDE_MAIN_CMD query bank balances stride1nf6v2paty9m22l3ecm7dpakq2c92ueyununayr - - - -## Test airdrop flow for chains who have non-standard coin types (not type 118). -# For example Evmos is using coin type 60, while Stride uses 118. Therefore, we can't map Evmos <> Stride addresses, because the one-way mapping works like this -# seed phrase ----> Evmos address (e.g. evmos123z469cfejeusvk87ufrs5520wmdxmmlc7qzuw) -# ----> Stride address (e.g. stride19uvw0azm9u0k6vqe4e22cga6kteskdqq3ulj6q) -# and there is no function that can map between the two addresses. - -# evmos airdrop-test address: cosmos16lmf7t0jhaatan6vnxlgv47h2wf0k5lnhvye5h (rly2) -# to test, we don't need to use evmos, just an address from a different mnemonic (can come from a coin_type 118 chain) -# here we choose to use an osmosis address with a new menmonic since we don't have an Evmos binary set up - -echo "Testing airdrop for coin types != 118..." - -# Transfer uatom from gaia to stride, so that we can liquid stake later -$GAIA_MAIN_CMD tx bank send cosmos1uk4ze0x4nvh4fk0xm4jdud58eqn4yxhrgl2scj cosmos16lmf7t0jhaatan6vnxlgv47h2wf0k5lnhvye5h 1uatom --from ${GAIA_VAL_PREFIX}1 -y - -# setup: set an airdrop allocation for the mechanically converted stride address, converted using utils.ConvertAddressToStrideAddress() -# mechanically-converted stride address: stride18y9zdh00fr2t6uw20anr6e89svqmfddgxfsxkh -$STRIDE_MAIN_CMD tx claim set-airdrop-allocations stride stride16lmf7t0jhaatan6vnxlgv47h2wf0k5ln58y9qm 1 --from distributor-test -y -sleep 5 - -# 1. Overwrite incorrectly-derived stride address associated with an airdrop account with the proper Stride address (e.g. stride1abc...xyz) -# a. query the claims module to verify that the airdrop-eligible address is as expected -$STRIDE_MAIN_CMD q claim claim-record stride stride16lmf7t0jhaatan6vnxlgv47h2wf0k5ln58y9qm - -# b. ibc-transfer from Osmo to Stride to change the airdrop account to stride1qz677nj82mszxjuh4mzy52zv5md5qrgg60pxpc -# Memo: { -# "autopilot": { -# "stakeibc": { -# "stride_address": "stride1qz677nj82mszxjuh4mzy52zv5md5qrgg60pxpc", -# }, -# "claim": { -# } -# }, -# } -# Receiver: "xxx" -memo='{"autopilot": {"receiver": "stride1qz677nj82mszxjuh4mzy52zv5md5qrgg60pxpc","claim": { "stride_address": "stride1qz677nj82mszxjuh4mzy52zv5md5qrgg60pxpc", "airdrop_id": "stride" } }}' -$GAIA_MAIN_CMD tx ibc-transfer transfer transfer channel-0 "$memo" 1uatom --from rly2 -y - -# c. query the claims module -# - to verify nothing is eligible from the old address anymore stride18y9zdh00fr2t6uw20anr6e89svqmfddgxfsxkh -# - to get the updated airdrop-eligible address's eligible amount from stride1qz677nj82mszxjuh4mzy52zv5md5qrgg60pxpc -$STRIDE_MAIN_CMD q claim claim-record stride stride16lmf7t0jhaatan6vnxlgv47h2wf0k5ln58y9qm -$STRIDE_MAIN_CMD q claim claim-record stride stride1qz677nj82mszxjuh4mzy52zv5md5qrgg60pxpc - - # d. claim the airdrop from this address -# $STRIDE_MAIN_CMD tx claim claim-free-amount --from stride1qz677nj82mszxjuh4mzy52zv5md5qrgg60pxpc - - # e. verify the vesting account is created for stride1qz677nj82mszxjuh4mzy52zv5md5qrgg60pxpc -# $STRIDE_MAIN_CMD q auth account stride1qz677nj82mszxjuh4mzy52zv5md5qrgg60pxpc - - - -### Test airdrop reset and multiple claims flow - # The Stride airdrop occurs in batches. We need to test three batches. - - # SETUP - # 1. Create a new airdrop that rolls into its next batch in just 30 seconds - # - include the add'l param that makes each batch 30 seconds long (after the first batch) - # 2. Set the airdrop allocations - -# Create the airdrop, so that the airdrop account can claim tokens -# $STRIDE_MAIN_CMD tx claim create-airdrop stride2 $(date +%s) 30 ustrd --from distributor-test -y -# sleep 5 -# Set airdrop allocations -# $STRIDE_MAIN_CMD tx claim set-airdrop-allocations stride2 stride1kd3z076usuqytj9rdfqnqaj9sdyx9aq5j2lqs5 1 --from distributor-test -y -# sleep 5 - - # BATCH 1 - # 3. Claim the airdrop -# $STRIDE_MAIN_CMD tx claim claim-free-amount --from stride1kd3z076usuqytj9rdfqnqaj9sdyx9aq5j2lqs5 - - # 4. Check that the claim worked -# $STRIDE_MAIN_CMD q bank balances stride1kd3z076usuqytj9rdfqnqaj9sdyx9aq5j2lqs5 - - # 5. Query to check airdrop vesting account was created (w/ correct amount) -# $STRIDE_MAIN_CMD q auth account stride1kd3z076usuqytj9rdfqnqaj9sdyx9aq5j2lqs5 - - - # BATCH 2 - # 6. Wait 30 seconds -# sleep 30 - # 7. Claim the airdrop -# $STRIDE_MAIN_CMD tx claim claim-free-amount --from stride1kd3z076usuqytj9rdfqnqaj9sdyx9aq5j2lqs5 - - # 8. Check that the claim worked -# $STRIDE_MAIN_CMD q bank balances stride1kd3z076usuqytj9rdfqnqaj9sdyx9aq5j2lqs5 - - # 9. Query to check airdrop vesting account was created (w/ correct amount) -# $STRIDE_MAIN_CMD q auth account stride1kd3z076usuqytj9rdfqnqaj9sdyx9aq5j2lqs5 - - # BATCH 3 - # 10. Wait 30 seconds -# sleep 30 - # 11. Claim the airdrop -# $STRIDE_MAIN_CMD tx claim claim-free-amount --from stride1kd3z076usuqytj9rdfqnqaj9sdyx9aq5j2lqs5 - - # 12. Check that the claim worked -# $STRIDE_MAIN_CMD q bank balances stride1kd3z076usuqytj9rdfqnqaj9sdyx9aq5j2lqs5 - - # 13. Query to check airdrop vesting account was created (w/ correct amount) -# $STRIDE_MAIN_CMD q auth account stride1kd3z076usuqytj9rdfqnqaj9sdyx9aq5j2lqs5 - - - -### Test staggered airdrops - -# create airdrop 1 with a 60 day start window, 60 sec reset, claim, sleep 35 -# $STRIDE_MAIN_CMD tx claim create-airdrop airdrop1 $(date +%s) 60 ustrd --from distributor-test -y -# sleep 5 -# $STRIDE_MAIN_CMD tx claim set-airdrop-allocations airdrop1 stride1kd3z076usuqytj9rdfqnqaj9sdyx9aq5j2lqs5 1 --from distributor-test -y -# sleep 5 -# $STRIDE_MAIN_CMD tx claim claim-free-amount --from stride1kd3z076usuqytj9rdfqnqaj9sdyx9aq5j2lqs5 -# sleep 35 - -# # create airdrop 2 with a 60 day start window, 60 sec reset, claim, sleep 35 -# $STRIDE_MAIN_CMD tx claim create-airdrop airdrop1 $(date +%s) 60 stuatom --from distributor-test -y -# sleep 5 -# $STRIDE_MAIN_CMD tx claim set-airdrop-allocations airdrop1 stride1kd3z076usuqytj9rdfqnqaj9sdyx9aq5j2lqs5 1 --from distributor-test -y -# sleep 5 -# $STRIDE_MAIN_CMD tx claim claim-free-amount --from stride1kd3z076usuqytj9rdfqnqaj9sdyx9aq5j2lqs5 -# sleep 35 - -# # airdrop 1 resets -# $STRIDE_MAIN_CMD q bank balances stride1kd3z076usuqytj9rdfqnqaj9sdyx9aq5j2lqs5 -# $STRIDE_MAIN_CMD tx claim claim-free-amount --from stride1kd3z076usuqytj9rdfqnqaj9sdyx9aq5j2lqs5 -# $STRIDE_MAIN_CMD q bank balances stride1kd3z076usuqytj9rdfqnqaj9sdyx9aq5j2lqs5 - diff --git a/dockernet/scripts/airdrop/README.md b/dockernet/scripts/airdrop/README.md new file mode 100644 index 0000000000..9d1cb59f1d --- /dev/null +++ b/dockernet/scripts/airdrop/README.md @@ -0,0 +1,25 @@ +## Airdrop Integration Tests +Each airdrop testing script (1 through 4) tests different aspects of the airdrop. + +### Overview +**NOTE**: You must update the airdrop timing params before running parts 4 and 5 (see next section) +* **Part 1: Standard**: Tests basic airdrop claims and actions +* **Part 2: Autopilot**: Tests claiming through autopilot on GAIA (ibc-go v3) +* **Part 3: Evmos**: Tests claiming through autopilot on EVMOS (ibc-go v5) +* **Part 4: Resets**: Tests that the airdrop resets properly at the epoch +* **Part 5: Staggered**: Tests two airdrops running side by side and staggered + +### Instructions +* If running part 3, change the `HOST_CHAINS` variable in `config.sh` to run only evmos. +* If running Part 4 or 5: Before building stride, you must update the following airdrop timing parameters in `x/claim/types/params.go`: + * `DefaultEpochDuration` to `time.Second * 60` + * `DefaultVestingInitialPeriod` to `time.Second * 120` +* Only the GAIA host zone is required. Start dockernet with: +```bash +make start-docker build=sgr +``` +* Run the corresponding script +```bash +bash dockernet/scripts/airdrop/airdrop{1/2/3/4}.sh +``` +* **NOTE**: Each script must be run independently, meaning you must restart dockernet between runs (`make start-docker build=sgr`) diff --git a/dockernet/scripts/airdrop/airdrop1.sh b/dockernet/scripts/airdrop/airdrop1_standard.sh similarity index 70% rename from dockernet/scripts/airdrop/airdrop1.sh rename to dockernet/scripts/airdrop/airdrop1_standard.sh index 01ca898088..67d9022c4b 100644 --- a/dockernet/scripts/airdrop/airdrop1.sh +++ b/dockernet/scripts/airdrop/airdrop1_standard.sh @@ -1,16 +1,19 @@ -### AIRDROP TESTING FLOW +#!/bin/bash SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) source ${SCRIPT_DIR}/../../config.sh -# CLEANUP if running tests twice, clear out and re-fund accounts -$STRIDE_MAIN_CMD keys delete airdrop-test -y &> /dev/null || true -$STRIDE_MAIN_CMD keys delete distributor-test -y &> /dev/null || true -$OSMO_MAIN_CMD keys delete host-address-test -y &> /dev/null || true +### AIRDROP TESTING FLOW Pt 1 (STANDARD) -# First, start the network with `make start-docker` -# Then, run this script with `bash dockernet/scripts/airdrop.sh` +# This script tests airdrop claiming directly on Stride +# This covers the case where the host zone has a coin type of 118 +# and autopilot is disabled for the claim + +# To run: +# 1. Start the network with `make start-docker` +# 2. Run this script with `bash dockernet/scripts/airdrop/airdrop1_standard.sh` # NOTE: First, store the keys using the following mnemonics +echo "Registering accounts..." # distributor address: stride1z835j3j65nqr6ng257q0xkkc9gta72gf48txwl # distributor mnemonic: barrel salmon half click confirm crunch sense defy salute process cart fiscal sport clump weasel render private manage picture spell wreck hill frozen before echo "barrel salmon half click confirm crunch sense defy salute process cart fiscal sport clump weasel render private manage picture spell wreck hill frozen before" | \ @@ -33,32 +36,32 @@ sleep 5 $STRIDE_MAIN_CMD tx bank send val1 stride1nf6v2paty9m22l3ecm7dpakq2c92ueyununayr 1000000000ustrd --from val1 -y | TRIM_TX sleep 5 # Create the airdrop, so that the airdrop account can claim tokens -$STRIDE_MAIN_CMD tx claim create-airdrop stride 1679715340 40000000 ustrd --from distributor-test -y | TRIM_TX +$STRIDE_MAIN_CMD tx claim create-airdrop gaia GAIA ustrd 1679715340 40000000 false --from distributor-test -y | TRIM_TX sleep 5 # Set airdrop allocations -$STRIDE_MAIN_CMD tx claim set-airdrop-allocations stride stride1nf6v2paty9m22l3ecm7dpakq2c92ueyununayr 1 --from distributor-test -y | TRIM_TX +$STRIDE_MAIN_CMD tx claim set-airdrop-allocations gaia stride1nf6v2paty9m22l3ecm7dpakq2c92ueyununayr 1 --from distributor-test -y | TRIM_TX sleep 5 # AIRDROP CLAIMS # Check balances before claims -echo "Initial balance before claim:" -$STRIDE_MAIN_CMD query bank balances stride1nf6v2paty9m22l3ecm7dpakq2c92ueyununayr +echo -e "\nInitial balance before claim [1000000000ustrd expected]:" +$STRIDE_MAIN_CMD query bank balances stride1nf6v2paty9m22l3ecm7dpakq2c92ueyununayr --denom ustrd # NOTE: You can claim here using the CLI, or from the frontend! # Claim 20% of the free tokens -echo "Claiming fee amount..." +echo -e "\nClaiming free amount..." $STRIDE_MAIN_CMD tx claim claim-free-amount --from airdrop-test --gas 400000 -y | TRIM_TX sleep 5 -echo "Balance after claim:" -$STRIDE_MAIN_CMD query bank balances stride1nf6v2paty9m22l3ecm7dpakq2c92ueyununayr +echo -e "\nBalance after claim [1000120000ustrd expected]:" +$STRIDE_MAIN_CMD query bank balances stride1nf6v2paty9m22l3ecm7dpakq2c92ueyununayr --denom ustrd # Stake, to claim another 20% -echo "Staking..." +echo -e "\nStaking..." $STRIDE_MAIN_CMD tx staking delegate stridevaloper1nnurja9zt97huqvsfuartetyjx63tc5zrj5x9f 100ustrd --from airdrop-test --gas 400000 -y | TRIM_TX sleep 5 -echo "Balance after stake:" -$STRIDE_MAIN_CMD query bank balances stride1nf6v2paty9m22l3ecm7dpakq2c92ueyununayr +echo -e "\nBalance after stake [1000239900ustrd expected]:" +$STRIDE_MAIN_CMD query bank balances stride1nf6v2paty9m22l3ecm7dpakq2c92ueyununayr --denom ustrd # Liquid stake, to claim the final 60% of tokens -echo "Liquid staking..." +echo -e "\nLiquid staking..." $STRIDE_MAIN_CMD tx stakeibc liquid-stake 1000 uatom --from airdrop-test --gas 400000 -y | TRIM_TX sleep 5 -echo "Balance after liquid stake:" -$STRIDE_MAIN_CMD query bank balances stride1nf6v2paty9m22l3ecm7dpakq2c92ueyununayr +echo -e "\nBalance after liquid stake [1000599900ustrd expected]:" +$STRIDE_MAIN_CMD query bank balances stride1nf6v2paty9m22l3ecm7dpakq2c92ueyununayr --denom ustrd diff --git a/dockernet/scripts/airdrop/airdrop2.sh b/dockernet/scripts/airdrop/airdrop2_autopilot.sh similarity index 63% rename from dockernet/scripts/airdrop/airdrop2.sh rename to dockernet/scripts/airdrop/airdrop2_autopilot.sh index 44d97a4f70..2fe7d75adb 100644 --- a/dockernet/scripts/airdrop/airdrop2.sh +++ b/dockernet/scripts/airdrop/airdrop2_autopilot.sh @@ -2,16 +2,18 @@ SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) source ${SCRIPT_DIR}/../../config.sh -# CLEANUP if running tests twice, clear out and re-fund accounts -$STRIDE_MAIN_CMD keys delete distributor-test -y &> /dev/null || true -$GAIA_MAIN_CMD keys delete hosttest -y &> /dev/null || true -$STRIDE_MAIN_CMD keys delete airdrop-test -y &> /dev/null || true -$OSMO_MAIN_CMD keys delete host-address-test -y &> /dev/null || true +### AIRDROP TESTING FLOW Pt 2 (AUTOPILOT) -# First, start the network with `make start-docker` -# Then, run this script with `bash dockernet/scripts/airdrop.sh` +# This script tests airdrop claiming via autopilot +# The claim is initiated by sending an IBC transfer with the stride address in the memo +# Gaia is used for this test with ibc v3 - and the memo is included in the receiver field of the transfer + +# To run: +# 1. Start the network with `make start-docker` +# 2. Run this script with `bash dockernet/scripts/airdrop/airdrop2_autopilot.sh` # NOTE: First, store the keys using the following mnemonics +echo "Registering accounts..." # distributor address: stride1z835j3j65nqr6ng257q0xkkc9gta72gf48txwl # distributor mnemonic: barrel salmon half click confirm crunch sense defy salute process cart fiscal sport clump weasel render private manage picture spell wreck hill frozen before echo "barrel salmon half click confirm crunch sense defy salute process cart fiscal sport clump weasel render private manage picture spell wreck hill frozen before" | \ @@ -26,7 +28,7 @@ sleep 5 $STRIDE_MAIN_CMD tx bank send val1 stride1z835j3j65nqr6ng257q0xkkc9gta72gf48txwl 600000ustrd --from val1 -y | TRIM_TX sleep 5 # Create the airdrop, so that the airdrop account can claim tokens -$STRIDE_MAIN_CMD tx claim create-airdrop stride 1666792900 40000000 ustrd --from distributor-test -y | TRIM_TX +$STRIDE_MAIN_CMD tx claim create-airdrop gaia GAIA ustrd $(date +%s) 40000000 true --from distributor-test -y | TRIM_TX sleep 5 ## Test airdrop flow for chains who have non-standard coin types (not type 118). @@ -39,54 +41,57 @@ sleep 5 # to test, we don't need to use evmos, just an address from a different mnemonic (can come from a coin_type 118 chain) # here we choose to use an osmosis address with a new menmonic since we don't have an Evmos binary set up -echo ">>>Testing airdrop for coin types != 118..." -echo ">>>Testing for ibc-go version 3" +echo -e "\n>>> Testing airdrop for coin types != 118, ibc-go version 3..." # Transfer uatom from gaia to stride, so that we can liquid stake later $GAIA_MAIN_CMD tx bank send cosmos1uk4ze0x4nvh4fk0xm4jdud58eqn4yxhrgl2scj cosmos16lmf7t0jhaatan6vnxlgv47h2wf0k5lnhvye5h 1uatom --from ${GAIA_VAL_PREFIX}1 -y | TRIM_TX # setup: set an airdrop allocation for the mechanically converted stride address, converted using utils.ConvertAddressToStrideAddress() # mechanically-converted stride address: stride16lmf7t0jhaatan6vnxlgv47h2wf0k5ln58y9qm -$STRIDE_MAIN_CMD tx claim set-airdrop-allocations stride stride16lmf7t0jhaatan6vnxlgv47h2wf0k5ln58y9qm 1 --from distributor-test -y | TRIM_TX +$STRIDE_MAIN_CMD tx claim set-airdrop-allocations gaia stride16lmf7t0jhaatan6vnxlgv47h2wf0k5ln58y9qm 1 --from distributor-test -y | TRIM_TX sleep 5 # 1. Overwrite incorrectly-derived stride address associated with an airdrop account with the proper Stride address (e.g. stride1abc...xyz) # a. query the claims module to verify that the airdrop-eligible address is as expected -$STRIDE_MAIN_CMD q claim claim-record stride stride16lmf7t0jhaatan6vnxlgv47h2wf0k5ln58y9qm +echo "> initial claim record [should show one record]:" +$STRIDE_MAIN_CMD q claim claim-record gaia stride16lmf7t0jhaatan6vnxlgv47h2wf0k5ln58y9qm # b. ibc-transfer from Osmo to Stride to change the airdrop account to stride1jrmtt5c6z8h5yrrwml488qnm7p3vxrrml2kgvl # Memo: { # "autopilot": { -# "stakeibc": { -# "stride_address": "stride1jrmtt5c6z8h5yrrwml488qnm7p3vxrrml2kgvl", -# }, -# "claim": { -# } -# }, -# } -# Receiver: "xxx" -memo='{"autopilot": {"receiver": "stride1jrmtt5c6z8h5yrrwml488qnm7p3vxrrml2kgvl","claim": { "stride_address": "stride1jrmtt5c6z8h5yrrwml488qnm7p3vxrrml2kgvl", "airdrop_id": "stride" } }}' +# "claim": { +# "stride_address": "stride1jrmtt5c6z8h5yrrwml488qnm7p3vxrrml2kgvl", +# }, +# }, +# } +# Receiver: "xxx" +echo -e ">>> Claiming airdrop via IBC transfer..." +memo='{"autopilot": {"receiver": "stride1jrmtt5c6z8h5yrrwml488qnm7p3vxrrml2kgvl","claim": { "stride_address": "stride1jrmtt5c6z8h5yrrwml488qnm7p3vxrrml2kgvl" } }}' $GAIA_MAIN_CMD tx ibc-transfer transfer transfer channel-0 "$memo" 1uatom --from rly2 -y | TRIM_TX -echo ">>> Waiting for 15 seconds to allow the IBC transfer to complete..." sleep 15 # c. query the claims module # - to verify nothing is eligible from the old address anymore stride16lmf7t0jhaatan6vnxlgv47h2wf0k5ln58y9qm # - to get the updated airdrop-eligible address's eligible amount from stride1jrmtt5c6z8h5yrrwml488qnm7p3vxrrml2kgvl -echo ">>> Querying the claims module to verify that the airdrop-eligible address is as expected" -echo "> previously eligible account, now should have 0:" -$STRIDE_MAIN_CMD q claim claim-record stride stride16lmf7t0jhaatan6vnxlgv47h2wf0k5ln58y9qm -echo "> new eligible account, now should have 1:" -$STRIDE_MAIN_CMD q claim claim-record stride stride1jrmtt5c6z8h5yrrwml488qnm7p3vxrrml2kgvl +echo -e "\n>>> Querying the claims module to verify that the airdrop-eligible address is as expected" +echo "> Previously eligible account, should no longer return any records:" +$STRIDE_MAIN_CMD q claim claim-record gaia stride16lmf7t0jhaatan6vnxlgv47h2wf0k5ln58y9qm +echo "> New eligible account, should show 1 record:" +$STRIDE_MAIN_CMD q claim claim-record gaia stride1jrmtt5c6z8h5yrrwml488qnm7p3vxrrml2kgvl # liquid stake as a task to increase eligibility, re-check eligibliity +echo -e "\n>>> Liquid staking..." $STRIDE_MAIN_CMD tx stakeibc liquid-stake 1 $ATOM_DENOM --from rly3 -y | TRIM_TX sleep 5 -echo "> after liquid staking eligiblity should be higher" -$STRIDE_MAIN_CMD q claim claim-record stride stride1jrmtt5c6z8h5yrrwml488qnm7p3vxrrml2kgvl +echo "> After liquid staking there should be one action complete" +$STRIDE_MAIN_CMD q claim claim-record gaia stride1jrmtt5c6z8h5yrrwml488qnm7p3vxrrml2kgvl | grep claim_record -A 4 # d. claim the airdrop from this address -echo "> Claiming the airdrop from the new address" +echo -e "\n>>> Claiming the airdrop from the new address" $STRIDE_MAIN_CMD tx claim claim-free-amount --from rly3 -y | TRIM_TX sleep 5 + +echo "> After claiming, there should be two action complete" +$STRIDE_MAIN_CMD q claim claim-record gaia stride1jrmtt5c6z8h5yrrwml488qnm7p3vxrrml2kgvl | grep claim_record -A 4 + # e. verify funds are vesting -echo "> Verifying funds are vesting, should be 1." -$STRIDE_MAIN_CMD q claim user-vestings stride1jrmtt5c6z8h5yrrwml488qnm7p3vxrrml2kgvl +echo "> Verifying vesting record [expected: 120000ustrd]:" +$STRIDE_MAIN_CMD q claim user-vestings stride1jrmtt5c6z8h5yrrwml488qnm7p3vxrrml2kgvl | grep spendable_coins -A 2 diff --git a/dockernet/scripts/airdrop/airdrop3.sh b/dockernet/scripts/airdrop/airdrop3.sh deleted file mode 100644 index 15d12bf953..0000000000 --- a/dockernet/scripts/airdrop/airdrop3.sh +++ /dev/null @@ -1,113 +0,0 @@ -### AIRDROP TESTING FLOW -SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) -source ${SCRIPT_DIR}/../../config.sh - -# CLEANUP if running tests twice, clear out and re-fund accounts -$STRIDE_MAIN_CMD keys delete distributor-test3 -y &> /dev/null || true -$GAIA_MAIN_CMD keys delete hosttest -y &> /dev/null || true -$STRIDE_MAIN_CMD keys delete airdrop-test -y &> /dev/null || true -$OSMO_MAIN_CMD keys delete host-address-test -y &> /dev/null || true - -# First, start the network with `make start-docker` -# Then, run this script with `bash dockernet/scripts/airdrop.sh` - -# NOTE: First, store the keys using the following mnemonics -# distributor address: stride12lw3587g97lgrwr2fjtr8gg5q6sku33e5yq9wl -# distributor mnemonic: barrel salmon half click confirm crunch sense defy salute process cart fiscal sport clump weasel render private manage picture spell wreck hill frozen before -echo "person pelican purchase boring theme eagle jaguar screen frame attract mad link ribbon ball poverty valley cross cradle real idea payment ramp nature anchor" | \ - $STRIDE_MAIN_CMD keys add distributor-test3 --recover - -# airdrop-test address: stride1nf6v2paty9m22l3ecm7dpakq2c92ueyununayr -# airdrop claimer mnemonic: royal auction state december october hip monster hotel south help bulk supreme history give deliver pigeon license gold carpet rabbit raw wool fatigue donate -echo "royal auction state december october hip monster hotel south help bulk supreme history give deliver pigeon license gold carpet rabbit raw wool fatigue donate" | \ - $STRIDE_MAIN_CMD keys add airdrop-test --recover - -## AIRDROP SETUP -echo "Funding accounts..." -# Transfer uatom from gaia to stride, so that we can liquid stake later -$GAIA_MAIN_CMD tx ibc-transfer transfer transfer channel-0 stride1nf6v2paty9m22l3ecm7dpakq2c92ueyununayr 1000000uatom --from ${GAIA_VAL_PREFIX}1 -y | TRIM_TX -sleep 5 -# Fund the distributor account -$STRIDE_MAIN_CMD tx bank send val1 stride12lw3587g97lgrwr2fjtr8gg5q6sku33e5yq9wl 600000ustrd --from val1 -y | TRIM_TX -sleep 5 -# Fund the airdrop account -$STRIDE_MAIN_CMD tx bank send val1 stride1nf6v2paty9m22l3ecm7dpakq2c92ueyununayr 1000000000ustrd --from val1 -y | TRIM_TX -sleep 5 - -### Test airdrop reset and multiple claims flow - # The Stride airdrop occurs in batches. We need to test three batches. - - # SETUP - # 1. Create a new airdrop that rolls into its next batch in just 30 seconds - # - include the add'l param that makes each batch 30 seconds long (after the first batch) - # 2. Set the airdrop allocations - -# Create the airdrop, so that the airdrop account can claim tokens -echo ">>> Testing multiple airdrop reset and claims flow..." -$STRIDE_MAIN_CMD tx claim create-airdrop stride2 $(date +%s) 30 ustrd --from distributor-test3 -y | TRIM_TX -sleep 5 -# # Set airdrop allocations -$STRIDE_MAIN_CMD tx claim set-airdrop-allocations stride2 stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z 1 --from distributor-test3 -y | TRIM_TX -sleep 5 - -# # BATCH 1 -# # 3. Check eligibility and claim the airdrop -echo "> Checking claim record elibility" -$STRIDE_MAIN_CMD q claim claim-record stride stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z -echo "> Claiming airdrop" -$STRIDE_MAIN_CMD tx claim claim-free-amount --from stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z -y | TRIM_TX -sleep 5 - -# # 5. Query to check airdrop vesting account was created (w/ correct amount) -echo "Verifying funds are vesting, should be 1." -$STRIDE_MAIN_CMD q claim user-vestings stride1jrmtt5c6z8h5yrrwml488qnm7p3vxrrml2kgvl - - - # BATCH 2 - # 6. Wait 30 seconds -echo "> Waiting 30 seconds for next batch..." -sleep 30 - # 7. Claim the airdrop -$STRIDE_MAIN_CMD tx claim claim-free-amount --from stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z -y | TRIM_TX - -# # 8. Query to check airdrop vesting account was created (w/ correct amount) -echo "> Verifying more funds are vesting, should be 2." -$STRIDE_MAIN_CMD q claim user-vestings stride1jrmtt5c6z8h5yrrwml488qnm7p3vxrrml2kgvl - -# # BATCH 3 -# # 10. Wait 30 seconds -echo "> Waiting 30 seconds for next batch..." -sleep 30 -# # 11. Claim the airdrop -$STRIDE_MAIN_CMD tx claim claim-free-amount --from stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z -y | TRIM_TX - -# # 12. Query to check airdrop vesting account was created (w/ correct amount) -echo "> Verifying more funds are vesting, should be 3." -$STRIDE_MAIN_CMD q claim user-vestings stride1jrmtt5c6z8h5yrrwml488qnm7p3vxrrml2kgvl - - - - -# ### Test staggered airdrops - -# # create airdrop 1 with a 60 day start window, 60 sec reset, claim, sleep 35 -# # $STRIDE_MAIN_CMD tx claim create-airdrop airdrop1 $(date +%s) 60 ustrd --from distributor-test3 -y -# # sleep 5 -# # $STRIDE_MAIN_CMD tx claim set-airdrop-allocations airdrop1 stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z 1 --from distributor-test3 -y -# # sleep 5 -# # $STRIDE_MAIN_CMD tx claim claim-free-amount --from stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z -# # sleep 35 - -# # # create airdrop 2 with a 60 day start window, 60 sec reset, claim, sleep 35 -# # $STRIDE_MAIN_CMD tx claim create-airdrop airdrop1 $(date +%s) 60 stuatom --from distributor-test3 -y -# # sleep 5 -# # $STRIDE_MAIN_CMD tx claim set-airdrop-allocations airdrop1 stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z 1 --from distributor-test3 -y -# # sleep 5 -# # $STRIDE_MAIN_CMD tx claim claim-free-amount --from stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z -# # sleep 35 - -# # # airdrop 1 resets -# # $STRIDE_MAIN_CMD q bank balances stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z -# # $STRIDE_MAIN_CMD tx claim claim-free-amount --from stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z -# # $STRIDE_MAIN_CMD q bank balances stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z - diff --git a/dockernet/scripts/airdrop/airdrop3_evmos.sh b/dockernet/scripts/airdrop/airdrop3_evmos.sh new file mode 100644 index 0000000000..a3997b501c --- /dev/null +++ b/dockernet/scripts/airdrop/airdrop3_evmos.sh @@ -0,0 +1,127 @@ +### AIRDROP TESTING FLOW +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +source ${SCRIPT_DIR}/../../config.sh + +### AIRDROP TESTING FLOW Pt 3 (EVMOS) + +# This script tests claiming an evmos airdrop via autopilot with ibc-go v5+ +# The claim is initiated by sending an IBC transfer with the stride address in the memo + +# To run: +# 1. Enable EVMOS as the only dockernet host chain +# 2. Start the network with `make start-docker` +# 3. Run this script with `bash dockernet/scripts/airdrop/airdrop3_evmos.sh` + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +source ${SCRIPT_DIR}/../../config.sh + +AIRDROP_NAME="evmos" +AIRDROP_CHAIN_ID="evmos_9001-2" + +# The STRIDE/EVMOS recipient addresses represent the actual addresses owned by the claimer +# The mechanical address is a transient address derived by converting the evmos address +# directly to a stride address, without taking the coin type into consideration +# The mechanical address can be thought of as the "key" to this user's airdrop +AIRDROP_RECIPIENT_1_STRIDE="stride1qlly03ar5ll85ww4usvkv09832vv5tkhtnnaep" +AIRDROP_RECIPIENT_1_EVMOS="evmos1nmwp5uh5a3g08668c5eynes0hyfaw94dfnj796" +AIRDROP_RECIPIENT_1_MECHANICAL="stride1nmwp5uh5a3g08668c5eynes0hyfaw94dgervt7" + +AIRDROP_RECIPIENT_2="stride17kht2x2ped6qytr2kklevtvmxpw7wq9rmuc3ca" +AIRDROP_RECIPIENT_3="stride1nnurja9zt97huqvsfuartetyjx63tc5zq8s6fv" +AIRDROP_RECIPIENT_4="stride16lmf7t0jhaatan6vnxlgv47h2wf0k5ln58y9qm" + +AIRDROP_DISTRIBUTOR_1="stride1qs6c3jgk7fcazrz328sqxqdv9d5lu5qqqgqsvj" + +# airdrop recipient 1 key +# add airdrop recipient 1 on Stride +echo "prosper vivid sign donkey involve flee behind save satoshi reason girl cable ranch can arrive unable coyote race model disagree buzz peasant mechanic position" | \ + $STRIDE_MAIN_CMD keys add airdrop-recipient-1 --recover +# add airdrop recipient 1 on Evmos +echo "prosper vivid sign donkey involve flee behind save satoshi reason girl cable ranch can arrive unable coyote race model disagree buzz peasant mechanic position" | \ + $EVMOS_MAIN_CMD keys add airdrop-recipient-1 --recover +# add the airdrop distributor account +echo "rebel tank crop gesture focus frozen essay taxi prison lesson prefer smile chaos summer attack boat abandon school average ginger rib struggle drum drop" | \ + $STRIDE_MAIN_CMD keys add distributor --recover + +## AIRDROP SETUP +echo "Funding accounts..." +# Fund the distributor account +$STRIDE_MAIN_CMD tx bank send val1 $AIRDROP_DISTRIBUTOR_1 100000000ustrd --from val1 -y | TRIM_TX +sleep 5 +# Fund the evmos airdrop-recipient-1 account +$EVMOS_MAIN_CMD tx bank send eval1 evmos1nmwp5uh5a3g08668c5eynes0hyfaw94dfnj796 1000000000000000000aevmos --from val1 -y | TRIM_TX +sleep 5 +# Fund the stride airdrop-recipient-1 account +$STRIDE_MAIN_CMD tx bank send val1 $AIRDROP_RECIPIENT_1_STRIDE 1000000ustrd --from val1 -y | TRIM_TX +sleep 5 + +# Verify initial balances +echo -e "\n>>> Initial Balances:" +# Distributor account +echo -e "\n> Distributor Account [100000000ustrd expected]:" +$STRIDE_MAIN_CMD q bank balances $AIRDROP_DISTRIBUTOR_1 --denom ustrd +# Airdrop recipient evmos account +echo -e "\n> Airdrop Recipient Account (on Evmos) [1000000000000000000aevmos expected]:" +$EVMOS_MAIN_CMD q bank balances $AIRDROP_RECIPIENT_1_EVMOS --denom aevmos +# Airdrop recipient stride account +echo -e "\n> Airdrop Recipient Stride (on Stride) [1000000ustrd expected]:" +$STRIDE_MAIN_CMD q bank balances $AIRDROP_RECIPIENT_1_STRIDE --denom ustrd + +# ### Set up the airdrop +# create airdrop 1 +echo -e "\n\n>>> Creating Evmos airdrop..." +$STRIDE_MAIN_CMD tx claim create-airdrop $AIRDROP_NAME $AIRDROP_CHAIN_ID ustrd $(date +%s) 40000000 true --from distributor -y | TRIM_TX +sleep 5 + +echo -e "\n>>> Setting up airdrop allocations across 4 recipients..." +# set allocations to each recipient +$STRIDE_MAIN_CMD tx claim set-airdrop-allocations $AIRDROP_NAME $AIRDROP_RECIPIENT_1_MECHANICAL 1 --from distributor -y | TRIM_TX +sleep 5 +$STRIDE_MAIN_CMD tx claim set-airdrop-allocations $AIRDROP_NAME $AIRDROP_RECIPIENT_2 2 --from distributor -y | TRIM_TX +sleep 5 +$STRIDE_MAIN_CMD tx claim set-airdrop-allocations $AIRDROP_NAME $AIRDROP_RECIPIENT_3 3 --from distributor -y | TRIM_TX +sleep 5 +$STRIDE_MAIN_CMD tx claim set-airdrop-allocations $AIRDROP_NAME $AIRDROP_RECIPIENT_4 4 --from distributor -y | TRIM_TX +sleep 5 + +echo -e "\n>>> Checking airdrop eligibility..." +echo -e "\n >Checking the mechanical address. This should show 10000000ustrd since the address has not been overwritten yet." +$STRIDE_MAIN_CMD q claim total-claimable $AIRDROP_NAME $AIRDROP_RECIPIENT_1_MECHANICAL true + +echo -e "\n >Checking for recipient 1's actual stride address. This should show no coins since we have not overwritten the mechanical address yet." +$STRIDE_MAIN_CMD q claim total-claimable $AIRDROP_NAME $AIRDROP_RECIPIENT_1_STRIDE true + +echo -e "\n >Checking all other recipients. If they're non-zero, the airdrop is setup properly! :)" +$STRIDE_MAIN_CMD q claim total-claimable $AIRDROP_NAME $AIRDROP_RECIPIENT_2 true +$STRIDE_MAIN_CMD q claim total-claimable $AIRDROP_NAME $AIRDROP_RECIPIENT_3 true +$STRIDE_MAIN_CMD q claim total-claimable $AIRDROP_NAME $AIRDROP_RECIPIENT_4 true + +echo -e "\n\n>>> Overwriting airdrop elibibility for recipient 1 (i.e. overriding the mechanical address with the true address)" +# b. ibc-transfer from Osmo to Stride to change the airdrop account to stride1qlly03ar5ll85ww4usvkv09832vv5tkhtnnaep +# Memo: { +# "autopilot": { +# "claim": { +# "stride_address": "stride1qlly03ar5ll85ww4usvkv09832vv5tkhtnnaep", +# }, +# }, +# } +# Receiver: "xxx" +# Note: autopilot will look at the sender of the packet (evmos1nmwp5uh5a3g08668c5eynes0hyfaw94dfnj796) and convert this address to the mechanical +# stride address (stride1nmwp5uh5a3g08668c5eynes0hyfaw94dgervt7) which will act as the key to lookup the claim record. +# Then the record will get set to the true stride address (stride1qlly03ar5ll85ww4usvkv09832vv5tkhtnnaep) +MEMO='{ "autopilot": { "receiver": "'"$AIRDROP_RECIPIENT_1_STRIDE"'", "claim": { "stride_address": "'"$AIRDROP_RECIPIENT_1_STRIDE"'" } } }' +$EVMOS_MAIN_CMD tx ibc-transfer transfer transfer channel-0 $AIRDROP_RECIPIENT_1_STRIDE 1aevmos --memo "$MEMO" --from airdrop-recipient-1 -y | TRIM_TX +sleep 15 + +echo -e "\n>>> Verify the new stride address is eligible [10000000ustrd expected]:" +$STRIDE_MAIN_CMD q claim total-claimable $AIRDROP_NAME $AIRDROP_RECIPIENT_1_STRIDE true + +echo -e "\n>>> Verify the old mechanical address is no longer eligible [empty array expected]:" +$STRIDE_MAIN_CMD q claim total-claimable $AIRDROP_NAME $AIRDROP_RECIPIENT_1_MECHANICAL true + +echo -e "\n>>> Claiming the airdrop from the new stride address" +$STRIDE_MAIN_CMD tx claim claim-free-amount --from airdrop-recipient-1 -y | TRIM_TX +sleep 5 + +echo "\n> After claiming, check that an action was complete" +$STRIDE_MAIN_CMD q claim claim-record $AIRDROP_NAME $AIRDROP_RECIPIENT_1_STRIDE | grep claim_record -A 4 \ No newline at end of file diff --git a/dockernet/scripts/airdrop/airdrop4.sh b/dockernet/scripts/airdrop/airdrop4.sh deleted file mode 100644 index e15484cbca..0000000000 --- a/dockernet/scripts/airdrop/airdrop4.sh +++ /dev/null @@ -1,64 +0,0 @@ -### AIRDROP TESTING FLOW -SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) -source ${SCRIPT_DIR}/../../config.sh - -# CLEANUP if running tests twice, clear out and re-fund accounts -$STRIDE_MAIN_CMD keys delete distributor-test3 -y &> /dev/null || true -$STRIDE_MAIN_CMD keys delete distributor-test4 -y &> /dev/null || true -$GAIA_MAIN_CMD keys delete hosttest -y &> /dev/null || true -$STRIDE_MAIN_CMD keys delete airdrop-test -y &> /dev/null || true -$OSMO_MAIN_CMD keys delete host-address-test -y &> /dev/null || true - -# First, start the network with `make start-docker` -# Then, run this script with `bash dockernet/scripts/airdrop.sh` - -echo "person pelican purchase boring theme eagle jaguar screen frame attract mad link ribbon ball poverty valley cross cradle real idea payment ramp nature anchor" | \ - $STRIDE_MAIN_CMD keys add distributor-test3 --recover - -# stride1wl22etyhepwmsmycnvt3ragjyv2r5ctrk4emv3 -echo "skill essence buddy slot trim rich acid end ability sketch evoke volcano fantasy visit maze mouse sword squirrel weasel mandate main author zebra lunar" | \ - $STRIDE_MAIN_CMD keys add distributor-test4 --recover - -## AIRDROP SETUP -echo "Funding accounts..." -# Transfer uatom from gaia to stride, so that we can liquid stake later -$GAIA_MAIN_CMD tx ibc-transfer transfer transfer channel-0 stride1wl22etyhepwmsmycnvt3ragjyv2r5ctrk4emv3 1000000uatom --from ${GAIA_VAL_PREFIX}1 -y | TRIM_TX -sleep 5 -# Fund the distributor3 account -$STRIDE_MAIN_CMD tx bank send val1 stride12lw3587g97lgrwr2fjtr8gg5q6sku33e5yq9wl 600000ustrd --from val1 -y | TRIM_TX -sleep 5 -# Fund the distributor4 account -$STRIDE_MAIN_CMD tx bank send val1 stride1wl22etyhepwmsmycnvt3ragjyv2r5ctrk4emv3 600000$ATOM_DENOM --from val1 -y | TRIM_TX -sleep 5 - - -# ### Test staggered airdrops -# airdrop1 is ustrd; airdrop2 is ibc/ATOM. this simplifies telling them apart after testing a reset of airdrop1 before airdrop 2 has a chance to reset. - -# create airdrop 1 with a 60 day start window, 60 sec reset, claim, sleep 35 -$STRIDE_MAIN_CMD tx claim create-airdrop airdrop1 $(date +%s) 60 ustrd --from distributor-test3 -y | TRIM_TX -sleep 5 -$STRIDE_MAIN_CMD tx claim set-airdrop-allocations airdrop1 stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z 1 --from distributor-test3 -y | TRIM_TX -sleep 5 -$STRIDE_MAIN_CMD tx claim claim-free-amount --from stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z -y | TRIM_TX -echo "...Sleeping 35 more sec to wait for reset to complete..." -sleep 35 - -# create airdrop 2 with a 60 day start window, 60 sec reset, claim, sleep 35 -$STRIDE_MAIN_CMD tx claim create-airdrop airdrop1 $(date +%s) 60 $ATOM_DENOM --from distributor-test4 -y | TRIM_TX -sleep 5 -$STRIDE_MAIN_CMD tx claim set-airdrop-allocations airdrop1 stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z 1 --from distributor-test4 -y | TRIM_TX -sleep 5 -$STRIDE_MAIN_CMD tx claim claim-free-amount --from stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z -y | TRIM_TX -echo "...Sleeping 35 more sec to wait for reset to complete..." -sleep 35 - -# airdrop 1 resets -echo "> Query how many funds are vesting before the reset + re-claim" -$STRIDE_MAIN_CMD q claim user-vestings stride1jrmtt5c6z8h5yrrwml488qnm7p3vxrrml2kgvl -echo "> Claiming more funds after reset" -$STRIDE_MAIN_CMD tx claim claim-free-amount --from stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z -y | TRIM_TX -sleep 5 -echo "> Verify more funds are vesting before the reset" -$STRIDE_MAIN_CMD q claim user-vestings stride1jrmtt5c6z8h5yrrwml488qnm7p3vxrrml2kgvl - diff --git a/dockernet/scripts/airdrop/airdrop4_resets.sh b/dockernet/scripts/airdrop/airdrop4_resets.sh new file mode 100644 index 0000000000..1ce018c98b --- /dev/null +++ b/dockernet/scripts/airdrop/airdrop4_resets.sh @@ -0,0 +1,126 @@ +### AIRDROP TESTING FLOW +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +source ${SCRIPT_DIR}/../../config.sh + +### AIRDROP TESTING FLOW Pt 4 (RESETS) + +# This script tests that the the airdrop resets properly + +# To run: +# 1. Update the following in `x/claim/types/params.go` +# * `DefaultEpochDuration` to `time.Second * 60` +# * `DefaultVestingInitialPeriod` to `time.Second * 120` +# 2. Start the network with `make start-docker` +# 3. Run this script with `bash dockernet/scripts/airdrop/airdrop4_resets.sh` + +# NOTE: First, store the keys using the following mnemonics +echo "Registering distributor account..." +# distributor address: stride12lw3587g97lgrwr2fjtr8gg5q6sku33e5yq9wl +# distributor mnemonic: barrel salmon half click confirm crunch sense defy salute process cart fiscal sport clump weasel render private manage picture spell wreck hill frozen before +echo "person pelican purchase boring theme eagle jaguar screen frame attract mad link ribbon ball poverty valley cross cradle real idea payment ramp nature anchor" | \ + $STRIDE_MAIN_CMD keys add distributor-test --recover + +## AIRDROP SETUP +echo "Funding accounts..." +# Transfer uatom from gaia to stride, so that we can liquid stake later +$GAIA_MAIN_CMD tx ibc-transfer transfer transfer channel-0 stride1nf6v2paty9m22l3ecm7dpakq2c92ueyununayr 1000000uatom --from ${GAIA_VAL_PREFIX}1 -y | TRIM_TX +sleep 15 +# Fund the distributor account +$STRIDE_MAIN_CMD tx bank send val1 stride12lw3587g97lgrwr2fjtr8gg5q6sku33e5yq9wl 100ustrd --from val1 -y | TRIM_TX +sleep 5 + +# Confirm initial balance setup +echo -e "\n>>> Initial Balances:" +echo "> Distributor Account [100ustrd expected]:" +$STRIDE_MAIN_CMD q bank balances stride12lw3587g97lgrwr2fjtr8gg5q6sku33e5yq9wl --denom ustrd + +echo "> Claim Account [5000000000000ustrd expected]:" +$STRIDE_MAIN_CMD q bank balances stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z --denom ustrd + +### Test airdrop reset and multiple claims flow + # The Stride airdrop occurs in batches. We need to test three batches. + + # SETUP + # 1. Create a new airdrop that rolls into its next batch in just 30 seconds + # - include the add'l param that makes each batch 30 seconds long (after the first batch) + # 2. Set the airdrop allocations + +# Create the airdrop, so that the airdrop account can claim tokens +echo -e "\n>>> Creating airdrop and setting allocations..." +$STRIDE_MAIN_CMD tx claim create-airdrop gaia GAIA ustrd $(date +%s) 40000000 false --from distributor-test -y | TRIM_TX +sleep 5 +# Set airdrop allocations +$STRIDE_MAIN_CMD tx claim set-airdrop-allocations gaia stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z 1 --from distributor-test -y | TRIM_TX +sleep 5 +# Check eligibility +echo "> Checking claim elibility, should return 1 claim record:" +$STRIDE_MAIN_CMD q claim claim-record gaia stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z + +# # BATCH 1 +# # 3. Check eligibility and claim the airdrop +echo -e "\n>>> Claiming airdrop" +$STRIDE_MAIN_CMD tx claim claim-free-amount --from stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z -y | TRIM_TX +sleep 5 + +# # 5. Query to check airdrop vesting account was created (w/ correct amount) +echo -e "\n>>> Claim verification..." +# Check actions +echo "> Checking claim record actions [expected: 1 action complete]:" +$STRIDE_MAIN_CMD q claim claim-record gaia stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z | grep claim_record -A 4 +# Check vesting +echo -e "\n> Verifying funds are vesting [expected: 20ustrd]:" +$STRIDE_MAIN_CMD q claim user-vestings stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z | grep spendable_coins -A 2 +# Check balance +echo -e "\n> Verifying balance [expected: 5000000000020ustrd]:" +$STRIDE_MAIN_CMD q bank balances stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z --denom ustrd + + +# # BATCH 2 +# # 6. Wait 120 seconds +echo -e "\n>>> Waiting 120 seconds for next batch..." +sleep 120 +echo -e "\n>>> Verify claim was reset [expected: no actions complete]:" +$STRIDE_MAIN_CMD q claim claim-record gaia stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z | grep claim_record -A 4 + + # 7. Claim the airdrop +echo -e "\n>>> Claim airdrop" +$STRIDE_MAIN_CMD tx claim claim-free-amount --from stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z -y | TRIM_TX +sleep 5 + +# # 8. Query to check airdrop vesting account was created (w/ correct amount) +echo -e "\n>>> Claim verification..." +# Check actions +echo "> Checking claim record actions [expected: 1 action complete]:" +$STRIDE_MAIN_CMD q claim claim-record gaia stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z | grep claim_record -A 4 +# Check vesting +echo -e "\n> Verifying the vesting tokens have not changed [expected: 20ustrd]:" +$STRIDE_MAIN_CMD q claim user-vestings stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z | grep spendable_coins -A 2 +# Check balance +echo -e "\n> Verifying balance [expected: 5000000000036ustrd]:" +$STRIDE_MAIN_CMD q bank balances stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z --denom ustrd + +# # BATCH 3 +# # 10. Wait 65 seconds +echo -e ">>> Waiting 65 seconds for next batch..." +sleep 65 +echo -e "\n>>> Verify claim was reset [expected: no actions complete]:" +$STRIDE_MAIN_CMD q claim claim-record gaia stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z | grep claim_record -A 4 + +# # 11. Claim the airdrop +echo -e "\n>>> Claim airdrop" +$STRIDE_MAIN_CMD tx claim claim-free-amount --from stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z -y | TRIM_TX +sleep 5 + +# # 12. Query to check airdrop vesting account was created (w/ correct amount) +echo -e "\n>>> Claim verification..." +# Check actions +echo "> Checking claim record actions [expected: 1 action complete]:" +$STRIDE_MAIN_CMD q claim claim-record gaia stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z | grep claim_record -A 4 +# Check vesting +echo -e "\n> Verifying the vesting tokens have not changed [expected: 20ustrd]:" +$STRIDE_MAIN_CMD q claim user-vestings stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z | grep spendable_coins -A 2 +# Check balance +echo -e "\n> Verifying balance [expected: 5000000000049ustrd]:" +$STRIDE_MAIN_CMD q bank balances stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z --denom ustrd + + diff --git a/dockernet/scripts/airdrop/airdrop5_staggered.sh b/dockernet/scripts/airdrop/airdrop5_staggered.sh new file mode 100644 index 0000000000..6909e373fb --- /dev/null +++ b/dockernet/scripts/airdrop/airdrop5_staggered.sh @@ -0,0 +1,127 @@ +### AIRDROP TESTING FLOW +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +source ${SCRIPT_DIR}/../../config.sh + +### AIRDROP TESTING FLOW Pt 5 (STAGGERED) + +# This script tests multiple staggered airdrops live in tandem + +# To run: +# 1. Update the following in `x/claim/types/params.go` +# * `DefaultEpochDuration` to `time.Second * 60` +# * `DefaultVestingInitialPeriod` to `time.Second * 120` +# 2. Start the network with `make start-docker` +# 3. Run this script with `bash dockernet/scripts/airdrop/airdrop5_staggered.sh` + +echo "Registering accounts..." +# distributor address: stride12lw3587g97lgrwr2fjtr8gg5q6sku33e5yq9wl +echo "person pelican purchase boring theme eagle jaguar screen frame attract mad link ribbon ball poverty valley cross cradle real idea payment ramp nature anchor" | \ + $STRIDE_MAIN_CMD keys add distributor-test1 --recover + +# stride1wl22etyhepwmsmycnvt3ragjyv2r5ctrk4emv3 +echo "skill essence buddy slot trim rich acid end ability sketch evoke volcano fantasy visit maze mouse sword squirrel weasel mandate main author zebra lunar" | \ + $STRIDE_MAIN_CMD keys add distributor-test2 --recover + +## AIRDROP SETUP +echo "Funding accounts..." +# Fund the distributor1 account +$STRIDE_MAIN_CMD tx bank send val1 stride12lw3587g97lgrwr2fjtr8gg5q6sku33e5yq9wl 100ustrd --from val1 -y | TRIM_TX +sleep 5 +# Fund the distributor2 account +$STRIDE_MAIN_CMD tx bank send val1 stride1wl22etyhepwmsmycnvt3ragjyv2r5ctrk4emv3 100ustrd --from val1 -y | TRIM_TX +sleep 5 + +echo -e "\n>>> Initial Balances:" +echo "> Distributor1 Account [100ustrd expected]:" +$STRIDE_MAIN_CMD q bank balances stride12lw3587g97lgrwr2fjtr8gg5q6sku33e5yq9wl --denom ustrd + +echo "> Distributor2 Account [100ustrd expected]:" +$STRIDE_MAIN_CMD q bank balances stride1wl22etyhepwmsmycnvt3ragjyv2r5ctrk4emv3 --denom ustrd + +echo "> Claim Account [5000000000000ustrd expected]:" +$STRIDE_MAIN_CMD q bank balances stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z --denom ustrd + +# ### Test staggered airdrops +# airdrop1 is ustrd; airdrop2 is ibc/ATOM. this simplifies telling them apart after testing a reset of airdrop1 before airdrop 2 has a chance to reset. + +# create airdrop 1 for ustrd +echo -e "\n>>> Creating airdrop1 and allocations..." +$STRIDE_MAIN_CMD tx claim create-airdrop airdrop1 GAIA ustrd $(date +%s) 40000000 false --from distributor-test1 -y | TRIM_TX +sleep 5 +$STRIDE_MAIN_CMD tx claim set-airdrop-allocations airdrop1 stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z 1 --from distributor-test1 -y | TRIM_TX +sleep 5 + +# claim airdrop +echo -e "\n>>> Claiming airdrop1..." +$STRIDE_MAIN_CMD tx claim claim-free-amount --from stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z -y | TRIM_TX +sleep 5 +# verify claim record +echo "> Checking claim eligibility for airdrop1, should return 1 claim-record:" +$STRIDE_MAIN_CMD q claim claim-record airdrop1 stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z +# verify total claimable +echo "> Checking total claimable for airdrop1 [expected: 100ustrd]:" +$STRIDE_MAIN_CMD q claim total-claimable airdrop1 stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z true + +echo -e "\n...Sleeping 30 before creating airdrop2..." +sleep 30 + +# create airdrop 2 +echo -e "\n>>> Creating airdrop2 and setting allocations..." +$STRIDE_MAIN_CMD tx claim create-airdrop airdrop2 GAIA2 ustrd $(date +%s) 40000000 false --from distributor-test2 -y | TRIM_TX +sleep 5 +$STRIDE_MAIN_CMD tx claim set-airdrop-allocations airdrop2 stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z 1 --from distributor-test2 -y | TRIM_TX +sleep 5 + +# claim airdrop +echo -e "\n>>> Claiming airdrop2..." +$STRIDE_MAIN_CMD tx claim claim-free-amount --from stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z -y | TRIM_TX +sleep 5 +# verify claim record +echo "> Checking claim eligibility for airdrop2, should return 1 claim-record:" +$STRIDE_MAIN_CMD q claim claim-record airdrop2 stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z +# verify total claimable +echo "> Checking total claimable for airdrop2 [expected: 100ustrd]:" +$STRIDE_MAIN_CMD q claim total-claimable airdrop2 stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z true + +echo -e "\n...Sleeping 60 more sec to wait for airdrop1 to reset..." +sleep 60 + +### airdrop 1 resets - check state before claim +echo -e "\n>>> Airdrop1 Reset <<<" +echo -e "\n>>> Verify claim for airdrop1 was reset [expected: no actions complete]:" +$STRIDE_MAIN_CMD q claim claim-record airdrop1 stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z | grep claim_record -A 4 + +# Claim again after reset +echo -e "\n>>> Claiming airdrop1 again..." +$STRIDE_MAIN_CMD tx claim claim-free-amount --from stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z -y | TRIM_TX +sleep 5 + +echo -e "\n>>> Claim verification for airdrop1..." +# Check actions +echo "> Checking claim record actions [expected: 1 action complete]:" +$STRIDE_MAIN_CMD q claim claim-record airdrop1 stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z | grep claim_record -A 4 +# Check balance +echo -e "\n> Verifying balance [expected: 5000000000056ustrd]:" +$STRIDE_MAIN_CMD q bank balances stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z --denom ustrd + +echo -e "\n...Sleeping 45 more sec to wait for airdrop2 to reset..." +sleep 45 + +### airdrop 2 resets before airdrop 1 has a chance to reset again +echo -e "\n>>> Airdrop2 Reset <<<" +echo -e "\n>>> Verify claim for airdrop2 was reset [expected: no actions complete]:" +$STRIDE_MAIN_CMD q claim claim-record airdrop2 stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z | grep claim_record -A 4 + +# Claim again after reset +echo -e "\n>>> Claiming airdrop2 again..." +$STRIDE_MAIN_CMD tx claim claim-free-amount --from stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z -y | TRIM_TX +sleep 5 + +echo -e "\n>>> Claim verification for airdrop2..." +# Check actions +echo "> Checking claim record actions [expected: 1 action complete]:" +$STRIDE_MAIN_CMD q claim claim-record airdrop2 stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z | grep claim_record -A 4 +# Check balance +echo -e "\n> Verifying balance [expected: 5000000000072ustrd]:" +$STRIDE_MAIN_CMD q bank balances stride1kwll0uet4mkj867s4q8dgskp03txgjnswc2u4z --denom ustrd + diff --git a/dockernet/scripts/airdrop/evmos_airdrop.sh b/dockernet/scripts/airdrop/evmos_airdrop.sh deleted file mode 100644 index 041a47fc17..0000000000 --- a/dockernet/scripts/airdrop/evmos_airdrop.sh +++ /dev/null @@ -1,139 +0,0 @@ -### AIRDROP SETUP SCRIPT -# -# Instructions: -# 1. First, start the network with `make start-docker` -# 2. Then, run this script with `bash dockernet/scripts/airdrop/evmos_airdrop.sh` -# 3. If the final stdout print lines from the script match what's below, the airdrop is live! -# -# \n Querying airdrop eligibilities -# coins: -# - amount: "22222255" -# denom: ustrd -# coins: -# - amount: "44444511" -# denom: ustrd -# coins: -# - amount: "111111279" -# denom: ustrd -# coins: -# - amount: "222222555" -# denom: ustrd -# - -SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) -source ${SCRIPT_DIR}/../../config.sh - - -$STRIDE_MAIN_CMD keys delete airdrop-recipient-1 -y &> /dev/null || true -$EVMOS_MAIN_CMD keys delete airdrop-recipient-1 -y &> /dev/null || true - -AIRDROP_NAME="evmos" - -# airdrop recipient 1 key -# add airdrop recipient 1 -echo "prosper vivid sign donkey involve flee behind save satoshi reason girl cable ranch can arrive unable coyote race model disagree buzz peasant mechanic position" | \ - $STRIDE_MAIN_CMD keys add airdrop-recipient-1 --recover - -echo "prosper vivid sign donkey involve flee behind save satoshi reason girl cable ranch can arrive unable coyote race model disagree buzz peasant mechanic position" | \ - $EVMOS_MAIN_CMD keys add airdrop-recipient-1 --recover - -AIRDROP_RECIPIENT_1_STRIDE="stride1qlly03ar5ll85ww4usvkv09832vv5tkhtnnaep" -AIRDROP_RECIPIENT_1_EVMOS="evmos1nmwp5uh5a3g08668c5eynes0hyfaw94dfnj796" -AIRDROP_RECIPIENT_1_MECHANICAL="stride1nmwp5uh5a3g08668c5eynes0hyfaw94dgervt7" - -AIRDROP_RECIPIENT_2="stride17kht2x2ped6qytr2kklevtvmxpw7wq9rmuc3ca" -AIRDROP_RECIPIENT_3="stride1nnurja9zt97huqvsfuartetyjx63tc5zq8s6fv" -AIRDROP_RECIPIENT_4_TO_BE_REPLACED="stride16lmf7t0jhaatan6vnxlgv47h2wf0k5ln58y9qm" -AIRDROP_DISTRIBUTOR_1="stride1qs6c3jgk7fcazrz328sqxqdv9d5lu5qqqgqsvj" - - -echo ">>> Querying the claims module to verify that the new address is eligible" -$STRIDE_MAIN_CMD q claim total-claimable $AIRDROP_NAME $AIRDROP_RECIPIENT_1_STRIDE true - -# cleanup: clear out and re-fund accounts -$STRIDE_MAIN_CMD keys delete d1 -y &> /dev/null || true -# add the airdrop distributor account -echo "rebel tank crop gesture focus frozen essay taxi prison lesson prefer smile chaos summer attack boat abandon school average ginger rib struggle drum drop" | \ - $STRIDE_MAIN_CMD keys add d1 --recover - -## AIRDROP SETUP -printf "Funding accounts..." -# Fund the d1 account -$STRIDE_MAIN_CMD tx bank send val1 $AIRDROP_DISTRIBUTOR_1 100000000ustrd --from val1 -y | TRIM_TX -sleep 5 -# query the balance of the d1 account to make sure it was funded -$STRIDE_MAIN_CMD q bank balances $AIRDROP_DISTRIBUTOR_1 - -# fund the evmos account -$EVMOS_MAIN_CMD tx bank send nval1 evmos1nmwp5uh5a3g08668c5eynes0hyfaw94dfnj796 1000000000000000000aevmos --from val1 -y -sleep 5 -# query the balance of the airdrop-recipient-1 account to make sure it was funded -$EVMOS_MAIN_CMD q bank balances $AIRDROP_RECIPIENT_1_EVMOS - -# Fund the airdrop-recipient-1 account -$STRIDE_MAIN_CMD tx bank send val1 $AIRDROP_RECIPIENT_1_STRIDE 1000000ustrd --from val1 -y | TRIM_TX -sleep 5 -# query the balance of the airdrop-recipient-1 account to make sure it was funded -$STRIDE_MAIN_CMD q bank balances $AIRDROP_RECIPIENT_1_STRIDE - - - -# ### Set up the airdrop - -# create airdrop 1 -printf "\n\nCreating first airdrop, should last 1 hour and reset every 60 seconds to allow for new claims every 60 seconds..." -$STRIDE_MAIN_CMD tx claim create-airdrop $AIRDROP_NAME $(date +%s) 3600 ustrd --from d1 -y | TRIM_TX -sleep 5 - -printf "\nSetting up first airdrop allocations...\n" -$STRIDE_MAIN_CMD tx claim set-airdrop-allocations $AIRDROP_NAME $AIRDROP_RECIPIENT_1_MECHANICAL 1 --from d1 -y | TRIM_TX -sleep 5 -$STRIDE_MAIN_CMD tx claim set-airdrop-allocations $AIRDROP_NAME $AIRDROP_RECIPIENT_2 2 --from d1 -y | TRIM_TX -sleep 5 -$STRIDE_MAIN_CMD tx claim set-airdrop-allocations $AIRDROP_NAME $AIRDROP_RECIPIENT_3 3 --from d1 -y | TRIM_TX -sleep 5 -$STRIDE_MAIN_CMD tx claim set-airdrop-allocations $AIRDROP_NAME $AIRDROP_RECIPIENT_4_TO_BE_REPLACED 4 --from d1 -y | TRIM_TX -sleep 5 - -echo "\n Querying airdrop eligibilities. The results of the query show the total claimable amount for each account. If they're non-zero, the airdrop is live! :)" -$STRIDE_MAIN_CMD q claim total-claimable $AIRDROP_NAME $AIRDROP_RECIPIENT_1_MECHANICAL true -$STRIDE_MAIN_CMD q claim total-claimable $AIRDROP_NAME $AIRDROP_RECIPIENT_2 true -$STRIDE_MAIN_CMD q claim total-claimable $AIRDROP_NAME $AIRDROP_RECIPIENT_3 true -$STRIDE_MAIN_CMD q claim total-claimable $AIRDROP_NAME $AIRDROP_RECIPIENT_4_TO_BE_REPLACED true - -echo "Do you want to link the addresses? (y/n)" -read user_input -if [ "$user_input" != "y" ]; then - echo "Exiting the script..." - exit 0 -fi - -echo "Sleeping 2 minutes before linking the evmos address to its stride address..." -sleep 10 -echo "\n Overwrite airdrop elibibility for recipient 4. They should no longer be eligible." -# b. ibc-transfer from Osmo to Stride to change the airdrop account to stride1qlly03ar5ll85ww4usvkv09832vv5tkhtnnaep -# Memo: { -# "autopilot": { -# "stakeibc": { -# "stride_address": "stride1qlly03ar5ll85ww4usvkv09832vv5tkhtnnaep", -# }, -# "claim": { -# } -# }, -# } -# Receiver: "xxx" -# Note: autopilot will look at the sender of the packet (evmos1nmwp5uh5a3g08668c5eynes0hyfaw94dfnj796) and convert this address to the mechanical -# stride address (stride1nmwp5uh5a3g08668c5eynes0hyfaw94dgervt7), then reset it to the true stride address (stride1qlly03ar5ll85ww4usvkv09832vv5tkhtnnaep) -MEMO='{"autopilot": {"receiver": "stride1qlly03ar5ll85ww4usvkv09832vv5tkhtnnaep","claim": { "stride_address": "stride1qlly03ar5ll85ww4usvkv09832vv5tkhtnnaep", "airdrop_id": "evmos" } }}' -# $EVMOS_MAIN_CMD tx ibc-transfer transfer transfer channel-0 "$MEMO" 1aevmos --from airdrop-recipient-1 -y | TRIM_TX -# echo "tx ibc-transfer transfer transfer channel-0 stride1qlly03ar5ll85ww4usvkv09832vv5tkhtnnaep 1aevmos --note "$MEMO" --from airdrop-recipient-1 -y | TRIM_TX" -# $EVMOS_MAIN_CMD tx ibc-transfer transfer transfer channel-0 stride1qlly03ar5ll85ww4usvkv09832vv5tkhtnnaep 1aevmos --note "$MEMO" --from airdrop-recipient-1 -y | TRIM_TX -# echo ">>> Waiting for 15 seconds to allow the IBC transfer to complete..." -# sleep 5 -$EVMOS_MAIN_CMD tx ibc-transfer transfer transfer channel-0 "$MEMO" 1aevmos --from airdrop-recipient-1 -y | TRIM_TX -# $EVMOS_MAIN_CMD tx ibc-transfer transfer transfer channel-0 stride1qlly03ar5ll85ww4usvkv09832vv5tkhtnnaep 1aevmos --memo "$MEMO" --from airdrop-recipient-1 -y | TRIM_TX -echo ">>> Waiting for 15 seconds to allow the IBC transfer to complete..." -sleep 150 - -echo ">>> Querying the claims module to verify that the new address is eligible" -$STRIDE_MAIN_CMD q claim total-claimable $AIRDROP_NAME $AIRDROP_RECIPIENT_1_STRIDE true \ No newline at end of file diff --git a/proto/stride/claim/params.proto b/proto/stride/claim/params.proto index 828ab8caf1..b158e99b65 100644 --- a/proto/stride/claim/params.proto +++ b/proto/stride/claim/params.proto @@ -13,6 +13,7 @@ message Params { repeated Airdrop airdrops = 1; } message Airdrop { string airdrop_identifier = 1 [ (gogoproto.moretags) = "yaml:\"airdrop_identifier\"" ]; + string chain_id = 7; // seconds google.protobuf.Timestamp airdrop_start_time = 2 [ (gogoproto.stdtime) = true, @@ -35,4 +36,6 @@ message Airdrop { (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", (gogoproto.nullable) = false ]; + // indicates the airdrop should be claimed via autopilot + bool autopilot_enabled = 8; } \ No newline at end of file diff --git a/proto/stride/claim/tx.proto b/proto/stride/claim/tx.proto index d8450cc859..0f24640543 100644 --- a/proto/stride/claim/tx.proto +++ b/proto/stride/claim/tx.proto @@ -41,9 +41,11 @@ message MsgClaimFreeAmountResponse { message MsgCreateAirdrop { string distributor = 1; string identifier = 2; + string chain_id = 6; + string denom = 5; uint64 start_time = 3; uint64 duration = 4; - string denom = 5; + bool autopilot_enabled = 7; } message MsgCreateAirdropResponse {} diff --git a/x/autopilot/keeper/airdrop.go b/x/autopilot/keeper/airdrop.go index 4f6b8fae96..3e922b9de6 100644 --- a/x/autopilot/keeper/airdrop.go +++ b/x/autopilot/keeper/airdrop.go @@ -9,13 +9,17 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" transfertypes "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" "github.com/Stride-Labs/stride/v8/utils" "github.com/Stride-Labs/stride/v8/x/autopilot/types" + claimtypes "github.com/Stride-Labs/stride/v8/x/claim/types" + stakeibctypes "github.com/Stride-Labs/stride/v8/x/stakeibc/types" ) func (k Keeper) TryUpdateAirdropClaim( ctx sdk.Context, + packet channeltypes.Packet, data transfertypes.FungibleTokenPacketData, packetMetadata types.ClaimPacketMetadata, ) error { @@ -24,6 +28,16 @@ func (k Keeper) TryUpdateAirdropClaim( return errors.New("packet forwarding param is not active") } + // verify packet originated on a registered host zone + if packet.GetDestPort() != transfertypes.PortID { + return errors.New("airdrop claim packet should be sent along a transfer channel") + } + hostZone, found := k.stakeibcKeeper.GetHostZoneFromTransferChannelID(ctx, packet.GetDestChannel()) + if !found { + return errorsmod.Wrapf(stakeibctypes.ErrHostZoneNotFound, + "host zone not found for transfer channel %s", packet.GetDestChannel()) + } + // grab relevant addresses senderStrideAddress := utils.ConvertAddressToStrideAddress(data.Sender) if senderStrideAddress == "" { @@ -31,8 +45,16 @@ func (k Keeper) TryUpdateAirdropClaim( } newStrideAddress := packetMetadata.StrideAddress - // update the airdrop - airdropId := packetMetadata.AirdropId + // find the airdrop for this host chain ID + airdrop, found := k.claimKeeper.GetAirdropByChainId(ctx, hostZone.ChainId) + if !found { + return errorsmod.Wrapf(claimtypes.ErrAirdropNotFound, "airdrop not found for chain-id %s", hostZone.ChainId) + } + if !airdrop.AutopilotEnabled { + return fmt.Errorf("autopilot claiming is not enabled for host zone %s", hostZone.ChainId) + } + + airdropId := airdrop.AirdropIdentifier k.Logger(ctx).Info(fmt.Sprintf("updating airdrop address %s (orig %s) to %s for airdrop %s", senderStrideAddress, data.Sender, newStrideAddress, airdropId)) diff --git a/x/autopilot/keeper/airdrop_test.go b/x/autopilot/keeper/airdrop_test.go index e496cf7992..b7ce0a52ab 100644 --- a/x/autopilot/keeper/airdrop_test.go +++ b/x/autopilot/keeper/airdrop_test.go @@ -8,6 +8,7 @@ import ( transfertypes "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" sdk "github.com/cosmos/cosmos-sdk/types" @@ -15,20 +16,23 @@ import ( "github.com/Stride-Labs/stride/v8/x/autopilot" "github.com/Stride-Labs/stride/v8/x/autopilot/types" claimtypes "github.com/Stride-Labs/stride/v8/x/claim/types" + stakeibctypes "github.com/Stride-Labs/stride/v8/x/stakeibc/types" ) // TODO: Separate out tests cases that are not necessarily Claim or Stakeibc related, // but more just test the parsing that occurs in OnRecvPacket // Move them to a different test file -func getClaimPacketMetadata(address, airdropId string) string { +var EvmosChainId = "evmos-1" + +func getClaimPacketMetadata(address string) string { return fmt.Sprintf(` { "autopilot": { "receiver": "%[1]s", - "claim": { "stride_address": "%[1]s", "airdrop_id": "%[2]s" } + "claim": { "stride_address": "%[1]s" } } - }`, address, airdropId) + }`, address) } func (s *KeeperTestSuite) TestAirdropOnRecvPacket() { @@ -50,14 +54,13 @@ func (s *KeeperTestSuite) TestAirdropOnRecvPacket() { // Build the template for the transfer packet (the data and channel fields will get updated from each unit test) packetTemplate := channeltypes.Packet{ - Sequence: 1, - SourcePort: "transfer", - SourceChannel: "channel-0", - DestinationPort: "transfer", - DestinationChannel: "channel-0", - Data: []byte{}, - TimeoutHeight: clienttypes.Height{}, - TimeoutTimestamp: 0, + Sequence: 1, + SourcePort: transfertypes.PortID, + SourceChannel: ibctesting.FirstChannelID, + DestinationPort: transfertypes.PortID, + Data: []byte{}, + TimeoutHeight: clienttypes.Height{}, + TimeoutTimestamp: 0, } packetDataTemplate := transfertypes.FungibleTokenPacketData{ Denom: evmosDenom, @@ -65,89 +68,116 @@ func (s *KeeperTestSuite) TestAirdropOnRecvPacket() { Sender: evmosAddress, } - prefixedDenom := transfertypes.GetPrefixedDenom(packetTemplate.GetSourcePort(), packetTemplate.GetSourceChannel(), evmosDenom) - evmosIbcDenom := transfertypes.ParseDenomTrace(prefixedDenom).IBCDenom() + // To test the case where the packet has a valid channel but for a host zone without an airdrop + channelIdForDifferentHostZone := "channel-1" testCases := []struct { - name string - forwardingActive bool - packetData transfertypes.FungibleTokenPacketData - transferShouldSucceed bool - airdropShouldUpdate bool + name string + autopilotClaimActive bool + autopilotClaimEnabledForHost bool + packetData transfertypes.FungibleTokenPacketData + destinationChannelID string + destinationPortID string + transferShouldSucceed bool + airdropShouldUpdate bool }{ { - name: "successful airdrop update from receiver", - forwardingActive: true, + name: "successful airdrop update from receiver", + autopilotClaimActive: true, + autopilotClaimEnabledForHost: true, + destinationChannelID: ibctesting.FirstChannelID, + destinationPortID: transfertypes.PortID, packetData: transfertypes.FungibleTokenPacketData{ - Receiver: getClaimPacketMetadata(strideAddress, evmosAirdropId), + Receiver: getClaimPacketMetadata(strideAddress), Memo: "", }, transferShouldSucceed: true, airdropShouldUpdate: true, }, { - name: "successful airdrop update from memo", - forwardingActive: true, + name: "successful airdrop update from memo", + autopilotClaimActive: true, + autopilotClaimEnabledForHost: true, + destinationChannelID: ibctesting.FirstChannelID, + destinationPortID: transfertypes.PortID, packetData: transfertypes.FungibleTokenPacketData{ Receiver: strideAddress, - Memo: getClaimPacketMetadata(strideAddress, evmosAirdropId), + Memo: getClaimPacketMetadata(strideAddress), }, transferShouldSucceed: true, airdropShouldUpdate: true, }, { - name: "memo receiver overrides original receiver field", - forwardingActive: true, + name: "memo receiver overrides original receiver field", + autopilotClaimActive: true, + autopilotClaimEnabledForHost: true, + destinationChannelID: ibctesting.FirstChannelID, + destinationPortID: transfertypes.PortID, packetData: transfertypes.FungibleTokenPacketData{ Receiver: "address-will-get-overriden", - Memo: getClaimPacketMetadata(strideAddress, evmosAirdropId), + Memo: getClaimPacketMetadata(strideAddress), }, transferShouldSucceed: true, airdropShouldUpdate: true, }, { - name: "valid receiver routing schema, but routing inactive", - forwardingActive: false, + name: "valid receiver routing schema, but routing inactive", + autopilotClaimActive: false, + autopilotClaimEnabledForHost: true, + destinationChannelID: ibctesting.FirstChannelID, + destinationPortID: transfertypes.PortID, packetData: transfertypes.FungibleTokenPacketData{ - Receiver: getClaimPacketMetadata(strideAddress, evmosAirdropId), + Receiver: getClaimPacketMetadata(strideAddress), Memo: "", }, transferShouldSucceed: false, airdropShouldUpdate: false, }, { - name: "valid memo routing schema, but routing inactive", - forwardingActive: false, + name: "valid receiver routing schema, but routing inactive for airdrop", + autopilotClaimActive: true, + autopilotClaimEnabledForHost: false, + destinationChannelID: ibctesting.FirstChannelID, + destinationPortID: transfertypes.PortID, packetData: transfertypes.FungibleTokenPacketData{ - Receiver: getClaimPacketMetadata(strideAddress, evmosAirdropId), + Receiver: getClaimPacketMetadata(strideAddress), Memo: "", }, transferShouldSucceed: false, airdropShouldUpdate: false, }, { - name: "airdrop does not exist", - forwardingActive: true, + name: "valid memo routing schema, but routing inactive", + autopilotClaimActive: false, + autopilotClaimEnabledForHost: true, + destinationChannelID: ibctesting.FirstChannelID, + destinationPortID: transfertypes.PortID, packetData: transfertypes.FungibleTokenPacketData{ - Receiver: strideAddress, - Memo: getClaimPacketMetadata(strideAddress, "fake_airdrop"), + Receiver: getClaimPacketMetadata(strideAddress), + Memo: "", }, transferShouldSucceed: false, airdropShouldUpdate: false, }, { - name: "invalid stride address", - forwardingActive: true, + name: "invalid stride address", + autopilotClaimActive: true, + autopilotClaimEnabledForHost: true, + destinationChannelID: ibctesting.FirstChannelID, + destinationPortID: transfertypes.PortID, packetData: transfertypes.FungibleTokenPacketData{ Receiver: strideAddress, - Memo: getClaimPacketMetadata("invalid_address", evmosAirdropId), + Memo: getClaimPacketMetadata("invalid_address"), }, transferShouldSucceed: false, airdropShouldUpdate: false, }, { - name: "normal transfer packet - no memo", - forwardingActive: true, + name: "normal transfer packet - no memo", + autopilotClaimActive: true, + autopilotClaimEnabledForHost: true, + destinationChannelID: ibctesting.FirstChannelID, + destinationPortID: transfertypes.PortID, packetData: transfertypes.FungibleTokenPacketData{ Receiver: strideAddress, Memo: "", @@ -156,8 +186,11 @@ func (s *KeeperTestSuite) TestAirdropOnRecvPacket() { airdropShouldUpdate: false, }, { - name: "normal transfer packet - empty JSON memo", - forwardingActive: true, + name: "normal transfer packet - empty JSON memo", + autopilotClaimActive: true, + autopilotClaimEnabledForHost: true, + destinationChannelID: ibctesting.FirstChannelID, + destinationPortID: transfertypes.PortID, packetData: transfertypes.FungibleTokenPacketData{ Receiver: strideAddress, Memo: "{}", @@ -166,8 +199,11 @@ func (s *KeeperTestSuite) TestAirdropOnRecvPacket() { airdropShouldUpdate: false, }, { - name: "normal transfer packet - different middleware", - forwardingActive: true, + name: "normal transfer packet - different middleware", + autopilotClaimActive: true, + autopilotClaimEnabledForHost: true, + destinationChannelID: ibctesting.FirstChannelID, + destinationPortID: transfertypes.PortID, packetData: transfertypes.FungibleTokenPacketData{ Receiver: strideAddress, Memo: `{ "other_module": { } }`, @@ -176,8 +212,11 @@ func (s *KeeperTestSuite) TestAirdropOnRecvPacket() { airdropShouldUpdate: false, }, { - name: "invalid autopilot JSON - no receiver", - forwardingActive: true, + name: "invalid autopilot JSON - no receiver", + autopilotClaimActive: true, + autopilotClaimEnabledForHost: true, + destinationChannelID: ibctesting.FirstChannelID, + destinationPortID: transfertypes.PortID, packetData: transfertypes.FungibleTokenPacketData{ Receiver: strideAddress, Memo: `{ "autopilot": {} }`, @@ -186,8 +225,11 @@ func (s *KeeperTestSuite) TestAirdropOnRecvPacket() { airdropShouldUpdate: false, }, { - name: "invalid autopilot JSON - no routing module", - forwardingActive: true, + name: "invalid autopilot JSON - no routing module", + autopilotClaimActive: true, + autopilotClaimEnabledForHost: true, + destinationChannelID: ibctesting.FirstChannelID, + destinationPortID: transfertypes.PortID, packetData: transfertypes.FungibleTokenPacketData{ Receiver: strideAddress, Memo: fmt.Sprintf(`{ "autopilot": { "receiver": "%s" } }`, strideAddress), @@ -196,8 +238,11 @@ func (s *KeeperTestSuite) TestAirdropOnRecvPacket() { airdropShouldUpdate: false, }, { - name: "memo too long", - forwardingActive: true, + name: "memo too long", + autopilotClaimActive: true, + autopilotClaimEnabledForHost: true, + destinationChannelID: ibctesting.FirstChannelID, + destinationPortID: transfertypes.PortID, packetData: transfertypes.FungibleTokenPacketData{ Receiver: strideAddress, Memo: strings.Repeat("X", 300), @@ -206,8 +251,11 @@ func (s *KeeperTestSuite) TestAirdropOnRecvPacket() { airdropShouldUpdate: false, }, { - name: "receiver too long", - forwardingActive: true, + name: "receiver too long", + autopilotClaimActive: true, + autopilotClaimEnabledForHost: true, + destinationChannelID: ibctesting.FirstChannelID, + destinationPortID: transfertypes.PortID, packetData: transfertypes.FungibleTokenPacketData{ Receiver: strings.Repeat("X", 300), Memo: "", @@ -215,22 +263,79 @@ func (s *KeeperTestSuite) TestAirdropOnRecvPacket() { transferShouldSucceed: false, airdropShouldUpdate: false, }, + { + name: "not transfer channel", + autopilotClaimActive: true, + autopilotClaimEnabledForHost: true, + destinationChannelID: ibctesting.FirstChannelID, + destinationPortID: "invalid_port", + packetData: transfertypes.FungibleTokenPacketData{ + Receiver: getClaimPacketMetadata(strideAddress), + Memo: "", + }, + transferShouldSucceed: false, + airdropShouldUpdate: false, + }, + { + name: "transfer channel from a different host zone", + autopilotClaimActive: true, + autopilotClaimEnabledForHost: true, + destinationChannelID: channelIdForDifferentHostZone, + destinationPortID: transfertypes.PortID, + packetData: transfertypes.FungibleTokenPacketData{ + Receiver: getClaimPacketMetadata(strideAddress), + Memo: "", + }, + transferShouldSucceed: false, + airdropShouldUpdate: false, + }, + { + name: "transfer channel does not exist", + autopilotClaimActive: true, + autopilotClaimEnabledForHost: true, + destinationChannelID: "channel-XXX", + destinationPortID: transfertypes.PortID, + packetData: transfertypes.FungibleTokenPacketData{ + Receiver: getClaimPacketMetadata(strideAddress), + Memo: "", + }, + transferShouldSucceed: false, + airdropShouldUpdate: false, + }, } for i, tc := range testCases { - s.Run(fmt.Sprintf("Case %d", i), func() { + s.Run(fmt.Sprintf("Case %d_%s", i, tc.name), func() { s.SetupTest() // Update the autopilot active flag - s.App.AutopilotKeeper.SetParams(s.Ctx, types.Params{ClaimActive: tc.forwardingActive}) + s.App.AutopilotKeeper.SetParams(s.Ctx, types.Params{ + ClaimActive: tc.autopilotClaimActive, + }) // Set evmos airdrop airdrops := claimtypes.Params{ - Airdrops: []*claimtypes.Airdrop{{AirdropIdentifier: evmosAirdropId}}, + Airdrops: []*claimtypes.Airdrop{{ + AirdropIdentifier: evmosAirdropId, + ChainId: EvmosChainId, + AutopilotEnabled: tc.autopilotClaimEnabledForHost, + }}, } err := s.App.ClaimKeeper.SetParams(s.Ctx, airdrops) s.Require().NoError(err, "no error expected when setting airdrop params") + // Store the host zone so that we can verify the channel + s.App.StakeibcKeeper.SetHostZone(s.Ctx, stakeibctypes.HostZone{ + ChainId: EvmosChainId, + TransferChannelId: ibctesting.FirstChannelID, + }) + + // Store a second host zone that does not have an airdrop + s.App.StakeibcKeeper.SetHostZone(s.Ctx, stakeibctypes.HostZone{ + ChainId: "differnet_host_zone", + TransferChannelId: channelIdForDifferentHostZone, + }) + // Set claim records using key'd address oldClaimRecord := claimtypes.ClaimRecord{ AirdropIdentifier: evmosAirdropId, @@ -255,8 +360,14 @@ func (s *KeeperTestSuite) TestAirdropOnRecvPacket() { packetData.Receiver = tc.packetData.Receiver packet := packetTemplate + packet.DestinationChannel = tc.destinationChannelID + packet.DestinationPort = tc.destinationPortID packet.Data = transfertypes.ModuleCdc.MustMarshalJSON(&packetData) + // Build the evmos denom from the packet + prefixedDenom := transfertypes.GetPrefixedDenom(packet.DestinationPort, packet.DestinationChannel, evmosDenom) + evmosIbcDenom := transfertypes.ParseDenomTrace(prefixedDenom).IBCDenom() + // Call OnRecvPacket for autopilot ack := autopilotStack.OnRecvPacket( s.Ctx, diff --git a/x/autopilot/module_ibc.go b/x/autopilot/module_ibc.go index 11adcb70ed..ca3d072823 100644 --- a/x/autopilot/module_ibc.go +++ b/x/autopilot/module_ibc.go @@ -212,7 +212,7 @@ func (im IBCModule) OnRecvPacket( } im.keeper.Logger(ctx).Info(fmt.Sprintf("Forwaring packet from %s to claim", newData.Sender)) - if err := im.keeper.TryUpdateAirdropClaim(ctx, newData, routingInfo); err != nil { + if err := im.keeper.TryUpdateAirdropClaim(ctx, packet, newData, routingInfo); err != nil { im.keeper.Logger(ctx).Error(fmt.Sprintf("Error updating airdrop claim from autopilot for %s: %s", newData.Sender, err.Error())) return channeltypes.NewErrorAcknowledgement(err) } diff --git a/x/autopilot/types/parser.go b/x/autopilot/types/parser.go index 06386968ab..20cb86ec35 100644 --- a/x/autopilot/types/parser.go +++ b/x/autopilot/types/parser.go @@ -2,7 +2,6 @@ package types import ( "encoding/json" - "strings" errorsmod "cosmossdk.io/errors" @@ -34,7 +33,6 @@ type StakeibcPacketMetadata struct { // Packet metadata info specific to Claim (e.g. airdrops for non-118 coins) type ClaimPacketMetadata struct { - AirdropId string `json:"airdrop_id"` StrideAddress string `json:"stride_address"` } @@ -52,16 +50,12 @@ func (m StakeibcPacketMetadata) Validate() error { return nil } -// Validate claim packet metadata fields including the -// stride address and Airdrop type +// Validate claim packet metadata includes the stride address func (m ClaimPacketMetadata) Validate() error { _, err := sdk.AccAddressFromBech32(m.StrideAddress) if err != nil { return err } - if len(strings.TrimSpace(m.AirdropId)) == 0 { - return ErrInvalidClaimAirdropId - } return nil } diff --git a/x/autopilot/types/parser_test.go b/x/autopilot/types/parser_test.go index a8c640f656..acc5227cef 100644 --- a/x/autopilot/types/parser_test.go +++ b/x/autopilot/types/parser_test.go @@ -24,25 +24,25 @@ func getStakeibcMemo(address, action string) string { }`, address, action) } -func getClaimMemo(address, airdropId string) string { +func getClaimMemo(address string) string { return fmt.Sprintf(` { "autopilot": { "receiver": "%[1]s", - "claim": { "stride_address": "%[1]s", "airdrop_id": "%[2]s" } + "claim": { "stride_address": "%[1]s" } } - }`, address, airdropId) + }`, address) } -func getClaimAndStakeibcMemo(address, action, airdropId string) string { +func getClaimAndStakeibcMemo(address, action string) string { return fmt.Sprintf(` { "autopilot": { "receiver": "%[1]s", "stakeibc": { "stride_address": "%[1]s", "action": "%[2]s" }, - "claim": { "stride_address": "%[1]s", "airdrop_id": "%[3]s" } + "claim": { "stride_address": "%[1]s" } } - }`, address, action, airdropId) + }`, address, action) } // Helper function to check the routingInfo with a switch statement @@ -62,7 +62,6 @@ func checkModuleRoutingInfoType(routingInfo types.ModuleRoutingInfo, expectedTyp func TestParsePacketMetadata(t *testing.T) { validAddress, invalidAddress := apptesting.GenerateTestAddrs() validStakeibcAction := "LiquidStake" - validAirdropId := "gaia" validParsedStakeibcPacketMetadata := types.StakeibcPacketMetadata{ StrideAddress: validAddress, @@ -71,7 +70,6 @@ func TestParsePacketMetadata(t *testing.T) { validParsedClaimPacketMetadata := types.ClaimPacketMetadata{ StrideAddress: validAddress, - AirdropId: validAirdropId, } testCases := []struct { @@ -89,7 +87,7 @@ func TestParsePacketMetadata(t *testing.T) { }, { name: "valid claim memo", - metadata: getClaimMemo(validAddress, validAirdropId), + metadata: getClaimMemo(validAddress), parsedClaim: &validParsedClaimPacketMetadata, }, { @@ -134,17 +132,12 @@ func TestParsePacketMetadata(t *testing.T) { }, { name: "invalid claim address", - metadata: getClaimMemo(invalidAddress, validAirdropId), + metadata: getClaimMemo(invalidAddress), expectedErr: "receiver address must be specified when using autopilot", }, - { - name: "invalid claim airdrop", - metadata: getClaimMemo(validAddress, ""), - expectedErr: "invalid claim airdrop ID", - }, { name: "both claim and stakeibc memo set", - metadata: getClaimAndStakeibcMemo(validAddress, validStakeibcAction, validAirdropId), + metadata: getClaimAndStakeibcMemo(validAddress, validStakeibcAction), expectedErr: "invalid number of module routes", }, } @@ -226,7 +219,6 @@ func TestValidateStakeibcPacketMetadata(t *testing.T) { func TestValidateClaimPacketMetadata(t *testing.T) { validAddress, _ := apptesting.GenerateTestAddrs() - validAirdropId := "gaia" testCases := []struct { name string @@ -237,25 +229,15 @@ func TestValidateClaimPacketMetadata(t *testing.T) { name: "valid metadata", metadata: &types.ClaimPacketMetadata{ StrideAddress: validAddress, - AirdropId: validAirdropId, }, }, { name: "invalid address", metadata: &types.ClaimPacketMetadata{ StrideAddress: "bad_address", - AirdropId: validAirdropId, }, expectedErr: "decoding bech32 failed", }, - { - name: "invalid airdrop-id", - metadata: &types.ClaimPacketMetadata{ - StrideAddress: validAddress, - AirdropId: "", - }, - expectedErr: "invalid claim airdrop ID", - }, } for _, tc := range testCases { diff --git a/x/claim/client/cli/cli_test.go b/x/claim/client/cli/cli_test.go index 4105c24ed3..99b66524b1 100644 --- a/x/claim/client/cli/cli_test.go +++ b/x/claim/client/cli/cli_test.go @@ -106,14 +106,19 @@ func (s *IntegrationTestSuite) SetupSuite() { cmd := cli.CmdCreateAirdrop() clientCtx := val.ClientCtx + strideChainId := "stride-1" + autopilotEnabled := "false" + _, err = clitestutil.ExecTestCLICmd(clientCtx, cmd, []string{ claimtypes.DefaultAirdropIdentifier, + strideChainId, + s.cfg.BondDenom, strconv.Itoa(int(time.Now().Unix())), strconv.Itoa(int(claimtypes.DefaultAirdropDuration.Seconds())), - s.cfg.BondDenom, + autopilotEnabled, + // common args fmt.Sprintf("--%s=json", tmcli.OutputFlag), fmt.Sprintf("--%s=%s", flags.FlagFrom, distributorAddrs[0]), - // common args fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), strideclitestutil.DefaultFeeString(s.cfg), @@ -249,11 +254,13 @@ func (s *IntegrationTestSuite) TestCmdTxCreateAirdrop() { val := s.network.Validators[0] airdrop := claimtypes.Airdrop{ - AirdropIdentifier: "stride-1", + AirdropIdentifier: "evmos", + ChainId: "evmos-1", AirdropStartTime: time.Now(), AirdropDuration: claimtypes.DefaultAirdropDuration, DistributorAddress: distributorAddrs[1], ClaimDenom: claimtypes.DefaultClaimDenom, + AutopilotEnabled: true, } testCases := []struct { @@ -264,13 +271,15 @@ func (s *IntegrationTestSuite) TestCmdTxCreateAirdrop() { { "create-airdrop tx", []string{ - "stride-1", - strconv.Itoa(int(time.Now().Unix())), - strconv.Itoa(int(claimtypes.DefaultAirdropDuration.Seconds())), - s.cfg.BondDenom, + airdrop.AirdropIdentifier, + airdrop.ChainId, + airdrop.ClaimDenom, + strconv.Itoa(int(time.Now().Unix())), // start time + strconv.Itoa(int(claimtypes.DefaultAirdropDuration.Seconds())), // duration + fmt.Sprintf("%v", airdrop.AutopilotEnabled), + // common args fmt.Sprintf("--%s=json", tmcli.OutputFlag), fmt.Sprintf("--%s=%s", flags.FlagFrom, distributorAddrs[1]), - // common args fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), strideclitestutil.DefaultFeeString(s.cfg), diff --git a/x/claim/client/cli/tx_create_airdrop.go b/x/claim/client/cli/tx_create_airdrop.go index fc72524f86..3823a49668 100644 --- a/x/claim/client/cli/tx_create_airdrop.go +++ b/x/claim/client/cli/tx_create_airdrop.go @@ -13,16 +13,22 @@ import ( func CmdCreateAirdrop() *cobra.Command { cmd := &cobra.Command{ - Use: "create-airdrop [identifier] [start] [duration] [denom]", + Use: "create-airdrop [identifier] [chain-id] [denom] [start] [duration] [autopilot-enabled]", Short: "Broadcast message create-airdrop", - Args: cobra.ExactArgs(4), + Args: cobra.ExactArgs(6), RunE: func(cmd *cobra.Command, args []string) (err error) { - argStartTime, err := strconv.Atoi(args[1]) + identifier := args[0] + chainId := args[1] + denom := args[2] + argStartTime, err := strconv.Atoi(args[3]) if err != nil { return err } - - argDuration, err := strconv.Atoi(args[2]) + argDuration, err := strconv.Atoi(args[4]) + if err != nil { + return err + } + autopilotEnabled, err := strconv.ParseBool(args[5]) if err != nil { return err } @@ -31,12 +37,15 @@ func CmdCreateAirdrop() *cobra.Command { if err != nil { return err } + distributor := clientCtx.GetFromAddress().String() msg := types.NewMsgCreateAirdrop( - clientCtx.GetFromAddress().String(), - args[0], + distributor, + identifier, + chainId, + denom, uint64(argStartTime), uint64(argDuration), - args[3], + autopilotEnabled, ) if err := msg.ValidateBasic(); err != nil { diff --git a/x/claim/keeper/claim.go b/x/claim/keeper/claim.go index 168a4a8d83..c2521f34a8 100644 --- a/x/claim/keeper/claim.go +++ b/x/claim/keeper/claim.go @@ -146,6 +146,26 @@ func (k Keeper) GetAirdropByIdentifier(ctx sdk.Context, airdropIdentifier string return nil } +// Get airdrop by chain id +func (k Keeper) GetAirdropByChainId(ctx sdk.Context, chainId string) (airdrop *types.Airdrop, found bool) { + params, err := k.GetParams(ctx) + if err != nil { + panic(err) + } + + if chainId == "" { + return nil, false + } + + for _, airdrop := range params.Airdrops { + if airdrop.ChainId == chainId { + return airdrop, true + } + } + + return nil, false +} + func (k Keeper) GetAirdropIds(ctx sdk.Context) []string { params, err := k.GetParams(ctx) if err != nil { @@ -722,31 +742,41 @@ func (k Keeper) ClaimCoinsForAction(ctx sdk.Context, addr sdk.AccAddress, action } // CreateAirdropAndEpoch creates a new airdrop and epoch for that. -func (k Keeper) CreateAirdropAndEpoch(ctx sdk.Context, distributor string, denom string, startTime uint64, duration uint64, identifier string) error { +func (k Keeper) CreateAirdropAndEpoch(ctx sdk.Context, msg types.MsgCreateAirdrop) error { params, err := k.GetParams(ctx) if err != nil { panic(err) } + // re-run validate basic in case this function is called directly from an upgrade handler + if err := msg.ValidateBasic(); err != nil { + return err + } + for _, airdrop := range params.Airdrops { - if airdrop.AirdropIdentifier == identifier { + if airdrop.AirdropIdentifier == msg.Identifier { return types.ErrAirdropAlreadyExists } + if airdrop.ChainId == msg.ChainId { + return types.ErrAirdropChainIdAlreadyExists + } } airdrop := types.Airdrop{ - AirdropIdentifier: identifier, - AirdropDuration: time.Duration(duration * uint64(time.Second)), - ClaimDenom: denom, - DistributorAddress: distributor, - AirdropStartTime: time.Unix(int64(startTime), 0), + AirdropIdentifier: msg.Identifier, + ChainId: msg.ChainId, + AirdropDuration: time.Duration(msg.Duration * uint64(time.Second)), + ClaimDenom: msg.Denom, + DistributorAddress: msg.Distributor, + AirdropStartTime: time.Unix(int64(msg.StartTime), 0), + AutopilotEnabled: msg.AutopilotEnabled, } params.Airdrops = append(params.Airdrops, &airdrop) k.epochsKeeper.SetEpochInfo(ctx, epochstypes.EpochInfo{ - Identifier: fmt.Sprintf("airdrop-%s", identifier), + Identifier: fmt.Sprintf("airdrop-%s", msg.Identifier), StartTime: airdrop.AirdropStartTime.Add(time.Minute), - Duration: time.Hour * 24 * 30, + Duration: types.DefaultEpochDuration, CurrentEpoch: 0, CurrentEpochStartHeight: 0, CurrentEpochStartTime: time.Time{}, diff --git a/x/claim/keeper/claim_test.go b/x/claim/keeper/claim_test.go index d6a8c28019..cc5fd0ee98 100644 --- a/x/claim/keeper/claim_test.go +++ b/x/claim/keeper/claim_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "fmt" "strings" "time" @@ -821,3 +822,30 @@ func (suite *KeeperTestSuite) TestUpdateAirdropAddress_HostAddressIncorrect() { err = suite.app.ClaimKeeper.UpdateAirdropAddress(suite.ctx, randomStrideAddress, tc.strideAddress, tc.airdropId) suite.Require().Error(err, "airdrop address update should fail with not present host address") } + +func (suite *KeeperTestSuite) TestGetAirdropByChainId() { + // Store 5 airdrops + airdrops := []*types.Airdrop{} + for i := 0; i < 5; i++ { + airdropId := fmt.Sprintf("airdrop-%d", i) + chainId := fmt.Sprintf("chain-%d", i) + + airdrops = append(airdrops, &types.Airdrop{ + AirdropIdentifier: airdropId, + ChainId: chainId, + }) + } + err := suite.app.ClaimKeeper.SetParams(suite.ctx, types.Params{Airdrops: airdrops}) + suite.Require().NoError(err, "no error expected when setting airdrops") + + // Lookup each airdrop by chain-id + for i, expected := range airdrops { + actual, found := suite.app.ClaimKeeper.GetAirdropByChainId(suite.ctx, expected.ChainId) + suite.Require().True(found, "should have found airdrop %d", i) + suite.Require().Equal(expected.AirdropIdentifier, actual.AirdropIdentifier, "airdrop identifier for %d", i) + } + + // Lookup a non-existent airdrop - it should not be found + _, found := suite.app.ClaimKeeper.GetAirdropByChainId(suite.ctx, "fake_chain_id") + suite.Require().False(found, "fake_chain_id should not have been found") +} diff --git a/x/claim/keeper/keeper_test.go b/x/claim/keeper/keeper_test.go index 2b54dde619..d9df43a57e 100644 --- a/x/claim/keeper/keeper_test.go +++ b/x/claim/keeper/keeper_test.go @@ -50,6 +50,12 @@ func (suite *KeeperTestSuite) SetupTest() { suite.app.AccountKeeper.SetAccount(suite.ctx, authtypes.NewBaseAccount(addr3, nil, 0, 0)) distributors["osmosis"] = addr3 + // Initiate a distributor account for evmos user airdrop + pub4 := secp256k1.GenPrivKey().PubKey() + addr4 := sdk.AccAddress(pub4.Address()) + suite.app.AccountKeeper.SetAccount(suite.ctx, authtypes.NewBaseAccount(addr4, nil, 0, 0)) + distributors["evmos"] = addr4 + // Mint coins to airdrop module err := suite.app.BankKeeper.MintCoins(suite.ctx, minttypes.ModuleName, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(300000000)))) if err != nil { @@ -70,19 +76,41 @@ func (suite *KeeperTestSuite) SetupTest() { // Stride airdrop airdropStartTime := time.Now() - err = suite.app.ClaimKeeper.CreateAirdropAndEpoch(suite.ctx, addr1.String(), sdk.DefaultBondDenom, uint64(airdropStartTime.Unix()), uint64(types.DefaultAirdropDuration.Seconds()), types.DefaultAirdropIdentifier) + err = suite.app.ClaimKeeper.CreateAirdropAndEpoch(suite.ctx, types.MsgCreateAirdrop{ + Distributor: addr1.String(), + Identifier: types.DefaultAirdropIdentifier, + ChainId: "stride-1", + Denom: sdk.DefaultBondDenom, + StartTime: uint64(airdropStartTime.Unix()), + Duration: uint64(types.DefaultAirdropDuration.Seconds()), + AutopilotEnabled: false, + }) if err != nil { panic(err) } // Juno airdrop - err = suite.app.ClaimKeeper.CreateAirdropAndEpoch(suite.ctx, addr2.String(), sdk.DefaultBondDenom, uint64(airdropStartTime.Add(time.Hour).Unix()), uint64(types.DefaultAirdropDuration.Seconds()), "juno") + err = suite.app.ClaimKeeper.CreateAirdropAndEpoch(suite.ctx, types.MsgCreateAirdrop{ + Distributor: addr2.String(), + Identifier: "juno", + ChainId: "juno-1", + Denom: sdk.DefaultBondDenom, + StartTime: uint64(airdropStartTime.Add(time.Hour).Unix()), + Duration: uint64(types.DefaultAirdropDuration.Seconds()), + }) if err != nil { panic(err) } // Osmosis airdrop - err = suite.app.ClaimKeeper.CreateAirdropAndEpoch(suite.ctx, addr3.String(), sdk.DefaultBondDenom, uint64(airdropStartTime.Unix()), uint64(types.DefaultAirdropDuration.Seconds()), "osmosis") + err = suite.app.ClaimKeeper.CreateAirdropAndEpoch(suite.ctx, types.MsgCreateAirdrop{ + Distributor: addr3.String(), + Identifier: "osmosis", + ChainId: "osmosis-1", + Denom: sdk.DefaultBondDenom, + StartTime: uint64(airdropStartTime.Unix()), + Duration: uint64(types.DefaultAirdropDuration.Seconds()), + }) if err != nil { panic(err) } diff --git a/x/claim/keeper/msg_server.go b/x/claim/keeper/msg_server.go index f627f7190d..dafa8360c5 100644 --- a/x/claim/keeper/msg_server.go +++ b/x/claim/keeper/msg_server.go @@ -96,7 +96,7 @@ func (server msgServer) CreateAirdrop(goCtx context.Context, msg *types.MsgCreat return nil, types.ErrDistributorAlreadyExists } - err = server.keeper.CreateAirdropAndEpoch(ctx, msg.Distributor, msg.Denom, msg.StartTime, msg.Duration, msg.Identifier) + err = server.keeper.CreateAirdropAndEpoch(ctx, *msg) if err != nil { return nil, err } diff --git a/x/claim/keeper/msg_server_test.go b/x/claim/keeper/msg_server_test.go index 6dcb0973b9..4980213fb6 100644 --- a/x/claim/keeper/msg_server_test.go +++ b/x/claim/keeper/msg_server_test.go @@ -1,8 +1,6 @@ package keeper_test import ( - "time" - "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" sdk "github.com/cosmos/cosmos-sdk/types" @@ -119,17 +117,56 @@ func (suite *KeeperTestSuite) TestSetAirdropAllocationsForMultiAirdrops() { suite.Require().Equal(3, len(claimRecords)) } -func (suite *KeeperTestSuite) TestCreateAirdrop() { +func getValidCreateEvmosAirdropMsg(ctx sdk.Context) types.MsgCreateAirdrop { + return types.MsgCreateAirdrop{ + Distributor: distributors["evmos"].String(), + Identifier: "evmos", + ChainId: "evmos-1", + Denom: sdk.DefaultBondDenom, + StartTime: uint64(ctx.BlockTime().Unix()), + Duration: uint64(types.DefaultAirdropDuration.Seconds()), + AutopilotEnabled: true, + } +} + +func (suite *KeeperTestSuite) TestCreateAirdrop_Successful() { + suite.SetupTest() + msgServer := keeper.NewMsgServerImpl(suite.app.ClaimKeeper) + + // Successfully create a new airdrop + validMsg := getValidCreateEvmosAirdropMsg(suite.ctx) + _, err := msgServer.CreateAirdrop(sdk.WrapSDKContext(suite.ctx), &validMsg) + suite.Require().NoError(err, "no error expected when adding evmos airdrop") + + // Check that it matches the evmos airdrop + airdrop := suite.app.ClaimKeeper.GetAirdropByIdentifier(suite.ctx, "evmos") + suite.Require().Equal("evmos", airdrop.AirdropIdentifier, "airdrop identifier") + suite.Require().Equal("evmos-1", airdrop.ChainId, "airdrop chain-id") + suite.Require().Equal(true, airdrop.AutopilotEnabled, "airdrop autopilot enabled") +} + +func (suite *KeeperTestSuite) TestCreateAirdrop_IdentifierAlreadyExists() { + suite.SetupTest() + msgServer := keeper.NewMsgServerImpl(suite.app.ClaimKeeper) + + // Attempt to create an airdrop with an identifier that already exists, it should fail + validMsg := getValidCreateEvmosAirdropMsg(suite.ctx) + invalidMsg := validMsg + invalidMsg.Identifier = types.DefaultAirdropIdentifier + + _, err := msgServer.CreateAirdrop(sdk.WrapSDKContext(suite.ctx), &invalidMsg) + suite.Require().ErrorContains(err, "airdrop with same identifier already exists") +} + +func (suite *KeeperTestSuite) TestCreateAirdrop_ChainIdAlreadyExists() { suite.SetupTest() msgServer := keeper.NewMsgServerImpl(suite.app.ClaimKeeper) - _, err := msgServer.CreateAirdrop(sdk.WrapSDKContext(suite.ctx), &types.MsgCreateAirdrop{ - Distributor: distributors[types.DefaultAirdropIdentifier].String(), - Identifier: "stride-1", - StartTime: uint64(time.Now().Unix()), - Duration: uint64(time.Hour), - Denom: "stake", - }) + // Attempt to create an airdrop with a chain-id that already exists, it should fail + validMsg := getValidCreateEvmosAirdropMsg(suite.ctx) + invalidMsg := validMsg + invalidMsg.ChainId = "stride-1" - suite.Require().Error(err) + _, err := msgServer.CreateAirdrop(sdk.WrapSDKContext(suite.ctx), &invalidMsg) + suite.Require().ErrorContains(err, "airdrop with same chain-id already exists") } diff --git a/x/claim/types/errors.go b/x/claim/types/errors.go index a095a88880..ad52edaa3d 100644 --- a/x/claim/types/errors.go +++ b/x/claim/types/errors.go @@ -28,4 +28,6 @@ var ( "the claim record was not found") ErrModifyingClaimRecord = errorsmod.Register(ModuleName, 1112, "failed to modify claim record") + ErrAirdropChainIdAlreadyExists = errorsmod.Register(ModuleName, 1113, + "airdrop with same chain-id already exists") ) diff --git a/x/claim/types/msgs.go b/x/claim/types/msgs.go index 36fb0cc259..de5ebff25e 100644 --- a/x/claim/types/msgs.go +++ b/x/claim/types/msgs.go @@ -133,13 +133,15 @@ const TypeMsgCreateAirdrop = "create_airdrop" var _ sdk.Msg = &MsgCreateAirdrop{} -func NewMsgCreateAirdrop(distributor string, identifier string, startTime uint64, duration uint64, denom string) *MsgCreateAirdrop { +func NewMsgCreateAirdrop(distributor, identifier, chainId, denom string, startTime, duration uint64, autopilotEnabled bool) *MsgCreateAirdrop { return &MsgCreateAirdrop{ - Distributor: distributor, - Identifier: identifier, - StartTime: startTime, - Duration: duration, - Denom: denom, + Distributor: distributor, + Identifier: identifier, + ChainId: chainId, + Denom: denom, + StartTime: startTime, + Duration: duration, + AutopilotEnabled: autopilotEnabled, } } @@ -174,6 +176,14 @@ func (msg *MsgCreateAirdrop) ValidateBasic() error { return errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "airdrop identifier not set") } + if msg.ChainId == "" { + return errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "airdrop chain-id not set") + } + + if msg.Denom == "" { + return errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "airdrop denom not set") + } + if msg.StartTime == 0 { return errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "airdrop start time not set") } diff --git a/x/claim/types/params.go b/x/claim/types/params.go index 0567433149..c89b0f0a2c 100644 --- a/x/claim/types/params.go +++ b/x/claim/types/params.go @@ -6,6 +6,7 @@ import ( var ( DefaultClaimDenom = "ustrd" + DefaultEpochDuration = time.Hour * 24 * 30 // 1 month DefaultAirdropDuration = time.Hour * 24 * 30 * 12 * 3 // 3 years DefaultVestingDurationForDelegateStake = time.Hour * 24 * 30 * 3 // 3 months DefaultVestingDurationForLiquidStake = time.Hour * 24 * 30 * 3 // 3 months diff --git a/x/claim/types/params.pb.go b/x/claim/types/params.pb.go index 7eb0b29cc5..645c4fd1e3 100644 --- a/x/claim/types/params.pb.go +++ b/x/claim/types/params.pb.go @@ -75,6 +75,7 @@ func (m *Params) GetAirdrops() []*Airdrop { type Airdrop struct { AirdropIdentifier string `protobuf:"bytes,1,opt,name=airdrop_identifier,json=airdropIdentifier,proto3" json:"airdrop_identifier,omitempty" yaml:"airdrop_identifier"` + ChainId string `protobuf:"bytes,7,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` // seconds AirdropStartTime time.Time `protobuf:"bytes,2,opt,name=airdrop_start_time,json=airdropStartTime,proto3,stdtime" json:"airdrop_start_time" yaml:"airdrop_start_time"` // seconds @@ -85,6 +86,8 @@ type Airdrop struct { DistributorAddress string `protobuf:"bytes,5,opt,name=distributor_address,json=distributorAddress,proto3" json:"distributor_address,omitempty"` // ustrd tokens claimed so far in the current period ClaimedSoFar github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,6,opt,name=claimed_so_far,json=claimedSoFar,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"claimed_so_far"` + // indicates the airdrop should be claimed via autopilot + AutopilotEnabled bool `protobuf:"varint,8,opt,name=autopilot_enabled,json=autopilotEnabled,proto3" json:"autopilot_enabled,omitempty"` } func (m *Airdrop) Reset() { *m = Airdrop{} } @@ -127,6 +130,13 @@ func (m *Airdrop) GetAirdropIdentifier() string { return "" } +func (m *Airdrop) GetChainId() string { + if m != nil { + return m.ChainId + } + return "" +} + func (m *Airdrop) GetAirdropStartTime() time.Time { if m != nil { return m.AirdropStartTime @@ -155,6 +165,13 @@ func (m *Airdrop) GetDistributorAddress() string { return "" } +func (m *Airdrop) GetAutopilotEnabled() bool { + if m != nil { + return m.AutopilotEnabled + } + return false +} + func init() { proto.RegisterType((*Params)(nil), "stride.claim.Params") proto.RegisterType((*Airdrop)(nil), "stride.claim.Airdrop") @@ -163,37 +180,40 @@ func init() { func init() { proto.RegisterFile("stride/claim/params.proto", fileDescriptor_dd7ac871d3875dc3) } var fileDescriptor_dd7ac871d3875dc3 = []byte{ - // 472 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x52, 0x3f, 0x8f, 0x13, 0x3f, - 0x10, 0xcd, 0xfe, 0xf2, 0x23, 0x80, 0x73, 0xe2, 0x8f, 0x01, 0xb1, 0x89, 0xc4, 0x6e, 0xb4, 0x12, - 0x28, 0x05, 0x67, 0x8b, 0xa3, 0x41, 0x50, 0x25, 0x3a, 0x21, 0x4e, 0xba, 0x02, 0x6d, 0xae, 0xa2, - 0x59, 0x79, 0x63, 0x67, 0xb1, 0x88, 0xe3, 0x95, 0xed, 0x20, 0xf2, 0x09, 0x68, 0xaf, 0xe4, 0x23, - 0x5d, 0x79, 0x25, 0xa2, 0x58, 0x50, 0xd2, 0x51, 0x5e, 0x49, 0x85, 0xec, 0xf5, 0xde, 0x85, 0x4b, - 0x95, 0xec, 0x7b, 0x6f, 0xde, 0xbc, 0x19, 0x0f, 0xe8, 0x69, 0xa3, 0x38, 0x65, 0x78, 0x3a, 0x27, - 0x5c, 0xe0, 0x92, 0x28, 0x22, 0x34, 0x2a, 0x95, 0x34, 0x12, 0xee, 0xd5, 0x14, 0x72, 0x54, 0xff, - 0x61, 0x21, 0x0b, 0xe9, 0x08, 0x6c, 0xff, 0xd5, 0x9a, 0x7e, 0x54, 0x48, 0x59, 0xcc, 0x19, 0x76, - 0x5f, 0xf9, 0x72, 0x86, 0xe9, 0x52, 0x11, 0xc3, 0xe5, 0xc2, 0xf3, 0xf1, 0x75, 0xde, 0x70, 0xc1, - 0xb4, 0x21, 0xa2, 0xac, 0x05, 0xc9, 0x1b, 0xd0, 0x79, 0xef, 0x9a, 0xc2, 0x17, 0xe0, 0x16, 0xe1, - 0x8a, 0x2a, 0x59, 0xea, 0x30, 0x18, 0xb4, 0x87, 0xdd, 0x83, 0x47, 0x68, 0x3b, 0x01, 0x1a, 0xd5, - 0x6c, 0x7a, 0x29, 0x4b, 0xfe, 0xb4, 0xc1, 0x4d, 0x8f, 0xc2, 0x63, 0x00, 0x3d, 0x9e, 0x71, 0xca, - 0x16, 0x86, 0xcf, 0x38, 0x53, 0x61, 0x30, 0x08, 0x86, 0xb7, 0xc7, 0x4f, 0x2e, 0xaa, 0xb8, 0xb7, - 0x22, 0x62, 0xfe, 0x3a, 0xd9, 0xd5, 0x24, 0xe9, 0x7d, 0x0f, 0x1e, 0x5d, 0x62, 0x50, 0x5e, 0xb9, - 0x69, 0x43, 0x94, 0xc9, 0x6c, 0xee, 0xf0, 0xbf, 0x41, 0x30, 0xec, 0x1e, 0xf4, 0x51, 0x3d, 0x14, - 0x6a, 0x86, 0x42, 0x27, 0xcd, 0x50, 0xe3, 0xa7, 0x67, 0x55, 0xdc, 0xda, 0xed, 0x76, 0xe5, 0x91, - 0x9c, 0xfe, 0x8c, 0x83, 0xf4, 0x9e, 0x27, 0x26, 0x16, 0xb7, 0xd5, 0xf0, 0x6b, 0x00, 0x1a, 0x30, - 0x6b, 0x76, 0x18, 0xb6, 0x5d, 0xbf, 0xde, 0x4e, 0xbf, 0x43, 0x2f, 0x18, 0x8f, 0x6c, 0xbb, 0xdf, - 0x55, 0xdc, 0xbf, 0x5e, 0xfa, 0x5c, 0x0a, 0x6e, 0x98, 0x28, 0xcd, 0xea, 0xa2, 0x8a, 0x1f, 0xff, - 0x1b, 0xa6, 0xd1, 0x24, 0xdf, 0x6c, 0x94, 0xbb, 0x1e, 0x6e, 0x3c, 0x61, 0x0c, 0xba, 0x6e, 0xdf, - 0x19, 0x65, 0x0b, 0x29, 0xc2, 0xff, 0xed, 0x06, 0x53, 0xe0, 0xa0, 0x43, 0x8b, 0x40, 0x0c, 0x1e, - 0x50, 0x6e, 0x5f, 0x26, 0x5f, 0x1a, 0xa9, 0x32, 0x42, 0xa9, 0x62, 0x5a, 0x87, 0x37, 0x9c, 0x10, - 0x6e, 0x51, 0xa3, 0x9a, 0x81, 0x27, 0xe0, 0x8e, 0x2b, 0x67, 0x34, 0xd3, 0x32, 0x9b, 0x11, 0x15, - 0x76, 0xdc, 0xb3, 0x20, 0x9b, 0xfe, 0x47, 0x15, 0x3f, 0x2b, 0xb8, 0xf9, 0xb8, 0xcc, 0xd1, 0x54, - 0x0a, 0x3c, 0x95, 0x5a, 0x48, 0xed, 0x7f, 0xf6, 0x35, 0xfd, 0x84, 0xcd, 0xaa, 0x64, 0x1a, 0x1d, - 0x2d, 0x4c, 0xba, 0xe7, 0x5d, 0x26, 0xf2, 0x2d, 0x51, 0xe3, 0x77, 0x67, 0xeb, 0x28, 0x38, 0x5f, - 0x47, 0xc1, 0xaf, 0x75, 0x14, 0x9c, 0x6e, 0xa2, 0xd6, 0xf9, 0x26, 0x6a, 0x7d, 0xdf, 0x44, 0xad, - 0x0f, 0x68, 0xcb, 0x6f, 0xe2, 0x2e, 0x68, 0xff, 0x98, 0xe4, 0x1a, 0xfb, 0x53, 0xff, 0xfc, 0x0a, - 0x7f, 0xf1, 0xf7, 0xee, 0xbc, 0xf3, 0x8e, 0x5b, 0xec, 0xcb, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, - 0xd5, 0x53, 0xc7, 0xfd, 0x0c, 0x03, 0x00, 0x00, + // 516 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x53, 0x41, 0x6f, 0xd3, 0x30, + 0x18, 0x6d, 0xd8, 0x68, 0x8b, 0x3b, 0xc1, 0x66, 0x40, 0xa4, 0x95, 0x48, 0xaa, 0x48, 0xa0, 0x4a, + 0x30, 0x47, 0x8c, 0x0b, 0x82, 0x53, 0xab, 0x81, 0xa8, 0xb4, 0x03, 0x4a, 0x77, 0xe2, 0x12, 0x39, + 0xb5, 0xdb, 0x59, 0x34, 0x71, 0x64, 0xbb, 0x88, 0xfe, 0x02, 0xae, 0x3b, 0xf2, 0x7b, 0x38, 0xed, + 0xb8, 0x23, 0xe2, 0x10, 0x50, 0x7b, 0xe3, 0xb8, 0x5f, 0x80, 0xec, 0x38, 0x5d, 0xb7, 0x9e, 0x5a, + 0xbf, 0xf7, 0xbe, 0xef, 0xbd, 0xcf, 0x5f, 0x0c, 0xda, 0x52, 0x09, 0x46, 0x68, 0x38, 0x9e, 0x61, + 0x96, 0x86, 0x39, 0x16, 0x38, 0x95, 0x28, 0x17, 0x5c, 0x71, 0xb8, 0x57, 0x52, 0xc8, 0x50, 0x9d, + 0x47, 0x53, 0x3e, 0xe5, 0x86, 0x08, 0xf5, 0xbf, 0x52, 0xd3, 0xf1, 0xa6, 0x9c, 0x4f, 0x67, 0x34, + 0x34, 0xa7, 0x64, 0x3e, 0x09, 0xc9, 0x5c, 0x60, 0xc5, 0x78, 0x66, 0x79, 0xff, 0x36, 0xaf, 0x58, + 0x4a, 0xa5, 0xc2, 0x69, 0x5e, 0x0a, 0x82, 0x77, 0xa0, 0xfe, 0xc9, 0x98, 0xc2, 0x57, 0xa0, 0x89, + 0x99, 0x20, 0x82, 0xe7, 0xd2, 0x75, 0xba, 0x3b, 0xbd, 0xd6, 0xd1, 0x63, 0xb4, 0x99, 0x00, 0xf5, + 0x4b, 0x36, 0x5a, 0xcb, 0x82, 0x9f, 0xbb, 0xa0, 0x61, 0x51, 0x78, 0x02, 0xa0, 0xc5, 0x63, 0x46, + 0x68, 0xa6, 0xd8, 0x84, 0x51, 0xe1, 0x3a, 0x5d, 0xa7, 0x77, 0x6f, 0xf0, 0xf4, 0xaa, 0xf0, 0xdb, + 0x0b, 0x9c, 0xce, 0xde, 0x06, 0xdb, 0x9a, 0x20, 0x3a, 0xb0, 0xe0, 0x70, 0x8d, 0xc1, 0x36, 0x68, + 0x8e, 0xcf, 0x30, 0xcb, 0x62, 0x46, 0xdc, 0x86, 0xee, 0x11, 0x35, 0xcc, 0x79, 0x48, 0x20, 0xbf, + 0x36, 0x92, 0x0a, 0x0b, 0x15, 0xeb, 0x91, 0xdc, 0x3b, 0x5d, 0xa7, 0xd7, 0x3a, 0xea, 0xa0, 0x72, + 0x5e, 0x54, 0xcd, 0x8b, 0x4e, 0xab, 0x79, 0x07, 0xcf, 0x2e, 0x0a, 0xbf, 0xb6, 0x1d, 0xe4, 0xba, + 0x47, 0x70, 0xfe, 0xc7, 0x77, 0xa2, 0x7d, 0x4b, 0x8c, 0x34, 0xae, 0xab, 0xe1, 0x77, 0x07, 0x54, + 0x60, 0x5c, 0x5d, 0xaf, 0xbb, 0x63, 0xfc, 0xda, 0x5b, 0x7e, 0xc7, 0x56, 0x30, 0xe8, 0x6b, 0xbb, + 0x7f, 0x85, 0xdf, 0xb9, 0x5d, 0xfa, 0x92, 0xa7, 0x4c, 0xd1, 0x34, 0x57, 0x8b, 0xab, 0xc2, 0x7f, + 0x72, 0x33, 0x4c, 0xa5, 0x09, 0x7e, 0xe8, 0x28, 0x0f, 0x2c, 0x5c, 0xf5, 0x84, 0x3e, 0x68, 0x99, + 0x55, 0xc4, 0x84, 0x66, 0x3c, 0x75, 0x77, 0xcd, 0xc5, 0x00, 0x03, 0x1d, 0x6b, 0x04, 0x86, 0xe0, + 0x21, 0x61, 0x7a, 0x69, 0xc9, 0x5c, 0x71, 0x11, 0x63, 0x42, 0x04, 0x95, 0xd2, 0xbd, 0x6b, 0x84, + 0x70, 0x83, 0xea, 0x97, 0x0c, 0x3c, 0x05, 0xf7, 0x4d, 0x39, 0x25, 0xb1, 0xe4, 0xf1, 0x04, 0x0b, + 0xb7, 0x6e, 0x36, 0x86, 0x74, 0xfa, 0xdf, 0x85, 0xff, 0x7c, 0xca, 0xd4, 0xd9, 0x3c, 0x41, 0x63, + 0x9e, 0x86, 0x63, 0x2e, 0x53, 0x2e, 0xed, 0xcf, 0xa1, 0x24, 0x5f, 0x42, 0xb5, 0xc8, 0xa9, 0x44, + 0xc3, 0x4c, 0x45, 0x7b, 0xb6, 0xcb, 0x88, 0x7f, 0xc0, 0x02, 0xbe, 0x00, 0x07, 0x78, 0xae, 0x78, + 0xce, 0x66, 0x5c, 0xc5, 0x34, 0xc3, 0xc9, 0x8c, 0x12, 0xb7, 0xd9, 0x75, 0x7a, 0xcd, 0x68, 0x7f, + 0x4d, 0xbc, 0x2f, 0xf1, 0xc1, 0xc7, 0x8b, 0xa5, 0xe7, 0x5c, 0x2e, 0x3d, 0xe7, 0xef, 0xd2, 0x73, + 0xce, 0x57, 0x5e, 0xed, 0x72, 0xe5, 0xd5, 0x7e, 0xad, 0xbc, 0xda, 0x67, 0xb4, 0x61, 0x3e, 0x32, + 0x5f, 0xe2, 0xe1, 0x09, 0x4e, 0x64, 0x68, 0x9f, 0xcc, 0xd7, 0x37, 0xe1, 0x37, 0xfb, 0x6e, 0x4c, + 0x90, 0xa4, 0x6e, 0xb6, 0xf0, 0xfa, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0x5c, 0xbf, 0x6f, + 0x54, 0x03, 0x00, 0x00, } func (m *Params) Marshal() (dAtA []byte, err error) { @@ -253,6 +273,23 @@ func (m *Airdrop) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.AutopilotEnabled { + i-- + if m.AutopilotEnabled { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x40 + } + if len(m.ChainId) > 0 { + i -= len(m.ChainId) + copy(dAtA[i:], m.ChainId) + i = encodeVarintParams(dAtA, i, uint64(len(m.ChainId))) + i-- + dAtA[i] = 0x3a + } { size := m.ClaimedSoFar.Size() i -= size @@ -353,6 +390,13 @@ func (m *Airdrop) Size() (n int) { } l = m.ClaimedSoFar.Size() n += 1 + l + sovParams(uint64(l)) + l = len(m.ChainId) + if l > 0 { + n += 1 + l + sovParams(uint64(l)) + } + if m.AutopilotEnabled { + n += 2 + } return n } @@ -671,6 +715,58 @@ func (m *Airdrop) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChainId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AutopilotEnabled", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.AutopilotEnabled = bool(v != 0) default: iNdEx = preIndex skippy, err := skipParams(dAtA[iNdEx:]) diff --git a/x/claim/types/tx.pb.go b/x/claim/types/tx.pb.go index d039a9f1f3..6806cc04da 100644 --- a/x/claim/types/tx.pb.go +++ b/x/claim/types/tx.pb.go @@ -216,11 +216,13 @@ func (m *MsgClaimFreeAmountResponse) GetClaimedAmount() github_com_cosmos_cosmos } type MsgCreateAirdrop struct { - Distributor string `protobuf:"bytes,1,opt,name=distributor,proto3" json:"distributor,omitempty"` - Identifier string `protobuf:"bytes,2,opt,name=identifier,proto3" json:"identifier,omitempty"` - StartTime uint64 `protobuf:"varint,3,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` - Duration uint64 `protobuf:"varint,4,opt,name=duration,proto3" json:"duration,omitempty"` - Denom string `protobuf:"bytes,5,opt,name=denom,proto3" json:"denom,omitempty"` + Distributor string `protobuf:"bytes,1,opt,name=distributor,proto3" json:"distributor,omitempty"` + Identifier string `protobuf:"bytes,2,opt,name=identifier,proto3" json:"identifier,omitempty"` + ChainId string `protobuf:"bytes,6,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + Denom string `protobuf:"bytes,5,opt,name=denom,proto3" json:"denom,omitempty"` + StartTime uint64 `protobuf:"varint,3,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + Duration uint64 `protobuf:"varint,4,opt,name=duration,proto3" json:"duration,omitempty"` + AutopilotEnabled bool `protobuf:"varint,7,opt,name=autopilot_enabled,json=autopilotEnabled,proto3" json:"autopilot_enabled,omitempty"` } func (m *MsgCreateAirdrop) Reset() { *m = MsgCreateAirdrop{} } @@ -270,6 +272,20 @@ func (m *MsgCreateAirdrop) GetIdentifier() string { return "" } +func (m *MsgCreateAirdrop) GetChainId() string { + if m != nil { + return m.ChainId + } + return "" +} + +func (m *MsgCreateAirdrop) GetDenom() string { + if m != nil { + return m.Denom + } + return "" +} + func (m *MsgCreateAirdrop) GetStartTime() uint64 { if m != nil { return m.StartTime @@ -284,11 +300,11 @@ func (m *MsgCreateAirdrop) GetDuration() uint64 { return 0 } -func (m *MsgCreateAirdrop) GetDenom() string { +func (m *MsgCreateAirdrop) GetAutopilotEnabled() bool { if m != nil { - return m.Denom + return m.AutopilotEnabled } - return "" + return false } type MsgCreateAirdropResponse struct { @@ -429,45 +445,48 @@ func init() { func init() { proto.RegisterFile("stride/claim/tx.proto", fileDescriptor_9d435242bf328977) } var fileDescriptor_9d435242bf328977 = []byte{ - // 600 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0x4d, 0x6f, 0xd3, 0x30, - 0x18, 0x6e, 0x68, 0x07, 0xd4, 0x63, 0x03, 0xac, 0x4d, 0xca, 0x22, 0x96, 0x46, 0x39, 0x4c, 0xb9, - 0xcc, 0x61, 0xe3, 0x82, 0x38, 0xd1, 0x6e, 0x42, 0x20, 0xad, 0x97, 0x6c, 0x12, 0xd2, 0x24, 0x54, - 0xe5, 0xc3, 0x64, 0x16, 0x4d, 0x5c, 0xd9, 0xee, 0xd8, 0x7e, 0x01, 0xd7, 0xfd, 0x09, 0x2e, 0xfc, - 0x92, 0x1d, 0x77, 0x44, 0x1c, 0x0a, 0x6a, 0xff, 0xc1, 0x4e, 0x1c, 0x91, 0xed, 0xb4, 0x4b, 0x3f, - 0x06, 0x3b, 0x70, 0x4a, 0xfc, 0xbc, 0xaf, 0x1f, 0xbf, 0x8f, 0x9f, 0xf7, 0x35, 0x58, 0xe7, 0x82, - 0x91, 0x04, 0xfb, 0x71, 0x37, 0x24, 0x99, 0x2f, 0xce, 0x50, 0x8f, 0x51, 0x41, 0xe1, 0x23, 0x0d, - 0x23, 0x05, 0x5b, 0x6b, 0x29, 0x4d, 0xa9, 0x0a, 0xf8, 0xf2, 0x4f, 0xe7, 0x58, 0x76, 0x4c, 0x79, - 0x46, 0xb9, 0x1f, 0x85, 0x1c, 0xfb, 0xa7, 0x3b, 0x11, 0x16, 0xe1, 0x8e, 0x1f, 0x53, 0x92, 0xeb, - 0xb8, 0xfb, 0xdb, 0x00, 0x66, 0x9b, 0xa7, 0x87, 0x58, 0x34, 0x09, 0x4b, 0x18, 0xed, 0x35, 0xbb, - 0x5d, 0x1a, 0x87, 0x82, 0xd0, 0x9c, 0xc3, 0x67, 0xa0, 0x1e, 0xea, 0x25, 0x65, 0xa6, 0xe1, 0x18, - 0x5e, 0x3d, 0xb8, 0x01, 0xe0, 0x01, 0x80, 0xa1, 0xde, 0xd3, 0x21, 0x09, 0xce, 0x05, 0xf9, 0x48, - 0x30, 0x33, 0xef, 0xc9, 0xb4, 0xd6, 0xe6, 0xf5, 0xa0, 0xb1, 0x71, 0x1e, 0x66, 0xdd, 0x57, 0xee, - 0x7c, 0x8e, 0x1b, 0x3c, 0x2d, 0xc0, 0x77, 0x13, 0x0c, 0xae, 0x81, 0xa5, 0x3e, 0xc7, 0x8c, 0x9b, - 0x55, 0xa7, 0xea, 0xd5, 0x03, 0xbd, 0x80, 0xc7, 0xe0, 0xc1, 0x67, 0x4c, 0xd2, 0x13, 0xc1, 0xcd, - 0x9a, 0xc4, 0x5b, 0xaf, 0x2f, 0x07, 0x8d, 0xca, 0x8f, 0x41, 0x63, 0x2b, 0x25, 0xe2, 0xa4, 0x1f, - 0xa1, 0x98, 0x66, 0x7e, 0x21, 0x51, 0x7f, 0xb6, 0x79, 0xf2, 0xc9, 0x17, 0xe7, 0x3d, 0xcc, 0xd1, - 0x3e, 0x8e, 0xaf, 0x07, 0x8d, 0x55, 0x5d, 0x46, 0x41, 0xe3, 0x06, 0x63, 0x42, 0xd7, 0x05, 0xce, - 0x6d, 0xca, 0x03, 0xcc, 0x7b, 0x34, 0xe7, 0xd8, 0xf5, 0x00, 0x6c, 0xf3, 0x74, 0x4f, 0x5e, 0xf0, - 0x1b, 0x86, 0x71, 0x33, 0xa3, 0xfd, 0x5c, 0x40, 0x08, 0x6a, 0xb2, 0xbc, 0xe2, 0x4a, 0xd4, 0xbf, - 0x7b, 0x61, 0x00, 0x6b, 0x3e, 0x75, 0x4c, 0x04, 0x19, 0x58, 0x55, 0x36, 0xe1, 0xa4, 0x13, 0xaa, - 0x88, 0xd2, 0xb9, 0xbc, 0xbb, 0x81, 0x74, 0xd9, 0x48, 0x1a, 0x84, 0x0a, 0x83, 0xd0, 0x1e, 0x25, - 0x79, 0xeb, 0xb9, 0x94, 0xfa, 0xed, 0x67, 0xc3, 0xbb, 0x83, 0x54, 0xb9, 0x81, 0x07, 0x2b, 0xc5, - 0x11, 0xfa, 0x6c, 0xf7, 0xab, 0x01, 0x9e, 0xc8, 0x92, 0x18, 0x0e, 0x05, 0x2e, 0x44, 0x42, 0x07, - 0x2c, 0x27, 0x44, 0x36, 0x4e, 0xd4, 0xbf, 0x71, 0xb5, 0x0c, 0x41, 0x1b, 0x80, 0x59, 0x3f, 0x83, - 0x12, 0x02, 0x37, 0x01, 0xe0, 0x22, 0x64, 0xa2, 0x23, 0x48, 0x86, 0xcd, 0xaa, 0x63, 0x78, 0xb5, - 0xa0, 0xae, 0x90, 0x23, 0x92, 0x61, 0x68, 0x81, 0x87, 0x49, 0x9f, 0xa9, 0x7b, 0x34, 0x6b, 0x2a, - 0x38, 0x59, 0x4b, 0x93, 0x13, 0x9c, 0xd3, 0xcc, 0x5c, 0x52, 0xac, 0x7a, 0xe1, 0x5a, 0xaa, 0x05, - 0xa7, 0xca, 0x9c, 0x18, 0x70, 0xa4, 0x24, 0xec, 0xe3, 0x2e, 0xfe, 0x8f, 0x12, 0x8a, 0x13, 0xa7, - 0x58, 0xc7, 0x27, 0xee, 0x7e, 0xa9, 0x82, 0x6a, 0x9b, 0xa7, 0x90, 0x82, 0xf5, 0xc5, 0x53, 0xb1, - 0x85, 0xca, 0x73, 0x87, 0x6e, 0xeb, 0x21, 0x0b, 0xdd, 0x2d, 0x6f, 0xd2, 0x22, 0x1f, 0xc0, 0xe3, - 0xd9, 0x46, 0x73, 0xe6, 0x28, 0x66, 0x32, 0x2c, 0xef, 0x5f, 0x19, 0x13, 0xfa, 0xf7, 0x60, 0x65, - 0xba, 0x13, 0xec, 0xf9, 0xad, 0xe5, 0xb8, 0xb5, 0xf5, 0xf7, 0x78, 0x99, 0x78, 0xda, 0x9f, 0x79, - 0xe2, 0xa9, 0xf8, 0x02, 0xe2, 0x85, 0x4e, 0xb4, 0xde, 0x5e, 0x0e, 0x6d, 0xe3, 0x6a, 0x68, 0x1b, - 0xbf, 0x86, 0xb6, 0x71, 0x31, 0xb2, 0x2b, 0x57, 0x23, 0xbb, 0xf2, 0x7d, 0x64, 0x57, 0x8e, 0x51, - 0x69, 0x24, 0x0e, 0x15, 0xd7, 0xf6, 0x41, 0x18, 0x71, 0xbf, 0x78, 0x27, 0x4f, 0x5f, 0xfa, 0x67, - 0xe3, 0xc7, 0x52, 0x8e, 0x47, 0x74, 0x5f, 0x3d, 0x76, 0x2f, 0xfe, 0x04, 0x00, 0x00, 0xff, 0xff, - 0xd6, 0x48, 0x49, 0x02, 0x49, 0x05, 0x00, 0x00, + // 643 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0xcf, 0x4f, 0xd4, 0x40, + 0x14, 0xde, 0xba, 0xcb, 0x8f, 0x1d, 0x04, 0x61, 0x02, 0x49, 0x69, 0xa4, 0xdb, 0xf4, 0x40, 0x9a, + 0x18, 0x5a, 0xc1, 0x8b, 0xf1, 0x24, 0x0b, 0x1a, 0x49, 0xe0, 0x52, 0x48, 0x4c, 0x48, 0xcc, 0x66, + 0xda, 0x3e, 0xcb, 0xc4, 0xb6, 0xb3, 0xe9, 0xcc, 0x22, 0xfc, 0x05, 0x5e, 0xf9, 0x3b, 0xfc, 0x4b, + 0x38, 0x72, 0x34, 0x1e, 0x56, 0x03, 0x67, 0x2f, 0x9c, 0x3c, 0x9a, 0xce, 0x74, 0x97, 0x2e, 0x0b, + 0xca, 0xc1, 0x53, 0xfb, 0xbe, 0xef, 0xcd, 0x37, 0xf3, 0xde, 0xf7, 0x66, 0xd0, 0x12, 0x17, 0x39, + 0x8d, 0xc0, 0x0b, 0x13, 0x42, 0x53, 0x4f, 0x9c, 0xb8, 0xdd, 0x9c, 0x09, 0x86, 0x1f, 0x2b, 0xd8, + 0x95, 0xb0, 0xb1, 0x18, 0xb3, 0x98, 0x49, 0xc2, 0x2b, 0xfe, 0x54, 0x8e, 0x61, 0x86, 0x8c, 0xa7, + 0x8c, 0x7b, 0x01, 0xe1, 0xe0, 0x1d, 0xaf, 0x07, 0x20, 0xc8, 0xba, 0x17, 0x32, 0x9a, 0x29, 0xde, + 0xfe, 0xad, 0x21, 0x7d, 0x8f, 0xc7, 0xfb, 0x20, 0x36, 0x69, 0x1e, 0xe5, 0xac, 0xbb, 0x99, 0x24, + 0x2c, 0x24, 0x82, 0xb2, 0x8c, 0xe3, 0xa7, 0xa8, 0x49, 0x54, 0xc8, 0x72, 0x5d, 0xb3, 0x34, 0xa7, + 0xe9, 0xdf, 0x00, 0x78, 0x17, 0x61, 0xa2, 0xd6, 0x74, 0x68, 0x04, 0x99, 0xa0, 0x1f, 0x29, 0xe4, + 0xfa, 0xa3, 0x22, 0xad, 0xbd, 0x72, 0xdd, 0x6f, 0x2d, 0x9f, 0x92, 0x34, 0x79, 0x65, 0x8f, 0xe7, + 0xd8, 0xfe, 0x42, 0x09, 0xee, 0x0c, 0x31, 0xbc, 0x88, 0x26, 0x7a, 0x1c, 0x72, 0xae, 0xd7, 0xad, + 0xba, 0xd3, 0xf4, 0x55, 0x80, 0x0f, 0xd1, 0xd4, 0x67, 0xa0, 0xf1, 0x91, 0xe0, 0x7a, 0xa3, 0xc0, + 0xdb, 0xaf, 0xcf, 0xfb, 0xad, 0xda, 0xf7, 0x7e, 0x6b, 0x35, 0xa6, 0xe2, 0xa8, 0x17, 0xb8, 0x21, + 0x4b, 0xbd, 0xb2, 0x44, 0xf5, 0x59, 0xe3, 0xd1, 0x27, 0x4f, 0x9c, 0x76, 0x81, 0xbb, 0xdb, 0x10, + 0x5e, 0xf7, 0x5b, 0x73, 0xea, 0x18, 0xa5, 0x8c, 0xed, 0x0f, 0x04, 0x6d, 0x1b, 0x59, 0xf7, 0x55, + 0xee, 0x03, 0xef, 0xb2, 0x8c, 0x83, 0xed, 0x20, 0xbc, 0xc7, 0xe3, 0xad, 0xa2, 0xc1, 0x6f, 0x73, + 0x80, 0xcd, 0x94, 0xf5, 0x32, 0x81, 0x31, 0x6a, 0x14, 0xc7, 0x2b, 0x5b, 0x22, 0xff, 0xed, 0x33, + 0x0d, 0x19, 0xe3, 0xa9, 0x03, 0x21, 0x9c, 0xa3, 0x39, 0x69, 0x13, 0x44, 0x1d, 0x22, 0x19, 0x59, + 0xe7, 0xcc, 0xc6, 0xb2, 0xab, 0x8e, 0xed, 0x16, 0x06, 0xb9, 0xa5, 0x41, 0xee, 0x16, 0xa3, 0x59, + 0xfb, 0x79, 0x51, 0xea, 0xd7, 0x1f, 0x2d, 0xe7, 0x01, 0xa5, 0x16, 0x0b, 0xb8, 0x3f, 0x5b, 0x6e, + 0xa1, 0xf6, 0xb6, 0x7f, 0x69, 0x68, 0xbe, 0x38, 0x52, 0x0e, 0x44, 0x40, 0x59, 0x24, 0xb6, 0xd0, + 0x4c, 0x44, 0x8b, 0xc1, 0x09, 0x7a, 0x37, 0xae, 0x56, 0x21, 0x6c, 0x22, 0x74, 0xdb, 0x4f, 0xbf, + 0x82, 0xe0, 0x65, 0x34, 0x1d, 0x1e, 0x11, 0x9a, 0x75, 0x68, 0xa4, 0x4f, 0x4a, 0x76, 0x4a, 0xc6, + 0x3b, 0x51, 0x61, 0x62, 0x04, 0x19, 0x4b, 0xf5, 0x09, 0x89, 0xab, 0x00, 0xaf, 0x20, 0xc4, 0x05, + 0xc9, 0x45, 0x47, 0xd0, 0x14, 0xf4, 0xba, 0xa5, 0x39, 0x0d, 0xbf, 0x29, 0x91, 0x03, 0x9a, 0x02, + 0x36, 0xd0, 0x74, 0xd4, 0xcb, 0x65, 0xe3, 0xf5, 0x86, 0x24, 0x87, 0x31, 0x7e, 0x86, 0x16, 0x48, + 0x4f, 0xb0, 0x2e, 0x4d, 0x98, 0xe8, 0x40, 0x46, 0x82, 0x04, 0x22, 0x7d, 0xca, 0xd2, 0x9c, 0x69, + 0x7f, 0x7e, 0x48, 0xbc, 0x51, 0xb8, 0x6d, 0xc8, 0x51, 0x1e, 0x29, 0x77, 0x68, 0xe4, 0x81, 0x6c, + 0xc5, 0x36, 0x24, 0xf0, 0x1f, 0x5b, 0x51, 0xee, 0x38, 0xa2, 0x3a, 0xd8, 0x71, 0xe3, 0x4b, 0x1d, + 0xd5, 0xf7, 0x78, 0x8c, 0x19, 0x5a, 0xba, 0xfb, 0x76, 0xad, 0xba, 0xd5, 0xfb, 0xeb, 0xde, 0x37, + 0x8b, 0x86, 0xfb, 0xb0, 0xbc, 0xe1, 0xa8, 0x7d, 0x40, 0x4f, 0x6e, 0x0f, 0xac, 0x35, 0x26, 0x71, + 0x2b, 0xc3, 0x70, 0xfe, 0x95, 0x31, 0x94, 0x7f, 0x8f, 0x66, 0x47, 0x27, 0xca, 0x1c, 0x5f, 0x5a, + 0xe5, 0x8d, 0xd5, 0xbf, 0xf3, 0x55, 0xe1, 0x51, 0x7f, 0xc6, 0x85, 0x47, 0xf8, 0x3b, 0x84, 0xef, + 0x74, 0xa2, 0xfd, 0xee, 0xfc, 0xd2, 0xd4, 0x2e, 0x2e, 0x4d, 0xed, 0xe7, 0xa5, 0xa9, 0x9d, 0x5d, + 0x99, 0xb5, 0x8b, 0x2b, 0xb3, 0xf6, 0xed, 0xca, 0xac, 0x1d, 0xba, 0x95, 0xab, 0xb5, 0x2f, 0xb5, + 0xd6, 0x76, 0x49, 0xc0, 0xbd, 0xf2, 0xbd, 0x3d, 0x7e, 0xe9, 0x9d, 0x0c, 0x1e, 0xdd, 0xe2, 0x9a, + 0x05, 0x93, 0xf2, 0xd1, 0x7c, 0xf1, 0x27, 0x00, 0x00, 0xff, 0xff, 0x18, 0xdb, 0x17, 0x6e, 0x91, + 0x05, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -828,6 +847,23 @@ func (m *MsgCreateAirdrop) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.AutopilotEnabled { + i-- + if m.AutopilotEnabled { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x38 + } + if len(m.ChainId) > 0 { + i -= len(m.ChainId) + copy(dAtA[i:], m.ChainId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ChainId))) + i-- + dAtA[i] = 0x32 + } if len(m.Denom) > 0 { i -= len(m.Denom) copy(dAtA[i:], m.Denom) @@ -1046,6 +1082,13 @@ func (m *MsgCreateAirdrop) Size() (n int) { if l > 0 { n += 1 + l + sovTx(uint64(l)) } + l = len(m.ChainId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.AutopilotEnabled { + n += 2 + } return n } @@ -1651,6 +1694,58 @@ func (m *MsgCreateAirdrop) Unmarshal(dAtA []byte) error { } m.Denom = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChainId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AutopilotEnabled", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.AutopilotEnabled = bool(v != 0) default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) diff --git a/x/stakeibc/keeper/host_zone.go b/x/stakeibc/keeper/host_zone.go index 43acbc2492..6f012a2bd8 100644 --- a/x/stakeibc/keeper/host_zone.go +++ b/x/stakeibc/keeper/host_zone.go @@ -51,6 +51,16 @@ func (k Keeper) GetHostZoneFromHostDenom(ctx sdk.Context, denom string) (*types. return nil, errorsmod.Wrapf(sdkerrors.ErrUnknownRequest, "No HostZone for %s found", denom) } +// GetHostZoneFromTransferChannelID returns a HostZone from a transfer channel ID +func (k Keeper) GetHostZoneFromTransferChannelID(ctx sdk.Context, channelID string) (hostZone types.HostZone, found bool) { + for _, hostZone := range k.GetAllActiveHostZone(ctx) { + if hostZone.TransferChannelId == channelID { + return hostZone, true + } + } + return types.HostZone{}, false +} + // RemoveHostZone removes a hostZone from the store func (k Keeper) RemoveHostZone(ctx sdk.Context, chain_id string) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.HostZoneKey)) diff --git a/x/stakeibc/keeper/host_zone_test.go b/x/stakeibc/keeper/host_zone_test.go index d6b58865dc..1911241504 100644 --- a/x/stakeibc/keeper/host_zone_test.go +++ b/x/stakeibc/keeper/host_zone_test.go @@ -148,3 +148,34 @@ func TestGetValidatorFromAddress(t *testing.T) { _, _, found := keeper.GetValidatorFromAddress(validators, "fake_validator") require.False(t, found) } + +func (s *KeeperTestSuite) TestGetHostZoneFromTransferChannelID() { + // Store 5 host zones + expectedHostZones := map[string]types.HostZone{} + for i := 0; i < 5; i++ { + chainId := fmt.Sprintf("chain-%d", i) + channelId := fmt.Sprintf("channel-%d", i) + + hostZone := types.HostZone{ + ChainId: chainId, + TransferChannelId: channelId, + } + s.App.StakeibcKeeper.SetHostZone(s.Ctx, hostZone) + expectedHostZones[channelId] = hostZone + } + + // Look up each host zone by the channel ID + for i := 0; i < 5; i++ { + channelId := fmt.Sprintf("channel-%d", i) + + expectedHostZone := expectedHostZones[channelId] + actualHostZone, found := s.App.StakeibcKeeper.GetHostZoneFromTransferChannelID(s.Ctx, channelId) + + s.Require().True(found, "found host zone %d", i) + s.Require().Equal(expectedHostZone.ChainId, actualHostZone.ChainId, "host zone %d chain-id", i) + } + + // Lookup a non-existent host zone - should not be found + _, found := s.App.StakeibcKeeper.GetHostZoneFromTransferChannelID(s.Ctx, "fake_channel") + s.Require().False(found, "fake channel should not be found") +} diff --git a/x/stakeibc/keeper/msg_server_register_host_zone.go b/x/stakeibc/keeper/msg_server_register_host_zone.go index 64ca779d71..1348248ee9 100644 --- a/x/stakeibc/keeper/msg_server_register_host_zone.go +++ b/x/stakeibc/keeper/msg_server_register_host_zone.go @@ -57,6 +57,11 @@ func (k msgServer) RegisterHostZone(goCtx context.Context, msg *types.MsgRegiste k.Logger(ctx).Error(errMsg) return nil, errorsmod.Wrapf(types.ErrFailedToRegisterHostZone, errMsg) } + if hostZone.TransferChannelId == msg.TransferChannelId { + errMsg := fmt.Sprintf("transfer channel %s already registered", msg.TransferChannelId) + k.Logger(ctx).Error(errMsg) + return nil, errorsmod.Wrapf(types.ErrFailedToRegisterHostZone, errMsg) + } if hostZone.Bech32Prefix == msg.Bech32Prefix { errMsg := fmt.Sprintf("bech32prefix %s already registered", msg.Bech32Prefix) k.Logger(ctx).Error(errMsg) diff --git a/x/stakeibc/keeper/msg_server_register_host_zone_test.go b/x/stakeibc/keeper/msg_server_register_host_zone_test.go index 8bfc3a00cb..e736a3d83d 100644 --- a/x/stakeibc/keeper/msg_server_register_host_zone_test.go +++ b/x/stakeibc/keeper/msg_server_register_host_zone_test.go @@ -237,6 +237,27 @@ func (s *KeeperTestSuite) TestRegisterHostZone_DuplicateHostDenom() { s.Require().EqualError(err, expectedErrMsg, "registering host zone with duplicate host denom should fail") } +func (s *KeeperTestSuite) TestRegisterHostZone_DuplicateTransferChannel() { + // tests for a failure if we register the same host zone twice (with a duplicate transfer) + tc := s.SetupRegisterHostZone() + + // Register host zones successfully + _, err := s.GetMsgServer().RegisterHostZone(sdk.WrapSDKContext(s.Ctx), &tc.validMsg) + s.Require().NoError(err, "able to successfully register host zone once") + + // Create the message for a brand new host zone + // (without modifications, you would expect this to be successful) + newHostZoneMsg := s.createNewHostZoneMessage("OSMO", "osmo", "osmo") + + // Try to register with a duplicate transfer channel - it should fail + invalidMsg := newHostZoneMsg + invalidMsg.TransferChannelId = tc.validMsg.TransferChannelId + + _, err = s.GetMsgServer().RegisterHostZone(sdk.WrapSDKContext(s.Ctx), &invalidMsg) + expectedErrMsg := "transfer channel channel-0 already registered: failed to register host zone" + s.Require().EqualError(err, expectedErrMsg, "registering host zone with duplicate host denom should fail") +} + func (s *KeeperTestSuite) TestRegisterHostZone_DuplicateBech32Prefix() { // tests for a failure if we register the same host zone twice (with a duplicate bech32 prefix) tc := s.SetupRegisterHostZone()