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

Closed
wants to merge 25 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
287 changes: 287 additions & 0 deletions x/accounts/defaults/asset/asset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
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"
)

// NewAssetAccount creates a new AssetAccount 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]assettypes.SendFunc),
mintFunc: make(map[string]assettypes.MintFunc),
burnFunc: make(map[string]assettypes.BurnFunc),

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)
}

// Init inits the AssetAccount for denom
// with balances, supply and custom logics
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
}

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

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

// GetBalance returns balance of an address
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
}

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

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

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

// Transfer transfers amt coins from a sending account to a receiving account.
// Using transfer logic provided for this denom.
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
}

// MintCoins creates new coins from thin air and adds it to the receiver account.
// Using mint logic provided for this denom.
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
}

// BurnCoins burns coins deletes coins from the balance of an account.
// Using burn logic provided for this denom.
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
}

// QueryOwner is query handler return owner of denom
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
}

// SubUnlockedCoins removes the unlocked amt of the given account.
// An error is returned if the resulting balance is negative.
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)
}

// AddCoins increases the balance of the given address by the specified amount.
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