cosmos-sdk/x/accounts/accountstd/exports.go

167 lines
5.7 KiB
Go

// Package accountstd exports the types and functions that are used by developers to implement smart accounts.
package accountstd
import (
"bytes"
"context"
"errors"
"fmt"
"cosmossdk.io/core/transaction"
"cosmossdk.io/x/accounts/internal/implementation"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/address"
)
var (
accountsModuleAddress = address.Module("accounts")
ErrInvalidType = errors.New("invalid type")
)
// Interface is the exported interface of an Account.
type Interface = implementation.Account
// ExecuteBuilder is the exported type of ExecuteBuilder.
type ExecuteBuilder = implementation.ExecuteBuilder
// QueryBuilder is the exported type of QueryBuilder.
type QueryBuilder = implementation.QueryBuilder
// InitBuilder is the exported type of InitBuilder.
type InitBuilder = implementation.InitBuilder
// AccountCreatorFunc is the exported type of AccountCreatorFunc.
type AccountCreatorFunc = implementation.AccountCreatorFunc
func DIAccount[A Interface](name string, constructor func(deps Dependencies) (A, error)) DepinjectAccount {
return DepinjectAccount{MakeAccount: AddAccount(name, constructor)}
}
type DepinjectAccount struct {
MakeAccount AccountCreatorFunc
}
func (DepinjectAccount) IsManyPerContainerType() {}
// Dependencies is the exported type of Dependencies.
type Dependencies = implementation.Dependencies
func RegisterExecuteHandler[
Req any, ProtoReq implementation.ProtoMsgG[Req], Resp any, ProtoResp implementation.ProtoMsgG[Resp],
](router *ExecuteBuilder, handler func(ctx context.Context, req ProtoReq) (ProtoResp, error),
) {
implementation.RegisterExecuteHandler(router, handler)
}
// RegisterQueryHandler registers a query handler for a smart account that uses protobuf.
func RegisterQueryHandler[
Req any, ProtoReq implementation.ProtoMsgG[Req], Resp any, ProtoResp implementation.ProtoMsgG[Resp],
](router *QueryBuilder, handler func(ctx context.Context, req ProtoReq) (ProtoResp, error),
) {
implementation.RegisterQueryHandler(router, handler)
}
// RegisterInitHandler registers an initialisation handler for a smart account that uses protobuf.
func RegisterInitHandler[
Req any, ProtoReq implementation.ProtoMsgG[Req], Resp any, ProtoResp implementation.ProtoMsgG[Resp],
](router *InitBuilder, handler func(ctx context.Context, req ProtoReq) (ProtoResp, error),
) {
implementation.RegisterInitHandler(router, handler)
}
// AddAccount is a helper function to add a smart account to the list of smart accounts.
func AddAccount[A Interface](name string, constructor func(deps Dependencies) (A, error)) AccountCreatorFunc {
return func(deps implementation.Dependencies) (string, implementation.Account, error) {
acc, err := constructor(deps)
return name, acc, err
}
}
// Whoami returns the address of the account being invoked.
func Whoami(ctx context.Context) []byte {
return implementation.Whoami(ctx)
}
// Sender returns the sender of the execution request.
func Sender(ctx context.Context) []byte {
return implementation.Sender(ctx)
}
// HasSender checks if the execution context was sent from the provided sender
func HasSender(ctx context.Context, wantSender []byte) bool {
return bytes.Equal(Sender(ctx), wantSender)
}
// SenderIsSelf checks if the sender of the request is the account itself.
func SenderIsSelf(ctx context.Context) bool { return HasSender(ctx, Whoami(ctx)) }
// SenderIsAccountsModule returns true if the sender of the execution request is the accounts module.
func SenderIsAccountsModule(ctx context.Context) bool {
return bytes.Equal(Sender(ctx), accountsModuleAddress)
}
// Funds returns if any funds were sent during the execute or init request. In queries this
// returns nil.
func Funds(ctx context.Context) sdk.Coins { return implementation.Funds(ctx) }
func ExecModule[MsgResp, Msg transaction.Msg](ctx context.Context, msg Msg) (resp MsgResp, err error) {
untyped, err := implementation.ExecModule(ctx, msg)
if err != nil {
return resp, err
}
return assertOrErr[MsgResp](untyped)
}
// QueryModule can be used by an account to execute a module query.
func QueryModule[Resp, Req transaction.Msg](ctx context.Context, req Req) (resp Resp, err error) {
untyped, err := implementation.QueryModule(ctx, req)
if err != nil {
return resp, err
}
return assertOrErr[Resp](untyped)
}
// UnpackAny unpacks a protobuf Any message generically.
func UnpackAny[Msg any, ProtoMsg implementation.ProtoMsgG[Msg]](any *implementation.Any) (*Msg, error) {
return implementation.UnpackAny[Msg, ProtoMsg](any)
}
// PackAny packs a protobuf Any message generically.
func PackAny(msg transaction.Msg) (*implementation.Any, error) {
return implementation.PackAny(msg)
}
// ExecModuleAnys can be used to execute a list of messages towards a module
// when those messages are packed in Any messages. The function returns a list
// of responses packed in Any messages.
func ExecModuleAnys(ctx context.Context, msgs []*implementation.Any) ([]*implementation.Any, error) {
responses := make([]*implementation.Any, len(msgs))
for i, msg := range msgs {
concreteMessage, err := implementation.UnpackAnyRaw(msg)
if err != nil {
return nil, fmt.Errorf("error unpacking message %d: %w", i, err)
}
resp, err := implementation.ExecModule(ctx, concreteMessage)
if err != nil {
return nil, fmt.Errorf("error executing message %d: %w", i, err)
}
// pack again
respAnyPB, err := implementation.PackAny(resp)
if err != nil {
return nil, fmt.Errorf("error packing response %d: %w", i, err)
}
responses[i] = respAnyPB
}
return responses, nil
}
// asserts the given any to the provided generic, returns ErrInvalidType if it can't.
func assertOrErr[T any](r any) (concrete T, err error) {
concrete, ok := r.(T)
if !ok {
return concrete, ErrInvalidType
}
return concrete, nil
}