feat(collectibles): Mint collectibles (ERC-721):
Add testing smart contract and go api. Add collectibles service. Issue #3051
This commit is contained in:
parent
51f99a2631
commit
8acc46f758
|
@ -0,0 +1,122 @@
|
||||||
|
// SPDX-License-Identifier: Mozilla Public License 2.0
|
||||||
|
pragma solidity ^0.8.17;
|
||||||
|
|
||||||
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||||
|
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
|
||||||
|
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
|
||||||
|
import "@openzeppelin/contracts/utils/Context.sol";
|
||||||
|
import "@openzeppelin/contracts/utils/Counters.sol";
|
||||||
|
|
||||||
|
contract CollectibleV1 is
|
||||||
|
Context,
|
||||||
|
ERC721Enumerable,
|
||||||
|
Ownable
|
||||||
|
{
|
||||||
|
using Counters for Counters.Counter;
|
||||||
|
|
||||||
|
// State variables
|
||||||
|
|
||||||
|
Counters.Counter private _tokenIdTracker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If we want unlimited total supply we should set maxSupply to 2^256-1.
|
||||||
|
*/
|
||||||
|
uint256 public maxSupply;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set to true, the contract owner can burn any token.
|
||||||
|
*/
|
||||||
|
bool public remoteBurnable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set to false it acts as a soulbound token.
|
||||||
|
*/
|
||||||
|
bool public transferable;
|
||||||
|
|
||||||
|
string public baseTokenURI;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
string memory _name,
|
||||||
|
string memory _symbol,
|
||||||
|
uint256 _maxSupply,
|
||||||
|
bool _remoteBurnable,
|
||||||
|
bool _transferable,
|
||||||
|
string memory _baseTokenURI
|
||||||
|
) ERC721(_name, _symbol) {
|
||||||
|
maxSupply = _maxSupply;
|
||||||
|
remoteBurnable = _remoteBurnable;
|
||||||
|
transferable = _transferable;
|
||||||
|
baseTokenURI = _baseTokenURI;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Events
|
||||||
|
|
||||||
|
// External functions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Creates a new token for each address in `addresses`. Its token ID will be automatically
|
||||||
|
* assigned (and available on the emitted {IERC721-Transfer} event), and the token
|
||||||
|
* 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.
|
||||||
|
require(_tokenIdTracker.current() + addresses.length < maxSupply, "MAX_SUPPLY_REACHED");
|
||||||
|
|
||||||
|
for (uint256 i = 0; i < addresses.length; i++) {
|
||||||
|
_safeMint(addresses[i], _tokenIdTracker.current(), "");
|
||||||
|
_tokenIdTracker.increment();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public functions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice remoteBurn allows the owner to burn a token
|
||||||
|
* @param tokenId The token ID to be burned
|
||||||
|
*/
|
||||||
|
function remoteBurn(uint256 tokenId) public onlyOwner {
|
||||||
|
require(remoteBurnable, "NOT_REMOTE_BURNABLE");
|
||||||
|
|
||||||
|
_burn(tokenId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev See {IERC165-supportsInterface}.
|
||||||
|
*/
|
||||||
|
function supportsInterface(bytes4 interfaceId)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
virtual
|
||||||
|
override(ERC721Enumerable)
|
||||||
|
returns (bool)
|
||||||
|
{
|
||||||
|
return super.supportsInterface(interfaceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal functions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice
|
||||||
|
* @dev
|
||||||
|
*/
|
||||||
|
function _baseURI() internal view virtual override returns (string memory) {
|
||||||
|
return baseTokenURI;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice
|
||||||
|
* @dev
|
||||||
|
*/
|
||||||
|
function _beforeTokenTransfer(
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 firstTokenId,
|
||||||
|
uint256 batchSize
|
||||||
|
) internal virtual override(ERC721Enumerable) {
|
||||||
|
super._beforeTokenTransfer(from, to, firstTokenId, batchSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private functions
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
|
@ -35,6 +35,7 @@ import (
|
||||||
appmetricsservice "github.com/status-im/status-go/services/appmetrics"
|
appmetricsservice "github.com/status-im/status-go/services/appmetrics"
|
||||||
"github.com/status-im/status-go/services/browsers"
|
"github.com/status-im/status-go/services/browsers"
|
||||||
"github.com/status-im/status-go/services/chat"
|
"github.com/status-im/status-go/services/chat"
|
||||||
|
"github.com/status-im/status-go/services/collectibles"
|
||||||
"github.com/status-im/status-go/services/ens"
|
"github.com/status-im/status-go/services/ens"
|
||||||
"github.com/status-im/status-go/services/gif"
|
"github.com/status-im/status-go/services/gif"
|
||||||
localnotifications "github.com/status-im/status-go/services/local-notifications"
|
localnotifications "github.com/status-im/status-go/services/local-notifications"
|
||||||
|
@ -120,6 +121,7 @@ type StatusNode struct {
|
||||||
wakuV2Srvc *wakuv2.Waku
|
wakuV2Srvc *wakuv2.Waku
|
||||||
wakuV2ExtSrvc *wakuv2ext.Service
|
wakuV2ExtSrvc *wakuv2ext.Service
|
||||||
ensSrvc *ens.Service
|
ensSrvc *ens.Service
|
||||||
|
collectiblesSrvc *collectibles.Service
|
||||||
gifSrvc *gif.Service
|
gifSrvc *gif.Service
|
||||||
stickersSrvc *stickers.Service
|
stickersSrvc *stickers.Service
|
||||||
chatSrvc *chat.Service
|
chatSrvc *chat.Service
|
||||||
|
@ -462,6 +464,7 @@ func (n *StatusNode) stop() error {
|
||||||
n.wakuV2Srvc = nil
|
n.wakuV2Srvc = nil
|
||||||
n.wakuV2ExtSrvc = nil
|
n.wakuV2ExtSrvc = nil
|
||||||
n.ensSrvc = nil
|
n.ensSrvc = nil
|
||||||
|
n.collectiblesSrvc = nil
|
||||||
n.stickersSrvc = nil
|
n.stickersSrvc = nil
|
||||||
n.publicMethods = make(map[string]bool)
|
n.publicMethods = make(map[string]bool)
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
appmetricsservice "github.com/status-im/status-go/services/appmetrics"
|
appmetricsservice "github.com/status-im/status-go/services/appmetrics"
|
||||||
"github.com/status-im/status-go/services/browsers"
|
"github.com/status-im/status-go/services/browsers"
|
||||||
"github.com/status-im/status-go/services/chat"
|
"github.com/status-im/status-go/services/chat"
|
||||||
|
"github.com/status-im/status-go/services/collectibles"
|
||||||
"github.com/status-im/status-go/services/ens"
|
"github.com/status-im/status-go/services/ens"
|
||||||
"github.com/status-im/status-go/services/ext"
|
"github.com/status-im/status-go/services/ext"
|
||||||
"github.com/status-im/status-go/services/gif"
|
"github.com/status-im/status-go/services/gif"
|
||||||
|
@ -75,6 +76,7 @@ func (b *StatusNode) initServices(config *params.NodeConfig, mediaServer *server
|
||||||
services = append(services, b.personalService())
|
services = append(services, b.personalService())
|
||||||
services = append(services, b.statusPublicService())
|
services = append(services, b.statusPublicService())
|
||||||
services = append(services, b.ensService())
|
services = append(services, b.ensService())
|
||||||
|
services = append(services, b.collectiblesService())
|
||||||
services = append(services, b.stickersService(accDB))
|
services = append(services, b.stickersService(accDB))
|
||||||
services = append(services, b.updatesService())
|
services = append(services, b.updatesService())
|
||||||
services = appendIf(config.EnableNTPSync, services, b.timeSource())
|
services = appendIf(config.EnableNTPSync, services, b.timeSource())
|
||||||
|
@ -400,6 +402,13 @@ func (b *StatusNode) ensService() *ens.Service {
|
||||||
return b.ensSrvc
|
return b.ensSrvc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *StatusNode) collectiblesService() *collectibles.Service {
|
||||||
|
if b.collectiblesSrvc == nil {
|
||||||
|
b.collectiblesSrvc = collectibles.NewService(b.rpcClient, b.gethAccountManager, b.config)
|
||||||
|
}
|
||||||
|
return b.collectiblesSrvc
|
||||||
|
}
|
||||||
|
|
||||||
func (b *StatusNode) stickersService(accountDB *accounts.Database) *stickers.Service {
|
func (b *StatusNode) stickersService(accountDB *accounts.Database) *stickers.Service {
|
||||||
if b.stickersSrvc == nil {
|
if b.stickersSrvc == nil {
|
||||||
b.stickersSrvc = stickers.NewService(accountDB, b.rpcClient, b.gethAccountManager, b.rpcFiltersSrvc, b.config, b.downloader, b.httpServer)
|
b.stickersSrvc = stickers.NewService(accountDB, b.rpcClient, b.gethAccountManager, b.rpcFiltersSrvc, b.config, b.downloader, b.httpServer)
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
package collectibles
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/status-im/status-go/account"
|
||||||
|
"github.com/status-im/status-go/contracts/collectibles"
|
||||||
|
"github.com/status-im/status-go/params"
|
||||||
|
"github.com/status-im/status-go/rpc"
|
||||||
|
"github.com/status-im/status-go/services/utils"
|
||||||
|
"github.com/status-im/status-go/transactions"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewAPI(rpcClient *rpc.Client, accountsManager *account.GethManager, config *params.NodeConfig) *API {
|
||||||
|
return &API{
|
||||||
|
RPCClient: rpcClient,
|
||||||
|
accountsManager: accountsManager,
|
||||||
|
config: config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type API struct {
|
||||||
|
RPCClient *rpc.Client
|
||||||
|
accountsManager *account.GethManager
|
||||||
|
config *params.NodeConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeploymentDetails struct {
|
||||||
|
ContractAddress string `json:"contractAddress"`
|
||||||
|
TransactionHash string `json:"transactionHash"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxSupply = 999999999
|
||||||
|
|
||||||
|
type DeploymentParameters struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Symbol string `json:"symbol"`
|
||||||
|
Supply int `json:"supply"`
|
||||||
|
InfiniteSupply bool `json:"infiniteSupply"`
|
||||||
|
Transferable bool `json:"transferable"`
|
||||||
|
RemoteSelfDestruct bool `json:"remoteSelfDestruct"`
|
||||||
|
TokenURI string `json:"tokenUri"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeploymentParameters) GetSupply() *big.Int {
|
||||||
|
if d.InfiniteSupply {
|
||||||
|
return d.GetInfiniteSupply()
|
||||||
|
}
|
||||||
|
return big.NewInt(int64(d.Supply))
|
||||||
|
}
|
||||||
|
|
||||||
|
// infinite supply for ERC721 is 2^256-1
|
||||||
|
func (d *DeploymentParameters) GetInfiniteSupply() *big.Int {
|
||||||
|
max := new(big.Int).Exp(big.NewInt(2), big.NewInt(256), nil)
|
||||||
|
max.Sub(max, big.NewInt(1))
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeploymentParameters) Validate() error {
|
||||||
|
if len(d.Name) <= 0 {
|
||||||
|
return errors.New("empty collectible name")
|
||||||
|
}
|
||||||
|
if len(d.Symbol) <= 0 {
|
||||||
|
return errors.New("empty collectible symbol")
|
||||||
|
}
|
||||||
|
if !d.InfiniteSupply && (d.Supply < 0 || d.Supply > maxSupply) {
|
||||||
|
return fmt.Errorf("wrong supply value: %v", d.Supply)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *API) Deploy(ctx context.Context, chainID uint64, deploymentParameters DeploymentParameters, txArgs transactions.SendTxArgs, password string) (DeploymentDetails, error) {
|
||||||
|
|
||||||
|
err := deploymentParameters.Validate()
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
address, tx, _, err := collectibles.DeployCollectibles(transactOpts, ethClient, deploymentParameters.Name,
|
||||||
|
deploymentParameters.Symbol, deploymentParameters.GetSupply(),
|
||||||
|
deploymentParameters.RemoteSelfDestruct, deploymentParameters.Transferable,
|
||||||
|
deploymentParameters.TokenURI)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
return DeploymentDetails{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return DeploymentDetails{address.Hex(), tx.Hash().Hex()}, nil
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package collectibles
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDeploymentParameters(t *testing.T) {
|
||||||
|
var testCases = []struct {
|
||||||
|
name string
|
||||||
|
parameters DeploymentParameters
|
||||||
|
isError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "emptyName",
|
||||||
|
parameters: DeploymentParameters{"", "SYMBOL", 123, false, false, false, ""},
|
||||||
|
isError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "emptySymbol",
|
||||||
|
parameters: DeploymentParameters{"NAME", "", 123, false, false, false, ""},
|
||||||
|
isError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "negativeSupply",
|
||||||
|
parameters: DeploymentParameters{"NAME", "SYM", -123, false, false, false, ""},
|
||||||
|
isError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "zeroSupply",
|
||||||
|
parameters: DeploymentParameters{"NAME", "SYM", 0, false, false, false, ""},
|
||||||
|
isError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "negativeSupplyAndInfinite",
|
||||||
|
parameters: DeploymentParameters{"NAME", "SYM", -123, true, false, false, ""},
|
||||||
|
isError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "supplyGreaterThanMax",
|
||||||
|
parameters: DeploymentParameters{"NAME", "SYM", maxSupply + 1, false, false, false, ""},
|
||||||
|
isError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "supplyIsMax",
|
||||||
|
parameters: DeploymentParameters{"NAME", "SYM", maxSupply, false, false, false, ""},
|
||||||
|
isError: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
err := tc.parameters.Validate()
|
||||||
|
if tc.isError {
|
||||||
|
require.Error(t, err)
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
notInfiniteSupplyParams := DeploymentParameters{"NAME", "SYM", 123, false, false, false, ""}
|
||||||
|
requiredSupply := big.NewInt(123)
|
||||||
|
require.Equal(t, notInfiniteSupplyParams.GetSupply(), requiredSupply)
|
||||||
|
infiniteSupplyParams := DeploymentParameters{"NAME", "SYM", 123, true, false, false, ""}
|
||||||
|
requiredSupply = infiniteSupplyParams.GetInfiniteSupply()
|
||||||
|
require.Equal(t, infiniteSupplyParams.GetSupply(), requiredSupply)
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package collectibles
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
|
ethRpc "github.com/ethereum/go-ethereum/rpc"
|
||||||
|
"github.com/status-im/status-go/account"
|
||||||
|
"github.com/status-im/status-go/params"
|
||||||
|
"github.com/status-im/status-go/rpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Collectibles service
|
||||||
|
type Service struct {
|
||||||
|
api *API
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a new Collectibles Service.
|
||||||
|
func NewService(rpcClient *rpc.Client, accountsManager *account.GethManager, config *params.NodeConfig) *Service {
|
||||||
|
return &Service{
|
||||||
|
NewAPI(rpcClient, accountsManager, config),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protocols returns a new protocols list. In this case, there are none.
|
||||||
|
func (s *Service) Protocols() []p2p.Protocol {
|
||||||
|
return []p2p.Protocol{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// APIs returns a list of new APIs.
|
||||||
|
func (s *Service) APIs() []ethRpc.API {
|
||||||
|
return []ethRpc.API{
|
||||||
|
{
|
||||||
|
Namespace: "collectibles",
|
||||||
|
Version: "0.1.0",
|
||||||
|
Service: s.api,
|
||||||
|
Public: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start is run when a service is started.
|
||||||
|
func (s *Service) Start() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop is run when a service is stopped.
|
||||||
|
func (s *Service) Stop() error {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -23,7 +23,6 @@ import (
|
||||||
"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/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
ethTypes "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"
|
"github.com/status-im/status-go/contracts"
|
||||||
|
@ -34,6 +33,7 @@ import (
|
||||||
"github.com/status-im/status-go/params"
|
"github.com/status-im/status-go/params"
|
||||||
"github.com/status-im/status-go/rpc"
|
"github.com/status-im/status-go/rpc"
|
||||||
"github.com/status-im/status-go/services/rpcfilters"
|
"github.com/status-im/status-go/services/rpcfilters"
|
||||||
|
"github.com/status-im/status-go/services/utils"
|
||||||
"github.com/status-im/status-go/transactions"
|
"github.com/status-im/status-go/transactions"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -310,17 +310,6 @@ func (api *API) Price(ctx context.Context, chainID uint64) (string, error) {
|
||||||
return fmt.Sprintf("%x", price), nil
|
return fmt.Sprintf("%x", price), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) getSigner(chainID uint64, from types.Address, password string) bind.SignerFn {
|
|
||||||
return func(addr common.Address, tx *ethTypes.Transaction) (*ethTypes.Transaction, error) {
|
|
||||||
selectedAccount, err := api.accountsManager.VerifyAccountPassword(api.config.KeyStoreDir, from.Hex(), password)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
s := ethTypes.NewLondonSigner(new(big.Int).SetUint64(chainID))
|
|
||||||
return ethTypes.SignTx(tx, s, selectedAccount.PrivateKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *API) Release(ctx context.Context, chainID uint64, txArgs transactions.SendTxArgs, password string, username string) (string, error) {
|
func (api *API) Release(ctx context.Context, chainID uint64, txArgs transactions.SendTxArgs, password string, username string) (string, error) {
|
||||||
registryAddr, err := api.usernameRegistrarAddr(ctx, chainID)
|
registryAddr, err := api.usernameRegistrarAddr(ctx, chainID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -332,7 +321,7 @@ func (api *API) Release(ctx context.Context, chainID uint64, txArgs transactions
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
txOpts := txArgs.ToTransactOpts(api.getSigner(chainID, txArgs.From, password))
|
txOpts := txArgs.ToTransactOpts(utils.GetSigner(chainID, api.accountsManager, api.config.KeyStoreDir, txArgs.From, password))
|
||||||
tx, err := registrar.Release(txOpts, usernameToLabel(username))
|
tx, err := registrar.Release(txOpts, usernameToLabel(username))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -411,7 +400,7 @@ func (api *API) Register(ctx context.Context, chainID uint64, txArgs transaction
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
txOpts := txArgs.ToTransactOpts(api.getSigner(chainID, txArgs.From, password))
|
txOpts := txArgs.ToTransactOpts(utils.GetSigner(chainID, api.accountsManager, api.config.KeyStoreDir, txArgs.From, password))
|
||||||
tx, err := snt.ApproveAndCall(
|
tx, err := snt.ApproveAndCall(
|
||||||
txOpts,
|
txOpts,
|
||||||
registryAddr,
|
registryAddr,
|
||||||
|
@ -523,7 +512,7 @@ func (api *API) SetPubKey(ctx context.Context, chainID uint64, txArgs transactio
|
||||||
}
|
}
|
||||||
|
|
||||||
x, y := extractCoordinates(pubkey)
|
x, y := extractCoordinates(pubkey)
|
||||||
txOpts := txArgs.ToTransactOpts(api.getSigner(chainID, txArgs.From, password))
|
txOpts := txArgs.ToTransactOpts(utils.GetSigner(chainID, api.accountsManager, api.config.KeyStoreDir, txArgs.From, password))
|
||||||
tx, err := resolver.SetPubkey(txOpts, nameHash(username), x, y)
|
tx, err := resolver.SetPubkey(txOpts, nameHash(username), x, y)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|
|
@ -11,25 +11,14 @@ 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"
|
||||||
ethTypes "github.com/ethereum/go-ethereum/core/types"
|
|
||||||
"github.com/status-im/status-go/contracts/snt"
|
"github.com/status-im/status-go/contracts/snt"
|
||||||
"github.com/status-im/status-go/contracts/stickers"
|
"github.com/status-im/status-go/contracts/stickers"
|
||||||
"github.com/status-im/status-go/eth-node/types"
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
|
"github.com/status-im/status-go/services/utils"
|
||||||
"github.com/status-im/status-go/services/wallet/bigint"
|
"github.com/status-im/status-go/services/wallet/bigint"
|
||||||
"github.com/status-im/status-go/transactions"
|
"github.com/status-im/status-go/transactions"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (api *API) getSigner(chainID uint64, from types.Address, password string) bind.SignerFn {
|
|
||||||
return func(addr common.Address, tx *ethTypes.Transaction) (*ethTypes.Transaction, error) {
|
|
||||||
selectedAccount, err := api.accountsManager.VerifyAccountPassword(api.keyStoreDir, from.Hex(), password)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
s := ethTypes.NewLondonSigner(new(big.Int).SetUint64(chainID))
|
|
||||||
return ethTypes.SignTx(tx, s, selectedAccount.PrivateKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *API) Buy(ctx context.Context, chainID uint64, txArgs transactions.SendTxArgs, packID *bigint.BigInt, password string) (string, error) {
|
func (api *API) Buy(ctx context.Context, chainID uint64, txArgs transactions.SendTxArgs, packID *bigint.BigInt, password string) (string, error) {
|
||||||
snt, err := api.contractMaker.NewSNT(chainID)
|
snt, err := api.contractMaker.NewSNT(chainID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -63,7 +52,7 @@ func (api *API) Buy(ctx context.Context, chainID uint64, txArgs transactions.Sen
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
txOpts := txArgs.ToTransactOpts(api.getSigner(chainID, txArgs.From, password))
|
txOpts := txArgs.ToTransactOpts(utils.GetSigner(chainID, api.accountsManager, api.keyStoreDir, txArgs.From, password))
|
||||||
tx, err := snt.ApproveAndCall(
|
tx, err := snt.ApproveAndCall(
|
||||||
txOpts,
|
txOpts,
|
||||||
stickerMarketAddress,
|
stickerMarketAddress,
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
package services
|
|
||||||
|
|
||||||
import "github.com/ethereum/go-ethereum/rpc"
|
|
||||||
|
|
||||||
// APIByNamespace retrieve an api by its namespace or returns nil.
|
|
||||||
func APIByNamespace(apis []rpc.API, namespace string) interface{} {
|
|
||||||
for _, api := range apis {
|
|
||||||
if api.Namespace == namespace {
|
|
||||||
return api.Service
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
ethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/status-im/status-go/account"
|
||||||
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetSigner(chainID uint64, accountsManager *account.GethManager, keyStoreDir string, from types.Address, password string) bind.SignerFn {
|
||||||
|
return func(addr common.Address, tx *ethTypes.Transaction) (*ethTypes.Transaction, error) {
|
||||||
|
selectedAccount, err := accountsManager.VerifyAccountPassword(keyStoreDir, from.Hex(), password)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s := ethTypes.NewLondonSigner(new(big.Int).SetUint64(chainID))
|
||||||
|
return ethTypes.SignTx(tx, s, selectedAccount.PrivateKey)
|
||||||
|
}
|
||||||
|
}
|
|
@ -58,11 +58,12 @@ type MultiTransactionResult struct {
|
||||||
type PendingTrxType string
|
type PendingTrxType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
RegisterENS PendingTrxType = "RegisterENS"
|
RegisterENS PendingTrxType = "RegisterENS"
|
||||||
ReleaseENS PendingTrxType = "ReleaseENS"
|
ReleaseENS PendingTrxType = "ReleaseENS"
|
||||||
SetPubKey PendingTrxType = "SetPubKey"
|
SetPubKey PendingTrxType = "SetPubKey"
|
||||||
BuyStickerPack PendingTrxType = "BuyStickerPack"
|
BuyStickerPack PendingTrxType = "BuyStickerPack"
|
||||||
WalletTransfer PendingTrxType = "WalletTransfer"
|
WalletTransfer PendingTrxType = "WalletTransfer"
|
||||||
|
CollectibleDeployment PendingTrxType = "CollectibleDeployment"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PendingTransaction struct {
|
type PendingTransaction struct {
|
||||||
|
|
Loading…
Reference in New Issue