feat(MasterToken): Add MasterToken.go

Small refactoring in Collectibles service in order to make API code more simple.
Add TokenInstance interface with some common functions.
Implementations of TokenInstance for TokenMaster, Collectibles and Assets contracts.

Issue #11276
This commit is contained in:
Michal Iskierko 2023-08-14 10:27:46 +02:00 committed by Michał Iskierko
parent 864b0686d2
commit c4cd5775db
5 changed files with 1631 additions and 155 deletions

File diff suppressed because one or more lines are too long

View File

@ -12,11 +12,11 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/status-im/status-go/account" "github.com/status-im/status-go/account"
"github.com/status-im/status-go/contracts/community-tokens/assets" "github.com/status-im/status-go/contracts/community-tokens/assets"
"github.com/status-im/status-go/contracts/community-tokens/collectibles" "github.com/status-im/status-go/contracts/community-tokens/collectibles"
"github.com/status-im/status-go/contracts/community-tokens/mastertoken"
"github.com/status-im/status-go/contracts/community-tokens/ownertoken" "github.com/status-im/status-go/contracts/community-tokens/ownertoken"
"github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/params" "github.com/status-im/status-go/params"
@ -257,7 +257,15 @@ func (api *API) DeployOwnerTokenEstimate(ctx context.Context) (uint64, error) {
return ownerGasAmount + uint64(float32(ownerGasAmount)*0.1), nil return ownerGasAmount + uint64(float32(ownerGasAmount)*0.1), nil
} }
func (api *API) newCollectiblesInstance(chainID uint64, contractAddress string) (*collectibles.Collectibles, error) { func (api *API) NewMasterTokenInstance(chainID uint64, contractAddress string) (*mastertoken.MasterToken, error) {
backend, err := api.RPCClient.EthClient(chainID)
if err != nil {
return nil, err
}
return mastertoken.NewMasterToken(common.HexToAddress(contractAddress), backend)
}
func (api *API) NewCollectiblesInstance(chainID uint64, contractAddress string) (*collectibles.Collectibles, error) {
backend, err := api.RPCClient.EthClient(chainID) backend, err := api.RPCClient.EthClient(chainID)
if err != nil { if err != nil {
return nil, err return nil, err
@ -265,7 +273,7 @@ func (api *API) newCollectiblesInstance(chainID uint64, contractAddress string)
return collectibles.NewCollectibles(common.HexToAddress(contractAddress), backend) return collectibles.NewCollectibles(common.HexToAddress(contractAddress), backend)
} }
func (api *API) newAssetsInstance(chainID uint64, contractAddress string) (*assets.Assets, error) { func (api *API) NewAssetsInstance(chainID uint64, contractAddress string) (*assets.Assets, error) {
backend, err := api.RPCClient.EthClient(chainID) backend, err := api.RPCClient.EthClient(chainID)
if err != nil { if err != nil {
return nil, err return nil, err
@ -284,7 +292,7 @@ func (api *API) multiplyWalletAddresses(amount *bigint.BigInt, contractAddresses
return totalAddresses return totalAddresses
} }
func (api *API) prepareMintCollectiblesData(walletAddresses []string, amount *bigint.BigInt) []common.Address { func (api *API) PrepareMintCollectiblesData(walletAddresses []string, amount *bigint.BigInt) []common.Address {
totalAddresses := api.multiplyWalletAddresses(amount, walletAddresses) totalAddresses := api.multiplyWalletAddresses(amount, walletAddresses)
var usersAddresses = []common.Address{} var usersAddresses = []common.Address{}
for _, k := range totalAddresses { for _, k := range totalAddresses {
@ -293,21 +301,34 @@ func (api *API) prepareMintCollectiblesData(walletAddresses []string, amount *bi
return usersAddresses return usersAddresses
} }
// Universal minting function for both assets and collectibles. // Universal minting function for every type of token.
// Checks contract type and runs MintCollectibles or MintAssets function.
func (api *API) MintTokens(ctx context.Context, chainID uint64, contractAddress string, txArgs transactions.SendTxArgs, password string, walletAddresses []string, amount *bigint.BigInt) (string, error) { func (api *API) MintTokens(ctx context.Context, chainID uint64, contractAddress string, txArgs transactions.SendTxArgs, password string, walletAddresses []string, amount *bigint.BigInt) (string, error) {
tokenType, err := api.db.GetTokenType(chainID, contractAddress)
err := api.ValidateWalletsAndAmounts(walletAddresses, amount)
if err != nil { if err != nil {
return "", err return "", err
} }
switch tokenType {
case protobuf.CommunityTokenType_ERC721: transactOpts := txArgs.ToTransactOpts(utils.GetSigner(chainID, api.accountsManager, api.config.KeyStoreDir, txArgs.From, password))
return api.MintCollectibles(ctx, chainID, contractAddress, txArgs, password, walletAddresses, amount)
case protobuf.CommunityTokenType_ERC20: contractInst, err := NewTokenInstance(api, chainID, contractAddress)
return api.MintAssets(ctx, chainID, contractAddress, txArgs, password, walletAddresses, amount) if err != nil {
default: return "", err
return "", fmt.Errorf("unknown token type: %v", tokenType)
} }
tx, err := contractInst.Mint(transactOpts, walletAddresses, amount)
if err != nil {
return "", err
}
go api.rpcFiltersSrvc.TriggerTransactionSentToUpstreamEvent(&rpcfilters.PendingTxInfo{
Hash: tx.Hash(),
Type: string(transactions.AirdropCommunityToken),
From: common.Address(txArgs.From),
ChainID: chainID,
})
return tx.Hash().Hex(), nil
} }
func (api *API) EstimateMintTokens(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, walletAddresses []string, amount *bigint.BigInt) (uint64, error) { func (api *API) EstimateMintTokens(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, walletAddresses []string, amount *bigint.BigInt) (uint64, error) {
@ -315,6 +336,7 @@ func (api *API) EstimateMintTokens(ctx context.Context, chainID uint64, contract
if err != nil { if err != nil {
return 0, err return 0, err
} }
switch tokenType { switch tokenType {
case protobuf.CommunityTokenType_ERC721: case protobuf.CommunityTokenType_ERC721:
return api.EstimateMintCollectibles(ctx, chainID, contractAddress, fromAddress, walletAddresses, amount) return api.EstimateMintCollectibles(ctx, chainID, contractAddress, fromAddress, walletAddresses, amount)
@ -325,47 +347,16 @@ func (api *API) EstimateMintTokens(ctx context.Context, chainID uint64, contract
} }
} }
// Create the amounty of collectible tokens and distribute them to all walletAddresses.
func (api *API) MintCollectibles(ctx context.Context, chainID uint64, contractAddress string, txArgs transactions.SendTxArgs, password string, walletAddresses []string, amount *bigint.BigInt) (string, error) {
err := api.validateWalletsAndAmounts(walletAddresses, amount)
if err != nil {
return "", err
}
contractInst, err := api.newCollectiblesInstance(chainID, contractAddress)
if err != nil {
return "", err
}
usersAddresses := api.prepareMintCollectiblesData(walletAddresses, amount)
transactOpts := txArgs.ToTransactOpts(utils.GetSigner(chainID, api.accountsManager, api.config.KeyStoreDir, txArgs.From, password))
tx, err := contractInst.MintTo(transactOpts, usersAddresses)
if err != nil {
return "", err
}
go api.rpcFiltersSrvc.TriggerTransactionSentToUpstreamEvent(&rpcfilters.PendingTxInfo{
Hash: tx.Hash(),
Type: string(transactions.AirdropCommunityToken),
From: common.Address(txArgs.From),
ChainID: chainID,
})
return tx.Hash().Hex(), nil
}
func (api *API) EstimateMintCollectibles(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, walletAddresses []string, amount *bigint.BigInt) (uint64, error) { func (api *API) EstimateMintCollectibles(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, walletAddresses []string, amount *bigint.BigInt) (uint64, error) {
err := api.validateWalletsAndAmounts(walletAddresses, amount) err := api.ValidateWalletsAndAmounts(walletAddresses, amount)
if err != nil { if err != nil {
return 0, err return 0, err
} }
usersAddresses := api.prepareMintCollectiblesData(walletAddresses, amount) usersAddresses := api.PrepareMintCollectiblesData(walletAddresses, amount)
return api.estimateMethod(ctx, chainID, contractAddress, fromAddress, "mintTo", usersAddresses) return api.estimateMethod(ctx, chainID, contractAddress, fromAddress, "mintTo", usersAddresses)
} }
func (api *API) prepareMintAssetsData(walletAddresses []string, amount *bigint.BigInt) ([]common.Address, []*big.Int) { func (api *API) PrepareMintAssetsData(walletAddresses []string, amount *bigint.BigInt) ([]common.Address, []*big.Int) {
var usersAddresses = []common.Address{} var usersAddresses = []common.Address{}
var amountsList = []*big.Int{} var amountsList = []*big.Int{}
for _, k := range walletAddresses { for _, k := range walletAddresses {
@ -375,53 +366,20 @@ func (api *API) prepareMintAssetsData(walletAddresses []string, amount *bigint.B
return usersAddresses, amountsList return usersAddresses, amountsList
} }
// Create the amount of assets tokens and distribute them to all walletAddresses.
// The amount should be in smallest denomination of the asset (like wei) with decimal = 18, eg.
// if we want to mint 2.34 of the token, then amount should be 234{16 zeros}.
func (api *API) MintAssets(ctx context.Context, chainID uint64, contractAddress string, txArgs transactions.SendTxArgs, password string, walletAddresses []string, amount *bigint.BigInt) (string, error) {
err := api.validateWalletsAndAmounts(walletAddresses, amount)
if err != nil {
return "", err
}
contractInst, err := api.newAssetsInstance(chainID, contractAddress)
if err != nil {
return "", err
}
usersAddresses, amountsList := api.prepareMintAssetsData(walletAddresses, amount)
transactOpts := txArgs.ToTransactOpts(utils.GetSigner(chainID, api.accountsManager, api.config.KeyStoreDir, txArgs.From, password))
tx, err := contractInst.MintTo(transactOpts, usersAddresses, amountsList)
if err != nil {
return "", err
}
go api.rpcFiltersSrvc.TriggerTransactionSentToUpstreamEvent(&rpcfilters.PendingTxInfo{
Hash: tx.Hash(),
Type: string(transactions.AirdropCommunityToken),
From: common.Address(txArgs.From),
ChainID: chainID,
})
return tx.Hash().Hex(), nil
}
// Estimate MintAssets cost. // Estimate MintAssets cost.
func (api *API) EstimateMintAssets(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, walletAddresses []string, amount *bigint.BigInt) (uint64, error) { func (api *API) EstimateMintAssets(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, walletAddresses []string, amount *bigint.BigInt) (uint64, error) {
err := api.validateWalletsAndAmounts(walletAddresses, amount) err := api.ValidateWalletsAndAmounts(walletAddresses, amount)
if err != nil { if err != nil {
return 0, err return 0, err
} }
usersAddresses, amountsList := api.prepareMintAssetsData(walletAddresses, amount) usersAddresses, amountsList := api.PrepareMintAssetsData(walletAddresses, amount)
return api.estimateMethod(ctx, chainID, contractAddress, fromAddress, "mintTo", usersAddresses, amountsList) return api.estimateMethod(ctx, chainID, contractAddress, fromAddress, "mintTo", usersAddresses, amountsList)
} }
// This is only ERC721 function // This is only ERC721 function
func (api *API) RemoteDestructedAmount(ctx context.Context, chainID uint64, contractAddress string) (*bigint.BigInt, error) { func (api *API) RemoteDestructedAmount(ctx context.Context, chainID uint64, contractAddress string) (*bigint.BigInt, error) {
callOpts := &bind.CallOpts{Context: ctx, Pending: false} callOpts := &bind.CallOpts{Context: ctx, Pending: false}
contractInst, err := api.newCollectiblesInstance(chainID, contractAddress) contractInst, err := api.NewCollectiblesInstance(chainID, contractAddress)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -451,11 +409,6 @@ func (api *API) RemoteBurn(ctx context.Context, chainID uint64, contractAddress
return "", err return "", err
} }
contractInst, err := api.newCollectiblesInstance(chainID, contractAddress)
if err != nil {
return "", err
}
transactOpts := txArgs.ToTransactOpts(utils.GetSigner(chainID, api.accountsManager, api.config.KeyStoreDir, txArgs.From, password)) transactOpts := txArgs.ToTransactOpts(utils.GetSigner(chainID, api.accountsManager, api.config.KeyStoreDir, txArgs.From, password))
var tempTokenIds []*big.Int var tempTokenIds []*big.Int
@ -463,6 +416,11 @@ func (api *API) RemoteBurn(ctx context.Context, chainID uint64, contractAddress
tempTokenIds = append(tempTokenIds, v.Int) tempTokenIds = append(tempTokenIds, v.Int)
} }
contractInst, err := NewTokenInstance(api, chainID, contractAddress)
if err != nil {
return "", err
}
tx, err := contractInst.RemoteBurn(transactOpts, tempTokenIds) tx, err := contractInst.RemoteBurn(transactOpts, tempTokenIds)
if err != nil { if err != nil {
return "", err return "", err
@ -494,7 +452,7 @@ func (api *API) EstimateRemoteBurn(ctx context.Context, chainID uint64, contract
} }
func (api *API) GetCollectiblesContractInstance(chainID uint64, contractAddress string) (*collectibles.Collectibles, error) { func (api *API) GetCollectiblesContractInstance(chainID uint64, contractAddress string) (*collectibles.Collectibles, error) {
contractInst, err := api.newCollectiblesInstance(chainID, contractAddress) contractInst, err := api.NewCollectiblesInstance(chainID, contractAddress)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -502,7 +460,7 @@ func (api *API) GetCollectiblesContractInstance(chainID uint64, contractAddress
} }
func (api *API) GetAssetContractInstance(chainID uint64, contractAddress string) (*assets.Assets, error) { func (api *API) GetAssetContractInstance(chainID uint64, contractAddress string) (*assets.Assets, error) {
contractInst, err := api.newAssetsInstance(chainID, contractAddress) contractInst, err := api.NewAssetsInstance(chainID, contractAddress)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -527,7 +485,7 @@ func (api *API) RemainingSupply(ctx context.Context, chainID uint64, contractAdd
// RemainingSupply = MaxSupply - MintedCount // RemainingSupply = MaxSupply - MintedCount
func (api *API) remainingCollectiblesSupply(ctx context.Context, chainID uint64, contractAddress string) (*bigint.BigInt, error) { func (api *API) remainingCollectiblesSupply(ctx context.Context, chainID uint64, contractAddress string) (*bigint.BigInt, error) {
callOpts := &bind.CallOpts{Context: ctx, Pending: false} callOpts := &bind.CallOpts{Context: ctx, Pending: false}
contractInst, err := api.newCollectiblesInstance(chainID, contractAddress) contractInst, err := api.NewCollectiblesInstance(chainID, contractAddress)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -547,7 +505,7 @@ func (api *API) remainingCollectiblesSupply(ctx context.Context, chainID uint64,
// RemainingSupply = MaxSupply - TotalSupply // RemainingSupply = MaxSupply - TotalSupply
func (api *API) remainingAssetsSupply(ctx context.Context, chainID uint64, contractAddress string) (*bigint.BigInt, error) { func (api *API) remainingAssetsSupply(ctx context.Context, chainID uint64, contractAddress string) (*bigint.BigInt, error) {
callOpts := &bind.CallOpts{Context: ctx, Pending: false} callOpts := &bind.CallOpts{Context: ctx, Pending: false}
contractInst, err := api.newAssetsInstance(chainID, contractAddress) contractInst, err := api.NewAssetsInstance(chainID, contractAddress)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -566,7 +524,7 @@ func (api *API) remainingAssetsSupply(ctx context.Context, chainID uint64, contr
func (api *API) maxSupplyCollectibles(ctx context.Context, chainID uint64, contractAddress string) (*big.Int, error) { func (api *API) maxSupplyCollectibles(ctx context.Context, chainID uint64, contractAddress string) (*big.Int, error) {
callOpts := &bind.CallOpts{Context: ctx, Pending: false} callOpts := &bind.CallOpts{Context: ctx, Pending: false}
contractInst, err := api.newCollectiblesInstance(chainID, contractAddress) contractInst, err := api.NewCollectiblesInstance(chainID, contractAddress)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -575,7 +533,7 @@ func (api *API) maxSupplyCollectibles(ctx context.Context, chainID uint64, contr
func (api *API) maxSupplyAssets(ctx context.Context, chainID uint64, contractAddress string) (*big.Int, error) { func (api *API) maxSupplyAssets(ctx context.Context, chainID uint64, contractAddress string) (*big.Int, error) {
callOpts := &bind.CallOpts{Context: ctx, Pending: false} callOpts := &bind.CallOpts{Context: ctx, Pending: false}
contractInst, err := api.newAssetsInstance(chainID, contractAddress) contractInst, err := api.NewAssetsInstance(chainID, contractAddress)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -608,38 +566,6 @@ func (api *API) prepareNewMaxSupply(ctx context.Context, chainID uint64, contrac
return newMaxSupply, nil return newMaxSupply, nil
} }
func (api *API) setMaxSupplyCollectibles(transactOpts *bind.TransactOpts, chainID uint64, contractAddress string, newMaxSupply *big.Int) (*types.Transaction, error) {
contractInst, err := api.newCollectiblesInstance(chainID, contractAddress)
if err != nil {
return nil, err
}
return contractInst.SetMaxSupply(transactOpts, newMaxSupply)
}
func (api *API) setMaxSupplyAssets(transactOpts *bind.TransactOpts, chainID uint64, contractAddress string, newMaxSupply *big.Int) (*types.Transaction, error) {
contractInst, err := api.newAssetsInstance(chainID, contractAddress)
if err != nil {
return nil, err
}
return contractInst.SetMaxSupply(transactOpts, newMaxSupply)
}
func (api *API) setMaxSupply(transactOpts *bind.TransactOpts, chainID uint64, contractAddress string, newMaxSupply *big.Int) (*types.Transaction, error) {
tokenType, err := api.db.GetTokenType(chainID, contractAddress)
if err != nil {
return nil, err
}
switch tokenType {
case protobuf.CommunityTokenType_ERC721:
return api.setMaxSupplyCollectibles(transactOpts, chainID, contractAddress, newMaxSupply)
case protobuf.CommunityTokenType_ERC20:
return api.setMaxSupplyAssets(transactOpts, chainID, contractAddress, newMaxSupply)
default:
return nil, fmt.Errorf("unknown token type: %v", tokenType)
}
}
func (api *API) Burn(ctx context.Context, chainID uint64, contractAddress string, txArgs transactions.SendTxArgs, password string, burnAmount *bigint.BigInt) (string, error) { func (api *API) Burn(ctx context.Context, chainID uint64, contractAddress string, txArgs transactions.SendTxArgs, password string, burnAmount *bigint.BigInt) (string, error) {
err := api.validateBurnAmount(ctx, burnAmount, chainID, contractAddress) err := api.validateBurnAmount(ctx, burnAmount, chainID, contractAddress)
if err != nil { if err != nil {
@ -653,7 +579,12 @@ func (api *API) Burn(ctx context.Context, chainID uint64, contractAddress string
return "", err return "", err
} }
tx, err := api.setMaxSupply(transactOpts, chainID, contractAddress, newMaxSupply) contractInst, err := NewTokenInstance(api, chainID, contractAddress)
if err != nil {
return "", err
}
tx, err := contractInst.SetMaxSupply(transactOpts, newMaxSupply)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -682,7 +613,7 @@ func (api *API) EstimateBurn(ctx context.Context, chainID uint64, contractAddres
return api.estimateMethod(ctx, chainID, contractAddress, fromAddress, "setMaxSupply", newMaxSupply) return api.estimateMethod(ctx, chainID, contractAddress, fromAddress, "setMaxSupply", newMaxSupply)
} }
func (api *API) validateWalletsAndAmounts(walletAddresses []string, amount *bigint.BigInt) error { func (api *API) ValidateWalletsAndAmounts(walletAddresses []string, amount *bigint.BigInt) error {
if len(walletAddresses) == 0 { if len(walletAddresses) == 0 {
return errors.New("wallet addresses list is empty") return errors.New("wallet addresses list is empty")
} }
@ -713,22 +644,6 @@ func (api *API) validateBurnAmount(ctx context.Context, burnAmount *bigint.BigIn
return nil return nil
} }
func (api *API) packCollectibleMethod(ctx context.Context, methodName string, args ...interface{}) ([]byte, error) {
collectiblesABI, err := abi.JSON(strings.NewReader(collectibles.CollectiblesABI))
if err != nil {
return []byte{}, err
}
return collectiblesABI.Pack(methodName, args...)
}
func (api *API) packAssetsMethod(ctx context.Context, methodName string, args ...interface{}) ([]byte, error) {
assetsABI, err := abi.JSON(strings.NewReader(assets.AssetsABI))
if err != nil {
return []byte{}, err
}
return assetsABI.Pack(methodName, args...)
}
func (api *API) estimateMethod(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, methodName string, args ...interface{}) (uint64, error) { func (api *API) estimateMethod(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, methodName string, args ...interface{}) (uint64, error) {
ethClient, err := api.RPCClient.EthClient(chainID) ethClient, err := api.RPCClient.EthClient(chainID)
if err != nil { if err != nil {
@ -736,20 +651,13 @@ func (api *API) estimateMethod(ctx context.Context, chainID uint64, contractAddr
return 0, err return 0, err
} }
tokenType, err := api.db.GetTokenType(chainID, contractAddress) contractInst, err := NewTokenInstance(api, chainID, contractAddress)
if err != nil { if err != nil {
return 0, err return 0, err
} }
var data []byte
switch tokenType { data, err := contractInst.PackMethod(ctx, methodName, args...)
case protobuf.CommunityTokenType_ERC721:
data, err = api.packCollectibleMethod(ctx, methodName, args...)
case protobuf.CommunityTokenType_ERC20:
data, err = api.packAssetsMethod(ctx, methodName, args...)
default:
err = fmt.Errorf("unknown token type: %v", tokenType)
}
if err != nil { if err != nil {
return 0, err return 0, err
} }

View File

@ -4,6 +4,7 @@ import (
"database/sql" "database/sql"
"fmt" "fmt"
"github.com/status-im/status-go/protocol/communities/token"
"github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/protobuf"
) )
@ -29,3 +30,18 @@ func (db *Database) GetTokenType(chainID uint64, contractAddress string) (protob
} }
return result, fmt.Errorf("can't find token: chainId %v, contractAddress %v", chainID, contractAddress) return result, fmt.Errorf("can't find token: chainId %v, contractAddress %v", chainID, contractAddress)
} }
func (db *Database) GetTokenPrivilegesLevel(chainID uint64, contractAddress string) (token.PrivilegesLevel, error) {
var result = token.CommunityLevel
rows, err := db.db.Query(`SELECT privileges_level FROM community_tokens WHERE chain_id=? AND address=? LIMIT 1`, chainID, contractAddress)
if err != nil {
return result, err
}
defer rows.Close()
if rows.Next() {
err := rows.Scan(&result)
return result, err
}
return result, fmt.Errorf("can't find privileges level: chainId %v, contractAddress %v", chainID, contractAddress)
}

View File

@ -109,3 +109,16 @@ func (s *DatabaseSuite) TestGetTokenType() {
_, err = s.db.GetTokenType(10, "0x777") _, err = s.db.GetTokenType(10, "0x777")
s.Require().Error(err) s.Require().Error(err)
} }
func (s *DatabaseSuite) TestGetPrivilegesLevel() {
privLevel, err := s.db.GetTokenPrivilegesLevel(1, "0x123")
s.Require().NoError(err)
s.Equal(privLevel, token.OwnerLevel)
privLevel, err = s.db.GetTokenPrivilegesLevel(2, "0x345")
s.Require().NoError(err)
s.Equal(privLevel, token.CommunityLevel)
_, err = s.db.GetTokenType(10, "0x777")
s.Require().Error(err)
}

View File

@ -0,0 +1,146 @@
package collectibles
import (
"context"
"fmt"
"math/big"
"strings"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/core/types"
"github.com/status-im/status-go/contracts/community-tokens/assets"
"github.com/status-im/status-go/contracts/community-tokens/collectibles"
"github.com/status-im/status-go/contracts/community-tokens/mastertoken"
"github.com/status-im/status-go/protocol/communities/token"
"github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/services/wallet/bigint"
)
type TokenInstance interface {
RemoteBurn(*bind.TransactOpts, []*big.Int) (*types.Transaction, error)
Mint(*bind.TransactOpts, []string, *bigint.BigInt) (*types.Transaction, error)
SetMaxSupply(*bind.TransactOpts, *big.Int) (*types.Transaction, error)
PackMethod(ctx context.Context, methodName string, args ...interface{}) ([]byte, error)
}
// Master Token
type MasterTokenInstance struct {
TokenInstance
instance *mastertoken.MasterToken
api *API
}
func (t MasterTokenInstance) RemoteBurn(transactOpts *bind.TransactOpts, tokenIds []*big.Int) (*types.Transaction, error) {
return t.instance.RemoteBurn(transactOpts, tokenIds)
}
func (t MasterTokenInstance) Mint(transactOpts *bind.TransactOpts, walletAddresses []string, amount *bigint.BigInt) (*types.Transaction, error) {
usersAddresses := t.api.PrepareMintCollectiblesData(walletAddresses, amount)
return t.instance.MintTo(transactOpts, usersAddresses)
}
func (t MasterTokenInstance) SetMaxSupply(transactOpts *bind.TransactOpts, maxSupply *big.Int) (*types.Transaction, error) {
return t.instance.SetMaxSupply(transactOpts, maxSupply)
}
func (t MasterTokenInstance) PackMethod(ctx context.Context, methodName string, args ...interface{}) ([]byte, error) {
masterTokenABI, err := abi.JSON(strings.NewReader(mastertoken.MasterTokenABI))
if err != nil {
return []byte{}, err
}
return masterTokenABI.Pack(methodName, args...)
}
// Collectible
type CollectibleInstance struct {
TokenInstance
instance *collectibles.Collectibles
api *API
}
func (t CollectibleInstance) RemoteBurn(transactOpts *bind.TransactOpts, tokenIds []*big.Int) (*types.Transaction, error) {
return t.instance.RemoteBurn(transactOpts, tokenIds)
}
func (t CollectibleInstance) Mint(transactOpts *bind.TransactOpts, walletAddresses []string, amount *bigint.BigInt) (*types.Transaction, error) {
usersAddresses := t.api.PrepareMintCollectiblesData(walletAddresses, amount)
return t.instance.MintTo(transactOpts, usersAddresses)
}
func (t CollectibleInstance) SetMaxSupply(transactOpts *bind.TransactOpts, maxSupply *big.Int) (*types.Transaction, error) {
return t.instance.SetMaxSupply(transactOpts, maxSupply)
}
func (t CollectibleInstance) PackMethod(ctx context.Context, methodName string, args ...interface{}) ([]byte, error) {
collectiblesABI, err := abi.JSON(strings.NewReader(collectibles.CollectiblesABI))
if err != nil {
return []byte{}, err
}
return collectiblesABI.Pack(methodName, args...)
}
// Asset
type AssetInstance struct {
TokenInstance
instance *assets.Assets
api *API
}
func (t AssetInstance) RemoteBurn(transactOpts *bind.TransactOpts, tokenIds []*big.Int) (*types.Transaction, error) {
return nil, fmt.Errorf("remote burn not implemented")
}
// The amount should be in smallest denomination of the asset (like wei) with decimal = 18, eg.
// if we want to mint 2.34 of the token, then amount should be 234{16 zeros}.
func (t AssetInstance) Mint(transactOpts *bind.TransactOpts, walletAddresses []string, amount *bigint.BigInt) (*types.Transaction, error) {
usersAddresses, amountsList := t.api.PrepareMintAssetsData(walletAddresses, amount)
return t.instance.MintTo(transactOpts, usersAddresses, amountsList)
}
func (t AssetInstance) SetMaxSupply(transactOpts *bind.TransactOpts, maxSupply *big.Int) (*types.Transaction, error) {
return t.instance.SetMaxSupply(transactOpts, maxSupply)
}
func (t AssetInstance) PackMethod(ctx context.Context, methodName string, args ...interface{}) ([]byte, error) {
assetsABI, err := abi.JSON(strings.NewReader(assets.AssetsABI))
if err != nil {
return []byte{}, err
}
return assetsABI.Pack(methodName, args...)
}
// creator
func NewTokenInstance(api *API, chainID uint64, contractAddress string) (TokenInstance, error) {
tokenType, err := api.db.GetTokenType(chainID, contractAddress)
if err != nil {
return nil, err
}
privLevel, err := api.db.GetTokenPrivilegesLevel(chainID, contractAddress)
if err != nil {
return nil, err
}
switch {
case privLevel == token.MasterLevel:
contractInst, err := api.NewMasterTokenInstance(chainID, contractAddress)
if err != nil {
return nil, err
}
return &MasterTokenInstance{instance: contractInst}, nil
case tokenType == protobuf.CommunityTokenType_ERC721:
contractInst, err := api.NewCollectiblesInstance(chainID, contractAddress)
if err != nil {
return nil, err
}
return &CollectibleInstance{instance: contractInst}, nil
case tokenType == protobuf.CommunityTokenType_ERC20:
contractInst, err := api.NewAssetsInstance(chainID, contractAddress)
if err != nil {
return nil, err
}
return &AssetInstance{instance: contractInst}, nil
}
return nil, fmt.Errorf("unknown type of contract: chain=%v, address=%v", chainID, contractAddress)
}