github.com/status-im/status-go@v1.1.0/services/communitytokens/api.go (about)

     1  package communitytokens
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"math/big"
     7  
     8  	"github.com/pkg/errors"
     9  
    10  	"github.com/ethereum/go-ethereum/accounts/abi/bind"
    11  	"github.com/ethereum/go-ethereum/common"
    12  
    13  	"github.com/ethereum/go-ethereum/log"
    14  	"github.com/status-im/status-go/contracts/community-tokens/assets"
    15  	"github.com/status-im/status-go/contracts/community-tokens/collectibles"
    16  	communitytokendeployer "github.com/status-im/status-go/contracts/community-tokens/deployer"
    17  	"github.com/status-im/status-go/contracts/community-tokens/ownertoken"
    18  	communityownertokenregistry "github.com/status-im/status-go/contracts/community-tokens/registry"
    19  	"github.com/status-im/status-go/eth-node/crypto"
    20  	"github.com/status-im/status-go/eth-node/types"
    21  	"github.com/status-im/status-go/images"
    22  	"github.com/status-im/status-go/protocol/communities/token"
    23  	"github.com/status-im/status-go/protocol/protobuf"
    24  	"github.com/status-im/status-go/services/utils"
    25  	"github.com/status-im/status-go/services/wallet/bigint"
    26  	wcommon "github.com/status-im/status-go/services/wallet/common"
    27  	"github.com/status-im/status-go/transactions"
    28  )
    29  
    30  func NewAPI(s *Service) *API {
    31  	return &API{
    32  		s: s,
    33  	}
    34  }
    35  
    36  type API struct {
    37  	s *Service
    38  }
    39  
    40  type DeploymentDetails struct {
    41  	ContractAddress string                `json:"contractAddress"`
    42  	TransactionHash string                `json:"transactionHash"`
    43  	CommunityToken  *token.CommunityToken `json:"communityToken"`
    44  	OwnerToken      *token.CommunityToken `json:"ownerToken"`
    45  	MasterToken     *token.CommunityToken `json:"masterToken"`
    46  }
    47  
    48  const maxSupply = 999999999
    49  
    50  type DeploymentParameters struct {
    51  	Name               string               `json:"name"`
    52  	Symbol             string               `json:"symbol"`
    53  	Supply             *bigint.BigInt       `json:"supply"`
    54  	InfiniteSupply     bool                 `json:"infiniteSupply"`
    55  	Transferable       bool                 `json:"transferable"`
    56  	RemoteSelfDestruct bool                 `json:"remoteSelfDestruct"`
    57  	TokenURI           string               `json:"tokenUri"`
    58  	OwnerTokenAddress  string               `json:"ownerTokenAddress"`
    59  	MasterTokenAddress string               `json:"masterTokenAddress"`
    60  	CommunityID        string               `json:"communityId"`
    61  	Description        string               `json:"description"`
    62  	CroppedImage       *images.CroppedImage `json:"croppedImage,omitempty"` // for community tokens
    63  	Base64Image        string               `json:"base64image"`            // for owner & master tokens
    64  	Decimals           int                  `json:"decimals"`
    65  }
    66  
    67  func (d *DeploymentParameters) GetSupply() *big.Int {
    68  	if d.InfiniteSupply {
    69  		return d.GetInfiniteSupply()
    70  	}
    71  	return d.Supply.Int
    72  }
    73  
    74  // infinite supply for ERC721 is 2^256-1
    75  func (d *DeploymentParameters) GetInfiniteSupply() *big.Int {
    76  	return GetInfiniteSupply()
    77  }
    78  
    79  func GetInfiniteSupply() *big.Int {
    80  	max := new(big.Int).Exp(big.NewInt(2), big.NewInt(256), nil)
    81  	max.Sub(max, big.NewInt(1))
    82  	return max
    83  }
    84  
    85  func (d *DeploymentParameters) Validate(isAsset bool) error {
    86  	if len(d.Name) <= 0 {
    87  		return errors.New("empty collectible name")
    88  	}
    89  	if len(d.Symbol) <= 0 {
    90  		return errors.New("empty collectible symbol")
    91  	}
    92  	var maxForType = big.NewInt(maxSupply)
    93  	if isAsset {
    94  		assetMultiplier, _ := big.NewInt(0).SetString("1000000000000000000", 10)
    95  		maxForType = maxForType.Mul(maxForType, assetMultiplier)
    96  	}
    97  	if !d.InfiniteSupply && (d.Supply.Cmp(big.NewInt(0)) < 0 || d.Supply.Cmp(maxForType) > 0) {
    98  		return fmt.Errorf("wrong supply value: %v", d.Supply)
    99  	}
   100  	return nil
   101  }
   102  
   103  func (api *API) DeployCollectibles(ctx context.Context, chainID uint64, deploymentParameters DeploymentParameters, txArgs transactions.SendTxArgs, password string) (DeploymentDetails, error) {
   104  	err := deploymentParameters.Validate(false)
   105  	if err != nil {
   106  		return DeploymentDetails{}, err
   107  	}
   108  	transactOpts := txArgs.ToTransactOpts(utils.GetSigner(chainID, api.s.accountsManager, api.s.config.KeyStoreDir, txArgs.From, password))
   109  
   110  	ethClient, err := api.s.manager.rpcClient.EthClient(chainID)
   111  	if err != nil {
   112  		log.Error(err.Error())
   113  		return DeploymentDetails{}, err
   114  	}
   115  	address, tx, _, err := collectibles.DeployCollectibles(transactOpts, ethClient, deploymentParameters.Name,
   116  		deploymentParameters.Symbol, deploymentParameters.GetSupply(),
   117  		deploymentParameters.RemoteSelfDestruct, deploymentParameters.Transferable,
   118  		deploymentParameters.TokenURI, common.HexToAddress(deploymentParameters.OwnerTokenAddress),
   119  		common.HexToAddress(deploymentParameters.MasterTokenAddress))
   120  	if err != nil {
   121  		log.Error(err.Error())
   122  		return DeploymentDetails{}, err
   123  	}
   124  
   125  	err = api.s.pendingTracker.TrackPendingTransaction(
   126  		wcommon.ChainID(chainID),
   127  		tx.Hash(),
   128  		common.Address(txArgs.From),
   129  		address,
   130  		transactions.DeployCommunityToken,
   131  		transactions.Keep,
   132  		"",
   133  	)
   134  	if err != nil {
   135  		log.Error("TrackPendingTransaction error", "error", err)
   136  		return DeploymentDetails{}, err
   137  	}
   138  
   139  	savedCommunityToken, err := api.s.CreateCommunityTokenAndSave(int(chainID), deploymentParameters, txArgs.From.Hex(), address.Hex(),
   140  		protobuf.CommunityTokenType_ERC721, token.CommunityLevel, tx.Hash().Hex())
   141  	if err != nil {
   142  		return DeploymentDetails{}, err
   143  	}
   144  
   145  	return DeploymentDetails{
   146  		ContractAddress: address.Hex(),
   147  		TransactionHash: tx.Hash().Hex(),
   148  		CommunityToken:  savedCommunityToken}, nil
   149  }
   150  
   151  func decodeSignature(sig []byte) (r [32]byte, s [32]byte, v uint8, err error) {
   152  	if len(sig) != crypto.SignatureLength {
   153  		return [32]byte{}, [32]byte{}, 0, fmt.Errorf("wrong size for signature: got %d, want %d", len(sig), crypto.SignatureLength)
   154  	}
   155  	copy(r[:], sig[:32])
   156  	copy(s[:], sig[32:64])
   157  	v = sig[64] + 27
   158  	return r, s, v, nil
   159  }
   160  
   161  func prepareDeploymentSignatureStruct(signature string, communityID string, addressFrom common.Address) (communitytokendeployer.CommunityTokenDeployerDeploymentSignature, error) {
   162  	r, s, v, err := decodeSignature(common.FromHex(signature))
   163  	if err != nil {
   164  		return communitytokendeployer.CommunityTokenDeployerDeploymentSignature{}, err
   165  	}
   166  	communityEthAddress, err := convert33BytesPubKeyToEthAddress(communityID)
   167  	if err != nil {
   168  		return communitytokendeployer.CommunityTokenDeployerDeploymentSignature{}, err
   169  	}
   170  	communitySignature := communitytokendeployer.CommunityTokenDeployerDeploymentSignature{
   171  		V:        v,
   172  		R:        r,
   173  		S:        s,
   174  		Deployer: addressFrom,
   175  		Signer:   communityEthAddress,
   176  	}
   177  	return communitySignature, nil
   178  }
   179  
   180  func (api *API) DeployOwnerToken(ctx context.Context, chainID uint64,
   181  	ownerTokenParameters DeploymentParameters, masterTokenParameters DeploymentParameters,
   182  	signerPubKey string, txArgs transactions.SendTxArgs, password string) (DeploymentDetails, error) {
   183  	err := ownerTokenParameters.Validate(false)
   184  	if err != nil {
   185  		return DeploymentDetails{}, err
   186  	}
   187  
   188  	if len(signerPubKey) <= 0 {
   189  		return DeploymentDetails{}, fmt.Errorf("signerPubKey is empty")
   190  	}
   191  
   192  	err = masterTokenParameters.Validate(false)
   193  	if err != nil {
   194  		return DeploymentDetails{}, err
   195  	}
   196  
   197  	transactOpts := txArgs.ToTransactOpts(utils.GetSigner(chainID, api.s.accountsManager, api.s.config.KeyStoreDir, txArgs.From, password))
   198  
   199  	deployerContractInst, err := api.NewCommunityTokenDeployerInstance(chainID)
   200  	if err != nil {
   201  		return DeploymentDetails{}, err
   202  	}
   203  
   204  	ownerTokenConfig := communitytokendeployer.CommunityTokenDeployerTokenConfig{
   205  		Name:    ownerTokenParameters.Name,
   206  		Symbol:  ownerTokenParameters.Symbol,
   207  		BaseURI: ownerTokenParameters.TokenURI,
   208  	}
   209  
   210  	masterTokenConfig := communitytokendeployer.CommunityTokenDeployerTokenConfig{
   211  		Name:    masterTokenParameters.Name,
   212  		Symbol:  masterTokenParameters.Symbol,
   213  		BaseURI: masterTokenParameters.TokenURI,
   214  	}
   215  
   216  	signature, err := api.s.Messenger.CreateCommunityTokenDeploymentSignature(context.Background(), chainID, txArgs.From.Hex(), ownerTokenParameters.CommunityID)
   217  	if err != nil {
   218  		return DeploymentDetails{}, err
   219  	}
   220  
   221  	communitySignature, err := prepareDeploymentSignatureStruct(types.HexBytes(signature).String(), ownerTokenParameters.CommunityID, common.Address(txArgs.From))
   222  	if err != nil {
   223  		return DeploymentDetails{}, err
   224  	}
   225  
   226  	log.Debug("Signature:", communitySignature)
   227  
   228  	tx, err := deployerContractInst.Deploy(transactOpts, ownerTokenConfig, masterTokenConfig, communitySignature, common.FromHex(signerPubKey))
   229  
   230  	if err != nil {
   231  		log.Error(err.Error())
   232  		return DeploymentDetails{}, err
   233  	}
   234  
   235  	log.Debug("Contract deployed hash:", tx.Hash().String())
   236  
   237  	err = api.s.pendingTracker.TrackPendingTransaction(
   238  		wcommon.ChainID(chainID),
   239  		tx.Hash(),
   240  		common.Address(txArgs.From),
   241  		common.Address{},
   242  		transactions.DeployOwnerToken,
   243  		transactions.Keep,
   244  		"",
   245  	)
   246  	if err != nil {
   247  		log.Error("TrackPendingTransaction error", "error", err)
   248  		return DeploymentDetails{}, err
   249  	}
   250  
   251  	savedOwnerToken, err := api.s.CreateCommunityTokenAndSave(int(chainID), ownerTokenParameters, txArgs.From.Hex(),
   252  		api.s.TemporaryOwnerContractAddress(tx.Hash().Hex()), protobuf.CommunityTokenType_ERC721, token.OwnerLevel, tx.Hash().Hex())
   253  	if err != nil {
   254  		return DeploymentDetails{}, err
   255  	}
   256  	savedMasterToken, err := api.s.CreateCommunityTokenAndSave(int(chainID), masterTokenParameters, txArgs.From.Hex(),
   257  		api.s.TemporaryMasterContractAddress(tx.Hash().Hex()), protobuf.CommunityTokenType_ERC721, token.MasterLevel, tx.Hash().Hex())
   258  	if err != nil {
   259  		return DeploymentDetails{}, err
   260  	}
   261  
   262  	return DeploymentDetails{
   263  		ContractAddress: "",
   264  		TransactionHash: tx.Hash().Hex(),
   265  		OwnerToken:      savedOwnerToken,
   266  		MasterToken:     savedMasterToken}, nil
   267  }
   268  
   269  // recovery function which starts transaction tracking again
   270  func (api *API) ReTrackOwnerTokenDeploymentTransaction(ctx context.Context, chainID uint64, contractAddress string) error {
   271  	return api.s.ReTrackOwnerTokenDeploymentTransaction(ctx, chainID, contractAddress)
   272  }
   273  
   274  func (api *API) DeployAssets(ctx context.Context, chainID uint64, deploymentParameters DeploymentParameters, txArgs transactions.SendTxArgs, password string) (DeploymentDetails, error) {
   275  
   276  	err := deploymentParameters.Validate(true)
   277  	if err != nil {
   278  		return DeploymentDetails{}, err
   279  	}
   280  
   281  	transactOpts := txArgs.ToTransactOpts(utils.GetSigner(chainID, api.s.accountsManager, api.s.config.KeyStoreDir, txArgs.From, password))
   282  
   283  	ethClient, err := api.s.manager.rpcClient.EthClient(chainID)
   284  	if err != nil {
   285  		log.Error(err.Error())
   286  		return DeploymentDetails{}, err
   287  	}
   288  
   289  	const decimals = 18
   290  	address, tx, _, err := assets.DeployAssets(transactOpts, ethClient, deploymentParameters.Name,
   291  		deploymentParameters.Symbol, decimals, deploymentParameters.GetSupply(),
   292  		deploymentParameters.TokenURI,
   293  		common.HexToAddress(deploymentParameters.OwnerTokenAddress),
   294  		common.HexToAddress(deploymentParameters.MasterTokenAddress))
   295  	if err != nil {
   296  		log.Error(err.Error())
   297  		return DeploymentDetails{}, err
   298  	}
   299  
   300  	err = api.s.pendingTracker.TrackPendingTransaction(
   301  		wcommon.ChainID(chainID),
   302  		tx.Hash(),
   303  		common.Address(txArgs.From),
   304  		address,
   305  		transactions.DeployCommunityToken,
   306  		transactions.Keep,
   307  		"",
   308  	)
   309  	if err != nil {
   310  		log.Error("TrackPendingTransaction error", "error", err)
   311  		return DeploymentDetails{}, err
   312  	}
   313  
   314  	savedCommunityToken, err := api.s.CreateCommunityTokenAndSave(int(chainID), deploymentParameters, txArgs.From.Hex(), address.Hex(),
   315  		protobuf.CommunityTokenType_ERC20, token.CommunityLevel, tx.Hash().Hex())
   316  	if err != nil {
   317  		return DeploymentDetails{}, err
   318  	}
   319  
   320  	return DeploymentDetails{
   321  		ContractAddress: address.Hex(),
   322  		TransactionHash: tx.Hash().Hex(),
   323  		CommunityToken:  savedCommunityToken}, nil
   324  }
   325  
   326  func (api *API) DeployCollectiblesEstimate(ctx context.Context, chainID uint64, fromAddress string) (*CommunityTokenFees, error) {
   327  	return api.s.deployCollectiblesEstimate(ctx, chainID, fromAddress)
   328  }
   329  
   330  func (api *API) DeployAssetsEstimate(ctx context.Context, chainID uint64, fromAddress string) (*CommunityTokenFees, error) {
   331  	return api.s.deployAssetsEstimate(ctx, chainID, fromAddress)
   332  }
   333  
   334  func (api *API) DeployOwnerTokenEstimate(ctx context.Context, chainID uint64, fromAddress string,
   335  	ownerTokenParameters DeploymentParameters, masterTokenParameters DeploymentParameters,
   336  	communityID string, signerPubKey string) (*CommunityTokenFees, error) {
   337  	return api.s.deployOwnerTokenEstimate(ctx, chainID, fromAddress, ownerTokenParameters, masterTokenParameters, communityID, signerPubKey)
   338  }
   339  
   340  func (api *API) EstimateMintTokens(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, walletAddresses []string, amount *bigint.BigInt) (*CommunityTokenFees, error) {
   341  	return api.s.mintTokensEstimate(ctx, chainID, contractAddress, fromAddress, walletAddresses, amount)
   342  }
   343  
   344  // This is only ERC721 function
   345  func (api *API) EstimateRemoteBurn(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, tokenIds []*bigint.BigInt) (*CommunityTokenFees, error) {
   346  	return api.s.remoteBurnEstimate(ctx, chainID, contractAddress, fromAddress, tokenIds)
   347  }
   348  
   349  func (api *API) EstimateBurn(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, burnAmount *bigint.BigInt) (*CommunityTokenFees, error) {
   350  	return api.s.burnEstimate(ctx, chainID, contractAddress, fromAddress, burnAmount)
   351  }
   352  
   353  func (api *API) EstimateSetSignerPubKey(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, newSignerPubKey string) (*CommunityTokenFees, error) {
   354  	return api.s.setSignerPubKeyEstimate(ctx, chainID, contractAddress, fromAddress, newSignerPubKey)
   355  }
   356  
   357  func (api *API) NewOwnerTokenInstance(chainID uint64, contractAddress string) (*ownertoken.OwnerToken, error) {
   358  	return api.s.NewOwnerTokenInstance(chainID, contractAddress)
   359  }
   360  
   361  func (api *API) NewCommunityTokenDeployerInstance(chainID uint64) (*communitytokendeployer.CommunityTokenDeployer, error) {
   362  	return api.s.manager.NewCommunityTokenDeployerInstance(chainID)
   363  }
   364  
   365  func (api *API) NewCommunityOwnerTokenRegistryInstance(chainID uint64, contractAddress string) (*communityownertokenregistry.CommunityOwnerTokenRegistry, error) {
   366  	return api.s.NewCommunityOwnerTokenRegistryInstance(chainID, contractAddress)
   367  }
   368  
   369  func (api *API) NewCollectiblesInstance(chainID uint64, contractAddress string) (*collectibles.Collectibles, error) {
   370  	return api.s.manager.NewCollectiblesInstance(chainID, contractAddress)
   371  }
   372  
   373  func (api *API) NewAssetsInstance(chainID uint64, contractAddress string) (*assets.Assets, error) {
   374  	return api.s.manager.NewAssetsInstance(chainID, contractAddress)
   375  }
   376  
   377  // Universal minting function for every type of token.
   378  func (api *API) MintTokens(ctx context.Context, chainID uint64, contractAddress string, txArgs transactions.SendTxArgs, password string, walletAddresses []string, amount *bigint.BigInt) (string, error) {
   379  
   380  	err := api.s.ValidateWalletsAndAmounts(walletAddresses, amount)
   381  	if err != nil {
   382  		return "", err
   383  	}
   384  
   385  	transactOpts := txArgs.ToTransactOpts(utils.GetSigner(chainID, api.s.accountsManager, api.s.config.KeyStoreDir, txArgs.From, password))
   386  
   387  	contractInst, err := NewTokenInstance(api.s, chainID, contractAddress)
   388  	if err != nil {
   389  		return "", err
   390  	}
   391  
   392  	tx, err := contractInst.Mint(transactOpts, walletAddresses, amount)
   393  	if err != nil {
   394  		return "", err
   395  	}
   396  
   397  	err = api.s.pendingTracker.TrackPendingTransaction(
   398  		wcommon.ChainID(chainID),
   399  		tx.Hash(),
   400  		common.Address(txArgs.From),
   401  		common.HexToAddress(contractAddress),
   402  		transactions.AirdropCommunityToken,
   403  		transactions.Keep,
   404  		"",
   405  	)
   406  	if err != nil {
   407  		log.Error("TrackPendingTransaction error", "error", err)
   408  		return "", err
   409  	}
   410  
   411  	return tx.Hash().Hex(), nil
   412  }
   413  
   414  // This is only ERC721 function
   415  func (api *API) RemoteDestructedAmount(ctx context.Context, chainID uint64, contractAddress string) (*bigint.BigInt, error) {
   416  	callOpts := &bind.CallOpts{Context: ctx, Pending: false}
   417  	contractInst, err := api.NewCollectiblesInstance(chainID, contractAddress)
   418  	if err != nil {
   419  		return nil, err
   420  	}
   421  
   422  	// total supply = airdropped only (w/o burnt)
   423  	totalSupply, err := contractInst.TotalSupply(callOpts)
   424  	if err != nil {
   425  		return nil, err
   426  	}
   427  
   428  	// minted = all created tokens (airdropped and remotely destructed)
   429  	mintedCount, err := contractInst.MintedCount(callOpts)
   430  	if err != nil {
   431  		return nil, err
   432  	}
   433  
   434  	var res = new(big.Int)
   435  	res.Sub(mintedCount, totalSupply)
   436  
   437  	return &bigint.BigInt{Int: res}, nil
   438  }
   439  
   440  // This is only ERC721 function
   441  func (api *API) RemoteBurn(ctx context.Context, chainID uint64, contractAddress string, txArgs transactions.SendTxArgs, password string, tokenIds []*bigint.BigInt, additionalData string) (string, error) {
   442  	err := api.s.validateTokens(tokenIds)
   443  	if err != nil {
   444  		return "", err
   445  	}
   446  
   447  	transactOpts := txArgs.ToTransactOpts(utils.GetSigner(chainID, api.s.accountsManager, api.s.config.KeyStoreDir, txArgs.From, password))
   448  
   449  	var tempTokenIds []*big.Int
   450  	for _, v := range tokenIds {
   451  		tempTokenIds = append(tempTokenIds, v.Int)
   452  	}
   453  
   454  	contractInst, err := NewTokenInstance(api.s, chainID, contractAddress)
   455  	if err != nil {
   456  		return "", err
   457  	}
   458  
   459  	tx, err := contractInst.RemoteBurn(transactOpts, tempTokenIds)
   460  	if err != nil {
   461  		return "", err
   462  	}
   463  
   464  	err = api.s.pendingTracker.TrackPendingTransaction(
   465  		wcommon.ChainID(chainID),
   466  		tx.Hash(),
   467  		common.Address(txArgs.From),
   468  		common.HexToAddress(contractAddress),
   469  		transactions.RemoteDestructCollectible,
   470  		transactions.Keep,
   471  		additionalData,
   472  	)
   473  	if err != nil {
   474  		log.Error("TrackPendingTransaction error", "error", err)
   475  		return "", err
   476  	}
   477  
   478  	return tx.Hash().Hex(), nil
   479  }
   480  
   481  func (api *API) GetCollectiblesContractInstance(chainID uint64, contractAddress string) (*collectibles.Collectibles, error) {
   482  	return api.s.manager.GetCollectiblesContractInstance(chainID, contractAddress)
   483  }
   484  
   485  func (api *API) GetAssetContractInstance(chainID uint64, contractAddress string) (*assets.Assets, error) {
   486  	return api.s.manager.GetAssetContractInstance(chainID, contractAddress)
   487  }
   488  
   489  func (api *API) RemainingSupply(ctx context.Context, chainID uint64, contractAddress string) (*bigint.BigInt, error) {
   490  	return api.s.remainingSupply(ctx, chainID, contractAddress)
   491  }
   492  
   493  func (api *API) Burn(ctx context.Context, chainID uint64, contractAddress string, txArgs transactions.SendTxArgs, password string, burnAmount *bigint.BigInt) (string, error) {
   494  	err := api.s.validateBurnAmount(ctx, burnAmount, chainID, contractAddress)
   495  	if err != nil {
   496  		return "", err
   497  	}
   498  
   499  	transactOpts := txArgs.ToTransactOpts(utils.GetSigner(chainID, api.s.accountsManager, api.s.config.KeyStoreDir, txArgs.From, password))
   500  
   501  	newMaxSupply, err := api.s.prepareNewMaxSupply(ctx, chainID, contractAddress, burnAmount)
   502  	if err != nil {
   503  		return "", err
   504  	}
   505  
   506  	contractInst, err := NewTokenInstance(api.s, chainID, contractAddress)
   507  	if err != nil {
   508  		return "", err
   509  	}
   510  
   511  	tx, err := contractInst.SetMaxSupply(transactOpts, newMaxSupply)
   512  	if err != nil {
   513  		return "", err
   514  	}
   515  
   516  	err = api.s.pendingTracker.TrackPendingTransaction(
   517  		wcommon.ChainID(chainID),
   518  		tx.Hash(),
   519  		common.Address(txArgs.From),
   520  		common.HexToAddress(contractAddress),
   521  		transactions.BurnCommunityToken,
   522  		transactions.Keep,
   523  		"",
   524  	)
   525  	if err != nil {
   526  		log.Error("TrackPendingTransaction error", "error", err)
   527  		return "", err
   528  	}
   529  
   530  	return tx.Hash().Hex(), nil
   531  }
   532  
   533  // Gets signer public key from smart contract with a given chainId and address
   534  func (api *API) GetSignerPubKey(ctx context.Context, chainID uint64, contractAddress string) (string, error) {
   535  	return api.s.GetSignerPubKey(ctx, chainID, contractAddress)
   536  }
   537  
   538  // Gets signer public key directly from deployer contract
   539  func (api *API) SafeGetSignerPubKey(ctx context.Context, chainID uint64, communityID string) (string, error) {
   540  	return api.s.SafeGetSignerPubKey(ctx, chainID, communityID)
   541  }
   542  
   543  // Gets owner token contract address from deployer contract
   544  func (api *API) SafeGetOwnerTokenAddress(ctx context.Context, chainID uint64, communityID string) (string, error) {
   545  	return api.s.SafeGetOwnerTokenAddress(ctx, chainID, communityID)
   546  }
   547  
   548  func (api *API) SetSignerPubKey(ctx context.Context, chainID uint64, contractAddress string, txArgs transactions.SendTxArgs, password string, newSignerPubKey string) (string, error) {
   549  	return api.s.SetSignerPubKey(ctx, chainID, contractAddress, txArgs, password, newSignerPubKey)
   550  }
   551  
   552  func (api *API) OwnerTokenOwnerAddress(ctx context.Context, chainID uint64, contractAddress string) (string, error) {
   553  	callOpts := &bind.CallOpts{Context: ctx, Pending: false}
   554  	contractInst, err := api.NewOwnerTokenInstance(chainID, contractAddress)
   555  	if err != nil {
   556  		return "", err
   557  	}
   558  	ownerAddress, err := contractInst.OwnerOf(callOpts, big.NewInt(0))
   559  	if err != nil {
   560  		return "", err
   561  	}
   562  	return ownerAddress.Hex(), nil
   563  }