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

WIP: AssetAccount base implement #22671

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 23 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6,246 changes: 6,246 additions & 0 deletions api/cosmos/accounts/defaults/asset/v1/asset.pulsar.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
go 1.23.2
go 1.23.3

module github.com/cosmos/cosmos-sdk

Expand Down
2 changes: 1 addition & 1 deletion server/v2/cometbft/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module cosmossdk.io/server/v2/cometbft

go 1.23.2
go 1.23.3

replace (
cosmossdk.io/api => ../../../api
Expand Down
4 changes: 3 additions & 1 deletion simapp/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ require (
cosmossdk.io/math v1.4.0
cosmossdk.io/store v1.1.1
cosmossdk.io/tools/confix v0.0.0-20230613133644-0a778132a60f
cosmossdk.io/x/accounts v0.0.0-20240913065641-0064ccbce64e
cosmossdk.io/x/accounts v0.0.0-20241127063259-f296a5005ce8
cosmossdk.io/x/accounts/defaults/base v0.0.0-00010101000000-000000000000
cosmossdk.io/x/accounts/defaults/lockup v0.0.0-20240417181816-5e7aae0db1f5
cosmossdk.io/x/accounts/defaults/multisig v0.0.0-00010101000000-000000000000
Expand Down Expand Up @@ -65,6 +65,7 @@ require (
cloud.google.com/go/storage v1.43.0 // indirect
cosmossdk.io/errors v1.0.1 // indirect
cosmossdk.io/schema v0.3.1-0.20241128094659-bd76b47e1d8b // indirect
cosmossdk.io/x/accounts/defaults/asset v0.0.0-00010101000000-000000000000 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
github.com/99designs/keyring v1.2.2 // indirect
Expand Down Expand Up @@ -280,6 +281,7 @@ replace (

// Below are the long-lived replace of the SimApp
replace (
cosmossdk.io/x/accounts/defaults/asset => ../x/accounts/defaults/asset
// use cosmos fork of keyring
github.com/99designs/keyring => github.com/cosmos/keyring v1.2.0
// Simapp always use the latest version of the cosmos-sdk
Expand Down
4 changes: 3 additions & 1 deletion simapp/v2/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ require (
cosmossdk.io/server/v2/cometbft v0.0.0-20241015140036-ee3d320eaa55
cosmossdk.io/store/v2 v2.0.0
cosmossdk.io/tools/confix v0.0.0-00010101000000-000000000000
cosmossdk.io/x/accounts v0.0.0-20240913065641-0064ccbce64e
cosmossdk.io/x/accounts v0.0.0-20241127063259-f296a5005ce8
cosmossdk.io/x/authz v0.0.0-00010101000000-000000000000
cosmossdk.io/x/bank v0.0.0-20240226161501-23359a0b6d91
cosmossdk.io/x/circuit v0.0.0-20230613133644-0a778132a60f
Expand Down Expand Up @@ -65,6 +65,7 @@ require (
cosmossdk.io/server/v2/appmanager v0.0.0-20240802110823-cffeedff643d // indirect
cosmossdk.io/server/v2/stf v0.0.0-20240708142107-25e99c54bac1 // indirect
cosmossdk.io/store v1.1.1 // indirect
cosmossdk.io/x/accounts/defaults/asset v0.0.0-00010101000000-000000000000 // indirect
cosmossdk.io/x/tx v1.0.0-alpha.1 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
Expand Down Expand Up @@ -254,6 +255,7 @@ replace (
cosmossdk.io/client/v2 => ../../client/v2
cosmossdk.io/tools/confix => ../../tools/confix
cosmossdk.io/x/accounts => ../../x/accounts
cosmossdk.io/x/accounts/defaults/asset => ../../x/accounts/defaults/asset
cosmossdk.io/x/accounts/defaults/base => ../../x/accounts/defaults/base
cosmossdk.io/x/accounts/defaults/lockup => ../../x/accounts/defaults/lockup
cosmossdk.io/x/accounts/defaults/multisig => ../../x/accounts/defaults/multisig
Expand Down
4 changes: 3 additions & 1 deletion tests/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ require (
cosmossdk.io/runtime/v2 v2.0.0-20240911143651-72620a577660
cosmossdk.io/server/v2/stf v0.0.0-00010101000000-000000000000
cosmossdk.io/store/v2 v2.0.0-00010101000000-000000000000
cosmossdk.io/x/accounts v0.0.0-20240913065641-0064ccbce64e
cosmossdk.io/x/accounts v0.0.0-20241127063259-f296a5005ce8
cosmossdk.io/x/accounts/defaults/base v0.0.0-00010101000000-000000000000
cosmossdk.io/x/accounts/defaults/lockup v0.0.0-20240417181816-5e7aae0db1f5
cosmossdk.io/x/accounts/defaults/multisig v0.0.0-00010101000000-000000000000
Expand Down Expand Up @@ -74,6 +74,7 @@ require (
cosmossdk.io/indexer/postgres v0.1.0 // indirect
cosmossdk.io/schema v0.3.1-0.20241128094659-bd76b47e1d8b // indirect
cosmossdk.io/server/v2/appmanager v0.0.0-00010101000000-000000000000 // indirect
cosmossdk.io/x/accounts/defaults/asset v0.0.0-00010101000000-000000000000 // indirect
cosmossdk.io/x/circuit v0.0.0-20230613133644-0a778132a60f // indirect
cosmossdk.io/x/epochs v0.0.0-20240522060652-a1ae4c3e0337 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
Expand Down Expand Up @@ -259,6 +260,7 @@ replace (
cosmossdk.io/store => ../store
cosmossdk.io/store/v2 => ../store/v2
cosmossdk.io/x/accounts => ../x/accounts
cosmossdk.io/x/accounts/defaults/asset => ../x/accounts/defaults/asset
cosmossdk.io/x/accounts/defaults/base => ../x/accounts/defaults/base
cosmossdk.io/x/accounts/defaults/lockup => ../x/accounts/defaults/lockup
cosmossdk.io/x/accounts/defaults/multisig => ../x/accounts/defaults/multisig
Expand Down
269 changes: 269 additions & 0 deletions x/accounts/defaults/asset/asset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
package asset

import (
"context"
"errors"

"cosmossdk.io/collections"
"cosmossdk.io/core/address"
"cosmossdk.io/core/header"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/math"
"cosmossdk.io/x/accounts/accountstd"
assettypes "cosmossdk.io/x/accounts/defaults/asset/v1"

sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

var (
DenomPrefix = collections.NewPrefix(0)
BalancePrefix = collections.NewPrefix(1)
SupplyPrefix = collections.NewPrefix(2)
OwnerPrefix = collections.NewPrefix(3)
Type = "asset-account"
)

// newBaseLockup creates a new BaseLockup object.
func NewAssetAccount(d accountstd.Dependencies) (*AssetAccount, error) {
AssetAccount := &AssetAccount{
Owner: collections.NewItem(d.SchemaBuilder, OwnerPrefix, "owner", collections.BytesValue),
Denom: collections.NewItem(d.SchemaBuilder, DenomPrefix, "denom", collections.StringValue),
Balance: collections.NewMap(d.SchemaBuilder, BalancePrefix, "balance", collections.BytesKey, sdk.IntValue),
Supply: collections.NewItem(d.SchemaBuilder, SupplyPrefix, "supply", sdk.IntValue),
transferFunc: make(map[string]func(ctx context.Context, from, to []byte, amount math.Int) ([][]byte, error)),
mintFunc: make(map[string]func(ctx context.Context, to []byte, amount math.Int) ([][]byte, error)),
burnFunc: make(map[string]func(ctx context.Context, from []byte, amount math.Int) ([][]byte, error)),

addressCodec: d.AddressCodec,
headerService: d.Environment.HeaderService,
}
return AssetAccount, nil
}

type AssetAccount struct {
// Owner is the address of the account owner.
Owner collections.Item[[]byte]
Denom collections.Item[string]
Balance collections.Map[[]byte, math.Int]
Supply collections.Item[math.Int]
addressCodec address.Codec
headerService header.Service
transferFunc map[string]func(ctx context.Context, from, to []byte, amount math.Int) ([][]byte, error)
mintFunc map[string]func(ctx context.Context, to []byte, amount math.Int) ([][]byte, error)
burnFunc map[string]func(ctx context.Context, from []byte, amount math.Int) ([][]byte, error)
}

func (aa *AssetAccount) Init(ctx context.Context, msg *assettypes.MsgInitAssetAccountWrapper) (
*assettypes.MsgInitAssetAccountResponse, error,
) {
owner, err := aa.addressCodec.StringToBytes(msg.Owner)
if err != nil {
return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid 'owner' address: %s", err)
}
err = aa.Owner.Set(ctx, owner)
if err != nil {
return nil, err
}

err = aa.Denom.Set(ctx, msg.Denom)
if err != nil {
return nil, err
}

totalSupply := math.ZeroInt()
for _, balance := range msg.InitBalance {
totalSupply = totalSupply.Add(balance.Amount)
err = aa.Balance.Set(ctx, balance.Addr, balance.Amount)
if err != nil {
return nil, err
}
}

err = aa.Supply.Set(ctx, totalSupply)
if err != nil {
return nil, err
}

aa.transferFunc[msg.Denom] = msg.TransferFunc(aa)
aa.mintFunc[msg.Denom] = msg.MintFunc(aa)
aa.burnFunc[msg.Denom] = msg.BurnFunc(aa)

return &assettypes.MsgInitAssetAccountResponse{}, nil
}

func (aa *AssetAccount) GetDenom(ctx context.Context) (
string, error,
) {
denom, err := aa.Denom.Get(ctx)
if err != nil {
return "", err
}
return denom, nil
}

func (aa *AssetAccount) GetOwner(ctx context.Context) (
[]byte, error,
) {
owner, err := aa.Owner.Get(ctx)
if err != nil {
return []byte{}, err
}
return owner, nil
}

func (aa *AssetAccount) GetBalance(ctx context.Context, addr []byte) math.Int {
balance, err := aa.Balance.Get(ctx, addr)
if err != nil {
return math.ZeroInt()
}
return balance
}

func (aa *AssetAccount) SetBalance(ctx context.Context, addr []byte, amt math.Int) error {
return aa.Balance.Set(ctx, addr, amt)
}

func (aa *AssetAccount) GetSupply(ctx context.Context) math.Int {
supply, err := aa.Supply.Get(ctx)
if err != nil {
return math.ZeroInt()
}
return supply
}

func (aa *AssetAccount) SetSupply(ctx context.Context, supply math.Int) error {
return aa.Supply.Set(ctx, supply)
}

func (aa *AssetAccount) Transfer(ctx context.Context, msg *assettypes.MsgTransfer) (*assettypes.MsgTransferResponse, error) {
if msg == nil {
return nil, errors.New("empty msg")
}
denom, err := aa.GetDenom(ctx)
if err != nil {
return nil, err
}
changeAddr, err := aa.transferFunc[denom](ctx, msg.From, msg.To, msg.Amount)
if err != nil {
return nil, err
}
resp := &assettypes.MsgTransferResponse{
Supply: aa.GetSupply(ctx),
}

for _, addr := range changeAddr {
balance := aa.GetBalance(ctx, addr)
resp.Balances = append(resp.Balances, assettypes.Balance{Addr: addr, Amount: balance})
}

return resp, nil
}

func (aa *AssetAccount) Mint(ctx context.Context, msg *assettypes.MsgMint) (*assettypes.MsgMintResponse, error) {
if msg == nil {
return nil, errors.New("empty msg")
}
denom, err := aa.GetDenom(ctx)
if err != nil {
return nil, err
}
changeAddr, err := aa.mintFunc[denom](ctx, msg.To, msg.Amount)
if err != nil {
return nil, err
}
resp := &assettypes.MsgMintResponse{
Supply: aa.GetSupply(ctx),
}

for _, addr := range changeAddr {
balance := aa.GetBalance(ctx, addr)
resp.Balances = append(resp.Balances, assettypes.Balance{Addr: addr, Amount: balance})
}

return resp, nil
}

func (aa *AssetAccount) Burn(ctx context.Context, msg *assettypes.MsgBurn) (*assettypes.MsgBurnResponse, error) {
if msg == nil {
return nil, errors.New("empty msg")
}

denom, err := aa.GetDenom(ctx)
if err != nil {
return nil, err
}

changeAddr, err := aa.burnFunc[denom](ctx, msg.From, msg.Amount)
if err != nil {
return nil, err
}
resp := &assettypes.MsgBurnResponse{
Supply: aa.GetSupply(ctx),
}

for _, addr := range changeAddr {
balance := aa.GetBalance(ctx, addr)
resp.Balances = append(resp.Balances, assettypes.Balance{Addr: addr, Amount: balance})
}

return resp, nil
}

func (aa *AssetAccount) QueryOwner(ctx context.Context, msg *assettypes.QueryOwnerRequest) (*assettypes.QueryOwnerResponse, error) {
if msg == nil {
return nil, errors.New("empty msg")
}
owner, err := aa.GetOwner(ctx)
if err != nil {
return nil, err
}

return &assettypes.QueryOwnerResponse{
Owner: owner,
}, nil
}

func (aa *AssetAccount) SubUnlockedCoins(ctx context.Context, addr []byte, amt math.Int) error {
denom, err := aa.GetDenom(ctx)
if err != nil {
return err
}

balance := aa.GetBalance(ctx, addr)
_, err = balance.SafeSub(amt)
if err != nil {
return errorsmod.Wrapf(
sdkerrors.ErrInsufficientFunds,
"%s spendable balance %s is smaller than %s",
denom, balance, amt,
)
}

newBalance := balance.Sub(amt)

return aa.SetBalance(ctx, addr, newBalance)
}

func (aa *AssetAccount) AddCoins(ctx context.Context, addr []byte, amt math.Int) error {
balance := aa.GetBalance(ctx, addr)

newBalance := balance.Add(amt)

return aa.SetBalance(ctx, addr, newBalance)
}

// RegisterInitHandler implements implementation.Account.
func (a *AssetAccount) RegisterInitHandler(builder *accountstd.InitBuilder) {
accountstd.RegisterInitHandler(builder, a.Init)
}

func (aa *AssetAccount) RegisterExecuteHandlers(builder *accountstd.ExecuteBuilder) {
accountstd.RegisterExecuteHandler(builder, aa.Transfer)
accountstd.RegisterExecuteHandler(builder, aa.Mint)
accountstd.RegisterExecuteHandler(builder, aa.Burn)
}

// RegisterQueryHandlers implements implementation.Account.
func (a *AssetAccount) RegisterQueryHandlers(builder *accountstd.QueryBuilder) {
}
Loading
Loading