feat(OwnerToken): Owner token and master token deployment
Adding new smart contracts and generated go files. Deploy token owner function and master token address getter. Adding deployer and privilegesLevel columns to community_tokens table. Passing addressFrom to API calls. Issue #11250
This commit is contained in:
parent
b49b9fe3c5
commit
9d0acc2265
File diff suppressed because one or more lines are too long
|
@ -3,14 +3,14 @@ pragma solidity ^0.8.17;
|
|||
|
||||
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
|
||||
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
|
||||
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
|
||||
import "@openzeppelin/contracts/utils/Context.sol";
|
||||
import "@openzeppelin/contracts/utils/Counters.sol";
|
||||
|
||||
contract CollectibleV1 is
|
||||
abstract contract BaseToken is
|
||||
Context,
|
||||
ERC721Enumerable,
|
||||
Ownable
|
||||
ERC721Enumerable
|
||||
{
|
||||
using Counters for Counters.Counter;
|
||||
|
||||
|
@ -23,6 +23,9 @@ contract CollectibleV1 is
|
|||
*/
|
||||
uint256 public maxSupply;
|
||||
|
||||
address public ownerToken;
|
||||
address public masterToken;
|
||||
|
||||
/**
|
||||
* If set to true, the contract owner can burn any token.
|
||||
*/
|
||||
|
@ -41,19 +44,35 @@ contract CollectibleV1 is
|
|||
uint256 _maxSupply,
|
||||
bool _remoteBurnable,
|
||||
bool _transferable,
|
||||
string memory _baseTokenURI
|
||||
string memory _baseTokenURI,
|
||||
address _ownerToken,
|
||||
address _masterToken
|
||||
) ERC721(_name, _symbol) {
|
||||
maxSupply = _maxSupply;
|
||||
remoteBurnable = _remoteBurnable;
|
||||
transferable = _transferable;
|
||||
baseTokenURI = _baseTokenURI;
|
||||
ownerToken = _ownerToken;
|
||||
masterToken = _masterToken;
|
||||
|
||||
require(ownerToken != address(0x0) || masterToken != address(0x0), "owner or master tokens required");
|
||||
}
|
||||
|
||||
modifier onlyOwner() {
|
||||
require(
|
||||
(ownerToken == address(0) || IERC721(ownerToken).balanceOf(msg.sender) > 0) ||
|
||||
(masterToken == address(0) || IERC721(masterToken).balanceOf(msg.sender) > 0),
|
||||
"Not authorized"
|
||||
);
|
||||
|
||||
_;
|
||||
}
|
||||
|
||||
// Events
|
||||
|
||||
// External functions
|
||||
|
||||
function setMaxSupply(uint256 newMaxSupply) external onlyOwner {
|
||||
function setMaxSupply(uint256 newMaxSupply) virtual external onlyOwner {
|
||||
require(newMaxSupply >= totalSupply(), "MAX_SUPPLY_LOWER_THAN_TOTAL_SUPPLY");
|
||||
maxSupply = newMaxSupply;
|
||||
}
|
||||
|
@ -64,15 +83,9 @@ contract CollectibleV1 is
|
|||
* URI autogenerated based on the base URI passed at construction.
|
||||
*
|
||||
*/
|
||||
function mintTo(address[] memory addresses) external onlyOwner {
|
||||
// We cannot just use totalSupply() to create the new tokenId because tokens
|
||||
// can be burned so we use a separate counter.
|
||||
function mintTo(address[] memory addresses) public onlyOwner {
|
||||
require(_tokenIdTracker.current() + addresses.length <= maxSupply, "MAX_SUPPLY_REACHED");
|
||||
|
||||
for (uint256 i = 0; i < addresses.length; i++) {
|
||||
_safeMint(addresses[i], _tokenIdTracker.current(), "");
|
||||
_tokenIdTracker.increment();
|
||||
}
|
||||
_mintTo(addresses);
|
||||
}
|
||||
|
||||
// Public functions
|
||||
|
@ -116,6 +129,15 @@ contract CollectibleV1 is
|
|||
return baseTokenURI;
|
||||
}
|
||||
|
||||
function _mintTo(address[] memory addresses) internal {
|
||||
// We cannot just use totalSupply() to create the new tokenId because tokens
|
||||
// can be burned so we use a separate counter.
|
||||
for (uint256 i = 0; i < addresses.length; i++) {
|
||||
_safeMint(addresses[i], _tokenIdTracker.current(), "");
|
||||
_tokenIdTracker.increment();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice
|
||||
* @dev
|
|
@ -0,0 +1,26 @@
|
|||
// SPDX-License-Identifier: Mozilla Public License 2.0
|
||||
pragma solidity ^0.8.17;
|
||||
|
||||
import "./BaseToken.sol";
|
||||
|
||||
contract CollectibleV1 is BaseToken {
|
||||
constructor(
|
||||
string memory _name,
|
||||
string memory _symbol,
|
||||
uint256 _maxSupply,
|
||||
bool _remoteBurnable,
|
||||
bool _transferable,
|
||||
string memory _baseTokenURI,
|
||||
address _ownerToken,
|
||||
address _masterToken
|
||||
) BaseToken(
|
||||
_name,
|
||||
_symbol,
|
||||
_maxSupply,
|
||||
_remoteBurnable,
|
||||
_transferable,
|
||||
_baseTokenURI,
|
||||
_ownerToken,
|
||||
_masterToken) {
|
||||
}
|
||||
}
|
|
@ -15,12 +15,16 @@ contract CommunityERC20 is
|
|||
*/
|
||||
uint256 public maxSupply;
|
||||
|
||||
uint8 private customDecimals;
|
||||
|
||||
constructor(
|
||||
string memory _name,
|
||||
string memory _symbol,
|
||||
uint8 _decimals,
|
||||
uint256 _maxSupply
|
||||
) ERC20(_name, _symbol) {
|
||||
maxSupply = _maxSupply;
|
||||
customDecimals = _decimals;
|
||||
}
|
||||
|
||||
// Events
|
||||
|
@ -48,6 +52,9 @@ contract CommunityERC20 is
|
|||
}
|
||||
|
||||
// Public functions
|
||||
function decimals() public view virtual override returns (uint8) {
|
||||
return customDecimals;
|
||||
}
|
||||
|
||||
// Internal functions
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// SPDX-License-Identifier: Mozilla Public License 2.0
|
||||
pragma solidity ^0.8.17;
|
||||
|
||||
import "./BaseToken.sol";
|
||||
|
||||
contract MasterToken is BaseToken {
|
||||
constructor(
|
||||
string memory _name,
|
||||
string memory _symbol,
|
||||
string memory _baseTokenURI,
|
||||
address _ownerToken
|
||||
) BaseToken(
|
||||
_name,
|
||||
_symbol,
|
||||
type(uint256).max,
|
||||
true,
|
||||
false,
|
||||
_baseTokenURI,
|
||||
_ownerToken,
|
||||
address(0x0)) {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
// SPDX-License-Identifier: Mozilla Public License 2.0
|
||||
pragma solidity ^0.8.17;
|
||||
|
||||
import "./BaseToken.sol";
|
||||
import "./MasterToken.sol";
|
||||
|
||||
contract OwnerToken is BaseToken {
|
||||
event MasterTokenCreated(address masterToken);
|
||||
|
||||
bytes public signerPublicKey;
|
||||
|
||||
constructor(
|
||||
string memory _name,
|
||||
string memory _symbol,
|
||||
string memory _baseTokenURI,
|
||||
string memory _masterName,
|
||||
string memory _masterSymbol,
|
||||
string memory _masterBaseTokenURI,
|
||||
bytes memory _signerPublicKey
|
||||
) BaseToken(
|
||||
_name,
|
||||
_symbol,
|
||||
1,
|
||||
false,
|
||||
true,
|
||||
_baseTokenURI,
|
||||
address(this),
|
||||
address(this))
|
||||
{
|
||||
signerPublicKey = _signerPublicKey;
|
||||
MasterToken masterToken = new MasterToken(_masterName, _masterSymbol, _masterBaseTokenURI, address(this));
|
||||
emit MasterTokenCreated(address(masterToken));
|
||||
address[] memory addresses = new address[](1);
|
||||
addresses[0] = msg.sender;
|
||||
_mintTo(addresses);
|
||||
}
|
||||
|
||||
function setMaxSupply(uint256 _newMaxSupply) override external onlyOwner {
|
||||
revert("max supply locked");
|
||||
}
|
||||
|
||||
function setSignerPublicKey(bytes memory _newSignerPublicKey) external onlyOwner {
|
||||
signerPublicKey = _newSignerPublicKey;
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -9,6 +9,7 @@ import (
|
|||
"image/jpeg"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/nfnt/resize"
|
||||
)
|
||||
|
@ -127,3 +128,7 @@ func GetPayloadFromURI(uri string) ([]byte, error) {
|
|||
}
|
||||
return base64.StdEncoding.DecodeString(res[2])
|
||||
}
|
||||
|
||||
func IsPayloadDataURI(uri string) bool {
|
||||
return strings.HasPrefix(uri, "data:image")
|
||||
}
|
||||
|
|
|
@ -4224,7 +4224,8 @@ func (m *Manager) SaveCommunityToken(token *community_token.CommunityToken, crop
|
|||
return nil, err
|
||||
}
|
||||
token.Base64Image = base64img
|
||||
} else {
|
||||
} else if !images.IsPayloadDataURI(token.Base64Image) {
|
||||
// if image is already base64 do not convert (owner and master tokens have already base64 image)
|
||||
token.Base64Image = m.ImageToBase64(token.Base64Image)
|
||||
}
|
||||
|
||||
|
@ -4267,6 +4268,10 @@ func (m *Manager) UpdateCommunityTokenState(chainID int, contractAddress string,
|
|||
return m.persistence.UpdateCommunityTokenState(chainID, contractAddress, deployState)
|
||||
}
|
||||
|
||||
func (m *Manager) UpdateCommunityTokenAddress(chainID int, oldContractAddress string, newContractAddress string) error {
|
||||
return m.persistence.UpdateCommunityTokenAddress(chainID, oldContractAddress, newContractAddress)
|
||||
}
|
||||
|
||||
func (m *Manager) UpdateCommunityTokenSupply(chainID int, contractAddress string, supply *bigint.BigInt) error {
|
||||
return m.persistence.UpdateCommunityTokenSupply(chainID, contractAddress, supply)
|
||||
}
|
||||
|
@ -4486,7 +4491,7 @@ func (m *Manager) HandleCommunityTokensMetadata(communityID string, communityTok
|
|||
|
||||
switch tokenMetadata.TokenType {
|
||||
case protobuf.CommunityTokenType_ERC721:
|
||||
contract, err := m.collectiblesService.API().GetContractInstance(chainID, address)
|
||||
contract, err := m.collectiblesService.API().GetCollectiblesContractInstance(chainID, address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1179,8 +1179,8 @@ func (p *Persistence) GetCommunityChatIDs(communityID types.HexBytes) ([]string,
|
|||
|
||||
func (p *Persistence) GetAllCommunityTokens() ([]*token.CommunityToken, error) {
|
||||
rows, err := p.db.Query(`SELECT community_id, address, type, name, symbol, description, supply_str,
|
||||
infinite_supply, transferable, remote_self_destruct, chain_id, deploy_state, image_base64, decimals
|
||||
FROM community_tokens`)
|
||||
infinite_supply, transferable, remote_self_destruct, chain_id, deploy_state, image_base64, decimals,
|
||||
deployer, privileges_level FROM community_tokens`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1191,7 +1191,8 @@ func (p *Persistence) GetAllCommunityTokens() ([]*token.CommunityToken, error) {
|
|||
|
||||
func (p *Persistence) GetCommunityTokens(communityID string) ([]*token.CommunityToken, error) {
|
||||
rows, err := p.db.Query(`SELECT community_id, address, type, name, symbol, description, supply_str,
|
||||
infinite_supply, transferable, remote_self_destruct, chain_id, deploy_state, image_base64, decimals
|
||||
infinite_supply, transferable, remote_self_destruct, chain_id, deploy_state, image_base64, decimals,
|
||||
deployer, privileges_level
|
||||
FROM community_tokens WHERE community_id = ?`, communityID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -1204,9 +1205,12 @@ func (p *Persistence) GetCommunityTokens(communityID string) ([]*token.Community
|
|||
func (p *Persistence) GetCommunityToken(communityID string, 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 FROM community_tokens WHERE community_id = ? AND chain_id = ? AND address = ?`, communityID, chainID, address).Scan(&token.CommunityID, &token.Address, &token.TokenType, &token.Name,
|
||||
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 community_id = ? AND chain_id = ? AND address = ?`, communityID, 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.RemoteSelfDestruct, &token.ChainID, &token.DeployState, &token.Base64Image, &token.Decimals,
|
||||
&token.Deployer, &token.PrivilegesLevel)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
} else if err != nil {
|
||||
|
@ -1229,7 +1233,8 @@ func (p *Persistence) getCommunityTokensInternal(rows *sql.Rows) ([]*token.Commu
|
|||
var supplyStr string
|
||||
err := rows.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.RemoteSelfDestruct, &token.ChainID, &token.DeployState, &token.Base64Image, &token.Decimals,
|
||||
&token.Deployer, &token.PrivilegesLevel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1257,10 +1262,10 @@ func (p *Persistence) HasCommunityToken(communityID string, address string, chai
|
|||
|
||||
func (p *Persistence) AddCommunityToken(token *token.CommunityToken) error {
|
||||
_, err := p.db.Exec(`INSERT INTO community_tokens (community_id, address, type, name, symbol, description, supply_str,
|
||||
infinite_supply, transferable, remote_self_destruct, chain_id, deploy_state, image_base64, decimals)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, token.CommunityID, token.Address, token.TokenType, token.Name,
|
||||
infinite_supply, transferable, remote_self_destruct, chain_id, deploy_state, image_base64, decimals, deployer, privileges_level)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, token.CommunityID, token.Address, token.TokenType, token.Name,
|
||||
token.Symbol, token.Description, token.Supply.String(), token.InfiniteSupply, token.Transferable, token.RemoteSelfDestruct,
|
||||
token.ChainID, token.DeployState, token.Base64Image, token.Decimals)
|
||||
token.ChainID, token.DeployState, token.Base64Image, token.Decimals, token.Deployer, token.PrivilegesLevel)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -1269,6 +1274,11 @@ func (p *Persistence) UpdateCommunityTokenState(chainID int, contractAddress str
|
|||
return err
|
||||
}
|
||||
|
||||
func (p *Persistence) UpdateCommunityTokenAddress(chainID int, oldContractAddress string, newContractAddress string) error {
|
||||
_, err := p.db.Exec(`UPDATE community_tokens SET address = ? WHERE address = ? AND chain_id = ?`, newContractAddress, oldContractAddress, chainID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *Persistence) UpdateCommunityTokenSupply(chainID int, contractAddress string, supply *bigint.BigInt) error {
|
||||
_, err := p.db.Exec(`UPDATE community_tokens SET supply_str = ? WHERE address = ? AND chain_id = ?`, supply.String(), contractAddress, chainID)
|
||||
return err
|
||||
|
|
|
@ -422,6 +422,8 @@ func (s *PersistenceSuite) TestGetCommunityTokens() {
|
|||
ChainID: 1,
|
||||
DeployState: token.InProgress,
|
||||
Base64Image: "ABCD",
|
||||
Deployer: "0xDep1",
|
||||
PrivilegesLevel: token.OwnerLevel,
|
||||
}
|
||||
|
||||
tokenERC20 := token.CommunityToken{
|
||||
|
@ -439,6 +441,8 @@ func (s *PersistenceSuite) TestGetCommunityTokens() {
|
|||
DeployState: token.Failed,
|
||||
Base64Image: "QWERTY",
|
||||
Decimals: 21,
|
||||
Deployer: "0xDep2",
|
||||
PrivilegesLevel: token.CommunityLevel,
|
||||
}
|
||||
|
||||
err = s.db.AddCommunityToken(&tokenERC721)
|
||||
|
@ -462,6 +466,13 @@ func (s *PersistenceSuite) TestGetCommunityTokens() {
|
|||
s.Require().NoError(err)
|
||||
s.Require().Len(tokens, 1)
|
||||
s.Require().Equal(tokenERC20, *tokens[0])
|
||||
|
||||
err = s.db.UpdateCommunityTokenAddress(1, "0x123", "0x123-newAddr")
|
||||
s.Require().NoError(err)
|
||||
tokens, err = s.db.GetCommunityTokens("123")
|
||||
s.Require().NoError(err)
|
||||
s.Require().Len(tokens, 1)
|
||||
s.Require().Equal("0x123-newAddr", tokens[0].Address)
|
||||
}
|
||||
|
||||
func (s *PersistenceSuite) TestSaveCheckChannelPermissionResponse() {
|
||||
|
|
|
@ -13,6 +13,14 @@ const (
|
|||
Deployed
|
||||
)
|
||||
|
||||
type PrivilegesLevel uint8
|
||||
|
||||
const (
|
||||
OwnerLevel PrivilegesLevel = iota
|
||||
MasterLevel
|
||||
CommunityLevel
|
||||
)
|
||||
|
||||
type CommunityToken struct {
|
||||
TokenType protobuf.CommunityTokenType `json:"tokenType"`
|
||||
CommunityID string `json:"communityId"`
|
||||
|
@ -28,4 +36,6 @@ type CommunityToken struct {
|
|||
DeployState DeployState `json:"deployState"`
|
||||
Base64Image string `json:"image"`
|
||||
Decimals int `json:"decimals"`
|
||||
Deployer string `json:"deployer"`
|
||||
PrivilegesLevel PrivilegesLevel `json:"privilegesLevel"`
|
||||
}
|
||||
|
|
|
@ -4160,6 +4160,10 @@ func (m *Messenger) UpdateCommunityTokenState(chainID int, contractAddress strin
|
|||
return m.communitiesManager.UpdateCommunityTokenState(chainID, contractAddress, deployState)
|
||||
}
|
||||
|
||||
func (m *Messenger) UpdateCommunityTokenAddress(chainID int, oldContractAddress string, newContractAddress string) error {
|
||||
return m.communitiesManager.UpdateCommunityTokenAddress(chainID, oldContractAddress, newContractAddress)
|
||||
}
|
||||
|
||||
func (m *Messenger) UpdateCommunityTokenSupply(chainID int, contractAddress string, supply *bigint.BigInt) error {
|
||||
return m.communitiesManager.UpdateCommunityTokenSupply(chainID, contractAddress, supply)
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE community_tokens ADD COLUMN deployer TEXT NOT NULL DEFAULT "";
|
||||
ALTER TABLE community_tokens ADD COLUMN privileges_level INT NOT NULL DEFAULT 2;
|
|
@ -15,8 +15,10 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/status-im/status-go/account"
|
||||
"github.com/status-im/status-go/contracts/assets"
|
||||
"github.com/status-im/status-go/contracts/collectibles"
|
||||
"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/ownertoken"
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
"github.com/status-im/status-go/rpc"
|
||||
|
@ -59,6 +61,8 @@ type DeploymentParameters struct {
|
|||
Transferable bool `json:"transferable"`
|
||||
RemoteSelfDestruct bool `json:"remoteSelfDestruct"`
|
||||
TokenURI string `json:"tokenUri"`
|
||||
OwnerTokenAddress string `json:"ownerTokenAddress"`
|
||||
MasterTokenAddress string `json:"masterTokenAddress"`
|
||||
}
|
||||
|
||||
func (d *DeploymentParameters) GetSupply() *big.Int {
|
||||
|
@ -115,7 +119,8 @@ func (api *API) DeployCollectibles(ctx context.Context, chainID uint64, deployme
|
|||
address, tx, _, err := collectibles.DeployCollectibles(transactOpts, ethClient, deploymentParameters.Name,
|
||||
deploymentParameters.Symbol, deploymentParameters.GetSupply(),
|
||||
deploymentParameters.RemoteSelfDestruct, deploymentParameters.Transferable,
|
||||
deploymentParameters.TokenURI)
|
||||
deploymentParameters.TokenURI, common.HexToAddress(deploymentParameters.OwnerTokenAddress),
|
||||
common.HexToAddress(deploymentParameters.MasterTokenAddress))
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
return DeploymentDetails{}, err
|
||||
|
@ -131,6 +136,77 @@ func (api *API) DeployCollectibles(ctx context.Context, chainID uint64, deployme
|
|||
return DeploymentDetails{address.Hex(), tx.Hash().Hex()}, nil
|
||||
}
|
||||
|
||||
func (api *API) DeployOwnerToken(ctx context.Context, chainID uint64, ownerTokenParameters DeploymentParameters, masterTokenParameters DeploymentParameters, txArgs transactions.SendTxArgs, password string) (DeploymentDetails, error) {
|
||||
err := ownerTokenParameters.Validate(false)
|
||||
if err != nil {
|
||||
return DeploymentDetails{}, err
|
||||
}
|
||||
|
||||
err = masterTokenParameters.Validate(false)
|
||||
if err != nil {
|
||||
return DeploymentDetails{}, err
|
||||
}
|
||||
|
||||
transactOpts := txArgs.ToTransactOpts(utils.GetSigner(chainID, api.accountsManager, api.config.KeyStoreDir, txArgs.From, password))
|
||||
|
||||
ethClient, err := api.RPCClient.EthClient(chainID)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
return DeploymentDetails{}, err
|
||||
}
|
||||
|
||||
signerPubKey := []byte{}
|
||||
|
||||
address, tx, _, err := ownertoken.DeployOwnerToken(transactOpts, ethClient, ownerTokenParameters.Name,
|
||||
ownerTokenParameters.Symbol, ownerTokenParameters.TokenURI,
|
||||
masterTokenParameters.Name, masterTokenParameters.Symbol,
|
||||
masterTokenParameters.TokenURI, signerPubKey)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
return DeploymentDetails{}, err
|
||||
}
|
||||
|
||||
go api.rpcFiltersSrvc.TriggerTransactionSentToUpstreamEvent(&rpcfilters.PendingTxInfo{
|
||||
Hash: tx.Hash(),
|
||||
Type: string(transactions.DeployOwnerToken),
|
||||
From: common.Address(txArgs.From),
|
||||
ChainID: chainID,
|
||||
})
|
||||
|
||||
return DeploymentDetails{address.Hex(), tx.Hash().Hex()}, nil
|
||||
}
|
||||
|
||||
func (api *API) GetMasterTokenContractAddressFromHash(ctx context.Context, chainID uint64, txHash string) (string, error) {
|
||||
ethClient, err := api.RPCClient.EthClient(chainID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
receipt, err := ethClient.TransactionReceipt(ctx, common.HexToHash(txHash))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
logMasterTokenCreatedSig := []byte("MasterTokenCreated(address)")
|
||||
logMasterTokenCreatedSigHash := crypto.Keccak256Hash(logMasterTokenCreatedSig)
|
||||
|
||||
for _, vLog := range receipt.Logs {
|
||||
if vLog.Topics[0].Hex() == logMasterTokenCreatedSigHash.Hex() {
|
||||
ownerTokenABI, err := abi.JSON(strings.NewReader(ownertoken.OwnerTokenABI))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
event := new(ownertoken.OwnerTokenMasterTokenCreated)
|
||||
err = ownerTokenABI.UnpackIntoInterface(event, "MasterTokenCreated", vLog.Data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return event.MasterToken.Hex(), nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("can't find master token address in transaction: %v", txHash)
|
||||
}
|
||||
|
||||
func (api *API) DeployAssets(ctx context.Context, chainID uint64, deploymentParameters DeploymentParameters, txArgs transactions.SendTxArgs, password string) (DeploymentDetails, error) {
|
||||
|
||||
err := deploymentParameters.Validate(true)
|
||||
|
@ -165,7 +241,7 @@ func (api *API) DeployAssets(ctx context.Context, chainID uint64, deploymentPara
|
|||
|
||||
// Returns gas units + 10%
|
||||
func (api *API) DeployCollectiblesEstimate(ctx context.Context) (uint64, error) {
|
||||
gasAmount := uint64(1960645)
|
||||
gasAmount := uint64(2091605)
|
||||
return gasAmount + uint64(float32(gasAmount)*0.1), nil
|
||||
}
|
||||
|
||||
|
@ -175,6 +251,12 @@ func (api *API) DeployAssetsEstimate(ctx context.Context) (uint64, error) {
|
|||
return gasAmount + uint64(float32(gasAmount)*0.1), nil
|
||||
}
|
||||
|
||||
// Returns gas units + 10%
|
||||
func (api *API) DeployOwnerTokenEstimate(ctx context.Context) (uint64, error) {
|
||||
ownerGasAmount := uint64(4389457)
|
||||
return ownerGasAmount + uint64(float32(ownerGasAmount)*0.1), nil
|
||||
}
|
||||
|
||||
func (api *API) newCollectiblesInstance(chainID uint64, contractAddress string) (*collectibles.Collectibles, error) {
|
||||
backend, err := api.RPCClient.EthClient(chainID)
|
||||
if err != nil {
|
||||
|
@ -228,16 +310,16 @@ func (api *API) MintTokens(ctx context.Context, chainID uint64, contractAddress
|
|||
}
|
||||
}
|
||||
|
||||
func (api *API) EstimateMintTokens(ctx context.Context, chainID uint64, contractAddress 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) {
|
||||
tokenType, err := api.db.GetTokenType(chainID, contractAddress)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
switch tokenType {
|
||||
case protobuf.CommunityTokenType_ERC721:
|
||||
return api.EstimateMintCollectibles(ctx, chainID, contractAddress, walletAddresses, amount)
|
||||
return api.EstimateMintCollectibles(ctx, chainID, contractAddress, fromAddress, walletAddresses, amount)
|
||||
case protobuf.CommunityTokenType_ERC20:
|
||||
return api.EstimateMintAssets(ctx, chainID, contractAddress, walletAddresses, amount)
|
||||
return api.EstimateMintAssets(ctx, chainID, contractAddress, fromAddress, walletAddresses, amount)
|
||||
default:
|
||||
return 0, fmt.Errorf("unknown token type: %v", tokenType)
|
||||
}
|
||||
|
@ -274,13 +356,13 @@ func (api *API) MintCollectibles(ctx context.Context, chainID uint64, contractAd
|
|||
return tx.Hash().Hex(), nil
|
||||
}
|
||||
|
||||
func (api *API) EstimateMintCollectibles(ctx context.Context, chainID uint64, contractAddress 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)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
usersAddresses := api.prepareMintCollectiblesData(walletAddresses, amount)
|
||||
return api.estimateMethod(ctx, chainID, contractAddress, "mintTo", usersAddresses)
|
||||
return api.estimateMethod(ctx, chainID, contractAddress, fromAddress, "mintTo", usersAddresses)
|
||||
}
|
||||
|
||||
func (api *API) prepareMintAssetsData(walletAddresses []string, amount *bigint.BigInt) ([]common.Address, []*big.Int) {
|
||||
|
@ -327,13 +409,13 @@ func (api *API) MintAssets(ctx context.Context, chainID uint64, contractAddress
|
|||
}
|
||||
|
||||
// Estimate MintAssets cost.
|
||||
func (api *API) EstimateMintAssets(ctx context.Context, chainID uint64, contractAddress 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)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
usersAddresses, amountsList := api.prepareMintAssetsData(walletAddresses, amount)
|
||||
return api.estimateMethod(ctx, chainID, contractAddress, "mintTo", usersAddresses, amountsList)
|
||||
return api.estimateMethod(ctx, chainID, contractAddress, fromAddress, "mintTo", usersAddresses, amountsList)
|
||||
}
|
||||
|
||||
// This is only ERC721 function
|
||||
|
@ -397,7 +479,7 @@ func (api *API) RemoteBurn(ctx context.Context, chainID uint64, contractAddress
|
|||
}
|
||||
|
||||
// This is only ERC721 function
|
||||
func (api *API) EstimateRemoteBurn(ctx context.Context, chainID uint64, contractAddress string, tokenIds []*bigint.BigInt) (uint64, error) {
|
||||
func (api *API) EstimateRemoteBurn(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, tokenIds []*bigint.BigInt) (uint64, error) {
|
||||
err := api.validateTokens(tokenIds)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
@ -408,10 +490,10 @@ func (api *API) EstimateRemoteBurn(ctx context.Context, chainID uint64, contract
|
|||
tempTokenIds = append(tempTokenIds, v.Int)
|
||||
}
|
||||
|
||||
return api.estimateMethod(ctx, chainID, contractAddress, "remoteBurn", tempTokenIds)
|
||||
return api.estimateMethod(ctx, chainID, contractAddress, fromAddress, "remoteBurn", tempTokenIds)
|
||||
}
|
||||
|
||||
func (api *API) GetContractInstance(chainID uint64, contractAddress string) (*collectibles.Collectibles, error) {
|
||||
func (api *API) GetCollectiblesContractInstance(chainID uint64, contractAddress string) (*collectibles.Collectibles, error) {
|
||||
contractInst, err := api.newCollectiblesInstance(chainID, contractAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -427,36 +509,6 @@ func (api *API) GetAssetContractInstance(chainID uint64, contractAddress string)
|
|||
return contractInst, nil
|
||||
}
|
||||
|
||||
func (api *API) ContractOwner(ctx context.Context, chainID uint64, contractAddress string) (string, error) {
|
||||
callOpts := &bind.CallOpts{Context: ctx, Pending: false}
|
||||
tokenType, err := api.db.GetTokenType(chainID, contractAddress)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if tokenType == protobuf.CommunityTokenType_ERC721 {
|
||||
contractInst, err := api.newCollectiblesInstance(chainID, contractAddress)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
owner, err := contractInst.Owner(callOpts)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return owner.String(), nil
|
||||
} else if tokenType == protobuf.CommunityTokenType_ERC20 {
|
||||
contractInst, err := api.newAssetsInstance(chainID, contractAddress)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
owner, err := contractInst.Owner(callOpts)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return owner.String(), nil
|
||||
}
|
||||
return "", fmt.Errorf("unknown token type: %v", tokenType)
|
||||
}
|
||||
|
||||
func (api *API) RemainingSupply(ctx context.Context, chainID uint64, contractAddress string) (*bigint.BigInt, error) {
|
||||
tokenType, err := api.db.GetTokenType(chainID, contractAddress)
|
||||
if err != nil {
|
||||
|
@ -616,7 +668,7 @@ func (api *API) Burn(ctx context.Context, chainID uint64, contractAddress string
|
|||
return tx.Hash().Hex(), nil
|
||||
}
|
||||
|
||||
func (api *API) EstimateBurn(ctx context.Context, chainID uint64, contractAddress string, burnAmount *bigint.BigInt) (uint64, error) {
|
||||
func (api *API) EstimateBurn(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, burnAmount *bigint.BigInt) (uint64, error) {
|
||||
err := api.validateBurnAmount(ctx, burnAmount, chainID, contractAddress)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
@ -627,7 +679,7 @@ func (api *API) EstimateBurn(ctx context.Context, chainID uint64, contractAddres
|
|||
return 0, err
|
||||
}
|
||||
|
||||
return api.estimateMethod(ctx, chainID, contractAddress, "setMaxSupply", newMaxSupply)
|
||||
return api.estimateMethod(ctx, chainID, contractAddress, fromAddress, "setMaxSupply", newMaxSupply)
|
||||
}
|
||||
|
||||
func (api *API) validateWalletsAndAmounts(walletAddresses []string, amount *bigint.BigInt) error {
|
||||
|
@ -677,7 +729,7 @@ func (api *API) packAssetsMethod(ctx context.Context, methodName string, args ..
|
|||
return assetsABI.Pack(methodName, args...)
|
||||
}
|
||||
|
||||
func (api *API) estimateMethod(ctx context.Context, chainID uint64, contractAddress 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)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
|
@ -702,13 +754,8 @@ func (api *API) estimateMethod(ctx context.Context, chainID uint64, contractAddr
|
|||
return 0, err
|
||||
}
|
||||
|
||||
ownerAddr, err := api.ContractOwner(ctx, chainID, contractAddress)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
toAddr := common.HexToAddress(contractAddress)
|
||||
fromAddr := common.HexToAddress(ownerAddr)
|
||||
fromAddr := common.HexToAddress(fromAddress)
|
||||
|
||||
callMsg := ethereum.CallMsg{
|
||||
From: fromAddr,
|
||||
|
|
|
@ -17,37 +17,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, "", "", ""},
|
||||
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, "", "", ""},
|
||||
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, "", "", ""},
|
||||
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, "", "", ""},
|
||||
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, "", "", ""},
|
||||
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, "", "", ""},
|
||||
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, "", "", ""},
|
||||
isError: false,
|
||||
},
|
||||
}
|
||||
|
@ -63,10 +63,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, "", "", ""}
|
||||
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, "", "", ""}
|
||||
requiredSupply = infiniteSupplyParams.GetInfiniteSupply()
|
||||
require.Equal(t, infiniteSupplyParams.GetSupply(), requiredSupply)
|
||||
}
|
||||
|
|
|
@ -27,10 +27,10 @@ type DatabaseSuite struct {
|
|||
|
||||
func (s *DatabaseSuite) addCommunityToken(db *sql.DB, token *token.CommunityToken) error {
|
||||
_, err := db.Exec(`INSERT INTO community_tokens (community_id, address, type, name, symbol, description, supply_str,
|
||||
infinite_supply, transferable, remote_self_destruct, chain_id, deploy_state, image_base64, decimals)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, token.CommunityID, token.Address, token.TokenType, token.Name,
|
||||
infinite_supply, transferable, remote_self_destruct, chain_id, deploy_state, image_base64, decimals, deployer, privileges_level)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, token.CommunityID, token.Address, token.TokenType, token.Name,
|
||||
token.Symbol, token.Description, token.Supply.String(), token.InfiniteSupply, token.Transferable, token.RemoteSelfDestruct,
|
||||
token.ChainID, token.DeployState, token.Base64Image, token.Decimals)
|
||||
token.ChainID, token.DeployState, token.Base64Image, token.Decimals, token.Deployer, token.PrivilegesLevel)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,8 @@ func (s *DatabaseSuite) setupDatabase(db *sql.DB) error {
|
|||
ChainID: 1,
|
||||
DeployState: token.InProgress,
|
||||
Base64Image: "ABCD",
|
||||
Deployer: "0xDEP1",
|
||||
PrivilegesLevel: token.OwnerLevel,
|
||||
}
|
||||
|
||||
token20 := &token.CommunityToken{
|
||||
|
@ -66,6 +68,8 @@ func (s *DatabaseSuite) setupDatabase(db *sql.DB) error {
|
|||
DeployState: token.Failed,
|
||||
Base64Image: "QWERTY",
|
||||
Decimals: 21,
|
||||
Deployer: "0xDEP2",
|
||||
PrivilegesLevel: token.CommunityLevel,
|
||||
}
|
||||
|
||||
err := s.addCommunityToken(db, token721)
|
||||
|
|
|
@ -1353,6 +1353,10 @@ func (api *PublicAPI) UpdateCommunityTokenState(chainID int, contractAddress str
|
|||
return api.service.messenger.UpdateCommunityTokenState(chainID, contractAddress, deployState)
|
||||
}
|
||||
|
||||
func (api *PublicAPI) UpdateCommunityTokenAddress(chainID int, oldContractAddress string, newContractAddress string) error {
|
||||
return api.service.messenger.UpdateCommunityTokenAddress(chainID, oldContractAddress, newContractAddress)
|
||||
}
|
||||
|
||||
func (api *PublicAPI) UpdateCommunityTokenSupply(chainID int, contractAddress string, supply *bigint.BigInt) error {
|
||||
return api.service.messenger.UpdateCommunityTokenSupply(chainID, contractAddress, supply)
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/status-im/status-go/contracts/collectibles"
|
||||
"github.com/status-im/status-go/contracts/community-tokens/collectibles"
|
||||
"github.com/status-im/status-go/rpc"
|
||||
"github.com/status-im/status-go/services/wallet/bigint"
|
||||
walletCommon "github.com/status-im/status-go/services/wallet/common"
|
||||
|
|
|
@ -103,6 +103,7 @@ const (
|
|||
AirdropCommunityToken PendingTrxType = "AirdropCommunityToken"
|
||||
RemoteDestructCollectible PendingTrxType = "RemoteDestructCollectible"
|
||||
BurnCommunityToken PendingTrxType = "BurnCommunityToken"
|
||||
DeployOwnerToken PendingTrxType = "DeployOwnerToken"
|
||||
)
|
||||
|
||||
type PendingTransaction struct {
|
||||
|
|
Loading…
Reference in New Issue