feat_: Move community tokens transaction listening to status-go

Use EventWatcher to catch wallet events.
Handling all community tokens wallet events in communitytokens service (database and messenger operations).
Adding new signal to nim: CommunityTokenTransactionSignal, which is emitted everytime when the event is received.

Issue #4351
This commit is contained in:
Michal Iskierko 2024-03-14 09:39:06 +01:00 committed by Michał Iskierko
parent 00ee631a22
commit 574450289c
24 changed files with 565 additions and 214 deletions

View File

@ -2366,6 +2366,7 @@ func (b *GethStatusBackend) injectAccountsIntoWakuService(w types.WakuKeyManager
}
b.statusNode.ChatService(accDB).Init(messenger)
b.statusNode.EnsService().Init(messenger.SyncEnsNamesWithDispatchMessage)
b.statusNode.CommunityTokensService().Init(messenger)
}
return nil

View File

@ -487,7 +487,7 @@ func (b *StatusNode) pendingTrackerService(walletFeed *event.Feed) *transactions
func (b *StatusNode) CommunityTokensService() *communitytokens.Service {
if b.communityTokensSrvc == nil {
b.communityTokensSrvc = communitytokens.NewService(b.rpcClient, b.gethAccountManager, b.pendingTracker, b.config, b.appDB)
b.communityTokensSrvc = communitytokens.NewService(b.rpcClient, b.gethAccountManager, b.pendingTracker, b.config, b.appDB, &b.walletFeed)
}
return b.communityTokensSrvc
}

View File

@ -42,12 +42,12 @@ import (
"github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/protocol/requests"
"github.com/status-im/status-go/protocol/transport"
"github.com/status-im/status-go/services/communitytokens"
"github.com/status-im/status-go/services/wallet/bigint"
walletcommon "github.com/status-im/status-go/services/wallet/common"
"github.com/status-im/status-go/services/wallet/thirdparty"
"github.com/status-im/status-go/services/wallet/token"
"github.com/status-im/status-go/signal"
"github.com/status-im/status-go/transactions"
)
var defaultAnnounceList = [][]string{
@ -102,7 +102,7 @@ type Manager struct {
torrentConfig *params.TorrentConfig
torrentClient *torrent.Client
walletConfig *params.WalletConfig
communityTokensService communitytokens.ServiceInterface
communityTokensService CommunityTokensServiceInterface
historyArchiveTasksWaitGroup sync.WaitGroup
historyArchiveTasks sync.Map // stores `chan struct{}`
membersReevaluationTasks sync.Map // stores `membersReevaluationTask`
@ -190,7 +190,7 @@ type managerOptions struct {
tokenManager TokenManager
collectiblesManager CollectiblesManager
walletConfig *params.WalletConfig
communityTokensService communitytokens.ServiceInterface
communityTokensService CommunityTokensServiceInterface
permissionChecker PermissionChecker
// allowForcingCommunityMembersReevaluation indicates whether we should allow forcing community members reevaluation.
@ -205,6 +205,26 @@ type TokenManager interface {
GetAllChainIDs() ([]uint64, error)
}
type CollectibleContractData struct {
TotalSupply *bigint.BigInt
Transferable bool
RemoteBurnable bool
InfiniteSupply bool
}
type AssetContractData struct {
TotalSupply *bigint.BigInt
InfiniteSupply bool
}
type CommunityTokensServiceInterface interface {
GetCollectibleContractData(chainID uint64, contractAddress string) (*CollectibleContractData, error)
SetSignerPubKey(ctx context.Context, chainID uint64, contractAddress string, txArgs transactions.SendTxArgs, password string, newSignerPubKey string) (string, error)
GetAssetContractData(chainID uint64, contractAddress string) (*AssetContractData, error)
SafeGetSignerPubKey(ctx context.Context, chainID uint64, communityID string) (string, error)
DeploymentSignatureDigest(chainID uint64, addressFrom string, communityID string) ([]byte, error)
}
type DefaultTokenManager struct {
tokenManager *token.Manager
}
@ -286,7 +306,7 @@ func WithWalletConfig(walletConfig *params.WalletConfig) ManagerOption {
}
}
func WithCommunityTokensService(communityTokensService communitytokens.ServiceInterface) ManagerOption {
func WithCommunityTokensService(communityTokensService CommunityTokensServiceInterface) ManagerOption {
return func(opts *managerOptions) {
opts.communityTokensService = communityTokensService
}
@ -4747,6 +4767,10 @@ func (m *Manager) GetCommunityToken(communityID string, chainID int, address str
return m.persistence.GetCommunityToken(communityID, chainID, address)
}
func (m *Manager) GetCommunityTokenByChainAndAddress(chainID int, address string) (*community_token.CommunityToken, error) {
return m.persistence.GetCommunityTokenByChainAndAddress(chainID, address)
}
func (m *Manager) GetCommunityTokens(communityID string) ([]*community_token.CommunityToken, error) {
return m.persistence.GetCommunityTokens(communityID)
}

View File

@ -1276,6 +1276,29 @@ func (p *Persistence) GetCommunityToken(communityID string, chainID int, address
return &token, nil
}
func (p *Persistence) GetCommunityTokenByChainAndAddress(chainID int, address string) (*token.CommunityToken, error) {
token := token.CommunityToken{}
var supplyStr string
err := p.db.QueryRow(`SELECT community_id, address, type, name, symbol, description, supply_str, infinite_supply,
transferable, remote_self_destruct, chain_id, deploy_state, image_base64, decimals, deployer, privileges_level
FROM community_tokens WHERE chain_id = ? AND address = ?`, chainID, address).Scan(&token.CommunityID, &token.Address, &token.TokenType, &token.Name,
&token.Symbol, &token.Description, &supplyStr, &token.InfiniteSupply, &token.Transferable,
&token.RemoteSelfDestruct, &token.ChainID, &token.DeployState, &token.Base64Image, &token.Decimals,
&token.Deployer, &token.PrivilegesLevel)
if err == sql.ErrNoRows {
return nil, nil
} else if err != nil {
return nil, err
}
supplyBigInt, ok := new(big.Int).SetString(supplyStr, 10)
if ok {
token.Supply = &bigint.BigInt{Int: supplyBigInt}
} else {
token.Supply = &bigint.BigInt{Int: big.NewInt(0)}
}
return &token, nil
}
func (p *Persistence) getCommunityTokensInternal(rows *sql.Rows) ([]*token.CommunityToken, error) {
tokens := []*token.CommunityToken{}

View File

@ -18,7 +18,6 @@ import (
"github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/protocol/requests"
"github.com/status-im/status-go/protocol/tt"
"github.com/status-im/status-go/services/communitytokens"
"github.com/status-im/status-go/services/wallet/bigint"
)
@ -2061,7 +2060,7 @@ func testEventSenderAddTokenMasterAndOwnerToken(base CommunityEventsTestsInterfa
}
func addCommunityTokenToCommunityTokensService(base CommunityEventsTestsInterface, token *token.CommunityToken) {
data := &communitytokens.CollectibleContractData{
data := &communities.CollectibleContractData{
TotalSupply: token.Supply,
Transferable: token.Transferable,
RemoteBurnable: token.RemoteSelfDestruct,

View File

@ -24,7 +24,6 @@ import (
"github.com/status-im/status-go/protocol/communities/token"
"github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/protocol/requests"
"github.com/status-im/status-go/services/communitytokens"
walletToken "github.com/status-im/status-go/services/wallet/token"
"github.com/status-im/status-go/transactions"
)
@ -74,8 +73,8 @@ func (m *TokenManagerMock) FindOrCreateTokenByAddress(ctx context.Context, chain
}
type CollectiblesServiceMock struct {
Collectibles map[uint64]map[string]*communitytokens.CollectibleContractData
Assets map[uint64]map[string]*communitytokens.AssetContractData
Collectibles map[uint64]map[string]*communities.CollectibleContractData
Assets map[uint64]map[string]*communities.AssetContractData
Signers map[string]string
}
@ -90,7 +89,7 @@ func (c *CollectiblesServiceMock) SetSignerPubKey(ctx context.Context, chainID u
return "", nil
}
func (c *CollectiblesServiceMock) GetCollectibleContractData(chainID uint64, contractAddress string) (*communitytokens.CollectibleContractData, error) {
func (c *CollectiblesServiceMock) GetCollectibleContractData(chainID uint64, contractAddress string) (*communities.CollectibleContractData, error) {
collectibleContractData, dataExists := c.Collectibles[chainID][contractAddress]
if dataExists {
return collectibleContractData, nil
@ -98,7 +97,7 @@ func (c *CollectiblesServiceMock) GetCollectibleContractData(chainID uint64, con
return nil, nil
}
func (c *CollectiblesServiceMock) GetAssetContractData(chainID uint64, contractAddress string) (*communitytokens.AssetContractData, error) {
func (c *CollectiblesServiceMock) GetAssetContractData(chainID uint64, contractAddress string) (*communities.AssetContractData, error) {
assetsContractData, dataExists := c.Assets[chainID][contractAddress]
if dataExists {
return assetsContractData, nil
@ -106,22 +105,22 @@ func (c *CollectiblesServiceMock) GetAssetContractData(chainID uint64, contractA
return nil, nil
}
func (c *CollectiblesServiceMock) SetMockCollectibleContractData(chainID uint64, contractAddress string, collectible *communitytokens.CollectibleContractData) {
func (c *CollectiblesServiceMock) SetMockCollectibleContractData(chainID uint64, contractAddress string, collectible *communities.CollectibleContractData) {
if c.Collectibles == nil {
c.Collectibles = make(map[uint64]map[string]*communitytokens.CollectibleContractData)
c.Collectibles = make(map[uint64]map[string]*communities.CollectibleContractData)
}
if _, ok := c.Collectibles[chainID]; !ok {
c.Collectibles[chainID] = make(map[string]*communitytokens.CollectibleContractData)
c.Collectibles[chainID] = make(map[string]*communities.CollectibleContractData)
}
c.Collectibles[chainID][contractAddress] = collectible
}
func (c *CollectiblesServiceMock) SetMockCommunityTokenData(token *token.CommunityToken) {
if c.Collectibles == nil {
c.Collectibles = make(map[uint64]map[string]*communitytokens.CollectibleContractData)
c.Collectibles = make(map[uint64]map[string]*communities.CollectibleContractData)
}
data := &communitytokens.CollectibleContractData{
data := &communities.CollectibleContractData{
TotalSupply: token.Supply,
Transferable: token.Transferable,
RemoteBurnable: token.RemoteSelfDestruct,
@ -138,11 +137,11 @@ func (c *CollectiblesServiceMock) SafeGetSignerPubKey(ctx context.Context, chain
return c.Signers[communityID], nil
}
func (c *CollectiblesServiceMock) SetMockAssetContractData(chainID uint64, contractAddress string, assetData *communitytokens.AssetContractData) {
func (c *CollectiblesServiceMock) SetMockAssetContractData(chainID uint64, contractAddress string, assetData *communities.AssetContractData) {
if c.Assets == nil {
c.Assets = make(map[uint64]map[string]*communitytokens.AssetContractData)
c.Assets = make(map[uint64]map[string]*communities.AssetContractData)
}
c.Assets[chainID] = make(map[string]*communitytokens.AssetContractData)
c.Assets[chainID] = make(map[string]*communities.AssetContractData)
c.Assets[chainID][contractAddress] = assetData
}
@ -159,7 +158,7 @@ type testCommunitiesMessengerConfig struct {
password string
walletAddresses []string
mockedBalances *map[uint64]map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big
collectiblesService communitytokens.ServiceInterface
collectiblesService communities.CommunityTokensServiceInterface
}
func (tcmc *testCommunitiesMessengerConfig) complete() error {

View File

@ -22,7 +22,6 @@ import (
"github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/protocol/requests"
"github.com/status-im/status-go/protocol/tt"
"github.com/status-im/status-go/services/communitytokens"
"github.com/status-im/status-go/services/wallet/bigint"
"github.com/status-im/status-go/waku"
)
@ -182,7 +181,7 @@ func (s *MessengerCommunitiesSignersSuite) TestControlNodeUpdateSigner() {
// update mock - the signer for the community returned by the contracts should be john
s.collectiblesServiceMock.SetSignerPubkeyForCommunity(community.ID(), common.PubkeyToHex(&s.john.identity.PublicKey))
s.collectiblesServiceMock.SetMockCollectibleContractData(chainID, tokenAddress,
&communitytokens.CollectibleContractData{TotalSupply: &bigint.BigInt{}})
&communities.CollectibleContractData{TotalSupply: &bigint.BigInt{}})
// bob accepts community update
_, err = WaitOnSignaledMessengerResponse(
@ -377,7 +376,7 @@ func (s *MessengerCommunitiesSignersSuite) TestAutoAcceptOnOwnershipChangeReques
// set john as contract owner
s.collectiblesServiceMock.SetSignerPubkeyForCommunity(community.ID(), common.PubkeyToHex(&s.john.identity.PublicKey))
s.collectiblesServiceMock.SetMockCollectibleContractData(chainID, tokenAddress,
&communitytokens.CollectibleContractData{TotalSupply: &bigint.BigInt{}})
&communities.CollectibleContractData{TotalSupply: &bigint.BigInt{}})
hasTokenPermission := func(r *MessengerResponse) bool {
return len(r.Communities()) > 0 && r.Communities()[0].HasTokenPermissions()
@ -515,7 +514,7 @@ func (s *MessengerCommunitiesSignersSuite) TestNewOwnerAcceptRequestToJoin() {
// update mock - the signer for the community returned by the contracts should be john
s.collectiblesServiceMock.SetSignerPubkeyForCommunity(community.ID(), common.PubkeyToHex(&s.john.identity.PublicKey))
s.collectiblesServiceMock.SetMockCollectibleContractData(chainID, tokenAddress,
&communitytokens.CollectibleContractData{TotalSupply: &bigint.BigInt{}})
&communities.CollectibleContractData{TotalSupply: &bigint.BigInt{}})
// alice accepts community update
_, err = WaitOnSignaledMessengerResponse(

View File

@ -62,7 +62,6 @@ import (
"github.com/status-im/status-go/protocol/verification"
"github.com/status-im/status-go/server"
"github.com/status-im/status-go/services/browsers"
"github.com/status-im/status-go/services/communitytokens"
ensservice "github.com/status-im/status-go/services/ens"
"github.com/status-im/status-go/services/ext/mailservers"
localnotifications "github.com/status-im/status-go/services/local-notifications"
@ -174,7 +173,7 @@ type Messenger struct {
// flag to enable backedup messages processing, false by default
processBackedupMessages bool
communityTokensService communitytokens.ServiceInterface
communityTokensService communities.CommunityTokensServiceInterface
// used to track dispatched messages
dispatchMessageTestCallback func(common.RawMessage)

View File

@ -4221,6 +4221,10 @@ func (m *Messenger) GetCommunityToken(communityID string, chainID int, address s
return m.communitiesManager.GetCommunityToken(communityID, chainID, address)
}
func (m *Messenger) GetCommunityTokenByChainAndAddress(chainID int, address string) (*token.CommunityToken, error) {
return m.communitiesManager.GetCommunityTokenByChainAndAddress(chainID, address)
}
func (m *Messenger) GetCommunityTokens(communityID string) ([]*token.CommunityToken, error) {
return m.communitiesManager.GetCommunityTokens(communityID)
}

View File

@ -9,7 +9,6 @@ import (
"github.com/status-im/status-go/rpc"
"github.com/status-im/status-go/server"
"github.com/status-im/status-go/services/browsers"
"github.com/status-im/status-go/services/communitytokens"
"github.com/status-im/status-go/wakuv2"
"go.uber.org/zap"
@ -90,7 +89,7 @@ type config struct {
torrentConfig *params.TorrentConfig
walletConfig *params.WalletConfig
walletService *wallet.Service
communityTokensService communitytokens.ServiceInterface
communityTokensService communities.CommunityTokensServiceInterface
httpServer *server.MediaServer
rpcClient *rpc.Client
tokenManager communities.TokenManager
@ -379,7 +378,7 @@ func WithWalletService(s *wallet.Service) Option {
}
}
func WithCommunityTokensService(s communitytokens.ServiceInterface) Option {
func WithCommunityTokensService(s communities.CommunityTokensServiceInterface) Option {
return func(c *config) error {
c.communityTokensService = s
return nil

View File

@ -29,7 +29,6 @@ import (
"github.com/status-im/status-go/protocol/requests"
"github.com/status-im/status-go/protocol/transport"
"github.com/status-im/status-go/protocol/tt"
"github.com/status-im/status-go/services/communitytokens"
mailserversDB "github.com/status-im/status-go/services/mailservers"
"github.com/status-im/status-go/services/wallet/bigint"
"github.com/status-im/status-go/t/helpers"
@ -1042,7 +1041,7 @@ func (s *MessengerStoreNodeRequestSuite) TestFetchRealCommunity() {
for _, communityToken := range communityTokens {
s.collectiblesServiceMock.SetSignerPubkeyForCommunity(communityIDBytes, ownerPublicKey)
s.collectiblesServiceMock.SetMockCollectibleContractData(communityToken.ChainID, communityToken.ContractAddress,
&communitytokens.CollectibleContractData{TotalSupply: &bigint.BigInt{}})
&communities.CollectibleContractData{TotalSupply: &bigint.BigInt{}})
}
results := map[string]singleResult{}
@ -1205,7 +1204,7 @@ func (s *MessengerStoreNodeRequestSuite) TestFetchingCommunityWithOwnerToken() {
// update mock - the signer for the community returned by the contracts should be owner
s.collectiblesServiceMock.SetSignerPubkeyForCommunity(community.ID(), common.PubkeyToHex(&s.owner.identity.PublicKey))
s.collectiblesServiceMock.SetMockCollectibleContractData(chainID, tokenAddress,
&communitytokens.CollectibleContractData{TotalSupply: &bigint.BigInt{}})
&communities.CollectibleContractData{TotalSupply: &bigint.BigInt{}})
community, err = s.owner.communitiesManager.GetByID(community.ID())
s.Require().NoError(err)

View File

@ -2,11 +2,12 @@ package communitytokens
import (
"context"
"errors"
"fmt"
"math/big"
"strings"
"github.com/pkg/errors"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
@ -19,6 +20,9 @@ import (
"github.com/status-im/status-go/contracts/community-tokens/ownertoken"
communityownertokenregistry "github.com/status-im/status-go/contracts/community-tokens/registry"
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/images"
"github.com/status-im/status-go/protocol/communities/token"
"github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/services/utils"
"github.com/status-im/status-go/services/wallet/bigint"
@ -39,6 +43,9 @@ type API struct {
type DeploymentDetails struct {
ContractAddress string `json:"contractAddress"`
TransactionHash string `json:"transactionHash"`
CommunityToken *token.CommunityToken `json:"communityToken"`
OwnerToken *token.CommunityToken `json:"ownerToken"`
MasterToken *token.CommunityToken `json:"masterToken"`
}
const maxSupply = 999999999
@ -53,6 +60,11 @@ type DeploymentParameters struct {
TokenURI string `json:"tokenUri"`
OwnerTokenAddress string `json:"ownerTokenAddress"`
MasterTokenAddress string `json:"masterTokenAddress"`
CommunityID string `json:"communityId"`
Description string `json:"description"`
CroppedImage *images.CroppedImage `json:"croppedImage,omitempty"` // for community tokens
Base64Image string `json:"base64image"` // for owner & master tokens
Decimals int `json:"decimals"`
}
func (d *DeploymentParameters) GetSupply() *big.Int {
@ -92,12 +104,10 @@ func (d *DeploymentParameters) Validate(isAsset bool) error {
}
func (api *API) DeployCollectibles(ctx context.Context, chainID uint64, deploymentParameters DeploymentParameters, txArgs transactions.SendTxArgs, password string) (DeploymentDetails, error) {
err := deploymentParameters.Validate(false)
if err != nil {
return DeploymentDetails{}, err
}
transactOpts := txArgs.ToTransactOpts(utils.GetSigner(chainID, api.s.accountsManager, api.s.config.KeyStoreDir, txArgs.From, password))
ethClient, err := api.s.manager.rpcClient.EthClient(chainID)
@ -105,7 +115,6 @@ func (api *API) DeployCollectibles(ctx context.Context, chainID uint64, deployme
log.Error(err.Error())
return DeploymentDetails{}, err
}
address, tx, _, err := collectibles.DeployCollectibles(transactOpts, ethClient, deploymentParameters.Name,
deploymentParameters.Symbol, deploymentParameters.GetSupply(),
deploymentParameters.RemoteSelfDestruct, deploymentParameters.Transferable,
@ -120,15 +129,26 @@ func (api *API) DeployCollectibles(ctx context.Context, chainID uint64, deployme
wcommon.ChainID(chainID),
tx.Hash(),
common.Address(txArgs.From),
address,
transactions.DeployCommunityToken,
transactions.AutoDelete,
transactions.Keep,
"",
)
if err != nil {
log.Error("TrackPendingTransaction error", "error", err)
return DeploymentDetails{}, err
}
return DeploymentDetails{address.Hex(), tx.Hash().Hex()}, nil
savedCommunityToken, err := api.s.CreateCommunityTokenAndSave(int(chainID), deploymentParameters, txArgs.From.Hex(), address.Hex(),
protobuf.CommunityTokenType_ERC721, token.CommunityLevel)
if err != nil {
return DeploymentDetails{}, err
}
return DeploymentDetails{
ContractAddress: address.Hex(),
TransactionHash: tx.Hash().Hex(),
CommunityToken: savedCommunityToken}, nil
}
func decodeSignature(sig []byte) (r [32]byte, s [32]byte, v uint8, err error) {
@ -162,8 +182,7 @@ func prepareDeploymentSignatureStruct(signature string, communityID string, addr
func (api *API) DeployOwnerToken(ctx context.Context, chainID uint64,
ownerTokenParameters DeploymentParameters, masterTokenParameters DeploymentParameters,
signature string, communityID string, signerPubKey string,
txArgs transactions.SendTxArgs, password string) (DeploymentDetails, error) {
signerPubKey string, txArgs transactions.SendTxArgs, password string) (DeploymentDetails, error) {
err := ownerTokenParameters.Validate(false)
if err != nil {
return DeploymentDetails{}, err
@ -197,7 +216,12 @@ func (api *API) DeployOwnerToken(ctx context.Context, chainID uint64,
BaseURI: masterTokenParameters.TokenURI,
}
communitySignature, err := prepareDeploymentSignatureStruct(signature, communityID, common.Address(txArgs.From))
signature, err := api.s.Messenger.CreateCommunityTokenDeploymentSignature(context.Background(), chainID, txArgs.From.Hex(), ownerTokenParameters.CommunityID)
if err != nil {
return DeploymentDetails{}, err
}
communitySignature, err := prepareDeploymentSignatureStruct(types.HexBytes(signature).String(), ownerTokenParameters.CommunityID, common.Address(txArgs.From))
if err != nil {
return DeploymentDetails{}, err
}
@ -211,81 +235,38 @@ func (api *API) DeployOwnerToken(ctx context.Context, chainID uint64,
return DeploymentDetails{}, err
}
log.Debug("Contract deployed hash:", tx.Hash().String())
err = api.s.pendingTracker.TrackPendingTransaction(
wcommon.ChainID(chainID),
tx.Hash(),
common.Address(txArgs.From),
common.Address{},
transactions.DeployOwnerToken,
transactions.AutoDelete,
transactions.Keep,
"",
)
if err != nil {
log.Error("TrackPendingTransaction error", "error", err)
return DeploymentDetails{}, err
}
return DeploymentDetails{"", tx.Hash().Hex()}, nil
}
func (api *API) GetMasterTokenContractAddressFromHash(ctx context.Context, chainID uint64, txHash string) (string, error) {
ethClient, err := api.s.manager.rpcClient.EthClient(chainID)
savedOwnerToken, err := api.s.CreateCommunityTokenAndSave(int(chainID), ownerTokenParameters, txArgs.From.Hex(),
api.s.TemporaryOwnerContractAddress(tx.Hash().Hex()), protobuf.CommunityTokenType_ERC721, token.OwnerLevel)
if err != nil {
return "", err
return DeploymentDetails{}, err
}
receipt, err := ethClient.TransactionReceipt(ctx, common.HexToHash(txHash))
savedMasterToken, err := api.s.CreateCommunityTokenAndSave(int(chainID), masterTokenParameters, txArgs.From.Hex(),
api.s.TemporaryMasterContractAddress(tx.Hash().Hex()), protobuf.CommunityTokenType_ERC721, token.MasterLevel)
if err != nil {
return "", err
return DeploymentDetails{}, err
}
deployerContractInst, err := api.NewCommunityTokenDeployerInstance(chainID)
if err != nil {
return "", err
}
logMasterTokenCreatedSig := []byte("DeployMasterToken(address)")
logMasterTokenCreatedSigHash := crypto.Keccak256Hash(logMasterTokenCreatedSig)
for _, vLog := range receipt.Logs {
if vLog.Topics[0].Hex() == logMasterTokenCreatedSigHash.Hex() {
event, err := deployerContractInst.ParseDeployMasterToken(*vLog)
if err != nil {
return "", err
}
return event.Arg0.Hex(), nil
}
}
return "", fmt.Errorf("can't find master token address in transaction: %v", txHash)
}
func (api *API) GetOwnerTokenContractAddressFromHash(ctx context.Context, chainID uint64, txHash string) (string, error) {
ethClient, err := api.s.manager.rpcClient.EthClient(chainID)
if err != nil {
return "", err
}
receipt, err := ethClient.TransactionReceipt(ctx, common.HexToHash(txHash))
if err != nil {
return "", err
}
deployerContractInst, err := api.NewCommunityTokenDeployerInstance(chainID)
if err != nil {
return "", err
}
logOwnerTokenCreatedSig := []byte("DeployOwnerToken(address)")
logOwnerTokenCreatedSigHash := crypto.Keccak256Hash(logOwnerTokenCreatedSig)
for _, vLog := range receipt.Logs {
if vLog.Topics[0].Hex() == logOwnerTokenCreatedSigHash.Hex() {
event, err := deployerContractInst.ParseDeployOwnerToken(*vLog)
if err != nil {
return "", err
}
return event.Arg0.Hex(), nil
}
}
return "", fmt.Errorf("can't find owner token address in transaction: %v", txHash)
return DeploymentDetails{
ContractAddress: "",
TransactionHash: tx.Hash().Hex(),
OwnerToken: savedOwnerToken,
MasterToken: savedMasterToken}, nil
}
func (api *API) DeployAssets(ctx context.Context, chainID uint64, deploymentParameters DeploymentParameters, txArgs transactions.SendTxArgs, password string) (DeploymentDetails, error) {
@ -318,15 +299,26 @@ func (api *API) DeployAssets(ctx context.Context, chainID uint64, deploymentPara
wcommon.ChainID(chainID),
tx.Hash(),
common.Address(txArgs.From),
address,
transactions.DeployCommunityToken,
transactions.AutoDelete,
transactions.Keep,
"",
)
if err != nil {
log.Error("TrackPendingTransaction error", "error", err)
return DeploymentDetails{}, err
}
return DeploymentDetails{address.Hex(), tx.Hash().Hex()}, nil
savedCommunityToken, err := api.s.CreateCommunityTokenAndSave(int(chainID), deploymentParameters, txArgs.From.Hex(), address.Hex(),
protobuf.CommunityTokenType_ERC20, token.CommunityLevel)
if err != nil {
return DeploymentDetails{}, err
}
return DeploymentDetails{
ContractAddress: address.Hex(),
TransactionHash: tx.Hash().Hex(),
CommunityToken: savedCommunityToken}, nil
}
// Returns gas units + 10%
@ -403,7 +395,7 @@ func (api *API) DeployAssetsEstimate(ctx context.Context, chainID uint64, fromAd
func (api *API) DeployOwnerTokenEstimate(ctx context.Context, chainID uint64, fromAddress string,
ownerTokenParameters DeploymentParameters, masterTokenParameters DeploymentParameters,
signature string, communityID string, signerPubKey string) (uint64, error) {
communityID string, signerPubKey string) (uint64, error) {
ethClient, err := api.s.manager.rpcClient.EthClient(chainID)
if err != nil {
log.Error(err.Error())
@ -432,7 +424,12 @@ func (api *API) DeployOwnerTokenEstimate(ctx context.Context, chainID uint64, fr
BaseURI: masterTokenParameters.TokenURI,
}
communitySignature, err := prepareDeploymentSignatureStruct(signature, communityID, common.HexToAddress(fromAddress))
signature, err := api.s.Messenger.CreateCommunityTokenDeploymentSignature(ctx, chainID, fromAddress, communityID)
if err != nil {
return 0, err
}
communitySignature, err := prepareDeploymentSignatureStruct(types.HexBytes(signature).String(), communityID, common.HexToAddress(fromAddress))
if err != nil {
return 0, err
}
@ -531,8 +528,10 @@ func (api *API) MintTokens(ctx context.Context, chainID uint64, contractAddress
wcommon.ChainID(chainID),
tx.Hash(),
common.Address(txArgs.From),
common.HexToAddress(contractAddress),
transactions.AirdropCommunityToken,
transactions.AutoDelete,
transactions.Keep,
"",
)
if err != nil {
log.Error("TrackPendingTransaction error", "error", err)
@ -614,7 +613,7 @@ func (api *API) RemoteDestructedAmount(ctx context.Context, chainID uint64, cont
}
// This is only ERC721 function
func (api *API) RemoteBurn(ctx context.Context, chainID uint64, contractAddress string, txArgs transactions.SendTxArgs, password string, tokenIds []*bigint.BigInt) (string, error) {
func (api *API) RemoteBurn(ctx context.Context, chainID uint64, contractAddress string, txArgs transactions.SendTxArgs, password string, tokenIds []*bigint.BigInt, additionalData string) (string, error) {
err := api.validateTokens(tokenIds)
if err != nil {
return "", err
@ -641,8 +640,10 @@ func (api *API) RemoteBurn(ctx context.Context, chainID uint64, contractAddress
wcommon.ChainID(chainID),
tx.Hash(),
common.Address(txArgs.From),
common.HexToAddress(contractAddress),
transactions.RemoteDestructCollectible,
transactions.AutoDelete,
transactions.Keep,
additionalData,
)
if err != nil {
log.Error("TrackPendingTransaction error", "error", err)
@ -730,42 +731,8 @@ func (api *API) remainingAssetsSupply(ctx context.Context, chainID uint64, contr
return &bigint.BigInt{Int: res}, nil
}
func (api *API) maxSupplyCollectibles(ctx context.Context, chainID uint64, contractAddress string) (*big.Int, error) {
callOpts := &bind.CallOpts{Context: ctx, Pending: false}
contractInst, err := api.NewCollectiblesInstance(chainID, contractAddress)
if err != nil {
return nil, err
}
return contractInst.MaxSupply(callOpts)
}
func (api *API) maxSupplyAssets(ctx context.Context, chainID uint64, contractAddress string) (*big.Int, error) {
callOpts := &bind.CallOpts{Context: ctx, Pending: false}
contractInst, err := api.NewAssetsInstance(chainID, contractAddress)
if err != nil {
return nil, err
}
return contractInst.MaxSupply(callOpts)
}
func (api *API) maxSupply(ctx context.Context, chainID uint64, contractAddress string) (*big.Int, error) {
tokenType, err := api.s.db.GetTokenType(chainID, contractAddress)
if err != nil {
return nil, err
}
switch tokenType {
case protobuf.CommunityTokenType_ERC721:
return api.maxSupplyCollectibles(ctx, chainID, contractAddress)
case protobuf.CommunityTokenType_ERC20:
return api.maxSupplyAssets(ctx, chainID, contractAddress)
default:
return nil, fmt.Errorf("unknown token type: %v", tokenType)
}
}
func (api *API) prepareNewMaxSupply(ctx context.Context, chainID uint64, contractAddress string, burnAmount *bigint.BigInt) (*big.Int, error) {
maxSupply, err := api.maxSupply(ctx, chainID, contractAddress)
maxSupply, err := api.s.maxSupply(ctx, chainID, contractAddress)
if err != nil {
return nil, err
}
@ -801,8 +768,10 @@ func (api *API) Burn(ctx context.Context, chainID uint64, contractAddress string
wcommon.ChainID(chainID),
tx.Hash(),
common.Address(txArgs.From),
common.HexToAddress(contractAddress),
transactions.BurnCommunityToken,
transactions.AutoDelete,
transactions.Keep,
"",
)
if err != nil {
log.Error("TrackPendingTransaction error", "error", err)

View File

@ -19,37 +19,37 @@ func TestDeploymentParameters(t *testing.T) {
}{
{
name: "emptyName",
parameters: DeploymentParameters{"", "SYMBOL", &bigint.BigInt{Int: big.NewInt(int64(123))}, false, false, false, "", "", ""},
parameters: DeploymentParameters{"", "SYMBOL", &bigint.BigInt{Int: big.NewInt(int64(123))}, false, false, false, "", "", "", "", "", nil, "", 0},
isError: true,
},
{
name: "emptySymbol",
parameters: DeploymentParameters{"NAME", "", &bigint.BigInt{Int: big.NewInt(123)}, false, false, false, "", "", ""},
parameters: DeploymentParameters{"NAME", "", &bigint.BigInt{Int: big.NewInt(123)}, false, false, false, "", "", "", "", "", nil, "", 0},
isError: true,
},
{
name: "negativeSupply",
parameters: DeploymentParameters{"NAME", "SYM", &bigint.BigInt{Int: big.NewInt(-123)}, false, false, false, "", "", ""},
parameters: DeploymentParameters{"NAME", "SYM", &bigint.BigInt{Int: big.NewInt(-123)}, false, false, false, "", "", "", "", "", nil, "", 0},
isError: true,
},
{
name: "zeroSupply",
parameters: DeploymentParameters{"NAME", "SYM", &bigint.BigInt{Int: big.NewInt(0)}, false, false, false, "", "", ""},
parameters: DeploymentParameters{"NAME", "SYM", &bigint.BigInt{Int: big.NewInt(0)}, false, false, false, "", "", "", "", "", nil, "", 0},
isError: false,
},
{
name: "negativeSupplyAndInfinite",
parameters: DeploymentParameters{"NAME", "SYM", &bigint.BigInt{Int: big.NewInt(-123)}, true, false, false, "", "", ""},
parameters: DeploymentParameters{"NAME", "SYM", &bigint.BigInt{Int: big.NewInt(-123)}, true, false, false, "", "", "", "", "", nil, "", 0},
isError: false,
},
{
name: "supplyGreaterThanMax",
parameters: DeploymentParameters{"NAME", "SYM", &bigint.BigInt{Int: big.NewInt(maxSupply + 1)}, false, false, false, "", "", ""},
parameters: DeploymentParameters{"NAME", "SYM", &bigint.BigInt{Int: big.NewInt(maxSupply + 1)}, false, false, false, "", "", "", "", "", nil, "", 0},
isError: true,
},
{
name: "supplyIsMax",
parameters: DeploymentParameters{"NAME", "SYM", &bigint.BigInt{Int: big.NewInt(maxSupply)}, false, false, false, "", "", ""},
parameters: DeploymentParameters{"NAME", "SYM", &bigint.BigInt{Int: big.NewInt(maxSupply)}, false, false, false, "", "", "", "", "", nil, "", 0},
isError: false,
},
}
@ -65,10 +65,10 @@ func TestDeploymentParameters(t *testing.T) {
})
}
notInfiniteSupplyParams := DeploymentParameters{"NAME", "SYM", &bigint.BigInt{Int: big.NewInt(123)}, false, false, false, "", "", ""}
notInfiniteSupplyParams := DeploymentParameters{"NAME", "SYM", &bigint.BigInt{Int: big.NewInt(123)}, false, false, false, "", "", "", "", "", nil, "", 0}
requiredSupply := big.NewInt(123)
require.Equal(t, notInfiniteSupplyParams.GetSupply(), requiredSupply)
infiniteSupplyParams := DeploymentParameters{"NAME", "SYM", &bigint.BigInt{Int: big.NewInt(123)}, true, false, false, "", "", ""}
infiniteSupplyParams := DeploymentParameters{"NAME", "SYM", &bigint.BigInt{Int: big.NewInt(123)}, true, false, false, "", "", "", "", "", nil, "", 0}
requiredSupply = infiniteSupplyParams.GetInfiniteSupply()
require.Equal(t, infiniteSupplyParams.GetSupply(), requiredSupply)
}

View File

@ -1,8 +0,0 @@
package communitytokens
import "github.com/status-im/status-go/services/wallet/bigint"
type AssetContractData struct {
TotalSupply *bigint.BigInt
InfiniteSupply bool
}

View File

@ -1,10 +0,0 @@
package communitytokens
import "github.com/status-im/status-go/services/wallet/bigint"
type CollectibleContractData struct {
TotalSupply *bigint.BigInt
Transferable bool
RemoteBurnable bool
InfiniteSupply bool
}

View File

@ -1,4 +1,4 @@
package communitytokens
package communitytokensdatabase
import (
"database/sql"

View File

@ -1,4 +1,4 @@
package communitytokens
package communitytokensdatabase
import (
"database/sql"

View File

@ -13,6 +13,7 @@ import (
communitytokendeployer "github.com/status-im/status-go/contracts/community-tokens/deployer"
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol/communities"
"github.com/status-im/status-go/rpc"
"github.com/status-im/status-go/services/wallet/bigint"
)
@ -71,7 +72,7 @@ func (m *Manager) GetAssetContractInstance(chainID uint64, contractAddress strin
return contractInst, nil
}
func (m *Manager) GetCollectibleContractData(chainID uint64, contractAddress string) (*CollectibleContractData, error) {
func (m *Manager) GetCollectibleContractData(chainID uint64, contractAddress string) (*communities.CollectibleContractData, error) {
callOpts := &bind.CallOpts{Context: context.Background(), Pending: false}
contract, err := m.GetCollectiblesContractInstance(chainID, contractAddress)
@ -91,7 +92,7 @@ func (m *Manager) GetCollectibleContractData(chainID uint64, contractAddress str
return nil, err
}
return &CollectibleContractData{
return &communities.CollectibleContractData{
TotalSupply: &bigint.BigInt{Int: totalSupply},
Transferable: transferable,
RemoteBurnable: remoteBurnable,
@ -99,7 +100,7 @@ func (m *Manager) GetCollectibleContractData(chainID uint64, contractAddress str
}, nil
}
func (m *Manager) GetAssetContractData(chainID uint64, contractAddress string) (*AssetContractData, error) {
func (m *Manager) GetAssetContractData(chainID uint64, contractAddress string) (*communities.AssetContractData, error) {
callOpts := &bind.CallOpts{Context: context.Background(), Pending: false}
contract, err := m.GetAssetContractInstance(chainID, contractAddress)
if err != nil {
@ -110,7 +111,7 @@ func (m *Manager) GetAssetContractData(chainID uint64, contractAddress string) (
return nil, err
}
return &AssetContractData{
return &communities.AssetContractData{
TotalSupply: &bigint.BigInt{Int: totalSupply},
InfiniteSupply: GetInfiniteSupply().Cmp(totalSupply) == 0,
}, nil

View File

@ -3,49 +3,60 @@ package communitytokens
import (
"context"
"database/sql"
"encoding/json"
"fmt"
"math/big"
"github.com/pkg/errors"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p"
ethRpc "github.com/ethereum/go-ethereum/rpc"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/contracts/community-tokens/ownertoken"
communityownertokenregistry "github.com/status-im/status-go/contracts/community-tokens/registry"
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/protocol"
"github.com/status-im/status-go/protocol/communities"
"github.com/status-im/status-go/protocol/communities/token"
"github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/rpc"
"github.com/status-im/status-go/services/communitytokens/communitytokensdatabase"
"github.com/status-im/status-go/services/utils"
"github.com/status-im/status-go/services/wallet/bigint"
wcommon "github.com/status-im/status-go/services/wallet/common"
"github.com/status-im/status-go/services/wallet/walletevent"
"github.com/status-im/status-go/signal"
"github.com/status-im/status-go/transactions"
)
type ServiceInterface interface {
GetCollectibleContractData(chainID uint64, contractAddress string) (*CollectibleContractData, error)
SetSignerPubKey(ctx context.Context, chainID uint64, contractAddress string, txArgs transactions.SendTxArgs, password string, newSignerPubKey string) (string, error)
GetAssetContractData(chainID uint64, contractAddress string) (*AssetContractData, error)
SafeGetSignerPubKey(ctx context.Context, chainID uint64, communityID string) (string, error)
DeploymentSignatureDigest(chainID uint64, addressFrom string, communityID string) ([]byte, error)
}
// Collectibles service
type Service struct {
manager *Manager
accountsManager *account.GethManager
pendingTracker *transactions.PendingTxTracker
config *params.NodeConfig
db *Database
db *communitytokensdatabase.Database
Messenger *protocol.Messenger
walletFeed *event.Feed
walletWatcher *walletevent.Watcher
}
// Returns a new Collectibles Service.
func NewService(rpcClient *rpc.Client, accountsManager *account.GethManager, pendingTracker *transactions.PendingTxTracker, config *params.NodeConfig, appDb *sql.DB) *Service {
func NewService(rpcClient *rpc.Client, accountsManager *account.GethManager, pendingTracker *transactions.PendingTxTracker,
config *params.NodeConfig, appDb *sql.DB, walletFeed *event.Feed) *Service {
return &Service{
manager: &Manager{rpcClient: rpcClient},
accountsManager: accountsManager,
pendingTracker: pendingTracker,
config: config,
db: NewCommunityTokensDatabase(appDb),
db: communitytokensdatabase.NewCommunityTokensDatabase(appDb),
walletFeed: walletFeed,
}
}
@ -68,14 +79,181 @@ func (s *Service) APIs() []ethRpc.API {
// Start is run when a service is started.
func (s *Service) Start() error {
s.walletWatcher = walletevent.NewWatcher(s.walletFeed, s.handleWalletEvent)
s.walletWatcher.Start()
return nil
}
func (s *Service) handleWalletEvent(event walletevent.Event) {
if event.Type == transactions.EventPendingTransactionStatusChanged {
var p transactions.StatusChangedPayload
err := json.Unmarshal([]byte(event.Message), &p)
if err != nil {
log.Error(errors.Wrap(err, fmt.Sprintf("can't parse transaction message %v\n", event.Message)).Error())
return
}
if p.Status == transactions.Pending {
return
}
pendingTransaction, err := s.pendingTracker.GetPendingEntry(p.ChainID, p.Hash)
if err != nil {
log.Error(errors.Wrap(err, fmt.Sprintf("no pending transaction with hash %v on chain %v\n", p.Hash, p.ChainID)).Error())
return
}
var communityToken, ownerToken, masterToken *token.CommunityToken = &token.CommunityToken{}, &token.CommunityToken{}, &token.CommunityToken{}
var tokenErr error
switch pendingTransaction.Type {
case transactions.DeployCommunityToken:
communityToken, tokenErr = s.handleDeployCommunityToken(p.Status, pendingTransaction)
case transactions.AirdropCommunityToken:
communityToken, tokenErr = s.handleAirdropCommunityToken(p.Status, pendingTransaction)
case transactions.RemoteDestructCollectible:
communityToken, tokenErr = s.handleRemoteDestructCollectible(p.Status, pendingTransaction)
case transactions.BurnCommunityToken:
communityToken, tokenErr = s.handleBurnCommunityToken(p.Status, pendingTransaction)
case transactions.DeployOwnerToken:
ownerToken, masterToken, tokenErr = s.handleDeployOwnerToken(p.Status, pendingTransaction)
case transactions.SetSignerPublicKey:
communityToken, tokenErr = s.handleSetSignerPubKey(p.Status, pendingTransaction)
default:
return
}
err = s.pendingTracker.Delete(context.Background(), p.ChainID, p.Hash)
if err != nil {
log.Error(errors.Wrap(err, fmt.Sprintf("can't delete pending transaction with hash %v on chain %v\n", p.Hash, p.ChainID)).Error())
}
errorStr := ""
if tokenErr != nil {
errorStr = tokenErr.Error()
}
signal.SendCommunityTokenTransactionStatusSignal(string(pendingTransaction.Type), p.Status == transactions.Success, pendingTransaction.Hash,
communityToken, ownerToken, masterToken, errorStr)
}
}
func (s *Service) handleAirdropCommunityToken(status string, pendingTransaction *transactions.PendingTransaction) (*token.CommunityToken, error) {
return s.Messenger.GetCommunityTokenByChainAndAddress(int(pendingTransaction.ChainID), pendingTransaction.To.String())
}
func (s *Service) handleRemoteDestructCollectible(status string, pendingTransaction *transactions.PendingTransaction) (*token.CommunityToken, error) {
return s.Messenger.GetCommunityTokenByChainAndAddress(int(pendingTransaction.ChainID), pendingTransaction.To.String())
}
func (s *Service) handleBurnCommunityToken(status string, pendingTransaction *transactions.PendingTransaction) (*token.CommunityToken, error) {
if status == transactions.Success {
// get new max supply and update database
newMaxSupply, err := s.maxSupply(context.Background(), uint64(pendingTransaction.ChainID), pendingTransaction.To.String())
if err != nil {
return nil, err
}
err = s.Messenger.UpdateCommunityTokenSupply(int(pendingTransaction.ChainID), pendingTransaction.To.String(), &bigint.BigInt{Int: newMaxSupply})
if err != nil {
return nil, err
}
}
return s.Messenger.GetCommunityTokenByChainAndAddress(int(pendingTransaction.ChainID), pendingTransaction.To.String())
}
func (s *Service) handleDeployOwnerToken(status string, pendingTransaction *transactions.PendingTransaction) (*token.CommunityToken, *token.CommunityToken, error) {
newMasterAddress, err := s.GetMasterTokenContractAddressFromHash(context.Background(), uint64(pendingTransaction.ChainID), pendingTransaction.Hash.Hex())
if err != nil {
return nil, nil, err
}
newOwnerAddress, err := s.GetOwnerTokenContractAddressFromHash(context.Background(), uint64(pendingTransaction.ChainID), pendingTransaction.Hash.Hex())
if err != nil {
return nil, nil, err
}
err = s.Messenger.UpdateCommunityTokenAddress(int(pendingTransaction.ChainID), s.TemporaryOwnerContractAddress(pendingTransaction.Hash.Hex()), newOwnerAddress)
if err != nil {
return nil, nil, err
}
err = s.Messenger.UpdateCommunityTokenAddress(int(pendingTransaction.ChainID), s.TemporaryMasterContractAddress(pendingTransaction.Hash.Hex()), newMasterAddress)
if err != nil {
return nil, nil, err
}
ownerToken, err := s.updateStateAndAddTokenToCommunityDescription(status, int(pendingTransaction.ChainID), newOwnerAddress)
if err != nil {
return nil, nil, err
}
masterToken, err := s.updateStateAndAddTokenToCommunityDescription(status, int(pendingTransaction.ChainID), newMasterAddress)
if err != nil {
return nil, nil, err
}
return ownerToken, masterToken, nil
}
func (s *Service) updateStateAndAddTokenToCommunityDescription(status string, chainID int, address string) (*token.CommunityToken, error) {
tokenToUpdate, err := s.Messenger.GetCommunityTokenByChainAndAddress(chainID, address)
if err != nil {
return nil, err
}
if tokenToUpdate == nil {
return nil, fmt.Errorf("token does not exist in database: chainID=%v, address=%v", chainID, address)
}
if status == transactions.Success {
err := s.Messenger.UpdateCommunityTokenState(chainID, address, token.Deployed)
if err != nil {
return nil, err
}
err = s.Messenger.AddCommunityToken(tokenToUpdate.CommunityID, chainID, address)
if err != nil {
return nil, err
}
} else {
err := s.Messenger.UpdateCommunityTokenState(chainID, address, token.Failed)
if err != nil {
return nil, err
}
}
return s.Messenger.GetCommunityTokenByChainAndAddress(chainID, address)
}
func (s *Service) handleDeployCommunityToken(status string, pendingTransaction *transactions.PendingTransaction) (*token.CommunityToken, error) {
return s.updateStateAndAddTokenToCommunityDescription(status, int(pendingTransaction.ChainID), pendingTransaction.To.String())
}
func (s *Service) handleSetSignerPubKey(status string, pendingTransaction *transactions.PendingTransaction) (*token.CommunityToken, error) {
communityToken, err := s.Messenger.GetCommunityTokenByChainAndAddress(int(pendingTransaction.ChainID), pendingTransaction.To.String())
if err != nil {
return nil, err
}
if communityToken == nil {
return nil, fmt.Errorf("token does not exist in database: chainId=%v, address=%v", pendingTransaction.ChainID, pendingTransaction.To.String())
}
if status == transactions.Success {
_, err := s.Messenger.PromoteSelfToControlNode(types.FromHex(communityToken.CommunityID))
if err != nil {
return nil, err
}
}
return communityToken, err
}
// Stop is run when a service is stopped.
func (s *Service) Stop() error {
s.walletWatcher.Stop()
return nil
}
func (s *Service) Init(messenger *protocol.Messenger) {
s.Messenger = messenger
}
func (s *Service) NewCommunityOwnerTokenRegistryInstance(chainID uint64, contractAddress string) (*communityownertokenregistry.CommunityOwnerTokenRegistry, error) {
backend, err := s.manager.rpcClient.EthClient(chainID)
if err != nil {
@ -142,11 +320,11 @@ func (s *Service) SafeGetOwnerTokenAddress(ctx context.Context, chainID uint64,
return ownerTokenAddress.Hex(), err
}
func (s *Service) GetCollectibleContractData(chainID uint64, contractAddress string) (*CollectibleContractData, error) {
func (s *Service) GetCollectibleContractData(chainID uint64, contractAddress string) (*communities.CollectibleContractData, error) {
return s.manager.GetCollectibleContractData(chainID, contractAddress)
}
func (s *Service) GetAssetContractData(chainID uint64, contractAddress string) (*AssetContractData, error) {
func (s *Service) GetAssetContractData(chainID uint64, contractAddress string) (*communities.AssetContractData, error) {
return s.manager.GetAssetContractData(chainID, contractAddress)
}
@ -176,8 +354,10 @@ func (s *Service) SetSignerPubKey(ctx context.Context, chainID uint64, contractA
wcommon.ChainID(chainID),
tx.Hash(),
common.Address(txArgs.From),
common.HexToAddress(contractAddress),
transactions.SetSignerPublicKey,
transactions.AutoDelete,
"",
)
if err != nil {
log.Error("TrackPendingTransaction error", "error", err)
@ -186,3 +366,132 @@ func (s *Service) SetSignerPubKey(ctx context.Context, chainID uint64, contractA
return tx.Hash().Hex(), nil
}
func (s *Service) maxSupplyCollectibles(ctx context.Context, chainID uint64, contractAddress string) (*big.Int, error) {
callOpts := &bind.CallOpts{Context: ctx, Pending: false}
contractInst, err := s.manager.NewCollectiblesInstance(chainID, contractAddress)
if err != nil {
return nil, err
}
return contractInst.MaxSupply(callOpts)
}
func (s *Service) maxSupplyAssets(ctx context.Context, chainID uint64, contractAddress string) (*big.Int, error) {
callOpts := &bind.CallOpts{Context: ctx, Pending: false}
contractInst, err := s.manager.NewAssetsInstance(chainID, contractAddress)
if err != nil {
return nil, err
}
return contractInst.MaxSupply(callOpts)
}
func (s *Service) maxSupply(ctx context.Context, chainID uint64, contractAddress string) (*big.Int, error) {
tokenType, err := s.db.GetTokenType(chainID, contractAddress)
if err != nil {
return nil, err
}
switch tokenType {
case protobuf.CommunityTokenType_ERC721:
return s.maxSupplyCollectibles(ctx, chainID, contractAddress)
case protobuf.CommunityTokenType_ERC20:
return s.maxSupplyAssets(ctx, chainID, contractAddress)
default:
return nil, fmt.Errorf("unknown token type: %v", tokenType)
}
}
func (s *Service) CreateCommunityTokenAndSave(chainID int, deploymentParameters DeploymentParameters,
deployerAddress string, contractAddress string, tokenType protobuf.CommunityTokenType, privilegesLevel token.PrivilegesLevel) (*token.CommunityToken, error) {
tokenToSave := &token.CommunityToken{
TokenType: tokenType,
CommunityID: deploymentParameters.CommunityID,
Address: contractAddress,
Name: deploymentParameters.Name,
Symbol: deploymentParameters.Symbol,
Description: deploymentParameters.Description,
Supply: &bigint.BigInt{Int: deploymentParameters.GetSupply()},
InfiniteSupply: deploymentParameters.InfiniteSupply,
Transferable: deploymentParameters.Transferable,
RemoteSelfDestruct: deploymentParameters.RemoteSelfDestruct,
ChainID: chainID,
DeployState: token.InProgress,
Decimals: deploymentParameters.Decimals,
Deployer: deployerAddress,
PrivilegesLevel: privilegesLevel,
Base64Image: deploymentParameters.Base64Image,
}
return s.Messenger.SaveCommunityToken(tokenToSave, deploymentParameters.CroppedImage)
}
func (s *Service) TemporaryMasterContractAddress(hash string) string {
return hash + "-master"
}
func (s *Service) TemporaryOwnerContractAddress(hash string) string {
return hash + "-owner"
}
func (s *Service) GetMasterTokenContractAddressFromHash(ctx context.Context, chainID uint64, txHash string) (string, error) {
ethClient, err := s.manager.rpcClient.EthClient(chainID)
if err != nil {
return "", err
}
receipt, err := ethClient.TransactionReceipt(ctx, common.HexToHash(txHash))
if err != nil {
return "", err
}
deployerContractInst, err := s.manager.NewCommunityTokenDeployerInstance(chainID)
if err != nil {
return "", err
}
logMasterTokenCreatedSig := []byte("DeployMasterToken(address)")
logMasterTokenCreatedSigHash := crypto.Keccak256Hash(logMasterTokenCreatedSig)
for _, vLog := range receipt.Logs {
if vLog.Topics[0].Hex() == logMasterTokenCreatedSigHash.Hex() {
event, err := deployerContractInst.ParseDeployMasterToken(*vLog)
if err != nil {
return "", err
}
return event.Arg0.Hex(), nil
}
}
return "", fmt.Errorf("can't find master token address in transaction: %v", txHash)
}
func (s *Service) GetOwnerTokenContractAddressFromHash(ctx context.Context, chainID uint64, txHash string) (string, error) {
ethClient, err := s.manager.rpcClient.EthClient(chainID)
if err != nil {
return "", err
}
receipt, err := ethClient.TransactionReceipt(ctx, common.HexToHash(txHash))
if err != nil {
return "", err
}
deployerContractInst, err := s.manager.NewCommunityTokenDeployerInstance(chainID)
if err != nil {
return "", err
}
logOwnerTokenCreatedSig := []byte("DeployOwnerToken(address)")
logOwnerTokenCreatedSigHash := crypto.Keccak256Hash(logOwnerTokenCreatedSig)
for _, vLog := range receipt.Logs {
if vLog.Topics[0].Hex() == logOwnerTokenCreatedSigHash.Hex() {
event, err := deployerContractInst.ParseDeployOwnerToken(*vLog)
if err != nil {
return "", err
}
return event.Arg0.Hex(), nil
}
}
return "", fmt.Errorf("can't find owner token address in transaction: %v", txHash)
}

View File

@ -357,8 +357,10 @@ func (api *API) Release(ctx context.Context, chainID uint64, txArgs transactions
wcommon.ChainID(chainID),
tx.Hash(),
common.Address(txArgs.From),
registryAddr,
transactions.ReleaseENS,
transactions.AutoDelete,
"",
)
if err != nil {
log.Error("TrackPendingTransaction error", "error", err)
@ -484,8 +486,10 @@ func (api *API) Register(ctx context.Context, chainID uint64, txArgs transaction
wcommon.ChainID(chainID),
tx.Hash(),
common.Address(txArgs.From),
registryAddr,
transactions.RegisterENS,
transactions.AutoDelete,
"",
)
if err != nil {
log.Error("TrackPendingTransaction error", "error", err)
@ -600,8 +604,10 @@ func (api *API) SetPubKey(ctx context.Context, chainID uint64, txArgs transactio
wcommon.ChainID(chainID),
tx.Hash(),
common.Address(txArgs.From),
*resolverAddress,
transactions.SetPubKey,
transactions.AutoDelete,
"",
)
if err != nil {
log.Error("TrackPendingTransaction error", "error", err)

View File

@ -29,7 +29,7 @@ import (
"github.com/status-im/status-go/rpc/network"
"github.com/status-im/status-go/server"
"github.com/status-im/status-go/services/accounts/accountsevent"
"github.com/status-im/status-go/services/communitytokens"
"github.com/status-im/status-go/services/communitytokens/communitytokensdatabase"
"github.com/status-im/status-go/services/utils"
"github.com/status-im/status-go/services/wallet/async"
"github.com/status-im/status-go/services/wallet/bigint"
@ -102,7 +102,7 @@ type Manager struct {
ContractMaker *contracts.ContractMaker
networkManager *network.Manager
stores []store // Set on init, not changed afterwards
communityTokensDB *communitytokens.Database
communityTokensDB *communitytokensdatabase.Database
communityManager *community.Manager
mediaServer *server.MediaServer
walletFeed *event.Feed
@ -178,7 +178,7 @@ func NewTokenManager(
networkManager: networkManager,
communityManager: communityManager,
stores: stores,
communityTokensDB: communitytokens.NewCommunityTokensDatabase(appDB),
communityTokensDB: communitytokensdatabase.NewCommunityTokensDatabase(appDB),
tokens: tokens,
mediaServer: mediaServer,
walletFeed: walletFeed,

View File

@ -0,0 +1,36 @@
package signal
import (
"github.com/ethereum/go-ethereum/common"
"github.com/status-im/status-go/protocol/communities/token"
)
const (
// EventCommunityTokenTransactionStatusChanged is triggered when community token contract
// transaction changed its status
EventCommunityTokenTransactionStatusChanged = "communityToken.communityTokenTransactionStatusChanged"
)
type CommunityTokenTransactionSignal struct {
TransactionType string `json:"transactionType"`
Success bool `json:"success"` // transaction's status
Hash common.Hash `json:"hash"` // transaction hash
CommunityToken *token.CommunityToken `json:"communityToken,omitempty"` // community token changed by transaction
OwnerToken *token.CommunityToken `json:"ownerToken,omitempty"` // owner token emitted by deployment transaction
MasterToken *token.CommunityToken `json:"masterToken,omitempty"` // master token emitted by deployment transaction
ErrorString string `json:"errorString"` // information about failed operation
}
func SendCommunityTokenTransactionStatusSignal(transactionType string, success bool, hash common.Hash,
communityToken *token.CommunityToken, ownerToken *token.CommunityToken, masterToken *token.CommunityToken, errorString string) {
send(EventCommunityTokenTransactionStatusChanged, CommunityTokenTransactionSignal{
TransactionType: transactionType,
Success: success,
Hash: hash,
CommunityToken: communityToken,
OwnerToken: ownerToken,
MasterToken: masterToken,
ErrorString: errorString,
})
}

View File

@ -343,14 +343,16 @@ func (tm *PendingTxTracker) emitNotifications(chainID common.ChainID, changes []
}
// PendingTransaction called with autoDelete = false will keep the transaction in the database until it is confirmed by the caller using Delete
func (tm *PendingTxTracker) TrackPendingTransaction(chainID common.ChainID, hash eth.Hash, from eth.Address, trType PendingTrxType, autoDelete AutoDeleteType) error {
func (tm *PendingTxTracker) TrackPendingTransaction(chainID common.ChainID, hash eth.Hash, from eth.Address, to eth.Address, trType PendingTrxType, autoDelete AutoDeleteType, additionalData string) error {
err := tm.addPending(&PendingTransaction{
ChainID: chainID,
Hash: hash,
From: from,
To: to,
Timestamp: uint64(time.Now().Unix()),
Type: trType,
AutoDelete: &autoDelete,
AdditionalData: additionalData,
})
if err != nil {
return err

View File

@ -290,7 +290,7 @@ func TestPendingTxTracker_MultipleClients(t *testing.T) {
sub := eventFeed.Subscribe(eventChan)
for i := range txs {
err := m.TrackPendingTransaction(txs[i].ChainID, txs[i].Hash, txs[i].From, txs[i].Type, AutoDelete)
err := m.TrackPendingTransaction(txs[i].ChainID, txs[i].Hash, txs[i].From, txs[i].To, txs[i].Type, AutoDelete, "")
require.NoError(t, err)
}
@ -365,7 +365,7 @@ func TestPendingTxTracker_Watch(t *testing.T) {
sub := eventFeed.Subscribe(eventChan)
// Track the first transaction
err := m.TrackPendingTransaction(txs[1].ChainID, txs[1].Hash, txs[1].From, txs[1].Type, Keep)
err := m.TrackPendingTransaction(txs[1].ChainID, txs[1].Hash, txs[1].From, txs[1].To, txs[1].Type, Keep, "")
require.NoError(t, err)
// Store the confirmed already
@ -464,7 +464,7 @@ func TestPendingTxTracker_Watch_StatusChangeIncrementally(t *testing.T) {
for i := range txs {
// Track the first transaction
err := m.TrackPendingTransaction(txs[i].ChainID, txs[i].Hash, txs[i].From, txs[i].Type, Keep)
err := m.TrackPendingTransaction(txs[i].ChainID, txs[i].Hash, txs[i].From, txs[i].To, txs[i].Type, Keep, "")
require.NoError(t, err)
}