Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

migrate: Core-1 vesting accounts to the Charter #741

Closed
wants to merge 35 commits into from
Closed
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
4946d28
Major WIP - still just messing about
Reecepbcups Jun 28, 2023
279246a
Clean up migration vesting massively, actually works now
Reecepbcups Jun 28, 2023
c7047f1
Cleanup force undelegate & redelegate logic
Reecepbcups Jun 28, 2023
677deef
Merge branch 'main' into reece/remove-wolf-vesting
Reecepbcups Jun 28, 2023
d8e94c6
Only migrate Core1 on juno-1 chainId
Reecepbcups Jun 28, 2023
d401f7f
Fix upgrade test for vesting with ictest
Reecepbcups Jun 29, 2023
03f2aab
Fix upgrade test (due to changed GFee params)
Reecepbcups Jun 28, 2023
5c4fef5
TF: if feeCoin != "", then add the fees
Reecepbcups Jun 28, 2023
1820573
WIP for PeriodicVestingAccount
Reecepbcups Jul 1, 2023
c3e33b8
Wip2
Reecepbcups Jul 1, 2023
1d8a40e
Much cleaner vesting functions for Core-1 transfer
Reecepbcups Jul 1, 2023
be42155
lintorrr
Reecepbcups Jul 1, 2023
88cb307
Only move non vested funds to Core-1. Validation checks
Reecepbcups Jul 3, 2023
ff36245
Comments
Reecepbcups Jul 3, 2023
cd2b106
Merge branch 'main' into reece/remove-wolf-vesting
Reecepbcups Jul 3, 2023
62d2dc6
Use Wolfs actual account
Reecepbcups Jul 3, 2023
f81e91f
Migrate Core-1 rough draft
Reecepbcups Jul 3, 2023
3392ee1
lint
Reecepbcups Jul 3, 2023
6ca1e8e
VestingContract{}, better logs, no more mint logic, insta complete ve…
Reecepbcups Jul 3, 2023
8f1c444
lint
Reecepbcups Jul 3, 2023
bb40aa1
Remove stale func, new newVestingContract func
Reecepbcups Jul 3, 2023
30596ad
stale comments
Reecepbcups Jul 3, 2023
fa09054
Merge branch 'main' into reece/migrate-core1-vesting
Reecepbcups Jul 10, 2023
d9978db
Merge branch 'main' into reece/migrate-core1-vesting
Reecepbcups Jul 19, 2023
6c05f90
Merge branch 'main' into reece/migrate-core1-vesting
Reecepbcups Aug 8, 2023
87558ef
Merge branch 'main' of https://github.com/CosmosContracts/juno into r…
joelsmith-2019 Dec 24, 2023
24f679d
Move Upgrade Logic to v19
joelsmith-2019 Dec 24, 2023
57ffcaa
Update Vesting Transfer Logic,
joelsmith-2019 Jan 23, 2024
5a79ce8
Unbond All Except Jack,
joelsmith-2019 Jan 25, 2024
f029efe
Implement Test Cases
joelsmith-2019 Jan 29, 2024
87c5ad1
Add v20 Upgrade Handler
joelsmith-2019 Jan 29, 2024
bd4df59
Remove Multisig (Handled in v19)
joelsmith-2019 Jan 29, 2024
c04a213
Merge branch 'main' of https://github.com/CosmosContracts/juno into r…
joelsmith-2019 Jan 29, 2024
c28a225
Merge branch 'main' into reece/migrate-core1-vesting
joelsmith-2019 Feb 2, 2024
1e1a2b2
Send Funds After Disabling Vesting,
joelsmith-2019 Feb 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions app/upgrades/v16/upgrades.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,24 @@ import (
tokenfactorytypes "github.com/CosmosContracts/juno/v16/x/tokenfactory/types"
)

const (
// Core-1 Mainnet Address
Core1SubDAOAddress = "juno1j6glql3xmrcnga0gytecsucq3kd88jexxamxg3yn2xnqhunyvflqr7lxx3"
)

// Core1VestingAccounts https://daodao.zone/dao/juno1j6glql3xmrcnga0gytecsucq3kd88jexxamxg3yn2xnqhunyvflqr7lxx3/members
var Core1VestingAccounts = map[string]string{
"block": "juno17py8gfneaam64vt9kaec0fseqwxvkq0flmsmhg",
"dimi": "juno1s33zct2zhhaf60x4a90cpe9yquw99jj0zen8pt",
"jack": "juno130mdu9a0etmeuw52qfxk73pn0ga6gawk4k539x",
"jake": "juno18qw9ydpewh405w4lvmuhlg9gtaep79vy2gmtr2",
// TODO: So, can the SubDAO be the owner of the init'ed contract to claim rewards?
"multisig": "juno190g5j8aszqhvtg7cprmev8xcxs6csra7xnk3n3",
"wolf": "juno1a8u47ggy964tv9trjxfjcldutau5ls705djqyu",

// "zlocalexample": "juno1xz599egrd3dhq5vx63mkwja38q5q3th8h3ukjj",
}

func CreateV16UpgradeHandler(
mm *module.Manager,
cfg module.Configurator,
Expand Down Expand Up @@ -144,6 +162,31 @@ func CreateV16UpgradeHandler(
return nil, err
}

// Migrate Core-1 vesting account remaining funds -> Core-1, then create a new vesting contract for them (if not wolf).
if ctx.ChainID() == "juno-1" {
if err := migrateCore1VestingAccounts(ctx, keepers, nativeDenom); err != nil {
return nil, err
}
}

return versionMap, err
}
}

func migrateCore1VestingAccounts(ctx sdk.Context, keepers *keepers.AppKeepers, bondDenom string) error {
for name, vestingAccount := range Core1VestingAccounts {
// A new vesting contract will not be created if the account name is 'wolf'.
if err := upgrades.MoveVestingCoinFromVestingAccount(ctx,
keepers,
bondDenom,
name,
sdk.MustAccAddressFromBech32(vestingAccount),
sdk.MustAccAddressFromBech32(Core1SubDAOAddress),
); err != nil {
return err
}
}
Fixed Show fixed Hide fixed

// return fmt.Errorf("DEBUGGING; not finished yet. (migrateCore1VestingAccounts)")
return nil
}
273 changes: 273 additions & 0 deletions app/upgrades/vesting.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
package upgrades

import (
"encoding/json"
"fmt"
"strconv"
"strings"
"time"

"cosmossdk.io/math"

sdk "github.com/cosmos/cosmos-sdk/types"
authvestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"

"github.com/CosmosContracts/juno/v16/app/keepers"
)

const (
// TODO: Ensure mainnet codeId is used here
// Same as Reece, Noah, and Ekez contracts.
// junod q wasm code 2453 $HOME/Desktop/vesting.wasm --node https://juno-rpc.reece.sh:443
vestingCodeID = 2453
// vestingCodeID = 1 // testing
junoUnbondingSeconds = 2419200
)

type VestingContract struct {
Owner string `json:"owner"`
Recipient string `json:"recipient"`
Title string `json:"title"`
Description string `json:"description"`
Schedule string `json:"schedule"`
UnbondingDurationSeconds uint64 `json:"unbonding_duration_seconds"`
VestingDurationSeconds uint64 `json:"vesting_duration_seconds"`
Total string `json:"total"`
Denom Denom `json:"denom"`
}

type Denom struct {
Native string `json:"native"`
}

// Stops a vesting account and returns all tokens back to the Core-1 SubDAO.
func MoveVestingCoinFromVestingAccount(ctx sdk.Context, keepers *keepers.AppKeepers, bondDenom string, name string, accAddr sdk.AccAddress, core1AccAddr sdk.AccAddress) error {
now := ctx.BlockHeader().Time

stdAcc := keepers.AccountKeeper.GetAccount(ctx, accAddr)
vacc, ok := stdAcc.(*authvestingtypes.PeriodicVestingAccount)
if !ok {
// For e2e testing
fmt.Printf("account " + accAddr.String() + " is not a vesting account.\n")
return nil
}

fmt.Printf("\n\n== Vesting Account Address: %s (%s) ==\n", vacc.GetAddress().String(), name)

// Gets non-vested coins (These get returned back to Core-1 SubDAO / a new vesting contract made from)
unvestedCoins := vacc.GetVestingCoins(now)
fmt.Printf("Locked / waiting to vest Coins: %v\n", unvestedCoins)

// Pre migration balance
core1BeforeBal := keepers.BankKeeper.GetBalance(ctx, core1AccAddr, bondDenom)

// Clears the account so all all future vesting periods are removed.
clearVestingAccount(ctx, vacc, keepers, unvestedCoins)

// Complete any re-deleations to become standard delegations.
if err := completeAllRedelegations(ctx, keepers, accAddr, now); err != nil {
return err
}

// Instant unbond all delegations.
_, err := unbondAllAndFinish(ctx, now, keepers, accAddr)
joelsmith-2019 marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return err
}

// set the vesting account to a base account
keepers.AccountKeeper.SetAccount(ctx, vacc.BaseAccount)

// Moves unvested tokens to the Core-1 SubDAO.
if err := transferUnvestedTokensToCore1SubDao(ctx, keepers, bondDenom, accAddr, core1AccAddr, unvestedCoins); err != nil {
return err
}

// Ensure the post validation checks are met.
if err := postValidation(ctx, keepers, bondDenom, accAddr, core1AccAddr, unvestedCoins, core1BeforeBal); err != nil {
return err
}

// Create a new vesting contract owned by Core-1 (and Juno Governance by proxy)
// Wolfs resignation terminates his future vesting.
if name != "wolf" {
if err := newVestingContract(ctx, keepers, core1AccAddr, accAddr, name, unvestedCoins); err != nil {
return err
}
}

return nil
}

func newVestingContract(ctx sdk.Context, keepers *keepers.AppKeepers, core1AccAddr, accAddr sdk.AccAddress, name string, unvestedCoins sdk.Coins) error {
now := ctx.BlockHeader().Time.Unix()

// End Vesting Time (Juno Network launch Oct 1st, 2021. Vested 12 years = 2033)
endVestingEpochDate := time.Date(2033, 10, 1, 0, 0, 0, 0, time.UTC)
endVestingEpochSeconds := uint64(endVestingEpochDate.Unix())
vestingDurationSeconds := endVestingEpochSeconds - uint64(now)

owner := core1AccAddr.String()
recipient := accAddr.String()

// https://github.com/DA0-DA0/dao-contracts/blob/main/contracts/external/cw-vesting/src/msg.rs#L11
msgBz, err := json.Marshal(VestingContract{
Owner: owner,
Recipient: recipient,
Title: fmt.Sprintf("%s core-1", name),
Description: "Core-1 Vesting contract",
Schedule: "saturating_linear",
UnbondingDurationSeconds: junoUnbondingSeconds,
VestingDurationSeconds: vestingDurationSeconds,
Total: strconv.FormatInt(unvestedCoins[0].Amount.Int64(), 10),
Denom: Denom{
Native: "ujuno",
},
})
if err != nil {
return err
}

fmt.Printf("Moving %v from Core1 to new contract\n", unvestedCoins)
fmt.Println(string(msgBz))

contractAcc, _, err := keepers.ContractKeeper.Instantiate(
ctx,
uint64(vestingCodeID),
core1AccAddr,
core1AccAddr,
msgBz,
fmt.Sprintf("vest_to_%s_%d", recipient, now),
unvestedCoins,
)
if err != nil {
if strings.HasSuffix(err.Error(), "no such code") {
fmt.Printf("No such codeId: %d - skipping (e2e testing, not mainnet)\n", vestingCodeID)
return nil
}

return err
}

fmt.Printf("Contract Address: %s for %s with amount %d\n", contractAcc.String(), name, unvestedCoins[0].Amount.Int64())
return nil
}

func postValidation(ctx sdk.Context, keepers *keepers.AppKeepers, bondDenom string, accAddr sdk.AccAddress, core1AccAddr sdk.AccAddress, unvestedCoins sdk.Coins, core1BeforeBal sdk.Coin) error {
// Core1 balance should only increase by exactly the core1Bal + unvestedCoins
core1BalAfter := keepers.BankKeeper.GetBalance(ctx, core1AccAddr, bondDenom)
if !core1BeforeBal.Add(unvestedCoins[0]).IsEqual(core1BalAfter) {
return fmt.Errorf("ERROR: core1BeforeBal (%v) + unvestedCoins (%v) != core1BalAfter (%v)", core1BeforeBal, unvestedCoins, core1BalAfter)
}

// vesting account should have no future vesting periods
newVacc := keepers.AccountKeeper.GetAccount(ctx, accAddr)
if _, ok := newVacc.(*authvestingtypes.PeriodicVestingAccount); ok {
return fmt.Errorf("ERROR: account %s still is a vesting account", accAddr.String())
}

// ensure the account has 0 delegations, redelegations, or unbonding delegations
delegations := keepers.StakingKeeper.GetAllDelegatorDelegations(ctx, accAddr)
if len(delegations) != 0 {
return fmt.Errorf("ERROR: account %s still has delegations", accAddr.String())
}

redelegations := keepers.StakingKeeper.GetRedelegations(ctx, accAddr, 65535)
if len(redelegations) != 0 {
return fmt.Errorf("ERROR: account %s still has redelegations", accAddr.String())
}

unbondingDelegations := keepers.StakingKeeper.GetAllUnbondingDelegations(ctx, accAddr)
if len(unbondingDelegations) != 0 {
return fmt.Errorf("ERROR: account %s still has unbonding delegations", accAddr.String())
}

return nil
}

func transferUnvestedTokensToCore1SubDao(ctx sdk.Context, keepers *keepers.AppKeepers, bondDenom string, accAddr, core1AccAddr sdk.AccAddress, unvestedCoins sdk.Coins) error {
fmt.Printf("Sending Unvested Coins back to Core-1: %v\n", unvestedCoins)
if err := keepers.BankKeeper.SendCoins(ctx, accAddr, core1AccAddr, unvestedCoins); err != nil {
return err
}

core1BalC := keepers.BankKeeper.GetBalance(ctx, core1AccAddr, bondDenom)
fmt.Printf("Updated Core1 SubDAO Balance: %v\n", core1BalC)

return nil
}

func completeAllRedelegations(ctx sdk.Context, keepers *keepers.AppKeepers, accAddr sdk.AccAddress, now time.Time) error {
for _, activeRedelegation := range keepers.StakingKeeper.GetRedelegations(ctx, accAddr, 65535) {
redelegationSrc, _ := sdk.ValAddressFromBech32(activeRedelegation.ValidatorSrcAddress)
redelegationDst, _ := sdk.ValAddressFromBech32(activeRedelegation.ValidatorDstAddress)

// set all entry completionTime to now so we can complete re-delegation
for i := range activeRedelegation.Entries {
activeRedelegation.Entries[i].CompletionTime = now
}

keepers.StakingKeeper.SetRedelegation(ctx, activeRedelegation)
_, err := keepers.StakingKeeper.CompleteRedelegation(ctx, accAddr, redelegationSrc, redelegationDst)
if err != nil {
return err
}
}

return nil
}

// Returns the amount of tokens which were unbonded (not rewards)
func unbondAllAndFinish(ctx sdk.Context, now time.Time, keepers *keepers.AppKeepers, accAddr sdk.AccAddress) (math.Int, error) {
unbondedAmt := math.ZeroInt()

// Unbond all delegations from the account
for _, delegation := range keepers.StakingKeeper.GetAllDelegatorDelegations(ctx, accAddr) {
validatorValAddr := delegation.GetValidatorAddr()
_, found := keepers.StakingKeeper.GetValidator(ctx, validatorValAddr)
if !found {
continue
}

_, err := keepers.StakingKeeper.Undelegate(ctx, accAddr, validatorValAddr, delegation.GetShares())
if err != nil {
return math.ZeroInt(), err
}
}

// Take all unbonding and complete them.
for _, unbondingDelegation := range keepers.StakingKeeper.GetAllUnbondingDelegations(ctx, accAddr) {
validatorStringAddr := unbondingDelegation.ValidatorAddress
validatorValAddr, _ := sdk.ValAddressFromBech32(validatorStringAddr)

// Complete unbonding delegation
for i := range unbondingDelegation.Entries {
unbondingDelegation.Entries[i].CompletionTime = now
unbondedAmt = unbondedAmt.Add(unbondingDelegation.Entries[i].Balance)
}

keepers.StakingKeeper.SetUnbondingDelegation(ctx, unbondingDelegation)
_, err := keepers.StakingKeeper.CompleteUnbonding(ctx, accAddr, validatorValAddr)
if err != nil {
return math.ZeroInt(), err
}
}

return unbondedAmt, nil
}

func clearVestingAccount(ctx sdk.Context, vacc *authvestingtypes.PeriodicVestingAccount, keepers *keepers.AppKeepers, unvestedCoins sdk.Coins) {
vacc.BaseVestingAccount.EndTime = ctx.BlockTime().Unix()

for i := range vacc.VestingPeriods {
vacc.VestingPeriods[i].Length = 0
}

vacc.DelegatedFree = unvestedCoins
vacc.DelegatedVesting = sdk.Coins{}

vacc.BaseVestingAccount.DelegatedFree = unvestedCoins
vacc.BaseVestingAccount.DelegatedVesting = sdk.Coins{}

keepers.AccountKeeper.SetAccount(ctx, vacc)
}
9 changes: 5 additions & 4 deletions interchaintest/chain_upgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,12 @@ func CosmosChainUpgradeTest(t *testing.T, chainName, initialVersion, upgradeBran
chainUser := users[0]

// create a tokenfactory denom before upgrade (invalid genesis for hard forking due to x/bank validation)
emptyFullDenom := helpers.CreateTokenFactoryDenom(t, ctx, chain, chainUser, "empty")
emptyFullDenom := helpers.CreateTokenFactoryDenom(t, ctx, chain, chainUser, "empty", "")

mintedDenom := helpers.CreateTokenFactoryDenom(t, ctx, chain, chainUser, "minted")
mintedDenom := helpers.CreateTokenFactoryDenom(t, ctx, chain, chainUser, "minted", "")
helpers.MintToTokenFactoryDenom(t, ctx, chain, chainUser, chainUser, 100, mintedDenom)

mintedAndModified := helpers.CreateTokenFactoryDenom(t, ctx, chain, chainUser, "mandm")
mintedAndModified := helpers.CreateTokenFactoryDenom(t, ctx, chain, chainUser, "mandm", "")
helpers.MintToTokenFactoryDenom(t, ctx, chain, chainUser, chainUser, 100, mintedAndModified)

ticker, desc, exponent := "TICKER", "desc", "6"
Expand Down Expand Up @@ -222,7 +222,8 @@ func CosmosChainUpgradeTest(t *testing.T, chainName, initialVersion, upgradeBran
require.Equal(t, postModified, modifiedRes)

// Ensure after the upgrade, the denoms are properly set with the Denom Metadata.
afterUpgrade := helpers.CreateTokenFactoryDenom(t, ctx, chain, chainUser, "post")
// (Due to migrating hardcoded, we have to set a fee after the upgrade).
afterUpgrade := helpers.CreateTokenFactoryDenom(t, ctx, chain, chainUser, "post", "250000"+Denom)
newRes := helpers.GetTokenFactoryDenomMetadata(t, ctx, chain, afterUpgrade)
require.Equal(t, newRes.Display, afterUpgrade)
require.Equal(t, newRes.Name, afterUpgrade)
Expand Down
8 changes: 6 additions & 2 deletions interchaintest/helpers/tokenfactory.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,23 @@ func debugOutput(t *testing.T, stdout string) {
}
}

func CreateTokenFactoryDenom(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, user ibc.Wallet, subDenomName string) (fullDenom string) {
func CreateTokenFactoryDenom(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, user ibc.Wallet, subDenomName, feeCoin string) (fullDenom string) {
// TF gas to create cost 2mil, so we set to 2.5 to be safe
cmd := []string{"junod", "tx", "tokenfactory", "create-denom", subDenomName,
"--node", chain.GetRPCAddress(),
"--home", chain.HomeDir(),
"--chain-id", chain.Config().ChainID,
"--from", user.KeyName(),
"--gas", "2500000",
"--gas-adjustment", "2.0",
"--keyring-dir", chain.HomeDir(),
"--keyring-backend", keyring.BackendTest,
"-y",
}

if feeCoin != "" {
cmd = append(cmd, "--fees", feeCoin)
}

stdout, _, err := chain.Exec(ctx, cmd, nil)
require.NoError(t, err)

Expand Down
2 changes: 1 addition & 1 deletion interchaintest/module_tokenfactory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func TestJunoTokenFactory(t *testing.T) {
user2 := users[1]
uaddr2 := user2.FormattedAddress()

tfDenom := helpers.CreateTokenFactoryDenom(t, ctx, juno, user, "ictestdenom")
tfDenom := helpers.CreateTokenFactoryDenom(t, ctx, juno, user, "ictestdenom", fmt.Sprintf("0%s", Denom))
t.Log("tfDenom", tfDenom)

// mint
Expand Down
Loading