diff --git a/contracts/community-tokens/contracts.go b/contracts/community-tokens/contracts.go new file mode 100644 index 00000000000..2413a9a5930 --- /dev/null +++ b/contracts/community-tokens/contracts.go @@ -0,0 +1,78 @@ +package communitytokens + +import ( + "errors" + + "github.com/ethereum/go-ethereum/common" + "github.com/status-im/status-go/contracts/community-tokens/assets" + "github.com/status-im/status-go/contracts/community-tokens/collectibles" + communitytokendeployer "github.com/status-im/status-go/contracts/community-tokens/deployer" + "github.com/status-im/status-go/contracts/community-tokens/mastertoken" + "github.com/status-im/status-go/contracts/community-tokens/ownertoken" + communityownertokenregistry "github.com/status-im/status-go/contracts/community-tokens/registry" + "github.com/status-im/status-go/rpc" +) + +type CommunityTokensContractMaker struct { + RPCClient rpc.ClientInterface +} + +func NewCommunityTokensContractMakerMaker(client rpc.ClientInterface) (*CommunityTokensContractMaker, error) { + if client == nil { + return nil, errors.New("could not initialize CommunityTokensContractMaker with an rpc client") + } + return &CommunityTokensContractMaker{RPCClient: client}, nil +} + +func (c *CommunityTokensContractMaker) NewOwnerTokenInstance(chainID uint64, contractAddress common.Address, +) (*ownertoken.OwnerToken, error) { + backend, err := c.RPCClient.EthClient(chainID) + if err != nil { + return nil, err + } + return ownertoken.NewOwnerToken(contractAddress, backend) +} + +func (c *CommunityTokensContractMaker) NewMasterTokenInstance(chainID uint64, contractAddress common.Address, +) (*mastertoken.MasterToken, error) { + backend, err := c.RPCClient.EthClient(chainID) + if err != nil { + return nil, err + } + return mastertoken.NewMasterToken(contractAddress, backend) +} + +func (c *CommunityTokensContractMaker) NewCollectiblesInstance(chainID uint64, contractAddress common.Address, +) (*collectibles.Collectibles, error) { + backend, err := c.RPCClient.EthClient(chainID) + if err != nil { + return nil, err + } + return collectibles.NewCollectibles(contractAddress, backend) +} + +func (c *CommunityTokensContractMaker) NewAssetsInstance(chainID uint64, contractAddress common.Address, +) (*assets.Assets, error) { + backend, err := c.RPCClient.EthClient(chainID) + if err != nil { + return nil, err + } + return assets.NewAssets(contractAddress, backend) +} + +func (c *CommunityTokensContractMaker) NewCommunityTokenDeployerInstance(chainID uint64, contractAddress common.Address, +) (*communitytokendeployer.CommunityTokenDeployer, error) { + backend, err := c.RPCClient.EthClient(chainID) + if err != nil { + return nil, err + } + return communitytokendeployer.NewCommunityTokenDeployer(contractAddress, backend) +} + +func (c *CommunityTokensContractMaker) NewCommunityOwnerTokenRegistryInstance(chainID uint64, contractAddress common.Address) (*communityownertokenregistry.CommunityOwnerTokenRegistry, error) { + backend, err := c.RPCClient.EthClient(chainID) + if err != nil { + return nil, err + } + return communityownertokenregistry.NewCommunityOwnerTokenRegistry(contractAddress, backend) +} diff --git a/node/status_node_services.go b/node/status_node_services.go index 3851f7894e0..bb846bf8b0c 100644 --- a/node/status_node_services.go +++ b/node/status_node_services.go @@ -513,7 +513,7 @@ func (b *StatusNode) CommunityTokensService() *communitytokens.Service { func (b *StatusNode) CommunityTokensServiceV2() *communitytokensv2.Service { if b.communityTokensSrvcV2 == nil { - b.communityTokensSrvcV2 = communitytokensv2.NewService(b.rpcClient, b.gethAccountManager, b.pendingTracker, b.config, b.appDB, &b.walletFeed, b.transactor) + b.communityTokensSrvcV2 = communitytokensv2.NewService(b.rpcClient, b.gethAccountManager, b.config, b.appDB, &b.walletFeed, b.transactor) } return b.communityTokensSrvcV2 } diff --git a/services/communitytokens/service.go b/services/communitytokens/service.go index e7863d08c2a..6aa451ac981 100644 --- a/services/communitytokens/service.go +++ b/services/communitytokens/service.go @@ -142,7 +142,7 @@ func (s *Service) handleWalletEvent(event walletevent.Event) { errorStr = tokenErr.Error() } - signal.SendCommunityTokenTransactionStatusSignal(string(pendingTransaction.Type), p.Status == transactions.Success, pendingTransaction.Hash, + signal.SendCommunityTokenTransactionStatusSignal(0, p.Status == transactions.Success, pendingTransaction.Hash, communityToken, ownerToken, masterToken, errorStr) } } diff --git a/services/communitytokensv2/api.go b/services/communitytokensv2/api.go index d962a27b472..20a2418e3c8 100644 --- a/services/communitytokensv2/api.go +++ b/services/communitytokensv2/api.go @@ -2,30 +2,16 @@ package communitytokensv2 import ( "context" - "fmt" "math/big" - "go.uber.org/zap" - "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/status-im/status-go/contracts/community-tokens/assets" - "github.com/status-im/status-go/contracts/community-tokens/collectibles" - communitytokendeployer "github.com/status-im/status-go/contracts/community-tokens/deployer" - "github.com/status-im/status-go/contracts/community-tokens/ownertoken" - communityownertokenregistry "github.com/status-im/status-go/contracts/community-tokens/registry" - "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" - "github.com/status-im/status-go/logutils" "github.com/status-im/status-go/protocol/communities/token" "github.com/status-im/status-go/protocol/protobuf" - "github.com/status-im/status-go/services/utils" "github.com/status-im/status-go/services/wallet/bigint" - wcommon "github.com/status-im/status-go/services/wallet/common" "github.com/status-im/status-go/services/wallet/requests" - "github.com/status-im/status-go/services/wallet/wallettypes" - "github.com/status-im/status-go/transactions" ) func NewAPI(s *Service) *API { @@ -38,321 +24,61 @@ type API struct { s *Service } -func (api *API) DeployCollectibles(ctx context.Context, chainID uint64, deploymentParameters requests.DeploymentParameters, txArgs wallettypes.SendTxArgs, password string) (requests.DeploymentDetails, error) { - err := deploymentParameters.Validate(false) - if err != nil { - return requests.DeploymentDetails{}, err - } - transactOpts := txArgs.ToTransactOpts(utils.VerifyPasswordAndGetSigner(chainID, api.s.accountsManager, api.s.config.KeyStoreDir, txArgs.From, password)) - - ethClient, err := api.s.manager.rpcClient.EthClient(chainID) - if err != nil { - logutils.ZapLogger().Error(err.Error()) - return requests.DeploymentDetails{}, err - } - address, tx, _, err := collectibles.DeployCollectibles(transactOpts, ethClient, deploymentParameters.Name, - deploymentParameters.Symbol, deploymentParameters.GetSupply(), - deploymentParameters.RemoteSelfDestruct, deploymentParameters.Transferable, - deploymentParameters.TokenURI, common.HexToAddress(deploymentParameters.OwnerTokenAddress), - common.HexToAddress(deploymentParameters.MasterTokenAddress)) - if err != nil { - logutils.ZapLogger().Error(err.Error()) - return requests.DeploymentDetails{}, err - } +func (api *API) StoreDeployedCollectibles(ctx context.Context, addressFrom types.Address, addressTo types.Address, chainID uint64, + txHash common.Hash, deploymentParameters requests.DeploymentParameters) (requests.DeploymentDetails, error) { - err = api.s.pendingTracker.TrackPendingTransaction( - wcommon.ChainID(chainID), - tx.Hash(), - common.Address(txArgs.From), - address, - transactions.DeployCommunityToken, - transactions.Keep, - "", - ) - if err != nil { - logutils.ZapLogger().Error("TrackPendingTransaction error", zap.Error(err)) - return requests.DeploymentDetails{}, err - } - - savedCommunityToken, err := api.s.CreateCommunityTokenAndSave(int(chainID), deploymentParameters, txArgs.From.Hex(), address.Hex(), - protobuf.CommunityTokenType_ERC721, token.CommunityLevel, tx.Hash().Hex()) + savedCommunityToken, err := api.s.CreateCommunityTokenAndSave(int(chainID), deploymentParameters, addressFrom.Hex(), addressTo.Hex(), + protobuf.CommunityTokenType_ERC721, token.CommunityLevel, txHash.Hex()) if err != nil { return requests.DeploymentDetails{}, err } return requests.DeploymentDetails{ - ContractAddress: address.Hex(), - TransactionHash: tx.Hash().Hex(), + ContractAddress: addressTo.Hex(), + TransactionHash: txHash.Hex(), CommunityToken: savedCommunityToken}, nil } -func decodeSignature(sig []byte) (r [32]byte, s [32]byte, v uint8, err error) { - if len(sig) != crypto.SignatureLength { - return [32]byte{}, [32]byte{}, 0, fmt.Errorf("wrong size for signature: got %d, want %d", len(sig), crypto.SignatureLength) - } - copy(r[:], sig[:32]) - copy(s[:], sig[32:64]) - v = sig[64] + 27 - return r, s, v, nil -} +func (api *API) StoreDeployedOwnerToken(ctx context.Context, addressFrom types.Address, chainID uint64, txHash common.Hash, + ownerTokenParameters requests.DeploymentParameters, masterTokenParameters requests.DeploymentParameters) (requests.DeploymentDetails, error) { -func prepareDeploymentSignatureStruct(signature string, communityID string, addressFrom common.Address) (communitytokendeployer.CommunityTokenDeployerDeploymentSignature, error) { - r, s, v, err := decodeSignature(common.FromHex(signature)) - if err != nil { - return communitytokendeployer.CommunityTokenDeployerDeploymentSignature{}, err - } - communityEthAddress, err := convert33BytesPubKeyToEthAddress(communityID) - if err != nil { - return communitytokendeployer.CommunityTokenDeployerDeploymentSignature{}, err - } - communitySignature := communitytokendeployer.CommunityTokenDeployerDeploymentSignature{ - V: v, - R: r, - S: s, - Deployer: addressFrom, - Signer: communityEthAddress, - } - return communitySignature, nil -} - -func (api *API) DeployOwnerToken(ctx context.Context, chainID uint64, - ownerTokenParameters requests.DeploymentParameters, masterTokenParameters requests.DeploymentParameters, - signerPubKey string, txArgs wallettypes.SendTxArgs, password string) (requests.DeploymentDetails, error) { - err := ownerTokenParameters.Validate(false) - if err != nil { - return requests.DeploymentDetails{}, err - } - - if len(signerPubKey) <= 0 { - return requests.DeploymentDetails{}, fmt.Errorf("signerPubKey is empty") - } - - err = masterTokenParameters.Validate(false) - if err != nil { - return requests.DeploymentDetails{}, err - } - - transactOpts := txArgs.ToTransactOpts(utils.VerifyPasswordAndGetSigner(chainID, api.s.accountsManager, api.s.config.KeyStoreDir, txArgs.From, password)) - - deployerContractInst, err := api.NewCommunityTokenDeployerInstance(chainID) + savedOwnerToken, err := api.s.CreateCommunityTokenAndSave(int(chainID), ownerTokenParameters, addressFrom.Hex(), + api.s.TemporaryOwnerContractAddress(txHash.Hex()), protobuf.CommunityTokenType_ERC721, token.OwnerLevel, txHash.Hex()) if err != nil { return requests.DeploymentDetails{}, err } - - ownerTokenConfig := communitytokendeployer.CommunityTokenDeployerTokenConfig{ - Name: ownerTokenParameters.Name, - Symbol: ownerTokenParameters.Symbol, - BaseURI: ownerTokenParameters.TokenURI, - } - - masterTokenConfig := communitytokendeployer.CommunityTokenDeployerTokenConfig{ - Name: masterTokenParameters.Name, - Symbol: masterTokenParameters.Symbol, - BaseURI: masterTokenParameters.TokenURI, - } - - signature, err := api.s.Messenger.CreateCommunityTokenDeploymentSignature(context.Background(), chainID, txArgs.From.Hex(), ownerTokenParameters.CommunityID) - if err != nil { - return requests.DeploymentDetails{}, err - } - - communitySignature, err := prepareDeploymentSignatureStruct(types.HexBytes(signature).String(), ownerTokenParameters.CommunityID, common.Address(txArgs.From)) - if err != nil { - return requests.DeploymentDetails{}, err - } - - logutils.ZapLogger().Debug("Prepare deployment", zap.Any("signature", communitySignature)) - - tx, err := deployerContractInst.Deploy(transactOpts, ownerTokenConfig, masterTokenConfig, communitySignature, common.FromHex(signerPubKey)) - - if err != nil { - logutils.ZapLogger().Error(err.Error()) - return requests.DeploymentDetails{}, err - } - - logutils.ZapLogger().Debug("Contract deployed", zap.Stringer("hash", tx.Hash())) - - err = api.s.pendingTracker.TrackPendingTransaction( - wcommon.ChainID(chainID), - tx.Hash(), - common.Address(txArgs.From), - common.Address{}, - transactions.DeployOwnerToken, - transactions.Keep, - "", - ) - if err != nil { - logutils.ZapLogger().Error("TrackPendingTransaction error", zap.Error(err)) - return requests.DeploymentDetails{}, err - } - - savedOwnerToken, err := api.s.CreateCommunityTokenAndSave(int(chainID), ownerTokenParameters, txArgs.From.Hex(), - api.s.TemporaryOwnerContractAddress(tx.Hash().Hex()), protobuf.CommunityTokenType_ERC721, token.OwnerLevel, tx.Hash().Hex()) - if err != nil { - return requests.DeploymentDetails{}, err - } - savedMasterToken, err := api.s.CreateCommunityTokenAndSave(int(chainID), masterTokenParameters, txArgs.From.Hex(), - api.s.TemporaryMasterContractAddress(tx.Hash().Hex()), protobuf.CommunityTokenType_ERC721, token.MasterLevel, tx.Hash().Hex()) + savedMasterToken, err := api.s.CreateCommunityTokenAndSave(int(chainID), masterTokenParameters, addressFrom.Hex(), + api.s.TemporaryMasterContractAddress(txHash.Hex()), protobuf.CommunityTokenType_ERC721, token.MasterLevel, txHash.Hex()) if err != nil { return requests.DeploymentDetails{}, err } return requests.DeploymentDetails{ ContractAddress: "", - TransactionHash: tx.Hash().Hex(), + TransactionHash: txHash.Hex(), OwnerToken: savedOwnerToken, MasterToken: savedMasterToken}, nil } -// recovery function which starts transaction tracking again -func (api *API) ReTrackOwnerTokenDeploymentTransaction(ctx context.Context, chainID uint64, contractAddress string) error { - return api.s.ReTrackOwnerTokenDeploymentTransaction(ctx, chainID, contractAddress) -} - -func (api *API) DeployAssets(ctx context.Context, chainID uint64, deploymentParameters requests.DeploymentParameters, txArgs wallettypes.SendTxArgs, password string) (requests.DeploymentDetails, error) { - - err := deploymentParameters.Validate(true) - if err != nil { - return requests.DeploymentDetails{}, err - } - - transactOpts := txArgs.ToTransactOpts(utils.VerifyPasswordAndGetSigner(chainID, api.s.accountsManager, api.s.config.KeyStoreDir, txArgs.From, password)) - - ethClient, err := api.s.manager.rpcClient.EthClient(chainID) - if err != nil { - logutils.ZapLogger().Error(err.Error()) - return requests.DeploymentDetails{}, err - } - - const decimals = 18 - address, tx, _, err := assets.DeployAssets(transactOpts, ethClient, deploymentParameters.Name, - deploymentParameters.Symbol, decimals, deploymentParameters.GetSupply(), - deploymentParameters.TokenURI, - common.HexToAddress(deploymentParameters.OwnerTokenAddress), - common.HexToAddress(deploymentParameters.MasterTokenAddress)) - if err != nil { - logutils.ZapLogger().Error(err.Error()) - return requests.DeploymentDetails{}, err - } - - err = api.s.pendingTracker.TrackPendingTransaction( - wcommon.ChainID(chainID), - tx.Hash(), - common.Address(txArgs.From), - address, - transactions.DeployCommunityToken, - transactions.Keep, - "", - ) - if err != nil { - logutils.ZapLogger().Error("TrackPendingTransaction error", zap.Error(err)) - return requests.DeploymentDetails{}, err - } +func (api *API) StoreDeployedAssets(ctx context.Context, addressFrom types.Address, addressTo types.Address, chainID uint64, + txHash common.Hash, deploymentParameters requests.DeploymentParameters) (requests.DeploymentDetails, error) { - savedCommunityToken, err := api.s.CreateCommunityTokenAndSave(int(chainID), deploymentParameters, txArgs.From.Hex(), address.Hex(), - protobuf.CommunityTokenType_ERC20, token.CommunityLevel, tx.Hash().Hex()) + savedCommunityToken, err := api.s.CreateCommunityTokenAndSave(int(chainID), deploymentParameters, addressFrom.Hex(), addressTo.Hex(), + protobuf.CommunityTokenType_ERC20, token.CommunityLevel, txHash.Hex()) if err != nil { return requests.DeploymentDetails{}, err } return requests.DeploymentDetails{ - ContractAddress: address.Hex(), - TransactionHash: tx.Hash().Hex(), + ContractAddress: addressTo.Hex(), + TransactionHash: txHash.Hex(), CommunityToken: savedCommunityToken}, nil } -func (api *API) DeployCollectiblesEstimate(ctx context.Context, chainID uint64, fromAddress string) (*CommunityTokenFees, error) { - return api.s.deployCollectiblesEstimate(ctx, chainID, fromAddress) -} - -func (api *API) DeployAssetsEstimate(ctx context.Context, chainID uint64, fromAddress string) (*CommunityTokenFees, error) { - return api.s.deployAssetsEstimate(ctx, chainID, fromAddress) -} - -func (api *API) DeployOwnerTokenEstimate(ctx context.Context, chainID uint64, fromAddress string, - ownerTokenParameters requests.DeploymentParameters, masterTokenParameters requests.DeploymentParameters, - communityID string, signerPubKey string) (*CommunityTokenFees, error) { - return api.s.deployOwnerTokenEstimate(ctx, chainID, fromAddress, ownerTokenParameters, masterTokenParameters, communityID, signerPubKey) -} - -func (api *API) EstimateMintTokens(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, walletAddresses []string, amount *bigint.BigInt) (*CommunityTokenFees, error) { - return api.s.mintTokensEstimate(ctx, chainID, contractAddress, fromAddress, walletAddresses, amount) -} - -// This is only ERC721 function -func (api *API) EstimateRemoteBurn(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, tokenIds []*bigint.BigInt) (*CommunityTokenFees, error) { - return api.s.remoteBurnEstimate(ctx, chainID, contractAddress, fromAddress, tokenIds) -} - -func (api *API) EstimateBurn(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, burnAmount *bigint.BigInt) (*CommunityTokenFees, error) { - return api.s.burnEstimate(ctx, chainID, contractAddress, fromAddress, burnAmount) -} - -func (api *API) EstimateSetSignerPubKey(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, newSignerPubKey string) (*CommunityTokenFees, error) { - return api.s.setSignerPubKeyEstimate(ctx, chainID, contractAddress, fromAddress, newSignerPubKey) -} - -func (api *API) NewOwnerTokenInstance(chainID uint64, contractAddress string) (*ownertoken.OwnerToken, error) { - return api.s.NewOwnerTokenInstance(chainID, contractAddress) -} - -func (api *API) NewCommunityTokenDeployerInstance(chainID uint64) (*communitytokendeployer.CommunityTokenDeployer, error) { - return api.s.manager.NewCommunityTokenDeployerInstance(chainID) -} - -func (api *API) NewCommunityOwnerTokenRegistryInstance(chainID uint64, contractAddress string) (*communityownertokenregistry.CommunityOwnerTokenRegistry, error) { - return api.s.NewCommunityOwnerTokenRegistryInstance(chainID, contractAddress) -} - -func (api *API) NewCollectiblesInstance(chainID uint64, contractAddress string) (*collectibles.Collectibles, error) { - return api.s.manager.NewCollectiblesInstance(chainID, contractAddress) -} - -func (api *API) NewAssetsInstance(chainID uint64, contractAddress string) (*assets.Assets, error) { - return api.s.manager.NewAssetsInstance(chainID, contractAddress) -} - -// Universal minting function for every type of token. -func (api *API) MintTokens(ctx context.Context, chainID uint64, contractAddress string, txArgs wallettypes.SendTxArgs, password string, walletAddresses []string, amount *bigint.BigInt) (string, error) { - - err := api.s.ValidateWalletsAndAmounts(walletAddresses, amount) - if err != nil { - return "", err - } - - transactOpts := txArgs.ToTransactOpts(utils.VerifyPasswordAndGetSigner(chainID, api.s.accountsManager, api.s.config.KeyStoreDir, txArgs.From, password)) - - contractInst, err := NewTokenInstance(api.s, chainID, contractAddress) - if err != nil { - return "", err - } - - tx, err := contractInst.Mint(transactOpts, walletAddresses, amount) - if err != nil { - return "", err - } - - err = api.s.pendingTracker.TrackPendingTransaction( - wcommon.ChainID(chainID), - tx.Hash(), - common.Address(txArgs.From), - common.HexToAddress(contractAddress), - transactions.AirdropCommunityToken, - transactions.Keep, - "", - ) - if err != nil { - logutils.ZapLogger().Error("TrackPendingTransaction error", zap.Error(err)) - return "", err - } - - return tx.Hash().Hex(), nil -} - // This is only ERC721 function func (api *API) RemoteDestructedAmount(ctx context.Context, chainID uint64, contractAddress string) (*bigint.BigInt, error) { callOpts := &bind.CallOpts{Context: ctx, Pending: false} - contractInst, err := api.NewCollectiblesInstance(chainID, contractAddress) + contractInst, err := api.s.manager.NewCollectiblesInstance(chainID, common.HexToAddress(contractAddress)) if err != nil { return nil, err } @@ -375,99 +101,10 @@ func (api *API) RemoteDestructedAmount(ctx context.Context, chainID uint64, cont return &bigint.BigInt{Int: res}, nil } -// This is only ERC721 function -func (api *API) RemoteBurn(ctx context.Context, chainID uint64, contractAddress string, txArgs wallettypes.SendTxArgs, password string, tokenIds []*bigint.BigInt, additionalData string) (string, error) { - err := api.s.validateTokens(tokenIds) - if err != nil { - return "", err - } - - transactOpts := txArgs.ToTransactOpts(utils.VerifyPasswordAndGetSigner(chainID, api.s.accountsManager, api.s.config.KeyStoreDir, txArgs.From, password)) - - var tempTokenIds []*big.Int - for _, v := range tokenIds { - tempTokenIds = append(tempTokenIds, v.Int) - } - - contractInst, err := NewTokenInstance(api.s, chainID, contractAddress) - if err != nil { - return "", err - } - - tx, err := contractInst.RemoteBurn(transactOpts, tempTokenIds) - if err != nil { - return "", err - } - - err = api.s.pendingTracker.TrackPendingTransaction( - wcommon.ChainID(chainID), - tx.Hash(), - common.Address(txArgs.From), - common.HexToAddress(contractAddress), - transactions.RemoteDestructCollectible, - transactions.Keep, - additionalData, - ) - if err != nil { - logutils.ZapLogger().Error("TrackPendingTransaction error", zap.Error(err)) - return "", err - } - - return tx.Hash().Hex(), nil -} - -func (api *API) GetCollectiblesContractInstance(chainID uint64, contractAddress string) (*collectibles.Collectibles, error) { - return api.s.manager.GetCollectiblesContractInstance(chainID, contractAddress) -} - -func (api *API) GetAssetContractInstance(chainID uint64, contractAddress string) (*assets.Assets, error) { - return api.s.manager.GetAssetContractInstance(chainID, contractAddress) -} - func (api *API) RemainingSupply(ctx context.Context, chainID uint64, contractAddress string) (*bigint.BigInt, error) { return api.s.remainingSupply(ctx, chainID, contractAddress) } -func (api *API) Burn(ctx context.Context, chainID uint64, contractAddress string, txArgs wallettypes.SendTxArgs, password string, burnAmount *bigint.BigInt) (string, error) { - err := api.s.validateBurnAmount(ctx, burnAmount, chainID, contractAddress) - if err != nil { - return "", err - } - - transactOpts := txArgs.ToTransactOpts(utils.VerifyPasswordAndGetSigner(chainID, api.s.accountsManager, api.s.config.KeyStoreDir, txArgs.From, password)) - - newMaxSupply, err := api.s.prepareNewMaxSupply(ctx, chainID, contractAddress, burnAmount) - if err != nil { - return "", err - } - - contractInst, err := NewTokenInstance(api.s, chainID, contractAddress) - if err != nil { - return "", err - } - - tx, err := contractInst.SetMaxSupply(transactOpts, newMaxSupply) - if err != nil { - return "", err - } - - err = api.s.pendingTracker.TrackPendingTransaction( - wcommon.ChainID(chainID), - tx.Hash(), - common.Address(txArgs.From), - common.HexToAddress(contractAddress), - transactions.BurnCommunityToken, - transactions.Keep, - "", - ) - if err != nil { - logutils.ZapLogger().Error("TrackPendingTransaction error", zap.Error(err)) - return "", err - } - - return tx.Hash().Hex(), nil -} - // Gets signer public key from smart contract with a given chainId and address func (api *API) GetSignerPubKey(ctx context.Context, chainID uint64, contractAddress string) (string, error) { return api.s.GetSignerPubKey(ctx, chainID, contractAddress) @@ -483,13 +120,9 @@ func (api *API) SafeGetOwnerTokenAddress(ctx context.Context, chainID uint64, co return api.s.SafeGetOwnerTokenAddress(ctx, chainID, communityID) } -func (api *API) SetSignerPubKey(ctx context.Context, chainID uint64, contractAddress string, txArgs wallettypes.SendTxArgs, password string, newSignerPubKey string) (string, error) { - return api.s.SetSignerPubKey(ctx, chainID, contractAddress, txArgs, password, newSignerPubKey) -} - func (api *API) OwnerTokenOwnerAddress(ctx context.Context, chainID uint64, contractAddress string) (string, error) { callOpts := &bind.CallOpts{Context: ctx, Pending: false} - contractInst, err := api.NewOwnerTokenInstance(chainID, contractAddress) + contractInst, err := api.s.manager.NewOwnerTokenInstance(chainID, common.HexToAddress(contractAddress)) if err != nil { return "", err } diff --git a/services/communitytokensv2/estimations.go b/services/communitytokensv2/estimations.go deleted file mode 100644 index 8e257f6416a..00000000000 --- a/services/communitytokensv2/estimations.go +++ /dev/null @@ -1,443 +0,0 @@ -package communitytokens - -import ( - "context" - "fmt" - "math/big" - "strings" - - "go.uber.org/zap" - - "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/params" - "github.com/status-im/status-go/contracts/community-tokens/assets" - "github.com/status-im/status-go/contracts/community-tokens/collectibles" - communitytokendeployer "github.com/status-im/status-go/contracts/community-tokens/deployer" - "github.com/status-im/status-go/eth-node/types" - "github.com/status-im/status-go/logutils" - "github.com/status-im/status-go/protocol/protobuf" - "github.com/status-im/status-go/services/wallet/bigint" - "github.com/status-im/status-go/services/wallet/requests" - "github.com/status-im/status-go/services/wallet/router/fees" - "github.com/status-im/status-go/services/wallet/wallettypes" -) - -type CommunityTokenFees struct { - GasUnits uint64 `json:"gasUnits"` - SuggestedFees *fees.SuggestedFeesGwei `json:"suggestedFees"` -} - -func weiToGwei(val *big.Int) *big.Float { - result := new(big.Float) - result.SetInt(val) - - unit := new(big.Int) - unit.SetInt64(params.GWei) - - return result.Quo(result, new(big.Float).SetInt(unit)) -} - -func gweiToWei(val *big.Float) *big.Int { - res, _ := new(big.Float).Mul(val, big.NewFloat(1000000000)).Int(nil) - return res -} - -func (s *Service) deployOwnerTokenEstimate(ctx context.Context, chainID uint64, fromAddress string, - ownerTokenParameters requests.DeploymentParameters, masterTokenParameters requests.DeploymentParameters, - communityID string, signerPubKey string) (*CommunityTokenFees, error) { - - gasUnits, err := s.deployOwnerTokenGasUnits(ctx, chainID, fromAddress, ownerTokenParameters, masterTokenParameters, - communityID, signerPubKey) - if err != nil { - return nil, err - } - - deployerAddress, err := communitytokendeployer.ContractAddress(chainID) - if err != nil { - return nil, err - } - - return s.prepareCommunityTokenFees(ctx, common.HexToAddress(fromAddress), &deployerAddress, gasUnits, chainID) -} - -func (s *Service) deployCollectiblesEstimate(ctx context.Context, chainID uint64, fromAddress string) (*CommunityTokenFees, error) { - gasUnits, err := s.deployCollectiblesGasUnits(ctx, chainID, fromAddress) - if err != nil { - return nil, err - } - return s.prepareCommunityTokenFees(ctx, common.HexToAddress(fromAddress), nil, gasUnits, chainID) -} - -func (s *Service) deployAssetsEstimate(ctx context.Context, chainID uint64, fromAddress string) (*CommunityTokenFees, error) { - gasUnits, err := s.deployAssetsGasUnits(ctx, chainID, fromAddress) - if err != nil { - return nil, err - } - return s.prepareCommunityTokenFees(ctx, common.HexToAddress(fromAddress), nil, gasUnits, chainID) -} - -func (s *Service) mintTokensEstimate(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, walletAddresses []string, amount *bigint.BigInt) (*CommunityTokenFees, error) { - gasUnits, err := s.mintTokensGasUnits(ctx, chainID, contractAddress, fromAddress, walletAddresses, amount) - if err != nil { - return nil, err - } - toAddress := common.HexToAddress(contractAddress) - return s.prepareCommunityTokenFees(ctx, common.HexToAddress(fromAddress), &toAddress, gasUnits, chainID) -} - -func (s *Service) remoteBurnEstimate(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, tokenIds []*bigint.BigInt) (*CommunityTokenFees, error) { - gasUnits, err := s.remoteBurnGasUnits(ctx, chainID, contractAddress, fromAddress, tokenIds) - if err != nil { - return nil, err - } - toAddress := common.HexToAddress(contractAddress) - return s.prepareCommunityTokenFees(ctx, common.HexToAddress(fromAddress), &toAddress, gasUnits, chainID) -} - -func (s *Service) burnEstimate(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, burnAmount *bigint.BigInt) (*CommunityTokenFees, error) { - gasUnits, err := s.burnGasUnits(ctx, chainID, contractAddress, fromAddress, burnAmount) - if err != nil { - return nil, err - } - toAddress := common.HexToAddress(contractAddress) - return s.prepareCommunityTokenFees(ctx, common.HexToAddress(fromAddress), &toAddress, gasUnits, chainID) -} - -func (s *Service) setSignerPubKeyEstimate(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, newSignerPubKey string) (*CommunityTokenFees, error) { - gasUnits, err := s.setSignerPubKeyGasUnits(ctx, chainID, contractAddress, fromAddress, newSignerPubKey) - if err != nil { - return nil, err - } - toAddress := common.HexToAddress(contractAddress) - return s.prepareCommunityTokenFees(ctx, common.HexToAddress(fromAddress), &toAddress, gasUnits, chainID) -} - -func (s *Service) setSignerPubKeyGasUnits(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, newSignerPubKey string) (uint64, error) { - if len(newSignerPubKey) <= 0 { - return 0, fmt.Errorf("signerPubKey is empty") - } - - contractInst, err := s.NewOwnerTokenInstance(chainID, contractAddress) - if err != nil { - return 0, err - } - ownerTokenInstance := &OwnerTokenInstance{instance: contractInst} - - return s.estimateMethodForTokenInstance(ctx, ownerTokenInstance, chainID, contractAddress, fromAddress, "setSignerPublicKey", common.FromHex(newSignerPubKey)) -} - -func (s *Service) burnGasUnits(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, burnAmount *bigint.BigInt) (uint64, error) { - err := s.validateBurnAmount(ctx, burnAmount, chainID, contractAddress) - if err != nil { - return 0, err - } - - newMaxSupply, err := s.prepareNewMaxSupply(ctx, chainID, contractAddress, burnAmount) - if err != nil { - return 0, err - } - - return s.estimateMethod(ctx, chainID, contractAddress, fromAddress, "setMaxSupply", newMaxSupply) -} - -func (s *Service) remoteBurnGasUnits(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, tokenIds []*bigint.BigInt) (uint64, error) { - err := s.validateTokens(tokenIds) - if err != nil { - return 0, err - } - - var tempTokenIds []*big.Int - for _, v := range tokenIds { - tempTokenIds = append(tempTokenIds, v.Int) - } - - return s.estimateMethod(ctx, chainID, contractAddress, fromAddress, "remoteBurn", tempTokenIds) -} - -func (s *Service) deployOwnerTokenGasUnits(ctx context.Context, chainID uint64, fromAddress string, - ownerTokenParameters requests.DeploymentParameters, masterTokenParameters requests.DeploymentParameters, - communityID string, signerPubKey string) (uint64, error) { - ethClient, err := s.manager.rpcClient.EthClient(chainID) - if err != nil { - logutils.ZapLogger().Error(err.Error()) - return 0, err - } - - deployerAddress, err := communitytokendeployer.ContractAddress(chainID) - if err != nil { - return 0, err - } - - deployerABI, err := abi.JSON(strings.NewReader(communitytokendeployer.CommunityTokenDeployerABI)) - if err != nil { - return 0, err - } - - ownerTokenConfig := communitytokendeployer.CommunityTokenDeployerTokenConfig{ - Name: ownerTokenParameters.Name, - Symbol: ownerTokenParameters.Symbol, - BaseURI: ownerTokenParameters.TokenURI, - } - - masterTokenConfig := communitytokendeployer.CommunityTokenDeployerTokenConfig{ - Name: masterTokenParameters.Name, - Symbol: masterTokenParameters.Symbol, - BaseURI: masterTokenParameters.TokenURI, - } - - signature, err := s.Messenger.CreateCommunityTokenDeploymentSignature(ctx, chainID, fromAddress, communityID) - if err != nil { - return 0, err - } - - communitySignature, err := prepareDeploymentSignatureStruct(types.HexBytes(signature).String(), communityID, common.HexToAddress(fromAddress)) - if err != nil { - return 0, err - } - - data, err := deployerABI.Pack("deploy", ownerTokenConfig, masterTokenConfig, communitySignature, common.FromHex(signerPubKey)) - - if err != nil { - return 0, err - } - - toAddr := deployerAddress - fromAddr := common.HexToAddress(fromAddress) - - callMsg := ethereum.CallMsg{ - From: fromAddr, - To: &toAddr, - Value: big.NewInt(0), - Data: data, - } - - estimate, err := ethClient.EstimateGas(ctx, callMsg) - if err != nil { - return 0, err - } - - finalEstimation := estimate + uint64(float32(estimate)*0.1) - logutils.ZapLogger().Debug("Owner token deployment estimation", zap.Uint64("gas", finalEstimation)) - return finalEstimation, nil -} - -func (s *Service) deployCollectiblesGasUnits(ctx context.Context, chainID uint64, fromAddress string) (uint64, error) { - ethClient, err := s.manager.rpcClient.EthClient(chainID) - if err != nil { - logutils.ZapLogger().Error(err.Error()) - return 0, err - } - - collectiblesABI, err := abi.JSON(strings.NewReader(collectibles.CollectiblesABI)) - if err != nil { - return 0, err - } - - // use random parameters, they will not have impact on deployment results - data, err := collectiblesABI.Pack("" /*constructor name is empty*/, "name", "SYMBOL", big.NewInt(20), true, false, "tokenUri", - common.HexToAddress("0x77b48394c650520012795a1a25696d7eb542d110"), common.HexToAddress("0x77b48394c650520012795a1a25696d7eb542d110")) - if err != nil { - return 0, err - } - - callMsg := ethereum.CallMsg{ - From: common.HexToAddress(fromAddress), - To: nil, - Value: big.NewInt(0), - Data: append(common.FromHex(collectibles.CollectiblesBin), data...), - } - estimate, err := ethClient.EstimateGas(ctx, callMsg) - if err != nil { - return 0, err - } - - finalEstimation := estimate + uint64(float32(estimate)*0.1) - logutils.ZapLogger().Debug("Collectibles deployment estimation", zap.Uint64("gas", finalEstimation)) - return finalEstimation, nil -} - -func (s *Service) deployAssetsGasUnits(ctx context.Context, chainID uint64, fromAddress string) (uint64, error) { - ethClient, err := s.manager.rpcClient.EthClient(chainID) - if err != nil { - logutils.ZapLogger().Error(err.Error()) - return 0, err - } - - assetsABI, err := abi.JSON(strings.NewReader(assets.AssetsABI)) - if err != nil { - return 0, err - } - - // use random parameters, they will not have impact on deployment results - data, err := assetsABI.Pack("" /*constructor name is empty*/, "name", "SYMBOL", uint8(18), big.NewInt(20), "tokenUri", - common.HexToAddress("0x77b48394c650520012795a1a25696d7eb542d110"), common.HexToAddress("0x77b48394c650520012795a1a25696d7eb542d110")) - if err != nil { - return 0, err - } - - callMsg := ethereum.CallMsg{ - From: common.HexToAddress(fromAddress), - To: nil, - Value: big.NewInt(0), - Data: append(common.FromHex(assets.AssetsBin), data...), - } - estimate, err := ethClient.EstimateGas(ctx, callMsg) - if err != nil { - return 0, err - } - - finalEstimation := estimate + uint64(float32(estimate)*0.1) - logutils.ZapLogger().Debug("Assets deployment estimation: ", zap.Uint64("gas", finalEstimation)) - return finalEstimation, nil -} - -// if we want to mint 2 tokens to addresses ["a", "b"] we need to mint -// twice to every address - we need to send to smart contract table ["a", "a", "b", "b"] -func multiplyWalletAddresses(amount *bigint.BigInt, contractAddresses []string) []string { - var totalAddresses []string - for i := big.NewInt(1); i.Cmp(amount.Int) <= 0; { - totalAddresses = append(totalAddresses, contractAddresses...) - i.Add(i, big.NewInt(1)) - } - return totalAddresses -} - -func prepareMintCollectiblesData(walletAddresses []string, amount *bigint.BigInt) []common.Address { - totalAddresses := multiplyWalletAddresses(amount, walletAddresses) - var usersAddresses = []common.Address{} - for _, k := range totalAddresses { - usersAddresses = append(usersAddresses, common.HexToAddress(k)) - } - return usersAddresses -} - -func prepareMintAssetsData(walletAddresses []string, amount *bigint.BigInt) ([]common.Address, []*big.Int) { - var usersAddresses = []common.Address{} - var amountsList = []*big.Int{} - for _, k := range walletAddresses { - usersAddresses = append(usersAddresses, common.HexToAddress(k)) - amountsList = append(amountsList, amount.Int) - } - return usersAddresses, amountsList -} - -func (s *Service) mintCollectiblesGasUnits(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, walletAddresses []string, amount *bigint.BigInt) (uint64, error) { - err := s.ValidateWalletsAndAmounts(walletAddresses, amount) - if err != nil { - return 0, err - } - usersAddresses := prepareMintCollectiblesData(walletAddresses, amount) - return s.estimateMethod(ctx, chainID, contractAddress, fromAddress, "mintTo", usersAddresses) -} - -func (s *Service) mintAssetsGasUnits(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, walletAddresses []string, amount *bigint.BigInt) (uint64, error) { - err := s.ValidateWalletsAndAmounts(walletAddresses, amount) - if err != nil { - return 0, err - } - usersAddresses, amountsList := prepareMintAssetsData(walletAddresses, amount) - return s.estimateMethod(ctx, chainID, contractAddress, fromAddress, "mintTo", usersAddresses, amountsList) -} - -func (s *Service) mintTokensGasUnits(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, walletAddresses []string, amount *bigint.BigInt) (uint64, error) { - tokenType, err := s.db.GetTokenType(chainID, contractAddress) - if err != nil { - return 0, err - } - - switch tokenType { - case protobuf.CommunityTokenType_ERC721: - return s.mintCollectiblesGasUnits(ctx, chainID, contractAddress, fromAddress, walletAddresses, amount) - case protobuf.CommunityTokenType_ERC20: - return s.mintAssetsGasUnits(ctx, chainID, contractAddress, fromAddress, walletAddresses, amount) - default: - return 0, fmt.Errorf("unknown token type: %v", tokenType) - } -} - -func (s *Service) prepareCommunityTokenFees(ctx context.Context, from common.Address, to *common.Address, gasUnits uint64, chainID uint64) (*CommunityTokenFees, error) { - suggestedFees, err := s.feeManager.SuggestedFeesGwei(ctx, chainID) - if err != nil { - return nil, err - } - - txArgs := s.suggestedFeesToSendTxArgs(from, to, gasUnits, suggestedFees) - - l1Fee, err := s.estimateL1Fee(ctx, chainID, txArgs) - if err == nil { - suggestedFees.L1GasFee = weiToGwei(big.NewInt(int64(l1Fee))) - } - return &CommunityTokenFees{ - GasUnits: gasUnits, - SuggestedFees: suggestedFees, - }, nil -} - -func (s *Service) suggestedFeesToSendTxArgs(from common.Address, to *common.Address, gas uint64, suggestedFees *fees.SuggestedFeesGwei) wallettypes.SendTxArgs { - sendArgs := wallettypes.SendTxArgs{} - sendArgs.From = types.Address(from) - sendArgs.To = (*types.Address)(to) - sendArgs.Gas = (*hexutil.Uint64)(&gas) - if suggestedFees.EIP1559Enabled { - sendArgs.MaxPriorityFeePerGas = (*hexutil.Big)(gweiToWei(suggestedFees.MaxPriorityFeePerGas)) - sendArgs.MaxFeePerGas = (*hexutil.Big)(gweiToWei(suggestedFees.MaxFeePerGasMedium)) - } else { - sendArgs.GasPrice = (*hexutil.Big)(gweiToWei(suggestedFees.GasPrice)) - } - return sendArgs -} - -func (s *Service) estimateL1Fee(ctx context.Context, chainID uint64, sendArgs wallettypes.SendTxArgs) (uint64, error) { - transaction, _, err := s.transactor.ValidateAndBuildTransaction(chainID, sendArgs, -1) - if err != nil { - return 0, err - } - - data, err := transaction.MarshalBinary() - if err != nil { - return 0, err - } - - return s.feeManager.GetL1Fee(ctx, chainID, data) -} - -func (s *Service) estimateMethodForTokenInstance(ctx context.Context, contractInstance TokenInstance, chainID uint64, contractAddress string, fromAddress string, methodName string, args ...interface{}) (uint64, error) { - ethClient, err := s.manager.rpcClient.EthClient(chainID) - if err != nil { - logutils.ZapLogger().Error(err.Error()) - return 0, err - } - - data, err := contractInstance.PackMethod(ctx, methodName, args...) - - if err != nil { - return 0, err - } - - toAddr := common.HexToAddress(contractAddress) - fromAddr := common.HexToAddress(fromAddress) - - callMsg := ethereum.CallMsg{ - From: fromAddr, - To: &toAddr, - Value: big.NewInt(0), - Data: data, - } - estimate, err := ethClient.EstimateGas(ctx, callMsg) - - if err != nil { - return 0, err - } - return estimate + uint64(float32(estimate)*0.1), nil -} - -func (s *Service) estimateMethod(ctx context.Context, chainID uint64, contractAddress string, fromAddress string, methodName string, args ...interface{}) (uint64, error) { - contractInst, err := NewTokenInstance(s, chainID, contractAddress) - if err != nil { - return 0, err - } - return s.estimateMethodForTokenInstance(ctx, contractInst, chainID, contractAddress, fromAddress, methodName, args...) -} diff --git a/services/communitytokensv2/manager.go b/services/communitytokensv2/manager.go index b2dd7d5d4fa..284529e88d7 100644 --- a/services/communitytokensv2/manager.go +++ b/services/communitytokensv2/manager.go @@ -8,9 +8,12 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/signer/core/apitypes" + communitytokens "github.com/status-im/status-go/contracts/community-tokens" "github.com/status-im/status-go/contracts/community-tokens/assets" "github.com/status-im/status-go/contracts/community-tokens/collectibles" communitytokendeployer "github.com/status-im/status-go/contracts/community-tokens/deployer" + "github.com/status-im/status-go/contracts/community-tokens/ownertoken" + communityownertokenregistry "github.com/status-im/status-go/contracts/community-tokens/registry" "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/protocol/communities" @@ -20,63 +23,45 @@ import ( ) type Manager struct { - rpcClient *rpc.Client + contractMaker *communitytokens.CommunityTokensContractMaker } func NewManager(rpcClient *rpc.Client) *Manager { return &Manager{ - rpcClient: rpcClient, + contractMaker: &communitytokens.CommunityTokensContractMaker{ + RPCClient: rpcClient, + }, } } -func (m *Manager) NewCollectiblesInstance(chainID uint64, contractAddress string) (*collectibles.Collectibles, error) { - backend, err := m.rpcClient.EthClient(chainID) - if err != nil { - return nil, err - } - return collectibles.NewCollectibles(common.HexToAddress(contractAddress), backend) +func (m *Manager) NewCollectiblesInstance(chainID uint64, contractAddress common.Address) (*collectibles.Collectibles, error) { + return m.contractMaker.NewCollectiblesInstance(chainID, contractAddress) } func (m *Manager) NewCommunityTokenDeployerInstance(chainID uint64) (*communitytokendeployer.CommunityTokenDeployer, error) { - backend, err := m.rpcClient.EthClient(chainID) - if err != nil { - return nil, err - } deployerAddr, err := communitytokendeployer.ContractAddress(chainID) if err != nil { return nil, err } - return communitytokendeployer.NewCommunityTokenDeployer(deployerAddr, backend) + return m.contractMaker.NewCommunityTokenDeployerInstance(chainID, deployerAddr) } -func (m *Manager) GetCollectiblesContractInstance(chainID uint64, contractAddress string) (*collectibles.Collectibles, error) { - contractInst, err := m.NewCollectiblesInstance(chainID, contractAddress) - if err != nil { - return nil, err - } - return contractInst, nil +func (m *Manager) NewAssetsInstance(chainID uint64, contractAddress common.Address) (*assets.Assets, error) { + return m.contractMaker.NewAssetsInstance(chainID, contractAddress) } -func (m *Manager) NewAssetsInstance(chainID uint64, contractAddress string) (*assets.Assets, error) { - backend, err := m.rpcClient.EthClient(chainID) - if err != nil { - return nil, err - } - return assets.NewAssets(common.HexToAddress(contractAddress), backend) +func (m *Manager) NewCommunityOwnerTokenRegistryInstance(chainID uint64, contractAddress common.Address) (*communityownertokenregistry.CommunityOwnerTokenRegistry, error) { + return m.contractMaker.NewCommunityOwnerTokenRegistryInstance(chainID, contractAddress) } -func (m *Manager) GetAssetContractInstance(chainID uint64, contractAddress string) (*assets.Assets, error) { - contractInst, err := m.NewAssetsInstance(chainID, contractAddress) - if err != nil { - return nil, err - } - return contractInst, nil +func (m *Manager) NewOwnerTokenInstance(chainID uint64, contractAddress common.Address) (*ownertoken.OwnerToken, error) { + return m.contractMaker.NewOwnerTokenInstance(chainID, contractAddress) } func (m *Manager) GetCollectibleContractData(chainID uint64, contractAddress string) (*communities.CollectibleContractData, error) { callOpts := &bind.CallOpts{Context: context.Background(), Pending: false} - contract, err := m.GetCollectiblesContractInstance(chainID, contractAddress) + contract, err := m.NewCollectiblesInstance(chainID, common.HexToAddress(contractAddress)) if err != nil { return nil, err } @@ -103,7 +88,7 @@ func (m *Manager) GetCollectibleContractData(chainID uint64, contractAddress str func (m *Manager) GetAssetContractData(chainID uint64, contractAddress string) (*communities.AssetContractData, error) { callOpts := &bind.CallOpts{Context: context.Background(), Pending: false} - contract, err := m.GetAssetContractInstance(chainID, contractAddress) + contract, err := m.NewAssetsInstance(chainID, common.HexToAddress(contractAddress)) if err != nil { return nil, err } diff --git a/services/communitytokensv2/service.go b/services/communitytokensv2/service.go index ed8c02c8b84..1ede591bdb6 100644 --- a/services/communitytokensv2/service.go +++ b/services/communitytokensv2/service.go @@ -6,10 +6,8 @@ import ( "encoding/json" "fmt" "math/big" - "strings" "github.com/pkg/errors" - "go.uber.org/zap" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" @@ -17,9 +15,6 @@ 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/contracts/community-tokens/mastertoken" - "github.com/status-im/status-go/contracts/community-tokens/ownertoken" - communityownertokenregistry "github.com/status-im/status-go/contracts/community-tokens/registry" "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/logutils" @@ -30,13 +25,12 @@ import ( "github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/rpc" "github.com/status-im/status-go/services/communitytokens/communitytokensdatabase" - "github.com/status-im/status-go/services/utils" "github.com/status-im/status-go/services/wallet/bigint" - wcommon "github.com/status-im/status-go/services/wallet/common" + walletCommon "github.com/status-im/status-go/services/wallet/common" "github.com/status-im/status-go/services/wallet/requests" "github.com/status-im/status-go/services/wallet/router/fees" + "github.com/status-im/status-go/services/wallet/router/sendtype" "github.com/status-im/status-go/services/wallet/walletevent" - "github.com/status-im/status-go/services/wallet/wallettypes" "github.com/status-im/status-go/signal" "github.com/status-im/status-go/transactions" ) @@ -45,7 +39,6 @@ import ( type Service struct { manager *Manager accountsManager *account.GethManager - pendingTracker *transactions.PendingTxTracker config *params.NodeConfig db *communitytokensdatabase.Database Messenger *protocol.Messenger @@ -56,12 +49,11 @@ type Service struct { } // Returns a new Collectibles Service. -func NewService(rpcClient *rpc.Client, accountsManager *account.GethManager, pendingTracker *transactions.PendingTxTracker, - config *params.NodeConfig, appDb *sql.DB, walletFeed *event.Feed, transactor *transactions.Transactor) *Service { +func NewService(rpcClient *rpc.Client, accountsManager *account.GethManager, config *params.NodeConfig, appDb *sql.DB, + walletFeed *event.Feed, transactor *transactions.Transactor) *Service { return &Service{ - manager: &Manager{rpcClient: rpcClient}, + manager: NewManager(rpcClient), accountsManager: accountsManager, - pendingTracker: pendingTracker, config: config, db: communitytokensdatabase.NewCommunityTokensDatabase(appDb), walletFeed: walletFeed, @@ -107,50 +99,55 @@ func (s *Service) handleWalletEvent(event walletevent.Event) { if p.Status == transactions.Pending { return } - pendingTransaction, err := s.pendingTracker.GetPendingEntry(p.ChainID, p.Hash) - if err != nil { - logutils.ZapLogger().Error(errors.Wrap(err, fmt.Sprintf("no pending transaction with hash %v on chain %v\n", p.Hash, p.ChainID)).Error()) + if p.SendDetails == nil { + logutils.ZapLogger().Error(errors.Wrap(err, fmt.Sprintf("cannot resolve details for tx hash %v on chain %v\n", p.Hash, p.ChainID)).Error()) return } + addressTo := common.Address(p.SendDetails.ToAddress) + fromChainID := walletCommon.ChainID(p.SendDetails.FromChain) + txHash := p.Hash + for _, tx := range p.SentTransactions { + if common.Hash(tx.Hash) == p.Hash { + addressTo = common.Address(tx.ToAddress) + fromChainID = walletCommon.ChainID(tx.FromChain) + } + } + var communityToken, ownerToken, masterToken *token.CommunityToken = &token.CommunityToken{}, &token.CommunityToken{}, &token.CommunityToken{} var tokenErr error - switch pendingTransaction.Type { - case transactions.DeployCommunityToken: - communityToken, tokenErr = s.handleDeployCommunityToken(p.Status, pendingTransaction) - case transactions.AirdropCommunityToken: - communityToken, tokenErr = s.handleAirdropCommunityToken(p.Status, pendingTransaction) - case transactions.RemoteDestructCollectible: - communityToken, tokenErr = s.handleRemoteDestructCollectible(p.Status, pendingTransaction) - case transactions.BurnCommunityToken: - communityToken, tokenErr = s.handleBurnCommunityToken(p.Status, pendingTransaction) - case transactions.DeployOwnerToken: - ownerToken, masterToken, tokenErr = s.handleDeployOwnerToken(p.Status, pendingTransaction) - case transactions.SetSignerPublicKey: - communityToken, tokenErr = s.handleSetSignerPubKey(p.Status, pendingTransaction) + switch p.SendDetails.SendType { + case int(sendtype.CommunityDeployAssets), + int(sendtype.CommunityDeployCollectibles): + communityToken, tokenErr = s.handleDeployCommunityToken(p.Status, addressTo, fromChainID) + case int(sendtype.CommunityMintTokens): + communityToken, tokenErr = s.handleAirdropCommunityToken(p.Status, addressTo, fromChainID) + case int(sendtype.CommunityRemoteBurn): + communityToken, tokenErr = s.handleRemoteDestructCollectible(p.Status, addressTo, fromChainID) + case int(sendtype.CommunityBurn): + communityToken, tokenErr = s.handleBurnCommunityToken(p.Status, addressTo, fromChainID) + case int(sendtype.CommunityDeployOwnerToken): + ownerToken, masterToken, tokenErr = s.handleDeployOwnerToken(p.Status, fromChainID, txHash) + case int(sendtype.CommunitySetSignerPubKey): + communityToken, tokenErr = s.handleSetSignerPubKey(p.Status, addressTo, fromChainID) default: return } - err = s.pendingTracker.Delete(context.Background(), p.ChainID, p.Hash) - if err != nil { - logutils.ZapLogger().Error(errors.Wrap(err, fmt.Sprintf("can't delete pending transaction with hash %v on chain %v\n", p.Hash, p.ChainID)).Error()) - } - errorStr := "" if tokenErr != nil { errorStr = tokenErr.Error() } - signal.SendCommunityTokenTransactionStatusSignal(string(pendingTransaction.Type), p.Status == transactions.Success, pendingTransaction.Hash, + signal.SendCommunityTokenTransactionStatusSignal(p.SendDetails.SendType, p.Status == transactions.Success, txHash, communityToken, ownerToken, masterToken, errorStr) } } -func (s *Service) handleAirdropCommunityToken(status string, pendingTransaction *transactions.PendingTransaction) (*token.CommunityToken, error) { - communityToken, err := s.Messenger.GetCommunityTokenByChainAndAddress(int(pendingTransaction.ChainID), pendingTransaction.To.String()) +func (s *Service) handleAirdropCommunityToken(status string, toAddress common.Address, chainID walletCommon.ChainID) (*token.CommunityToken, error) { + communityToken, err := s.Messenger.GetCommunityTokenByChainAndAddress(int(chainID), toAddress.String()) if communityToken == nil { - return nil, fmt.Errorf("token does not exist in database: chainId=%v, address=%v", pendingTransaction.ChainID, pendingTransaction.To.String()) + return nil, fmt.Errorf("token does not exist in database: chainId=%v, address=%v", chainID, toAddress.String()) } else { publishErr := s.publishTokenActionToPrivilegedMembers(communityToken.CommunityID, uint64(communityToken.ChainID), communityToken.Address, protobuf.CommunityTokenAction_AIRDROP) @@ -161,10 +158,10 @@ func (s *Service) handleAirdropCommunityToken(status string, pendingTransaction return communityToken, err } -func (s *Service) handleRemoteDestructCollectible(status string, pendingTransaction *transactions.PendingTransaction) (*token.CommunityToken, error) { - communityToken, err := s.Messenger.GetCommunityTokenByChainAndAddress(int(pendingTransaction.ChainID), pendingTransaction.To.String()) +func (s *Service) handleRemoteDestructCollectible(status string, toAddress common.Address, chainID walletCommon.ChainID) (*token.CommunityToken, error) { + communityToken, err := s.Messenger.GetCommunityTokenByChainAndAddress(int(chainID), toAddress.String()) if communityToken == nil { - return nil, fmt.Errorf("token does not exist in database: chainId=%v, address=%v", pendingTransaction.ChainID, pendingTransaction.To.String()) + return nil, fmt.Errorf("token does not exist in database: chainId=%v, address=%v", chainID, toAddress.String()) } else { publishErr := s.publishTokenActionToPrivilegedMembers(communityToken.CommunityID, uint64(communityToken.ChainID), communityToken.Address, protobuf.CommunityTokenAction_REMOTE_DESTRUCT) @@ -175,23 +172,23 @@ func (s *Service) handleRemoteDestructCollectible(status string, pendingTransact return communityToken, err } -func (s *Service) handleBurnCommunityToken(status string, pendingTransaction *transactions.PendingTransaction) (*token.CommunityToken, error) { +func (s *Service) handleBurnCommunityToken(status string, toAddress common.Address, chainID walletCommon.ChainID) (*token.CommunityToken, error) { if status == transactions.Success { // get new max supply and update database - newMaxSupply, err := s.maxSupply(context.Background(), uint64(pendingTransaction.ChainID), pendingTransaction.To.String()) + newMaxSupply, err := s.maxSupply(context.Background(), uint64(chainID), toAddress.String()) if err != nil { return nil, err } - err = s.Messenger.UpdateCommunityTokenSupply(int(pendingTransaction.ChainID), pendingTransaction.To.String(), &bigint.BigInt{Int: newMaxSupply}) + err = s.Messenger.UpdateCommunityTokenSupply(int(chainID), toAddress.String(), &bigint.BigInt{Int: newMaxSupply}) if err != nil { return nil, err } } - communityToken, err := s.Messenger.GetCommunityTokenByChainAndAddress(int(pendingTransaction.ChainID), pendingTransaction.To.String()) + communityToken, err := s.Messenger.GetCommunityTokenByChainAndAddress(int(chainID), toAddress.String()) if communityToken == nil { - return nil, fmt.Errorf("token does not exist in database: chainId=%v, address=%v", pendingTransaction.ChainID, pendingTransaction.To.String()) + return nil, fmt.Errorf("token does not exist in database: chainId=%v, address=%v", chainID, toAddress.String()) } else { publishErr := s.publishTokenActionToPrivilegedMembers(communityToken.CommunityID, uint64(communityToken.ChainID), communityToken.Address, protobuf.CommunityTokenAction_BURN) @@ -202,31 +199,31 @@ func (s *Service) handleBurnCommunityToken(status string, pendingTransaction *tr return communityToken, err } -func (s *Service) handleDeployOwnerToken(status string, pendingTransaction *transactions.PendingTransaction) (*token.CommunityToken, *token.CommunityToken, error) { - newMasterAddress, err := s.GetMasterTokenContractAddressFromHash(context.Background(), uint64(pendingTransaction.ChainID), pendingTransaction.Hash.Hex()) +func (s *Service) handleDeployOwnerToken(status string, chainID walletCommon.ChainID, txHash common.Hash) (*token.CommunityToken, *token.CommunityToken, error) { + newMasterAddress, err := s.GetMasterTokenContractAddressFromHash(context.Background(), uint64(chainID), txHash.Hex()) if err != nil { return nil, nil, err } - newOwnerAddress, err := s.GetOwnerTokenContractAddressFromHash(context.Background(), uint64(pendingTransaction.ChainID), pendingTransaction.Hash.Hex()) + newOwnerAddress, err := s.GetOwnerTokenContractAddressFromHash(context.Background(), uint64(chainID), txHash.Hex()) if err != nil { return nil, nil, err } - err = s.Messenger.UpdateCommunityTokenAddress(int(pendingTransaction.ChainID), s.TemporaryOwnerContractAddress(pendingTransaction.Hash.Hex()), newOwnerAddress) + err = s.Messenger.UpdateCommunityTokenAddress(int(chainID), s.TemporaryOwnerContractAddress(txHash.Hex()), newOwnerAddress) if err != nil { return nil, nil, err } - err = s.Messenger.UpdateCommunityTokenAddress(int(pendingTransaction.ChainID), s.TemporaryMasterContractAddress(pendingTransaction.Hash.Hex()), newMasterAddress) + err = s.Messenger.UpdateCommunityTokenAddress(int(chainID), s.TemporaryMasterContractAddress(txHash.Hex()), newMasterAddress) if err != nil { return nil, nil, err } - ownerToken, err := s.updateStateAndAddTokenToCommunityDescription(status, int(pendingTransaction.ChainID), newOwnerAddress) + ownerToken, err := s.updateStateAndAddTokenToCommunityDescription(status, int(chainID), newOwnerAddress) if err != nil { return nil, nil, err } - masterToken, err := s.updateStateAndAddTokenToCommunityDescription(status, int(pendingTransaction.ChainID), newMasterAddress) + masterToken, err := s.updateStateAndAddTokenToCommunityDescription(status, int(chainID), newMasterAddress) if err != nil { return nil, nil, err } @@ -261,18 +258,18 @@ func (s *Service) updateStateAndAddTokenToCommunityDescription(status string, ch return s.Messenger.GetCommunityTokenByChainAndAddress(chainID, address) } -func (s *Service) handleDeployCommunityToken(status string, pendingTransaction *transactions.PendingTransaction) (*token.CommunityToken, error) { - return s.updateStateAndAddTokenToCommunityDescription(status, int(pendingTransaction.ChainID), pendingTransaction.To.String()) +func (s *Service) handleDeployCommunityToken(status string, toAddress common.Address, chainID walletCommon.ChainID) (*token.CommunityToken, error) { + return s.updateStateAndAddTokenToCommunityDescription(status, int(chainID), toAddress.String()) } -func (s *Service) handleSetSignerPubKey(status string, pendingTransaction *transactions.PendingTransaction) (*token.CommunityToken, error) { +func (s *Service) handleSetSignerPubKey(status string, toAddress common.Address, chainID walletCommon.ChainID) (*token.CommunityToken, error) { - communityToken, err := s.Messenger.GetCommunityTokenByChainAndAddress(int(pendingTransaction.ChainID), pendingTransaction.To.String()) + communityToken, err := s.Messenger.GetCommunityTokenByChainAndAddress(int(chainID), toAddress.String()) if err != nil { return nil, err } if communityToken == nil { - return nil, fmt.Errorf("token does not exist in database: chainId=%v, address=%v", pendingTransaction.ChainID, pendingTransaction.To.String()) + return nil, fmt.Errorf("token does not exist in database: chainId=%v, address=%v", chainID, toAddress.String()) } if status == transactions.Success { @@ -294,53 +291,6 @@ func (s *Service) Init(messenger *protocol.Messenger) { s.Messenger = messenger } -func (s *Service) NewCommunityOwnerTokenRegistryInstance(chainID uint64, contractAddress string) (*communityownertokenregistry.CommunityOwnerTokenRegistry, error) { - backend, err := s.manager.rpcClient.EthClient(chainID) - if err != nil { - return nil, err - } - return communityownertokenregistry.NewCommunityOwnerTokenRegistry(common.HexToAddress(contractAddress), backend) -} - -func (s *Service) NewOwnerTokenInstance(chainID uint64, contractAddress string) (*ownertoken.OwnerToken, error) { - - backend, err := s.manager.rpcClient.EthClient(chainID) - if err != nil { - return nil, err - } - return ownertoken.NewOwnerToken(common.HexToAddress(contractAddress), backend) - -} - -func (s *Service) NewMasterTokenInstance(chainID uint64, contractAddress string) (*mastertoken.MasterToken, error) { - backend, err := s.manager.rpcClient.EthClient(chainID) - if err != nil { - return nil, err - } - return mastertoken.NewMasterToken(common.HexToAddress(contractAddress), backend) -} - -func (s *Service) validateTokens(tokenIds []*bigint.BigInt) error { - if len(tokenIds) == 0 { - return errors.New("token list is empty") - } - return nil -} - -func (s *Service) validateBurnAmount(ctx context.Context, burnAmount *bigint.BigInt, chainID uint64, contractAddress string) error { - if burnAmount.Cmp(big.NewInt(0)) <= 0 { - return errors.New("burnAmount is less than 0") - } - remainingSupply, err := s.remainingSupply(ctx, chainID, contractAddress) - if err != nil { - return err - } - if burnAmount.Cmp(remainingSupply.Int) > 1 { - return errors.New("burnAmount is bigger than remaining amount") - } - return nil -} - func (s *Service) remainingSupply(ctx context.Context, chainID uint64, contractAddress string) (*bigint.BigInt, error) { tokenType, err := s.db.GetTokenType(chainID, contractAddress) if err != nil { @@ -356,20 +306,10 @@ func (s *Service) remainingSupply(ctx context.Context, chainID uint64, contractA } } -func (s *Service) prepareNewMaxSupply(ctx context.Context, chainID uint64, contractAddress string, burnAmount *bigint.BigInt) (*big.Int, error) { - maxSupply, err := s.maxSupply(ctx, chainID, contractAddress) - if err != nil { - return nil, err - } - var newMaxSupply = new(big.Int) - newMaxSupply.Sub(maxSupply, burnAmount.Int) - return newMaxSupply, nil -} - // RemainingSupply = MaxSupply - MintedCount func (s *Service) remainingCollectiblesSupply(ctx context.Context, chainID uint64, contractAddress string) (*bigint.BigInt, error) { callOpts := &bind.CallOpts{Context: ctx, Pending: false} - contractInst, err := s.manager.NewCollectiblesInstance(chainID, contractAddress) + contractInst, err := s.manager.NewCollectiblesInstance(chainID, common.HexToAddress(contractAddress)) if err != nil { return nil, err } @@ -389,7 +329,7 @@ func (s *Service) remainingCollectiblesSupply(ctx context.Context, chainID uint6 // RemainingSupply = MaxSupply - TotalSupply func (s *Service) remainingAssetsSupply(ctx context.Context, chainID uint64, contractAddress string) (*bigint.BigInt, error) { callOpts := &bind.CallOpts{Context: ctx, Pending: false} - contractInst, err := s.manager.NewAssetsInstance(chainID, contractAddress) + contractInst, err := s.manager.NewAssetsInstance(chainID, common.HexToAddress(contractAddress)) if err != nil { return nil, err } @@ -419,7 +359,7 @@ func (s *Service) ValidateWalletsAndAmounts(walletAddresses []string, amount *bi func (s *Service) GetSignerPubKey(ctx context.Context, chainID uint64, contractAddress string) (string, error) { callOpts := &bind.CallOpts{Context: ctx, Pending: false} - contractInst, err := s.NewOwnerTokenInstance(chainID, contractAddress) + contractInst, err := s.manager.NewOwnerTokenInstance(chainID, common.HexToAddress(contractAddress)) if err != nil { return "", err } @@ -451,7 +391,7 @@ func (s *Service) SafeGetOwnerTokenAddress(ctx context.Context, chainID uint64, if err != nil { return "", err } - registryContractInst, err := s.NewCommunityOwnerTokenRegistryInstance(chainID, registryAddr.Hex()) + registryContractInst, err := s.manager.NewCommunityOwnerTokenRegistryInstance(chainID, registryAddr) if err != nil { return "", err } @@ -503,44 +443,9 @@ func (s *Service) ProcessCommunityTokenAction(message *protobuf.CommunityTokenAc return nil } -func (s *Service) SetSignerPubKey(ctx context.Context, chainID uint64, contractAddress string, txArgs wallettypes.SendTxArgs, password string, newSignerPubKey string) (string, error) { - - if len(newSignerPubKey) <= 0 { - return "", fmt.Errorf("signerPubKey is empty") - } - - transactOpts := txArgs.ToTransactOpts(utils.VerifyPasswordAndGetSigner(chainID, s.accountsManager, s.config.KeyStoreDir, txArgs.From, password)) - - contractInst, err := s.NewOwnerTokenInstance(chainID, contractAddress) - if err != nil { - return "", err - } - - tx, err := contractInst.SetSignerPublicKey(transactOpts, common.FromHex(newSignerPubKey)) - if err != nil { - return "", err - } - - err = s.pendingTracker.TrackPendingTransaction( - wcommon.ChainID(chainID), - tx.Hash(), - common.Address(txArgs.From), - common.HexToAddress(contractAddress), - transactions.SetSignerPublicKey, - transactions.Keep, - "", - ) - if err != nil { - logutils.ZapLogger().Error("TrackPendingTransaction error", zap.Error(err)) - return "", err - } - - return tx.Hash().Hex(), nil -} - func (s *Service) maxSupplyCollectibles(ctx context.Context, chainID uint64, contractAddress string) (*big.Int, error) { callOpts := &bind.CallOpts{Context: ctx, Pending: false} - contractInst, err := s.manager.NewCollectiblesInstance(chainID, contractAddress) + contractInst, err := s.manager.NewCollectiblesInstance(chainID, common.HexToAddress(contractAddress)) if err != nil { return nil, err } @@ -549,7 +454,7 @@ func (s *Service) maxSupplyCollectibles(ctx context.Context, chainID uint64, con func (s *Service) maxSupplyAssets(ctx context.Context, chainID uint64, contractAddress string) (*big.Int, error) { callOpts := &bind.CallOpts{Context: ctx, Pending: false} - contractInst, err := s.manager.NewAssetsInstance(chainID, contractAddress) + contractInst, err := s.manager.NewAssetsInstance(chainID, common.HexToAddress(contractAddress)) if err != nil { return nil, err } @@ -617,17 +522,8 @@ func (s *Service) TemporaryOwnerContractAddress(hash string) string { return hash + OwnerSuffix } -func (s *Service) HashFromTemporaryContractAddress(address string) string { - if strings.HasSuffix(address, OwnerSuffix) { - return strings.TrimSuffix(address, OwnerSuffix) - } else if strings.HasSuffix(address, MasterSuffix) { - return strings.TrimSuffix(address, MasterSuffix) - } - return "" -} - func (s *Service) GetMasterTokenContractAddressFromHash(ctx context.Context, chainID uint64, txHash string) (string, error) { - ethClient, err := s.manager.rpcClient.EthClient(chainID) + ethClient, err := s.manager.contractMaker.RPCClient.EthClient(chainID) if err != nil { return "", err } @@ -658,7 +554,7 @@ func (s *Service) GetMasterTokenContractAddressFromHash(ctx context.Context, cha } func (s *Service) GetOwnerTokenContractAddressFromHash(ctx context.Context, chainID uint64, txHash string) (string, error) { - ethClient, err := s.manager.rpcClient.EthClient(chainID) + ethClient, err := s.manager.contractMaker.RPCClient.EthClient(chainID) if err != nil { return "", err } @@ -688,51 +584,6 @@ func (s *Service) GetOwnerTokenContractAddressFromHash(ctx context.Context, chai return "", fmt.Errorf("can't find owner token address in transaction: %v", txHash) } -func (s *Service) ReTrackOwnerTokenDeploymentTransaction(ctx context.Context, chainID uint64, contractAddress string) error { - communityToken, err := s.Messenger.GetCommunityTokenByChainAndAddress(int(chainID), contractAddress) - if err != nil { - return err - } - if communityToken == nil { - return fmt.Errorf("can't find token with address %v on chain %v", contractAddress, chainID) - } - if communityToken.DeployState != token.InProgress { - return fmt.Errorf("token with address %v on chain %v is not in progress", contractAddress, chainID) - } - - hashString := communityToken.TransactionHash - if hashString == "" && (communityToken.PrivilegesLevel == token.OwnerLevel || communityToken.PrivilegesLevel == token.MasterLevel) { - hashString = s.HashFromTemporaryContractAddress(communityToken.Address) - } - - if hashString == "" { - return fmt.Errorf("can't find transaction hash for token with address %v on chain %v", contractAddress, chainID) - } - - transactionType := transactions.DeployCommunityToken - if communityToken.PrivilegesLevel == token.OwnerLevel || communityToken.PrivilegesLevel == token.MasterLevel { - transactionType = transactions.DeployOwnerToken - } - - _, err = s.pendingTracker.GetPendingEntry(wcommon.ChainID(chainID), common.HexToHash(hashString)) - if errors.Is(err, sql.ErrNoRows) { - // start only if no pending transaction in database - err = s.pendingTracker.TrackPendingTransaction( - wcommon.ChainID(chainID), - common.HexToHash(hashString), - common.HexToAddress(communityToken.Deployer), - common.Address{}, - transactionType, - transactions.Keep, - "", - ) - logutils.ZapLogger().Debug("retracking pending transaction", zap.String("hashId", hashString)) - } else { - logutils.ZapLogger().Debug("pending transaction already tracked", zap.String("hashId", hashString)) - } - return err -} - func (s *Service) publishTokenActionToPrivilegedMembers(communityID string, chainID uint64, contractAddress string, actionType protobuf.CommunityTokenAction_ActionType) error { decodedCommunityID, err := types.DecodeHex(communityID) if err != nil { diff --git a/services/communitytokensv2/token_instances.go b/services/communitytokensv2/token_instances.go deleted file mode 100644 index 247a49557be..00000000000 --- a/services/communitytokensv2/token_instances.go +++ /dev/null @@ -1,176 +0,0 @@ -package communitytokens - -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/contracts/community-tokens/ownertoken" - "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) -} - -// Owner Token -type OwnerTokenInstance struct { - TokenInstance - instance *ownertoken.OwnerToken -} - -func (t OwnerTokenInstance) RemoteBurn(transactOpts *bind.TransactOpts, tokenIds []*big.Int) (*types.Transaction, error) { - return nil, fmt.Errorf("remote destruction for owner token not implemented") -} - -func (t OwnerTokenInstance) Mint(transactOpts *bind.TransactOpts, walletAddresses []string, amount *bigint.BigInt) (*types.Transaction, error) { - return nil, fmt.Errorf("minting for owner token not implemented") -} - -func (t OwnerTokenInstance) SetMaxSupply(transactOpts *bind.TransactOpts, maxSupply *big.Int) (*types.Transaction, error) { - return nil, fmt.Errorf("setting max supply for owner token not implemented") -} - -func (t OwnerTokenInstance) PackMethod(ctx context.Context, methodName string, args ...interface{}) ([]byte, error) { - ownerTokenABI, err := abi.JSON(strings.NewReader(ownertoken.OwnerTokenABI)) - if err != nil { - return []byte{}, err - } - return ownerTokenABI.Pack(methodName, args...) -} - -// Master Token -type MasterTokenInstance struct { - TokenInstance - instance *mastertoken.MasterToken -} - -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 := 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 -} - -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 := 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 -} - -func (t AssetInstance) RemoteBurn(transactOpts *bind.TransactOpts, tokenIds []*big.Int) (*types.Transaction, error) { - return nil, fmt.Errorf("remote destruction for assets 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 := 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(s *Service, chainID uint64, contractAddress string) (TokenInstance, error) { - tokenType, err := s.db.GetTokenType(chainID, contractAddress) - if err != nil { - return nil, err - } - privLevel, err := s.db.GetTokenPrivilegesLevel(chainID, contractAddress) - if err != nil { - return nil, err - } - switch { - case privLevel == token.OwnerLevel: - contractInst, err := s.NewOwnerTokenInstance(chainID, contractAddress) - if err != nil { - return nil, err - } - return &OwnerTokenInstance{instance: contractInst}, nil - case privLevel == token.MasterLevel: - contractInst, err := s.NewMasterTokenInstance(chainID, contractAddress) - if err != nil { - return nil, err - } - return &MasterTokenInstance{instance: contractInst}, nil - case tokenType == protobuf.CommunityTokenType_ERC721: - contractInst, err := s.manager.NewCollectiblesInstance(chainID, contractAddress) - if err != nil { - return nil, err - } - return &CollectibleInstance{instance: contractInst}, nil - case tokenType == protobuf.CommunityTokenType_ERC20: - contractInst, err := s.manager.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) -} diff --git a/services/wallet/requests/router_input_community_params.go b/services/wallet/requests/router_input_community_params.go index 8aa9b21ef9c..a0ad8b2d7f1 100644 --- a/services/wallet/requests/router_input_community_params.go +++ b/services/wallet/requests/router_input_community_params.go @@ -4,20 +4,48 @@ import ( "fmt" "math/big" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/status-im/status-go/errors" "github.com/status-im/status-go/images" "github.com/status-im/status-go/protocol/communities/token" "github.com/status-im/status-go/services/wallet/bigint" + "github.com/status-im/status-go/services/wallet/router/sendtype" ) const maxSupply = 999999999 var ( - ErrEmptyCollectibleName = &errors.ErrorResponse{Code: errors.ErrorCode("WRRC-001"), Details: "empty collectible name"} - ErrEmptyCollectibleSymbol = &errors.ErrorResponse{Code: errors.ErrorCode("WRRC-002"), Details: "empty collectible symbol"} - ErrWrongSupplyValue = &errors.ErrorResponse{Code: errors.ErrorCode("WRRC-003"), Details: "wrong supply value: %v"} + ErrEmptyCollectibleName = &errors.ErrorResponse{Code: errors.ErrorCode("WRRC-001"), Details: "empty collectible name"} + ErrEmptyCollectibleSymbol = &errors.ErrorResponse{Code: errors.ErrorCode("WRRC-002"), Details: "empty collectible symbol"} + ErrWrongSupplyValue = &errors.ErrorResponse{Code: errors.ErrorCode("WRRC-003"), Details: "wrong supply value: %v"} + ErrWalletAddressesEmpty = &errors.ErrorResponse{Code: errors.ErrorCode("WRRC-004"), Details: "wallet addresses list is empty"} + ErrTokenAmountMustBePositive = &errors.ErrorResponse{Code: errors.ErrorCode("WRRC-005"), Details: "token amount must be positive"} + ErrNoCommunityIdProvided = &errors.ErrorResponse{Code: errors.ErrorCode("WRRC-006"), Details: "community id is required for community related transfers"} + ErrNoCommunitySignerPubKey = &errors.ErrorResponse{Code: errors.ErrorCode("WRRC-007"), Details: "signer pub key is required"} + ErrNoCommunityTokenDeploymentSignature = &errors.ErrorResponse{Code: errors.ErrorCode("WRRC-008"), Details: "signature is required"} + ErrNoCommunityOwnerTokenParameters = &errors.ErrorResponse{Code: errors.ErrorCode("WRRC-009"), Details: "owner token parameters are required"} + ErrNoCommunityMasterTokenParameters = &errors.ErrorResponse{Code: errors.ErrorCode("WRRC-010"), Details: "master token parameters are required"} + ErrNoCommunityDeploymentParameters = &errors.ErrorResponse{Code: errors.ErrorCode("WRRC-011"), Details: "deployment parameters are required"} + ErrNoCommunityContractAddress = &errors.ErrorResponse{Code: errors.ErrorCode("WRRC-012"), Details: "contract address is required"} + ErrNoCommunityBurnAmount = &errors.ErrorResponse{Code: errors.ErrorCode("WRRC-013"), Details: "burn amount is required"} + ErrCommunityBurnAmountMustBePositive = &errors.ErrorResponse{Code: errors.ErrorCode("WRRC-014"), Details: "burn amount must be positive"} + ErrCommunityTokenIdsListEmpty = &errors.ErrorResponse{Code: errors.ErrorCode("WRRC-015"), Details: "token list is empty"} ) +type CommunityRouteInputParams struct { + CommunityID string `json:"communityID"` + TokenContractAddress string `json:"tokenContractAddress"` + SignerPubKey string `json:"signerPubKey"` + Amount *hexutil.Big `json:"amount"` + TokenIds []*hexutil.Big `json:"tokenIds"` + WalletAddresses []common.Address `json:"walletAddresses"` + TokenDeploymentSignature string `json:"tokenDeploymentSignature"` + OwnerTokenParameters *DeploymentParameters `json:"ownerTokenParameters"` + MasterTokenParameters *DeploymentParameters `json:"masterTokenParameters"` + DeploymentParameters *DeploymentParameters `json:"deploymentParameters"` +} + type DeploymentDetails struct { ContractAddress string `json:"contractAddress"` TransactionHash string `json:"transactionHash"` @@ -81,3 +109,79 @@ func (d *DeploymentParameters) Validate(isAsset bool) error { } return nil } + +func (c *CommunityRouteInputParams) validateCommunityRelatedInputs(sendType sendtype.SendType) error { + if c.CommunityID == "" { + return ErrNoCommunityIdProvided + } + + if sendType == sendtype.CommunityBurn { + if c.TokenContractAddress == "" { + return ErrNoCommunityContractAddress + } + if c.Amount == nil { + return ErrNoCommunityBurnAmount + } + if c.Amount.ToInt().Cmp(big.NewInt(0)) <= 0 { + return ErrCommunityBurnAmountMustBePositive + } + } + + if sendType == sendtype.CommunityDeployAssets { + if c.DeploymentParameters == nil { + return ErrNoCommunityDeploymentParameters + } + err := c.DeploymentParameters.Validate(true) + if err != nil { + return err + } + } + + if sendType == sendtype.CommunityDeployCollectibles { + if c.DeploymentParameters == nil { + return ErrNoCommunityDeploymentParameters + } + err := c.DeploymentParameters.Validate(false) + if err != nil { + return err + } + } + + if sendType == sendtype.CommunityDeployOwnerToken { + if c.SignerPubKey == "" { + return ErrNoCommunitySignerPubKey + } + if c.TokenDeploymentSignature == "" { + return ErrNoCommunityTokenDeploymentSignature + } + if c.OwnerTokenParameters == nil { + return ErrNoCommunityOwnerTokenParameters + } + if c.MasterTokenParameters == nil { + return ErrNoCommunityMasterTokenParameters + } + } + + if sendType == sendtype.CommunityMintTokens { + if len(c.WalletAddresses) == 0 { + return ErrWalletAddressesEmpty + } + if c.Amount.ToInt().Cmp(big.NewInt(0)) <= 0 { + return ErrTokenAmountMustBePositive + } + } + + if sendType == sendtype.CommunityRemoteBurn { + if len(c.TokenIds) == 0 { + return ErrCommunityTokenIdsListEmpty + } + } + + if sendType == sendtype.CommunitySetSignerPubKey { + if c.SignerPubKey == "" { + return ErrNoCommunitySignerPubKey + } + } + + return nil +} diff --git a/services/wallet/requests/router_input_params.go b/services/wallet/requests/router_input_params.go index f0ab9adf79d..1cdc50219b1 100644 --- a/services/wallet/requests/router_input_params.go +++ b/services/wallet/requests/router_input_params.go @@ -32,6 +32,7 @@ var ( ErrENSSetPubKeyInvalidUsername = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-017"), Details: "a valid username, ending in '.eth', is required for ENSSetPubKey"} ErrLockedAmountExcludesAllSupported = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-018"), Details: "all supported chains are excluded, routing impossible"} ErrCannotCheckLockedAmounts = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-019"), Details: "cannot check locked amounts"} + ErrNoCommunityParametersProvided = &errors.ErrorResponse{Code: errors.ErrorCode("WRR-020"), Details: "no community parameters provided"} ) type RouteInputParams struct { @@ -55,6 +56,9 @@ type RouteInputParams struct { PublicKey string `json:"publicKey"` PackID *hexutil.Big `json:"packID"` + // community related params + CommunityRouteInputParams *CommunityRouteInputParams `json:"communityRouteInputParams"` + // TODO: Remove two fields below once we implement a better solution for tests // Currently used for tests only TestsMode bool @@ -141,6 +145,13 @@ func (i *RouteInputParams) Validate() error { } } + if i.SendType.IsCommunityRelatedTransfer() { + if i.CommunityRouteInputParams == nil { + return ErrNoCommunityParametersProvided + } + return i.CommunityRouteInputParams.validateCommunityRelatedInputs(i.SendType) + } + return i.validateFromLockedAmount() } diff --git a/services/wallet/responses/router_transactions.go b/services/wallet/responses/router_transactions.go index 9a533c50467..589041afa4c 100644 --- a/services/wallet/responses/router_transactions.go +++ b/services/wallet/responses/router_transactions.go @@ -27,6 +27,9 @@ type SendDetails struct { Username string `json:"username"` PublicKey string `json:"publicKey"` PackID string `json:"packId"` + + // community related params + CommunityParams *requests.CommunityRouteInputParams `json:"communityParams"` } type SigningDetails struct { @@ -112,4 +115,8 @@ func (sd *SendDetails) UpdateFields(inputParams requests.RouteInputParams, fromC // Set chain IDs, in case of an error while sending a transaction sd.FromChain = fromChain sd.ToChain = toChain + + if inputParams.CommunityRouteInputParams != nil { + sd.CommunityParams = inputParams.CommunityRouteInputParams + } } diff --git a/services/wallet/routeexecution/manager.go b/services/wallet/routeexecution/manager.go index c4f3460b581..5da713fb6ea 100644 --- a/services/wallet/routeexecution/manager.go +++ b/services/wallet/routeexecution/manager.go @@ -19,6 +19,7 @@ import ( "github.com/status-im/status-go/services/wallet/responses" "github.com/status-im/status-go/services/wallet/routeexecution/storage" "github.com/status-im/status-go/services/wallet/router" + "github.com/status-im/status-go/services/wallet/router/pathprocessor" pathProcessorCommon "github.com/status-im/status-go/services/wallet/router/pathprocessor/common" "github.com/status-im/status-go/services/wallet/router/routes" "github.com/status-im/status-go/services/wallet/router/sendtype" @@ -87,6 +88,13 @@ func (m *Manager) BuildTransactionsFromRoute(ctx context.Context, buildInputPara return } + // re-use path processor input params structure to pass extra params to transaction manager + var extraParams pathprocessor.ProcessorInputParams + extraParams, err = m.router.CreateProcessorInputParams(&routeInputParams, nil, nil, nil, nil, nil, buildInputParams.SlippagePercentage) + if err != nil { + return + } + m.buildInputParams = buildInputParams fromChainID, toChainID := route.GetFirstPathChains() @@ -99,14 +107,7 @@ func (m *Manager) BuildTransactionsFromRoute(ctx context.Context, buildInputPara response.SigningDetails, fromChainID, toChainID, err = m.transactionManager.BuildTransactionsFromRoute( route, m.router.GetPathProcessors(), - transfer.BuildRouteExtraParams{ - AddressFrom: routeInputParams.AddrFrom, - AddressTo: routeInputParams.AddrTo, - Username: routeInputParams.Username, - PublicKey: routeInputParams.PublicKey, - PackID: routeInputParams.PackID.ToInt(), - SlippagePercentage: buildInputParams.SlippagePercentage, - }, + &extraParams, ) if err != nil { response.SendDetails.UpdateFields(routeInputParams, fromChainID, toChainID) diff --git a/services/wallet/router/pathprocessor/common/constants.go b/services/wallet/router/pathprocessor/common/constants.go index 522fd32c2c2..4965c963cbe 100644 --- a/services/wallet/router/pathprocessor/common/constants.go +++ b/services/wallet/router/pathprocessor/common/constants.go @@ -4,14 +4,23 @@ const ( IncreaseEstimatedGasFactor = 1.2 SevenDaysInSeconds = 60 * 60 * 24 * 7 - ProcessorTransferName = "Transfer" - ProcessorBridgeHopName = "Hop" - ProcessorBridgeCelerName = "CBridge" - ProcessorSwapParaswapName = "Paraswap" - ProcessorERC721Name = "ERC721Transfer" - ProcessorERC1155Name = "ERC1155Transfer" - ProcessorENSRegisterName = "ENSRegister" - ProcessorENSReleaseName = "ENSRelease" - ProcessorENSPublicKeyName = "ENSPublicKey" - ProcessorStickersBuyName = "StickersBuy" + CommunityDeploymentTokenDecimals = uint8(18) + + ProcessorTransferName = "Transfer" + ProcessorBridgeHopName = "Hop" + ProcessorBridgeCelerName = "CBridge" + ProcessorSwapParaswapName = "Paraswap" + ProcessorERC721Name = "ERC721Transfer" + ProcessorERC1155Name = "ERC1155Transfer" + ProcessorENSRegisterName = "ENSRegister" + ProcessorENSReleaseName = "ENSRelease" + ProcessorENSPublicKeyName = "ENSPublicKey" + ProcessorStickersBuyName = "StickersBuy" + ProcessorCommunityDeployCollectiblesName = "CommunityDeployCollectibles" + ProcessorCommunityDeployOwnerTokenName = "CommunityDeployOwnerToken" + ProcessorCommunityBurnName = "CommunityBurn" + ProcessorCommunityDeployAssetsName = "CommunityDeployAssets" + ProcessorCommunityMintTokensName = "CommunityMintTokens" + ProcessorCommunityRemoteBurnName = "CommunityRemoteBurn" + ProcessorCommunitySetSignerPubKeyName = "CommunitySetSignerPubKey" ) diff --git a/services/wallet/router/pathprocessor/errors.go b/services/wallet/router/pathprocessor/errors.go index 0b9ae01f167..f16cd44b6ab 100644 --- a/services/wallet/router/pathprocessor/errors.go +++ b/services/wallet/router/pathprocessor/errors.go @@ -49,6 +49,9 @@ var ( ErrPriceTimeout = &errors.ErrorResponse{Code: errors.ErrorCode("WPP-037"), Details: "price timeout"} ErrNotEnoughLiquidity = &errors.ErrorResponse{Code: errors.ErrorCode("WPP-038"), Details: "not enough liquidity"} ErrPriceImpactTooHigh = &errors.ErrorResponse{Code: errors.ErrorCode("WPP-039"), Details: "price impact too high"} + ErrBurnAmountTooHigh = &errors.ErrorResponse{Code: errors.ErrorCode("WPP-040"), Details: "burn amount too high"} + ErrCommunityTokenType = &errors.ErrorResponse{Code: errors.ErrorCode("WPP-041"), Details: "invalid community token type"} + ErrIncorrectSignatureFormat = &errors.ErrorResponse{Code: errors.ErrorCode("WPP-042"), Details: "incorrect signature: got %d, want %d"} ) func createErrorResponse(processorName string, err error) error { diff --git a/services/wallet/router/pathprocessor/processor.go b/services/wallet/router/pathprocessor/processor.go index 084064c1857..61017371100 100644 --- a/services/wallet/router/pathprocessor/processor.go +++ b/services/wallet/router/pathprocessor/processor.go @@ -11,6 +11,8 @@ import ( "github.com/status-im/status-go/account" "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/params" + communitiestoken "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/requests" "github.com/status-im/status-go/services/wallet/token" "github.com/status-im/status-go/services/wallet/wallettypes" @@ -44,6 +46,17 @@ type PathProcessorClearable interface { Clear() } +type ProcessorCommunityTokenParams struct { + Name string + Symbol string + TokenURI string + Transferable bool + RemoteSelfDestruct bool + Supply *big.Int + OwnerTokenAddress string + MasterTokenAddress string +} + type ProcessorInputParams struct { FromChain *params.Network ToChain *params.Network @@ -55,10 +68,25 @@ type ProcessorInputParams struct { AmountOut *big.Int // extra params - BonderFee *big.Int - Username string - PublicKey string - PackID *big.Int + BonderFee *big.Int + Username string + PublicKey string + PackID *big.Int + SlippagePercentage float32 + + // community related params + CommunityID string + CommunityTokenContractAddress common.Address + CommunitySignerPubKey string + CommunityAmount *big.Int + CommunityTokenIds []*big.Int + CommunityWalletAddresses []common.Address + CommunityTokenDeploymentSignature string + CommunityTokenType protobuf.CommunityTokenType + CommunityPrivilegeLevel communitiestoken.PrivilegesLevel + CommunityOwnerTokenParameters ProcessorCommunityTokenParams + CommunityMasterTokenParameters ProcessorCommunityTokenParams + CommunityDeploymentParameters ProcessorCommunityTokenParams // for testing purposes TestsMode bool diff --git a/services/wallet/router/pathprocessor/processor_community_burn.go b/services/wallet/router/pathprocessor/processor_community_burn.go new file mode 100644 index 00000000000..17b9049dffe --- /dev/null +++ b/services/wallet/router/pathprocessor/processor_community_burn.go @@ -0,0 +1,234 @@ +package pathprocessor + +import ( + "context" + "math/big" + "strings" + + "go.uber.org/zap" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "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" + communitytokens "github.com/status-im/status-go/contracts/community-tokens" + "github.com/status-im/status-go/contracts/community-tokens/assets" + "github.com/status-im/status-go/contracts/community-tokens/collectibles" + communitytokendeployer "github.com/status-im/status-go/contracts/community-tokens/deployer" + "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/logutils" + "github.com/status-im/status-go/protocol/protobuf" + "github.com/status-im/status-go/rpc" + walletCommon "github.com/status-im/status-go/services/wallet/common" + pathProcessorCommon "github.com/status-im/status-go/services/wallet/router/pathprocessor/common" + "github.com/status-im/status-go/services/wallet/wallettypes" + "github.com/status-im/status-go/transactions" +) + +type CommunityBurnProcessor struct { + contractMaker *communitytokens.CommunityTokensContractMaker + transactor transactions.TransactorIface +} + +func NewCommunityBurnProcessor(rpcClient *rpc.Client, transactor transactions.TransactorIface) *CommunityBurnProcessor { + return &CommunityBurnProcessor{ + contractMaker: &communitytokens.CommunityTokensContractMaker{ + RPCClient: rpcClient, + }, + transactor: transactor, + } +} + +func createCommunityBurnErrorResponse(err error) error { + return createErrorResponse(pathProcessorCommon.ProcessorCommunityBurnName, err) +} + +func (s *CommunityBurnProcessor) Name() string { + return pathProcessorCommon.ProcessorCommunityBurnName +} + +func (s *CommunityBurnProcessor) AvailableFor(params ProcessorInputParams) (bool, error) { + return true, nil +} + +func (s *CommunityBurnProcessor) CalculateFees(params ProcessorInputParams) (*big.Int, *big.Int, error) { + return walletCommon.ZeroBigIntValue(), walletCommon.ZeroBigIntValue(), nil +} + +func (s *CommunityBurnProcessor) validateBurnAmount(ctx context.Context, params ProcessorInputParams) error { + remainingSupply, err := s.remainingSupply(ctx, params) + if err != nil { + return err + } + if params.CommunityAmount.Cmp(remainingSupply) > 1 { + return ErrBurnAmountTooHigh + } + return nil +} + +func (s *CommunityBurnProcessor) remainingSupply(ctx context.Context, params ProcessorInputParams) (*big.Int, error) { + switch params.CommunityTokenType { + case protobuf.CommunityTokenType_ERC721: + return s.remainingCollectiblesSupply(ctx, params.FromChain.ChainID, params.CommunityTokenContractAddress) + case protobuf.CommunityTokenType_ERC20: + return s.remainingAssetsSupply(ctx, params.FromChain.ChainID, params.CommunityTokenContractAddress) + default: + return nil, ErrCommunityTokenType + } +} + +func (s *CommunityBurnProcessor) maxSupply(ctx context.Context, params ProcessorInputParams) (*big.Int, error) { + switch params.CommunityTokenType { + case protobuf.CommunityTokenType_ERC721: + return s.maxSupplyCollectibles(ctx, params.FromChain.ChainID, params.CommunityTokenContractAddress) + case protobuf.CommunityTokenType_ERC20: + return s.maxSupplyAssets(ctx, params.FromChain.ChainID, params.CommunityTokenContractAddress) + default: + return nil, ErrCommunityTokenType + } +} + +// RemainingSupply = MaxSupply - MintedCount +func (s *CommunityBurnProcessor) remainingCollectiblesSupply(ctx context.Context, chainID uint64, contractAddress common.Address) (*big.Int, error) { + callOpts := &bind.CallOpts{Context: ctx, Pending: false} + contractInst, err := s.contractMaker.NewCollectiblesInstance(chainID, contractAddress) + if err != nil { + return nil, err + } + maxSupply, err := contractInst.MaxSupply(callOpts) + if err != nil { + return nil, err + } + mintedCount, err := contractInst.MintedCount(callOpts) + if err != nil { + return nil, err + } + res := big.NewInt(0) + res.Sub(maxSupply, mintedCount) + return res, nil +} + +// RemainingSupply = MaxSupply - TotalSupply +func (s *CommunityBurnProcessor) remainingAssetsSupply(ctx context.Context, chainID uint64, contractAddress common.Address) (*big.Int, error) { + callOpts := &bind.CallOpts{Context: ctx, Pending: false} + contractInst, err := s.contractMaker.NewAssetsInstance(chainID, contractAddress) + if err != nil { + return nil, err + } + maxSupply, err := contractInst.MaxSupply(callOpts) + if err != nil { + return nil, err + } + totalSupply, err := contractInst.TotalSupply(callOpts) + if err != nil { + return nil, err + } + res := big.NewInt(0) + res.Sub(maxSupply, totalSupply) + return res, nil +} + +func (s *CommunityBurnProcessor) maxSupplyCollectibles(ctx context.Context, chainID uint64, contractAddress common.Address) (*big.Int, error) { + callOpts := &bind.CallOpts{Context: ctx, Pending: false} + contractInst, err := s.contractMaker.NewCollectiblesInstance(chainID, contractAddress) + if err != nil { + return nil, err + } + return contractInst.MaxSupply(callOpts) +} + +func (s *CommunityBurnProcessor) maxSupplyAssets(ctx context.Context, chainID uint64, contractAddress common.Address) (*big.Int, error) { + callOpts := &bind.CallOpts{Context: ctx, Pending: false} + contractInst, err := s.contractMaker.NewAssetsInstance(chainID, contractAddress) + if err != nil { + return nil, err + } + return contractInst.MaxSupply(callOpts) +} + +func (s *CommunityBurnProcessor) PackTxInputData(params ProcessorInputParams) ([]byte, error) { + ctx := context.Background() + err := s.validateBurnAmount(ctx, params) + if err != nil { + return []byte{}, createCommunityBurnErrorResponse(err) + } + + maxSupply, err := s.maxSupply(ctx, params) + if err != nil { + return []byte{}, createCommunityBurnErrorResponse(err) + } + newMaxSupply := big.NewInt(0) + newMaxSupply.Sub(maxSupply, params.CommunityAmount) + + switch params.CommunityTokenType { + case protobuf.CommunityTokenType_ERC721: + collectiblesABI, err := abi.JSON(strings.NewReader(collectibles.CollectiblesABI)) + if err != nil { + return []byte{}, createCommunityBurnErrorResponse(err) + } + return collectiblesABI.Pack("setMaxSupply", newMaxSupply) + case protobuf.CommunityTokenType_ERC20: + assetsABI, err := abi.JSON(strings.NewReader(assets.AssetsABI)) + if err != nil { + return []byte{}, createCommunityBurnErrorResponse(err) + } + return assetsABI.Pack("setMaxSupply", newMaxSupply) + default: + return nil, ErrCommunityTokenType + } +} + +func (s *CommunityBurnProcessor) EstimateGas(params ProcessorInputParams) (uint64, error) { + if params.TestsMode { + return 0, ErrNoEstimationFound + } + + input, err := s.PackTxInputData(params) + if err != nil { + return 0, createCommunityBurnErrorResponse(err) + } + + ethClient, err := s.contractMaker.RPCClient.EthClient(params.FromChain.ChainID) + if err != nil { + return 0, createCommunityBurnErrorResponse(err) + } + + msg := ethereum.CallMsg{ + From: params.FromAddr, + To: ¶ms.CommunityTokenContractAddress, + Value: walletCommon.ZeroBigIntValue(), + Data: input, + } + + estimation, err := ethClient.EstimateGas(context.Background(), msg) + if err != nil { + return 0, createCommunityBurnErrorResponse(err) + } + + increasedEstimation := float64(estimation) * pathProcessorCommon.IncreaseEstimatedGasFactor + logutils.ZapLogger().Debug("CommunityBurnProcessor estimation", zap.Uint64("gas", uint64(increasedEstimation))) + + return uint64(increasedEstimation), nil +} + +func (s *CommunityBurnProcessor) Send(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64, verifiedAccount *account.SelectedExtKey) (hash types.Hash, usedNonce uint64, err error) { + return s.transactor.SendTransactionWithChainID(sendArgs.ChainID, *sendArgs.TransferTx, lastUsedNonce, verifiedAccount) +} + +func (s *CommunityBurnProcessor) BuildTransaction(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error) { + return s.transactor.ValidateAndBuildTransaction(sendArgs.ChainID, *sendArgs.TransferTx, lastUsedNonce) +} + +func (s *CommunityBurnProcessor) BuildTransactionV2(sendArgs *wallettypes.SendTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error) { + return s.transactor.ValidateAndBuildTransaction(sendArgs.FromChainID, *sendArgs, lastUsedNonce) +} + +func (s *CommunityBurnProcessor) CalculateAmountOut(params ProcessorInputParams) (*big.Int, error) { + return params.AmountIn, nil +} + +func (s *CommunityBurnProcessor) GetContractAddress(params ProcessorInputParams) (common.Address, error) { + return communitytokendeployer.ContractAddress(params.FromChain.ChainID) +} diff --git a/services/wallet/router/pathprocessor/processor_community_deploy_assets.go b/services/wallet/router/pathprocessor/processor_community_deploy_assets.go new file mode 100644 index 00000000000..394c90f6fd3 --- /dev/null +++ b/services/wallet/router/pathprocessor/processor_community_deploy_assets.go @@ -0,0 +1,126 @@ +package pathprocessor + +import ( + "context" + "math/big" + "strings" + + "go.uber.org/zap" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + ethTypes "github.com/ethereum/go-ethereum/core/types" + "github.com/status-im/status-go/account" + communitytokens "github.com/status-im/status-go/contracts/community-tokens" + "github.com/status-im/status-go/contracts/community-tokens/assets" + communitytokendeployer "github.com/status-im/status-go/contracts/community-tokens/deployer" + "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/logutils" + "github.com/status-im/status-go/rpc" + walletCommon "github.com/status-im/status-go/services/wallet/common" + pathProcessorCommon "github.com/status-im/status-go/services/wallet/router/pathprocessor/common" + "github.com/status-im/status-go/services/wallet/wallettypes" + "github.com/status-im/status-go/transactions" +) + +type CommunityDeployAssetsProcessor struct { + contractMaker *communitytokens.CommunityTokensContractMaker + transactor transactions.TransactorIface +} + +func NewCommunityDeployAssetsProcessor(rpcClient *rpc.Client, transactor transactions.TransactorIface) *CommunityDeployAssetsProcessor { + return &CommunityDeployAssetsProcessor{ + contractMaker: &communitytokens.CommunityTokensContractMaker{ + RPCClient: rpcClient, + }, + transactor: transactor, + } +} + +func createCommunityDeployAssetsErrorResponse(err error) error { + return createErrorResponse(pathProcessorCommon.ProcessorCommunityDeployAssetsName, err) +} + +func (s *CommunityDeployAssetsProcessor) Name() string { + return pathProcessorCommon.ProcessorCommunityDeployAssetsName +} + +func (s *CommunityDeployAssetsProcessor) AvailableFor(params ProcessorInputParams) (bool, error) { + return true, nil +} + +func (s *CommunityDeployAssetsProcessor) CalculateFees(params ProcessorInputParams) (*big.Int, *big.Int, error) { + return walletCommon.ZeroBigIntValue(), walletCommon.ZeroBigIntValue(), nil +} + +func (s *CommunityDeployAssetsProcessor) PackTxInputData(params ProcessorInputParams) ([]byte, error) { + assetsABI, err := abi.JSON(strings.NewReader(assets.AssetsABI)) + if err != nil { + return []byte{}, err + } + + data, err := assetsABI.Pack("" /*constructor name is empty*/, params.CommunityDeploymentParameters.Name, + params.CommunityDeploymentParameters.Symbol, pathProcessorCommon.CommunityDeploymentTokenDecimals, + params.CommunityDeploymentParameters.Supply, params.CommunityDeploymentParameters.TokenURI, + common.HexToAddress(params.CommunityDeploymentParameters.OwnerTokenAddress), + common.HexToAddress(params.CommunityDeploymentParameters.MasterTokenAddress)) + if err != nil { + return []byte{}, err + } + + return append(common.FromHex(assets.AssetsBin), data...), nil +} + +func (s *CommunityDeployAssetsProcessor) EstimateGas(params ProcessorInputParams) (uint64, error) { + if params.TestsMode { + return 0, ErrNoEstimationFound + } + + input, err := s.PackTxInputData(params) + if err != nil { + return 0, createCommunityDeployAssetsErrorResponse(err) + } + + ethClient, err := s.contractMaker.RPCClient.EthClient(params.FromChain.ChainID) + if err != nil { + return 0, createCommunityDeployAssetsErrorResponse(err) + } + + msg := ethereum.CallMsg{ + From: params.FromAddr, + To: nil, + Value: walletCommon.ZeroBigIntValue(), + Data: input, + } + + estimation, err := ethClient.EstimateGas(context.Background(), msg) + if err != nil { + return 0, createCommunityDeployAssetsErrorResponse(err) + } + + increasedEstimation := float64(estimation) * pathProcessorCommon.IncreaseEstimatedGasFactor + logutils.ZapLogger().Debug("CommunityDeployAssetsProcessor estimation", zap.Uint64("gas", uint64(increasedEstimation))) + + return uint64(increasedEstimation), nil +} + +func (s *CommunityDeployAssetsProcessor) Send(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64, verifiedAccount *account.SelectedExtKey) (hash types.Hash, usedNonce uint64, err error) { + return s.transactor.SendTransactionWithChainID(sendArgs.ChainID, *sendArgs.TransferTx, lastUsedNonce, verifiedAccount) +} + +func (s *CommunityDeployAssetsProcessor) BuildTransaction(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error) { + return s.transactor.ValidateAndBuildTransaction(sendArgs.ChainID, *sendArgs.TransferTx, lastUsedNonce) +} + +func (s *CommunityDeployAssetsProcessor) BuildTransactionV2(sendArgs *wallettypes.SendTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error) { + return s.transactor.ValidateAndBuildTransaction(sendArgs.FromChainID, *sendArgs, lastUsedNonce) +} + +func (s *CommunityDeployAssetsProcessor) CalculateAmountOut(params ProcessorInputParams) (*big.Int, error) { + return params.AmountIn, nil +} + +func (s *CommunityDeployAssetsProcessor) GetContractAddress(params ProcessorInputParams) (common.Address, error) { + return communitytokendeployer.ContractAddress(params.FromChain.ChainID) +} diff --git a/services/wallet/router/pathprocessor/processor_community_deploy_collectibles.go b/services/wallet/router/pathprocessor/processor_community_deploy_collectibles.go new file mode 100644 index 00000000000..406b6a68bbd --- /dev/null +++ b/services/wallet/router/pathprocessor/processor_community_deploy_collectibles.go @@ -0,0 +1,126 @@ +package pathprocessor + +import ( + "context" + "math/big" + "strings" + + "go.uber.org/zap" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + ethTypes "github.com/ethereum/go-ethereum/core/types" + "github.com/status-im/status-go/account" + communitytokens "github.com/status-im/status-go/contracts/community-tokens" + "github.com/status-im/status-go/contracts/community-tokens/collectibles" + communitytokendeployer "github.com/status-im/status-go/contracts/community-tokens/deployer" + "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/logutils" + "github.com/status-im/status-go/rpc" + walletCommon "github.com/status-im/status-go/services/wallet/common" + pathProcessorCommon "github.com/status-im/status-go/services/wallet/router/pathprocessor/common" + "github.com/status-im/status-go/services/wallet/wallettypes" + "github.com/status-im/status-go/transactions" +) + +type CommunityDeployCollectiblesProcessor struct { + contractMaker *communitytokens.CommunityTokensContractMaker + transactor transactions.TransactorIface +} + +func NewCommunityDeployCollectiblesProcessor(rpcClient *rpc.Client, transactor transactions.TransactorIface) *CommunityDeployCollectiblesProcessor { + return &CommunityDeployCollectiblesProcessor{ + contractMaker: &communitytokens.CommunityTokensContractMaker{ + RPCClient: rpcClient, + }, + transactor: transactor, + } +} + +func createCommunityDeployCollectiblesErrorResponse(err error) error { + return createErrorResponse(pathProcessorCommon.ProcessorCommunityDeployCollectiblesName, err) +} + +func (s *CommunityDeployCollectiblesProcessor) Name() string { + return pathProcessorCommon.ProcessorCommunityDeployCollectiblesName +} + +func (s *CommunityDeployCollectiblesProcessor) AvailableFor(params ProcessorInputParams) (bool, error) { + return true, nil +} + +func (s *CommunityDeployCollectiblesProcessor) CalculateFees(params ProcessorInputParams) (*big.Int, *big.Int, error) { + return walletCommon.ZeroBigIntValue(), walletCommon.ZeroBigIntValue(), nil +} + +func (s *CommunityDeployCollectiblesProcessor) PackTxInputData(params ProcessorInputParams) ([]byte, error) { + collectiblesABI, err := abi.JSON(strings.NewReader(collectibles.CollectiblesABI)) + if err != nil { + return []byte{}, err + } + + data, err := collectiblesABI.Pack("" /*constructor name is empty*/, params.CommunityDeploymentParameters.Name, + params.CommunityDeploymentParameters.Symbol, params.CommunityDeploymentParameters.Supply, + params.CommunityDeploymentParameters.RemoteSelfDestruct, params.CommunityDeploymentParameters.Transferable, + params.CommunityDeploymentParameters.TokenURI, common.HexToAddress(params.CommunityDeploymentParameters.OwnerTokenAddress), + common.HexToAddress(params.CommunityDeploymentParameters.MasterTokenAddress)) + if err != nil { + return []byte{}, err + } + + return append(common.FromHex(collectibles.CollectiblesBin), data...), nil +} + +func (s *CommunityDeployCollectiblesProcessor) EstimateGas(params ProcessorInputParams) (uint64, error) { + if params.TestsMode { + return 0, ErrNoEstimationFound + } + + input, err := s.PackTxInputData(params) + if err != nil { + return 0, createCommunityDeployCollectiblesErrorResponse(err) + } + + ethClient, err := s.contractMaker.RPCClient.EthClient(params.FromChain.ChainID) + if err != nil { + return 0, createCommunityDeployCollectiblesErrorResponse(err) + } + + msg := ethereum.CallMsg{ + From: params.FromAddr, + To: nil, + Value: walletCommon.ZeroBigIntValue(), + Data: input, + } + + estimation, err := ethClient.EstimateGas(context.Background(), msg) + if err != nil { + return 0, createCommunityDeployCollectiblesErrorResponse(err) + } + + increasedEstimation := float64(estimation) * pathProcessorCommon.IncreaseEstimatedGasFactor + logutils.ZapLogger().Debug("CommunityDeployCollectiblesProcessor estimation", zap.Uint64("gas", uint64(increasedEstimation))) + + return uint64(increasedEstimation), nil +} + +func (s *CommunityDeployCollectiblesProcessor) Send(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64, verifiedAccount *account.SelectedExtKey) (hash types.Hash, usedNonce uint64, err error) { + return s.transactor.SendTransactionWithChainID(sendArgs.ChainID, *sendArgs.TransferTx, lastUsedNonce, verifiedAccount) +} + +func (s *CommunityDeployCollectiblesProcessor) BuildTransaction(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error) { + return s.transactor.ValidateAndBuildTransaction(sendArgs.ChainID, *sendArgs.TransferTx, lastUsedNonce) +} + +func (s *CommunityDeployCollectiblesProcessor) BuildTransactionV2(sendArgs *wallettypes.SendTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error) { + return s.transactor.ValidateAndBuildTransaction(sendArgs.FromChainID, *sendArgs, lastUsedNonce) +} + +func (s *CommunityDeployCollectiblesProcessor) CalculateAmountOut(params ProcessorInputParams) (*big.Int, error) { + return params.AmountIn, nil +} + +func (s *CommunityDeployCollectiblesProcessor) GetContractAddress(params ProcessorInputParams) (common.Address, error) { + return communitytokendeployer.ContractAddress(params.FromChain.ChainID) +} diff --git a/services/wallet/router/pathprocessor/processor_community_deploy_owner_token.go b/services/wallet/router/pathprocessor/processor_community_deploy_owner_token.go new file mode 100644 index 00000000000..c8073dbf15d --- /dev/null +++ b/services/wallet/router/pathprocessor/processor_community_deploy_owner_token.go @@ -0,0 +1,190 @@ +package pathprocessor + +import ( + "context" + "fmt" + "math/big" + "strings" + + "go.uber.org/zap" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + ethTypes "github.com/ethereum/go-ethereum/core/types" + "github.com/status-im/status-go/account" + communitytokens "github.com/status-im/status-go/contracts/community-tokens" + communitytokendeployer "github.com/status-im/status-go/contracts/community-tokens/deployer" + "github.com/status-im/status-go/errors" + "github.com/status-im/status-go/eth-node/crypto" + "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/logutils" + "github.com/status-im/status-go/rpc" + walletCommon "github.com/status-im/status-go/services/wallet/common" + pathProcessorCommon "github.com/status-im/status-go/services/wallet/router/pathprocessor/common" + "github.com/status-im/status-go/services/wallet/wallettypes" + "github.com/status-im/status-go/transactions" +) + +type CommunityDeployOwnerTokenProcessor struct { + contractMaker *communitytokens.CommunityTokensContractMaker + transactor transactions.TransactorIface +} + +func NewCommunityDeployOwnerTokenProcessor(rpcClient *rpc.Client, transactor transactions.TransactorIface) *CommunityDeployOwnerTokenProcessor { + return &CommunityDeployOwnerTokenProcessor{ + contractMaker: &communitytokens.CommunityTokensContractMaker{ + RPCClient: rpcClient, + }, + transactor: transactor, + } +} + +func createCommunityDeployOwnerTokenErrorResponse(err error) error { + return createErrorResponse(pathProcessorCommon.ProcessorCommunityDeployOwnerTokenName, err) +} + +func (s *CommunityDeployOwnerTokenProcessor) Name() string { + return pathProcessorCommon.ProcessorCommunityDeployOwnerTokenName +} + +func (s *CommunityDeployOwnerTokenProcessor) AvailableFor(params ProcessorInputParams) (bool, error) { + return true, nil +} + +func (s *CommunityDeployOwnerTokenProcessor) CalculateFees(params ProcessorInputParams) (*big.Int, *big.Int, error) { + return walletCommon.ZeroBigIntValue(), walletCommon.ZeroBigIntValue(), nil +} + +func decodeSignature(sig []byte) (r [32]byte, s [32]byte, v uint8, err error) { + if len(sig) != crypto.SignatureLength { + err = &errors.ErrorResponse{ + Code: ErrIncorrectSignatureFormat.Code, + Details: fmt.Sprintf(ErrIncorrectSignatureFormat.Details, len(sig), crypto.SignatureLength), + } + return [32]byte{}, [32]byte{}, 0, err + } + copy(r[:], sig[:32]) + copy(s[:], sig[32:64]) + v = sig[64] + 27 + return r, s, v, nil +} + +func convert33BytesPubKeyToEthAddress(pubKey string) (common.Address, error) { + decoded, err := types.DecodeHex(pubKey) + if err != nil { + return common.Address{}, err + } + communityPubKey, err := crypto.DecompressPubkey(decoded) + if err != nil { + return common.Address{}, err + } + return common.Address(crypto.PubkeyToAddress(*communityPubKey)), nil +} + +func prepareDeploymentSignatureStruct(signature string, communityID string, addressFrom common.Address) (communitytokendeployer.CommunityTokenDeployerDeploymentSignature, error) { + r, s, v, err := decodeSignature(common.FromHex(signature)) + if err != nil { + return communitytokendeployer.CommunityTokenDeployerDeploymentSignature{}, err + } + communityEthAddress, err := convert33BytesPubKeyToEthAddress(communityID) + if err != nil { + return communitytokendeployer.CommunityTokenDeployerDeploymentSignature{}, err + } + communitySignature := communitytokendeployer.CommunityTokenDeployerDeploymentSignature{ + V: v, + R: r, + S: s, + Deployer: addressFrom, + Signer: communityEthAddress, + } + return communitySignature, nil +} + +func (s *CommunityDeployOwnerTokenProcessor) PackTxInputData(params ProcessorInputParams) ([]byte, error) { + deployerABI, err := abi.JSON(strings.NewReader(communitytokendeployer.CommunityTokenDeployerABI)) + if err != nil { + return []byte{}, err + } + + ownerTokenConfig := communitytokendeployer.CommunityTokenDeployerTokenConfig{ + Name: params.CommunityOwnerTokenParameters.Name, + Symbol: params.CommunityOwnerTokenParameters.Symbol, + BaseURI: params.CommunityOwnerTokenParameters.TokenURI, + } + + masterTokenConfig := communitytokendeployer.CommunityTokenDeployerTokenConfig{ + Name: params.CommunityMasterTokenParameters.Name, + Symbol: params.CommunityMasterTokenParameters.Symbol, + BaseURI: params.CommunityMasterTokenParameters.TokenURI, + } + + communitySignature, err := prepareDeploymentSignatureStruct(params.CommunityTokenDeploymentSignature, params.CommunityID, params.FromAddr) + if err != nil { + return []byte{}, err + } + + return deployerABI.Pack("deploy", ownerTokenConfig, masterTokenConfig, communitySignature, common.FromHex(params.CommunitySignerPubKey)) +} + +func (s *CommunityDeployOwnerTokenProcessor) EstimateGas(params ProcessorInputParams) (uint64, error) { + if params.TestsMode { + return 0, ErrNoEstimationFound + } + + contractAddress, err := s.GetContractAddress(params) + if err != nil { + return 0, createCommunityDeployOwnerTokenErrorResponse(err) + } + + input, err := s.PackTxInputData(params) + if err != nil { + return 0, createCommunityDeployOwnerTokenErrorResponse(err) + } + + ethClient, err := s.contractMaker.RPCClient.EthClient(params.FromChain.ChainID) + if err != nil { + return 0, createCommunityDeployOwnerTokenErrorResponse(err) + } + + msg := ethereum.CallMsg{ + From: params.FromAddr, + To: &contractAddress, + Value: walletCommon.ZeroBigIntValue(), + Data: input, + } + + estimation, err := ethClient.EstimateGas(context.Background(), msg) + if err != nil { + return 0, createCommunityDeployOwnerTokenErrorResponse(err) + } + + increasedEstimation := float64(estimation) * pathProcessorCommon.IncreaseEstimatedGasFactor + logutils.ZapLogger().Debug("CommunityDeployOwnerTokenProcessor estimation", zap.Uint64("gas", uint64(increasedEstimation))) + + return uint64(increasedEstimation), nil +} + +func (s *CommunityDeployOwnerTokenProcessor) Send(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64, verifiedAccount *account.SelectedExtKey) (hash types.Hash, usedNonce uint64, err error) { + return s.transactor.SendTransactionWithChainID(sendArgs.ChainID, *sendArgs.TransferTx, lastUsedNonce, verifiedAccount) +} + +func (s *CommunityDeployOwnerTokenProcessor) BuildTransaction(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error) { + return s.transactor.ValidateAndBuildTransaction(sendArgs.ChainID, *sendArgs.TransferTx, lastUsedNonce) +} + +func (s *CommunityDeployOwnerTokenProcessor) BuildTransactionV2(sendArgs *wallettypes.SendTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error) { + tx, n, e := s.transactor.ValidateAndBuildTransaction(sendArgs.FromChainID, *sendArgs, lastUsedNonce) + if e != nil { + return nil, 0, e + } + return tx, n, nil +} + +func (s *CommunityDeployOwnerTokenProcessor) CalculateAmountOut(params ProcessorInputParams) (*big.Int, error) { + return params.AmountIn, nil +} + +func (s *CommunityDeployOwnerTokenProcessor) GetContractAddress(params ProcessorInputParams) (common.Address, error) { + return communitytokendeployer.ContractAddress(params.FromChain.ChainID) +} diff --git a/services/wallet/router/pathprocessor/processor_community_mint_tokens.go b/services/wallet/router/pathprocessor/processor_community_mint_tokens.go new file mode 100644 index 00000000000..c622c29a593 --- /dev/null +++ b/services/wallet/router/pathprocessor/processor_community_mint_tokens.go @@ -0,0 +1,160 @@ +package pathprocessor + +import ( + "context" + "math/big" + "strings" + + "go.uber.org/zap" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + ethTypes "github.com/ethereum/go-ethereum/core/types" + "github.com/status-im/status-go/account" + communitytokens "github.com/status-im/status-go/contracts/community-tokens" + "github.com/status-im/status-go/contracts/community-tokens/assets" + "github.com/status-im/status-go/contracts/community-tokens/collectibles" + communitytokendeployer "github.com/status-im/status-go/contracts/community-tokens/deployer" + "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/logutils" + "github.com/status-im/status-go/protocol/protobuf" + "github.com/status-im/status-go/rpc" + walletCommon "github.com/status-im/status-go/services/wallet/common" + pathProcessorCommon "github.com/status-im/status-go/services/wallet/router/pathprocessor/common" + "github.com/status-im/status-go/services/wallet/wallettypes" + "github.com/status-im/status-go/transactions" +) + +type CommunityMintTokensProcessor struct { + contractMaker *communitytokens.CommunityTokensContractMaker + transactor transactions.TransactorIface +} + +func NewCommunityMintTokensProcessor(rpcClient *rpc.Client, transactor transactions.TransactorIface) *CommunityMintTokensProcessor { + return &CommunityMintTokensProcessor{ + contractMaker: &communitytokens.CommunityTokensContractMaker{ + RPCClient: rpcClient, + }, + transactor: transactor, + } +} + +func createCommunityMintTokensErrorResponse(err error) error { + return createErrorResponse(pathProcessorCommon.ProcessorCommunityMintTokensName, err) +} + +func (s *CommunityMintTokensProcessor) Name() string { + return pathProcessorCommon.ProcessorCommunityMintTokensName +} + +func (s *CommunityMintTokensProcessor) AvailableFor(params ProcessorInputParams) (bool, error) { + return true, nil +} + +func (s *CommunityMintTokensProcessor) CalculateFees(params ProcessorInputParams) (*big.Int, *big.Int, error) { + return walletCommon.ZeroBigIntValue(), walletCommon.ZeroBigIntValue(), nil +} + +// if we want to mint 2 tokens to addresses ["a", "b"] we need to mint +// twice to every address - we need to send to smart contract table ["a", "a", "b", "b"] +func multiplyWalletAddresses(amount *big.Int, walletAddresses []common.Address) (result []common.Address) { + n := amount.Int64() + for _, address := range walletAddresses { + for i := int64(0); i < n; i++ { + result = append(result, address) + } + } + return +} + +func prepareMintAssetsData(amount *big.Int, walletAddresses []common.Address) (result []*big.Int) { + length := len(walletAddresses) + for i := 0; i < length; i++ { + result = append(result, new(big.Int).Set(amount)) + } + return result +} + +func (s *CommunityMintTokensProcessor) packTxInputDataInternally(params ProcessorInputParams) ([]byte, error) { + switch params.CommunityTokenType { + case protobuf.CommunityTokenType_ERC721: + destinationAddresses := multiplyWalletAddresses(params.CommunityAmount, params.CommunityWalletAddresses) + collectiblesABI, err := abi.JSON(strings.NewReader(collectibles.CollectiblesABI)) + if err != nil { + return []byte{}, err + } + return collectiblesABI.Pack("mintTo", destinationAddresses) + case protobuf.CommunityTokenType_ERC20: + destinationAmounts := prepareMintAssetsData(params.CommunityAmount, params.CommunityWalletAddresses) + assetsABI, err := abi.JSON(strings.NewReader(assets.AssetsABI)) + if err != nil { + return []byte{}, err + } + return assetsABI.Pack("mintTo", params.CommunityWalletAddresses, destinationAmounts) + default: + return nil, ErrCommunityTokenType + } +} + +func (s *CommunityMintTokensProcessor) PackTxInputData(params ProcessorInputParams) ([]byte, error) { + data, err := s.packTxInputDataInternally(params) + if err != nil { + return []byte{}, createCommunityMintTokensErrorResponse(err) + } + + return append(common.FromHex(collectibles.CollectiblesBin), data...), nil +} + +func (s *CommunityMintTokensProcessor) EstimateGas(params ProcessorInputParams) (uint64, error) { + if params.TestsMode { + return 0, ErrNoEstimationFound + } + + input, err := s.PackTxInputData(params) + if err != nil { + return 0, createCommunityMintTokensErrorResponse(err) + } + + ethClient, err := s.contractMaker.RPCClient.EthClient(params.FromChain.ChainID) + if err != nil { + return 0, createCommunityMintTokensErrorResponse(err) + } + + msg := ethereum.CallMsg{ + From: params.FromAddr, + To: nil, + Value: walletCommon.ZeroBigIntValue(), + Data: input, + } + + estimation, err := ethClient.EstimateGas(context.Background(), msg) + if err != nil { + return 0, createCommunityMintTokensErrorResponse(err) + } + + increasedEstimation := float64(estimation) * pathProcessorCommon.IncreaseEstimatedGasFactor + logutils.ZapLogger().Debug("CommunityMintTokensProcessor estimation", zap.Uint64("gas", uint64(increasedEstimation))) + + return uint64(increasedEstimation), nil +} + +func (s *CommunityMintTokensProcessor) Send(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64, verifiedAccount *account.SelectedExtKey) (hash types.Hash, usedNonce uint64, err error) { + return s.transactor.SendTransactionWithChainID(sendArgs.ChainID, *sendArgs.TransferTx, lastUsedNonce, verifiedAccount) +} + +func (s *CommunityMintTokensProcessor) BuildTransaction(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error) { + return s.transactor.ValidateAndBuildTransaction(sendArgs.ChainID, *sendArgs.TransferTx, lastUsedNonce) +} + +func (s *CommunityMintTokensProcessor) BuildTransactionV2(sendArgs *wallettypes.SendTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error) { + return s.transactor.ValidateAndBuildTransaction(sendArgs.FromChainID, *sendArgs, lastUsedNonce) +} + +func (s *CommunityMintTokensProcessor) CalculateAmountOut(params ProcessorInputParams) (*big.Int, error) { + return params.AmountIn, nil +} + +func (s *CommunityMintTokensProcessor) GetContractAddress(params ProcessorInputParams) (common.Address, error) { + return communitytokendeployer.ContractAddress(params.FromChain.ChainID) +} diff --git a/services/wallet/router/pathprocessor/processor_community_remote_burn.go b/services/wallet/router/pathprocessor/processor_community_remote_burn.go new file mode 100644 index 00000000000..363180c1de5 --- /dev/null +++ b/services/wallet/router/pathprocessor/processor_community_remote_burn.go @@ -0,0 +1,116 @@ +package pathprocessor + +import ( + "context" + "math/big" + "strings" + + "go.uber.org/zap" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + ethTypes "github.com/ethereum/go-ethereum/core/types" + "github.com/status-im/status-go/account" + communitytokens "github.com/status-im/status-go/contracts/community-tokens" + "github.com/status-im/status-go/contracts/community-tokens/collectibles" + communitytokendeployer "github.com/status-im/status-go/contracts/community-tokens/deployer" + "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/logutils" + "github.com/status-im/status-go/rpc" + walletCommon "github.com/status-im/status-go/services/wallet/common" + pathProcessorCommon "github.com/status-im/status-go/services/wallet/router/pathprocessor/common" + "github.com/status-im/status-go/services/wallet/wallettypes" + "github.com/status-im/status-go/transactions" +) + +type CommunityRemoteBurnProcessor struct { + contractMaker *communitytokens.CommunityTokensContractMaker + transactor transactions.TransactorIface +} + +func NewCommunityRemoteBurnProcessor(rpcClient *rpc.Client, transactor transactions.TransactorIface) *CommunityRemoteBurnProcessor { + return &CommunityRemoteBurnProcessor{ + contractMaker: &communitytokens.CommunityTokensContractMaker{ + RPCClient: rpcClient, + }, + transactor: transactor, + } +} + +func createCommunityRemoteBurnErrorResponse(err error) error { + return createErrorResponse(pathProcessorCommon.ProcessorCommunityRemoteBurnName, err) +} + +func (s *CommunityRemoteBurnProcessor) Name() string { + return pathProcessorCommon.ProcessorCommunityRemoteBurnName +} + +func (s *CommunityRemoteBurnProcessor) AvailableFor(params ProcessorInputParams) (bool, error) { + return true, nil +} + +func (s *CommunityRemoteBurnProcessor) CalculateFees(params ProcessorInputParams) (*big.Int, *big.Int, error) { + return walletCommon.ZeroBigIntValue(), walletCommon.ZeroBigIntValue(), nil +} + +func (s *CommunityRemoteBurnProcessor) PackTxInputData(params ProcessorInputParams) ([]byte, error) { + collectiblesABI, err := abi.JSON(strings.NewReader(collectibles.CollectiblesABI)) + if err != nil { + return []byte{}, createCommunityRemoteBurnErrorResponse(err) + } + return collectiblesABI.Pack("remoteBurn", params.CommunityTokenIds) +} + +func (s *CommunityRemoteBurnProcessor) EstimateGas(params ProcessorInputParams) (uint64, error) { + if params.TestsMode { + return 0, ErrNoEstimationFound + } + + input, err := s.PackTxInputData(params) + if err != nil { + return 0, createCommunityRemoteBurnErrorResponse(err) + } + + ethClient, err := s.contractMaker.RPCClient.EthClient(params.FromChain.ChainID) + if err != nil { + return 0, createCommunityRemoteBurnErrorResponse(err) + } + + msg := ethereum.CallMsg{ + From: params.FromAddr, + To: ¶ms.CommunityTokenContractAddress, + Value: walletCommon.ZeroBigIntValue(), + Data: input, + } + + estimation, err := ethClient.EstimateGas(context.Background(), msg) + if err != nil { + return 0, createCommunityRemoteBurnErrorResponse(err) + } + + increasedEstimation := float64(estimation) * pathProcessorCommon.IncreaseEstimatedGasFactor + logutils.ZapLogger().Debug("CommunityRemoteBurnProcessor estimation", zap.Uint64("gas", uint64(increasedEstimation))) + + return uint64(increasedEstimation), nil +} + +func (s *CommunityRemoteBurnProcessor) Send(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64, verifiedAccount *account.SelectedExtKey) (hash types.Hash, usedNonce uint64, err error) { + return s.transactor.SendTransactionWithChainID(sendArgs.ChainID, *sendArgs.TransferTx, lastUsedNonce, verifiedAccount) +} + +func (s *CommunityRemoteBurnProcessor) BuildTransaction(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error) { + return s.transactor.ValidateAndBuildTransaction(sendArgs.ChainID, *sendArgs.TransferTx, lastUsedNonce) +} + +func (s *CommunityRemoteBurnProcessor) BuildTransactionV2(sendArgs *wallettypes.SendTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error) { + return s.transactor.ValidateAndBuildTransaction(sendArgs.FromChainID, *sendArgs, lastUsedNonce) +} + +func (s *CommunityRemoteBurnProcessor) CalculateAmountOut(params ProcessorInputParams) (*big.Int, error) { + return params.AmountIn, nil +} + +func (s *CommunityRemoteBurnProcessor) GetContractAddress(params ProcessorInputParams) (common.Address, error) { + return communitytokendeployer.ContractAddress(params.FromChain.ChainID) +} diff --git a/services/wallet/router/pathprocessor/processor_community_set_signer_pub_key.go b/services/wallet/router/pathprocessor/processor_community_set_signer_pub_key.go new file mode 100644 index 00000000000..fe35355572a --- /dev/null +++ b/services/wallet/router/pathprocessor/processor_community_set_signer_pub_key.go @@ -0,0 +1,117 @@ +package pathprocessor + +import ( + "context" + "math/big" + "strings" + + "go.uber.org/zap" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + ethTypes "github.com/ethereum/go-ethereum/core/types" + "github.com/status-im/status-go/account" + communitytokens "github.com/status-im/status-go/contracts/community-tokens" + communitytokendeployer "github.com/status-im/status-go/contracts/community-tokens/deployer" + "github.com/status-im/status-go/contracts/community-tokens/ownertoken" + "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/logutils" + "github.com/status-im/status-go/rpc" + walletCommon "github.com/status-im/status-go/services/wallet/common" + pathProcessorCommon "github.com/status-im/status-go/services/wallet/router/pathprocessor/common" + "github.com/status-im/status-go/services/wallet/wallettypes" + "github.com/status-im/status-go/transactions" +) + +type CommunitySetSignerPubKeyProcessor struct { + contractMaker *communitytokens.CommunityTokensContractMaker + transactor transactions.TransactorIface +} + +func NewCommunitySetSignerPubKeyProcessor(rpcClient *rpc.Client, transactor transactions.TransactorIface) *CommunitySetSignerPubKeyProcessor { + return &CommunitySetSignerPubKeyProcessor{ + contractMaker: &communitytokens.CommunityTokensContractMaker{ + RPCClient: rpcClient, + }, + transactor: transactor, + } +} + +func createCommunitySetSignerPubKeyErrorResponse(err error) error { + return createErrorResponse(pathProcessorCommon.ProcessorCommunitySetSignerPubKeyName, err) +} + +func (s *CommunitySetSignerPubKeyProcessor) Name() string { + return pathProcessorCommon.ProcessorCommunitySetSignerPubKeyName +} + +func (s *CommunitySetSignerPubKeyProcessor) AvailableFor(params ProcessorInputParams) (bool, error) { + return true, nil +} + +func (s *CommunitySetSignerPubKeyProcessor) CalculateFees(params ProcessorInputParams) (*big.Int, *big.Int, error) { + return walletCommon.ZeroBigIntValue(), walletCommon.ZeroBigIntValue(), nil +} + +func (s *CommunitySetSignerPubKeyProcessor) PackTxInputData(params ProcessorInputParams) ([]byte, error) { + ownerTokenABI, err := abi.JSON(strings.NewReader(ownertoken.OwnerTokenABI)) + if err != nil { + return []byte{}, err + } + + return ownerTokenABI.Pack("setSignerPublicKey", common.FromHex(params.CommunitySignerPubKey)) +} + +func (s *CommunitySetSignerPubKeyProcessor) EstimateGas(params ProcessorInputParams) (uint64, error) { + if params.TestsMode { + return 0, ErrNoEstimationFound + } + + input, err := s.PackTxInputData(params) + if err != nil { + return 0, createCommunitySetSignerPubKeyErrorResponse(err) + } + + ethClient, err := s.contractMaker.RPCClient.EthClient(params.FromChain.ChainID) + if err != nil { + return 0, createCommunitySetSignerPubKeyErrorResponse(err) + } + + msg := ethereum.CallMsg{ + From: params.FromAddr, + To: nil, + Value: walletCommon.ZeroBigIntValue(), + Data: input, + } + + estimation, err := ethClient.EstimateGas(context.Background(), msg) + if err != nil { + return 0, createCommunitySetSignerPubKeyErrorResponse(err) + } + + increasedEstimation := float64(estimation) * pathProcessorCommon.IncreaseEstimatedGasFactor + logutils.ZapLogger().Debug("CommunitySetSignerPubKeyProcessor estimation", zap.Uint64("gas", uint64(increasedEstimation))) + + return uint64(increasedEstimation), nil +} + +func (s *CommunitySetSignerPubKeyProcessor) Send(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64, verifiedAccount *account.SelectedExtKey) (hash types.Hash, usedNonce uint64, err error) { + return s.transactor.SendTransactionWithChainID(sendArgs.ChainID, *sendArgs.TransferTx, lastUsedNonce, verifiedAccount) +} + +func (s *CommunitySetSignerPubKeyProcessor) BuildTransaction(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error) { + return s.transactor.ValidateAndBuildTransaction(sendArgs.ChainID, *sendArgs.TransferTx, lastUsedNonce) +} + +func (s *CommunitySetSignerPubKeyProcessor) BuildTransactionV2(sendArgs *wallettypes.SendTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error) { + return s.transactor.ValidateAndBuildTransaction(sendArgs.FromChainID, *sendArgs, lastUsedNonce) +} + +func (s *CommunitySetSignerPubKeyProcessor) CalculateAmountOut(params ProcessorInputParams) (*big.Int, error) { + return params.AmountIn, nil +} + +func (s *CommunitySetSignerPubKeyProcessor) GetContractAddress(params ProcessorInputParams) (common.Address, error) { + return communitytokendeployer.ContractAddress(params.FromChain.ChainID) +} diff --git a/services/wallet/router/router.go b/services/wallet/router/router.go index 3cff0162eb5..3b70a0966a8 100644 --- a/services/wallet/router/router.go +++ b/services/wallet/router/router.go @@ -580,6 +580,109 @@ func (r *Router) getSelectedChains(input *requests.RouteInputParams) (selectedFr return selectedFromChains, selectedToChains, nil } +func (r *Router) CreateProcessorInputParams(input *requests.RouteInputParams, fromNetwork *params.Network, toNetwork *params.Network, + fromToken *token.Token, toToken *token.Token, amountIn *big.Int, slippagePercentage float32) (pathprocessor.ProcessorInputParams, error) { + var err error + processorInputParams := pathprocessor.ProcessorInputParams{ + FromChain: fromNetwork, + ToChain: toNetwork, + FromToken: fromToken, + ToToken: toToken, + ToAddr: input.AddrTo, + FromAddr: input.AddrFrom, + AmountIn: amountIn, + SlippagePercentage: slippagePercentage, + + Username: input.Username, + PublicKey: input.PublicKey, + PackID: input.PackID.ToInt(), + } + + if input.AmountOut != nil { + processorInputParams.AmountOut = input.AmountOut.ToInt() + } + + if input.PackID != nil { + processorInputParams.PackID = input.PackID.ToInt() + } + + if input.SendType.IsCommunityRelatedTransfer() { + processorInputParams.CommunityID = input.CommunityRouteInputParams.CommunityID + processorInputParams.CommunityTokenContractAddress = common.HexToAddress(input.CommunityRouteInputParams.TokenContractAddress) + processorInputParams.CommunitySignerPubKey = input.CommunityRouteInputParams.SignerPubKey + processorInputParams.CommunityTokenDeploymentSignature = input.CommunityRouteInputParams.TokenDeploymentSignature + + if input.SendType == sendtype.CommunityBurn { + if input.CommunityRouteInputParams.Amount != nil { + processorInputParams.CommunityAmount = input.CommunityRouteInputParams.Amount.ToInt() + } + + processorInputParams.CommunityTokenType, err = r.tokenManager.GetCommunityTokenType(fromNetwork.ChainID, input.CommunityRouteInputParams.TokenContractAddress) + if err != nil { + return processorInputParams, err + } + + processorInputParams.CommunityPrivilegeLevel, err = r.tokenManager.GetCommunityTokenPrivilegesLevel(fromNetwork.ChainID, input.CommunityRouteInputParams.TokenContractAddress) + if err != nil { + return processorInputParams, err + } + } + + if input.SendType == sendtype.CommunityRemoteBurn { + for _, v := range input.CommunityRouteInputParams.TokenIds { + processorInputParams.CommunityTokenIds = append(processorInputParams.CommunityTokenIds, v.ToInt()) + } + } + + if input.SendType == sendtype.CommunityMintTokens { + if input.CommunityRouteInputParams.Amount != nil { + processorInputParams.CommunityAmount = input.CommunityRouteInputParams.Amount.ToInt() + } + + processorInputParams.CommunityWalletAddresses = input.CommunityRouteInputParams.WalletAddresses + } + + if input.CommunityRouteInputParams.OwnerTokenParameters != nil { + processorInputParams.CommunityOwnerTokenParameters = pathprocessor.ProcessorCommunityTokenParams{ + Name: input.CommunityRouteInputParams.OwnerTokenParameters.Name, + Symbol: input.CommunityRouteInputParams.OwnerTokenParameters.Symbol, + TokenURI: input.CommunityRouteInputParams.OwnerTokenParameters.TokenURI, + } + } + + if input.CommunityRouteInputParams.MasterTokenParameters != nil { + processorInputParams.CommunityMasterTokenParameters = pathprocessor.ProcessorCommunityTokenParams{ + Name: input.CommunityRouteInputParams.MasterTokenParameters.Name, + Symbol: input.CommunityRouteInputParams.MasterTokenParameters.Symbol, + TokenURI: input.CommunityRouteInputParams.MasterTokenParameters.TokenURI, + } + } + + if input.CommunityRouteInputParams.DeploymentParameters != nil { + processorInputParams.CommunityDeploymentParameters = pathprocessor.ProcessorCommunityTokenParams{ + Name: input.CommunityRouteInputParams.DeploymentParameters.Name, + Symbol: input.CommunityRouteInputParams.DeploymentParameters.Symbol, + TokenURI: input.CommunityRouteInputParams.DeploymentParameters.TokenURI, + Transferable: input.CommunityRouteInputParams.DeploymentParameters.Transferable, + RemoteSelfDestruct: input.CommunityRouteInputParams.DeploymentParameters.RemoteSelfDestruct, + Supply: input.CommunityRouteInputParams.DeploymentParameters.GetSupply(), + OwnerTokenAddress: input.CommunityRouteInputParams.DeploymentParameters.OwnerTokenAddress, + MasterTokenAddress: input.CommunityRouteInputParams.DeploymentParameters.MasterTokenAddress, + } + } + } + + if input.TestsMode { + processorInputParams.TestsMode = input.TestsMode + processorInputParams.TestEstimationMap = input.TestParams.EstimationMap + processorInputParams.TestBonderFeeMap = input.TestParams.BonderFeeMap + processorInputParams.TestApprovalGasEstimation = input.TestParams.ApprovalGasEstimation + processorInputParams.TestApprovalL1Fee = input.TestParams.ApprovalL1Fee + } + + return processorInputParams, err +} + func (r *Router) resolveCandidates(ctx context.Context, input *requests.RouteInputParams, selectedFromChains []*params.Network, selectedToChains []*params.Network) (candidates routes.Route, processorErrors []*ProcessorError, err error) { var ( @@ -692,26 +795,10 @@ func (r *Router) resolveCandidates(ctx context.Context, input *requests.RouteInp continue } - processorInputParams := pathprocessor.ProcessorInputParams{ - FromChain: network, - ToChain: dest, - FromToken: token, - ToToken: toToken, - ToAddr: input.AddrTo, - FromAddr: input.AddrFrom, - AmountIn: amountOption.amount, - AmountOut: input.AmountOut.ToInt(), - - Username: input.Username, - PublicKey: input.PublicKey, - PackID: input.PackID.ToInt(), - } - if input.TestsMode { - processorInputParams.TestsMode = input.TestsMode - processorInputParams.TestEstimationMap = input.TestParams.EstimationMap - processorInputParams.TestBonderFeeMap = input.TestParams.BonderFeeMap - processorInputParams.TestApprovalGasEstimation = input.TestParams.ApprovalGasEstimation - processorInputParams.TestApprovalL1Fee = input.TestParams.ApprovalL1Fee + processorInputParams, err := r.CreateProcessorInputParams(input, network, dest, token, toToken, amountOption.amount, 0) + if err != nil { + appendProcessorErrorFn(pProcessor.Name(), input.SendType, network.ChainID, dest.ChainID, amountOption.amount, err) + continue } can, err := pProcessor.AvailableFor(processorInputParams) @@ -735,12 +822,12 @@ func (r *Router) resolveCandidates(ctx context.Context, input *requests.RouteInp continue } - approvalContractAddress, err := pProcessor.GetContractAddress(processorInputParams) + contractAddress, err := pProcessor.GetContractAddress(processorInputParams) if err != nil { appendProcessorErrorFn(pProcessor.Name(), input.SendType, processorInputParams.FromChain.ChainID, processorInputParams.ToChain.ChainID, processorInputParams.AmountIn, err) continue } - approvalRequired, approvalAmountRequired, err := r.requireApproval(ctx, input.SendType, &approvalContractAddress, processorInputParams) + approvalRequired, approvalAmountRequired, err := r.requireApproval(ctx, input.SendType, &contractAddress, processorInputParams) if err != nil { appendProcessorErrorFn(pProcessor.Name(), input.SendType, processorInputParams.FromChain.ChainID, processorInputParams.ToChain.ChainID, processorInputParams.AmountIn, err) continue @@ -751,7 +838,7 @@ func (r *Router) resolveCandidates(ctx context.Context, input *requests.RouteInp if processorInputParams.TestsMode { approvalGasLimit = processorInputParams.TestApprovalGasEstimation } else { - approvalGasLimit, err = r.estimateGasForApproval(processorInputParams, &approvalContractAddress) + approvalGasLimit, err = r.estimateGasForApproval(processorInputParams, &contractAddress) if err != nil { appendProcessorErrorFn(pProcessor.Name(), input.SendType, processorInputParams.FromChain.ChainID, processorInputParams.ToChain.ChainID, processorInputParams.AmountIn, err) continue @@ -783,13 +870,14 @@ func (r *Router) resolveCandidates(ctx context.Context, input *requests.RouteInp AmountOut: (*hexutil.Big)(amountOut), // set params that we don't want to be recalculated with every new block creation - TxGasAmount: gasLimit, - TxBonderFees: (*hexutil.Big)(bonderFees), - TxTokenFees: (*hexutil.Big)(tokenFees), + UsedContractAddress: &contractAddress, + TxGasAmount: gasLimit, + TxBonderFees: (*hexutil.Big)(bonderFees), + TxTokenFees: (*hexutil.Big)(tokenFees), ApprovalRequired: approvalRequired, ApprovalAmountRequired: (*hexutil.Big)(approvalAmountRequired), - ApprovalContractAddress: &approvalContractAddress, + ApprovalContractAddress: &contractAddress, ApprovalGasAmount: approvalGasLimit, EstimatedTime: estimatedTime, diff --git a/services/wallet/router/router_helper.go b/services/wallet/router/router_helper.go index 404a4dcdc08..732d419e03d 100644 --- a/services/wallet/router/router_helper.go +++ b/services/wallet/router/router_helper.go @@ -254,6 +254,11 @@ func findToken(sendType sendtype.SendType, tokenManager *token.Manager, collecti return tokenManager.FindToken(network, tokenID) } + if sendType.IsCommunityRelatedTransfer() { + // TODO: optimize tokens to handle community tokens + return nil + } + parts := strings.Split(tokenID, ":") contractAddress := common.HexToAddress(parts[0]) collectibleTokenID, success := new(big.Int).SetString(parts[1], 10) diff --git a/services/wallet/router/routes/router_path.go b/services/wallet/router/routes/router_path.go index 0154113ed04..07ff2adab84 100644 --- a/services/wallet/router/routes/router_path.go +++ b/services/wallet/router/routes/router_path.go @@ -23,6 +23,7 @@ type Path struct { SuggestedLevelsForMaxFeesPerGas *fees.MaxFeesLevels // Suggested max fees for the transaction (in ETH WEI) MaxFeesPerGas *hexutil.Big // Max fees per gas (determined by client via GasFeeMode, in ETH WEI) + UsedContractAddress *common.Address // Address of the contract that will be used for the transaction TxBaseFee *hexutil.Big // Base fee for the transaction (in ETH WEI) TxPriorityFee *hexutil.Big // Priority fee for the transaction (in ETH WEI) @@ -35,7 +36,7 @@ type Path struct { ApprovalRequired bool // Is approval required for the transaction ApprovalAmountRequired *hexutil.Big // Amount required for the approval transaction - ApprovalContractAddress *common.Address // Address of the contract that needs to be approved + ApprovalContractAddress *common.Address // Address of the contract that will be used for the approval transaction, the same as UsedContractAddress. We can remove this field and use UsedContractAddress instead. ApprovalBaseFee *hexutil.Big // Base fee for the approval transaction (in ETH WEI) ApprovalPriorityFee *hexutil.Big // Priority fee for the approval transaction (in ETH WEI) ApprovalGasAmount uint64 // Gas used for the approval transaction @@ -107,6 +108,11 @@ func (p *Path) Copy() *Path { newPath.MaxFeesPerGas = (*hexutil.Big)(big.NewInt(0).Set(p.MaxFeesPerGas.ToInt())) } + if p.UsedContractAddress != nil { + addr := common.HexToAddress(p.UsedContractAddress.Hex()) + newPath.UsedContractAddress = &addr + } + if p.TxBaseFee != nil { newPath.TxBaseFee = (*hexutil.Big)(big.NewInt(0).Set(p.TxBaseFee.ToInt())) } diff --git a/services/wallet/router/sendtype/send_type.go b/services/wallet/router/sendtype/send_type.go index cf17007b8f1..1a979506449 100644 --- a/services/wallet/router/sendtype/send_type.go +++ b/services/wallet/router/sendtype/send_type.go @@ -20,6 +20,13 @@ const ( ERC721Transfer ERC1155Transfer Swap + CommunityBurn + CommunityDeployAssets + CommunityDeployCollectibles + CommunityDeployOwnerToken + CommunityMintTokens + CommunityRemoteBurn + CommunitySetSignerPubKey ) func (s SendType) IsCollectiblesTransfer() bool { @@ -34,6 +41,11 @@ func (s SendType) IsStickersTransfer() bool { return s == StickersBuy } +func (s SendType) IsCommunityRelatedTransfer() bool { + return s == CommunityDeployOwnerToken || s == CommunityDeployCollectibles || s == CommunityDeployAssets || + s == CommunityMintTokens || s == CommunityRemoteBurn || s == CommunityBurn || s == CommunitySetSignerPubKey +} + // canUseProcessor is used to check if certain SendType can be used with a given path processor func (s SendType) CanUseProcessor(pathProcessorName string) bool { switch s { @@ -56,6 +68,20 @@ func (s SendType) CanUseProcessor(pathProcessorName string) bool { return pathProcessorName == pathProcessorCommon.ProcessorENSPublicKeyName case StickersBuy: return pathProcessorName == pathProcessorCommon.ProcessorStickersBuyName + case CommunityBurn: + return pathProcessorName == pathProcessorCommon.ProcessorCommunityBurnName + case CommunityDeployAssets: + return pathProcessorName == pathProcessorCommon.ProcessorCommunityDeployAssetsName + case CommunityDeployCollectibles: + return pathProcessorName == pathProcessorCommon.ProcessorCommunityDeployCollectiblesName + case CommunityDeployOwnerToken: + return pathProcessorName == pathProcessorCommon.ProcessorCommunityDeployOwnerTokenName + case CommunityMintTokens: + return pathProcessorName == pathProcessorCommon.ProcessorCommunityMintTokensName + case CommunityRemoteBurn: + return pathProcessorName == pathProcessorCommon.ProcessorCommunityRemoteBurnName + case CommunitySetSignerPubKey: + return pathProcessorName == pathProcessorCommon.ProcessorCommunitySetSignerPubKeyName default: return true } @@ -71,6 +97,8 @@ func (s SendType) ProcessZeroAmountInProcessor(amountIn *big.Int, amountOut *big if amountOut.Cmp(walletCommon.ZeroBigIntValue()) == 0 { return false } + } else if s.IsCommunityRelatedTransfer() { + return true } else if s != ENSRelease { return false } @@ -83,6 +111,7 @@ func (s SendType) IsAvailableBetween(from, to *params.Network) bool { if s.IsCollectiblesTransfer() || s.IsEnsTransfer() || s.IsStickersTransfer() || + s.IsCommunityRelatedTransfer() || s == Swap { return from.ChainID == to.ChainID } @@ -95,32 +124,20 @@ func (s SendType) IsAvailableBetween(from, to *params.Network) bool { } func (s SendType) IsAvailableFor(network *params.Network) bool { - // Set of network ChainIDs allowed for any type of transaction - allAllowedNetworks := map[uint64]bool{ - walletCommon.EthereumMainnet: true, - walletCommon.EthereumSepolia: true, - } - - // Additional specific networks for the Swap SendType - swapAllowedNetworks := map[uint64]bool{ - walletCommon.EthereumMainnet: true, - walletCommon.OptimismMainnet: true, - walletCommon.ArbitrumMainnet: true, - } - // Check for Swap specific networks if s == Swap { - return swapAllowedNetworks[network.ChainID] + swapAllowedNetworks := map[uint64]bool{ + walletCommon.EthereumMainnet: true, + walletCommon.OptimismMainnet: true, + walletCommon.ArbitrumMainnet: true, + } + _, ok := swapAllowedNetworks[network.ChainID] + return ok } if s.IsEnsTransfer() || s.IsStickersTransfer() { return network.ChainID == walletCommon.EthereumMainnet || network.ChainID == walletCommon.EthereumSepolia } - // Check for any SendType available for all networks - if s == Transfer || s == Bridge || s.IsCollectiblesTransfer() || allAllowedNetworks[network.ChainID] { - return true - } - - return false + return true } diff --git a/services/wallet/service.go b/services/wallet/service.go index 3b5abebd404..0f464f92c05 100644 --- a/services/wallet/service.go +++ b/services/wallet/service.go @@ -280,6 +280,27 @@ func buildPathProcessors( buyStickers := pathprocessor.NewStickersBuyProcessor(rpcClient, transactor) ret = append(ret, buyStickers) + communityBurn := pathprocessor.NewCommunityBurnProcessor(rpcClient, transactor) + ret = append(ret, communityBurn) + + communityDeployAssets := pathprocessor.NewCommunityDeployAssetsProcessor(rpcClient, transactor) + ret = append(ret, communityDeployAssets) + + communityDeployCollectibles := pathprocessor.NewCommunityDeployCollectiblesProcessor(rpcClient, transactor) + ret = append(ret, communityDeployCollectibles) + + communityDeployOwnerToken := pathprocessor.NewCommunityDeployOwnerTokenProcessor(rpcClient, transactor) + ret = append(ret, communityDeployOwnerToken) + + communityMintTokens := pathprocessor.NewCommunityMintTokensProcessor(rpcClient, transactor) + ret = append(ret, communityMintTokens) + + communityRemoteBurn := pathprocessor.NewCommunityRemoteBurnProcessor(rpcClient, transactor) + ret = append(ret, communityRemoteBurn) + + communitySetSignerPubKey := pathprocessor.NewCommunitySetSignerPubKeyProcessor(rpcClient, transactor) + ret = append(ret, communitySetSignerPubKey) + return ret } diff --git a/services/wallet/token/token.go b/services/wallet/token/token.go index fa47b7b565f..9e3f77a7fa1 100644 --- a/services/wallet/token/token.go +++ b/services/wallet/token/token.go @@ -28,6 +28,7 @@ import ( "github.com/status-im/status-go/multiaccounts/accounts" "github.com/status-im/status-go/params" "github.com/status-im/status-go/protocol/communities/token" + "github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/rpc" "github.com/status-im/status-go/rpc/network" "github.com/status-im/status-go/server" @@ -623,6 +624,20 @@ func (tm *Manager) DiscoverToken(ctx context.Context, chainID uint64, address co }, nil } +func (tm *Manager) GetCommunityTokenType(chainID uint64, tokenContractAddress string) (protobuf.CommunityTokenType, error) { + if tm.communityTokensDB != nil { + return tm.communityTokensDB.GetTokenType(chainID, tokenContractAddress) + } + return protobuf.CommunityTokenType_UNKNOWN_TOKEN_TYPE, nil +} + +func (tm *Manager) GetCommunityTokenPrivilegesLevel(chainID uint64, tokenContractAddress string) (token.PrivilegesLevel, error) { + if tm.communityTokensDB != nil { + return tm.communityTokensDB.GetTokenPrivilegesLevel(chainID, tokenContractAddress) + } + return token.CommunityLevel, nil +} + func (tm *Manager) getTokensFromDB(query string, args ...any) ([]*Token, error) { communityTokens := []*token.CommunityToken{} if tm.communityTokensDB != nil { diff --git a/services/wallet/transfer/transaction_manager_route.go b/services/wallet/transfer/transaction_manager_route.go index 4330e1f15db..004dbaee3c7 100644 --- a/services/wallet/transfer/transaction_manager_route.go +++ b/services/wallet/transfer/transaction_manager_route.go @@ -22,15 +22,6 @@ import ( "github.com/status-im/status-go/transactions" ) -type BuildRouteExtraParams struct { - AddressFrom common.Address - AddressTo common.Address - Username string - PublicKey string - PackID *big.Int - SlippagePercentage float32 -} - func (tm *TransactionManager) ClearLocalRouterTransactionsData() { tm.routerTransactions = nil } @@ -125,40 +116,32 @@ func buildApprovalTxForPath(transactor transactions.TransactorIface, path *route }, nil } -func buildTxForPath(transactor transactions.TransactorIface, path *routes.Path, pathProcessors map[string]pathprocessor.PathProcessor, - usedNonces map[uint64]int64, signer ethTypes.Signer, params BuildRouteExtraParams) (*wallettypes.TransactionData, error) { +func buildTxForPath(path *routes.Path, pathProcessors map[string]pathprocessor.PathProcessor, + usedNonces map[uint64]int64, signer ethTypes.Signer, processorInputParams *pathprocessor.ProcessorInputParams) (*wallettypes.TransactionData, error) { lastUsedNonce := int64(-1) if nonce, ok := usedNonces[path.FromChain.ChainID]; ok { lastUsedNonce = nonce } - processorInputParams := pathprocessor.ProcessorInputParams{ - FromAddr: params.AddressFrom, - ToAddr: params.AddressTo, - FromChain: path.FromChain, - ToChain: path.ToChain, - FromToken: path.FromToken, - ToToken: path.ToToken, - AmountIn: path.AmountIn.ToInt(), - AmountOut: path.AmountOut.ToInt(), - Username: params.Username, - PublicKey: params.PublicKey, - PackID: params.PackID, - } + // update processor input params for the current path + processorInputParams.FromChain = path.FromChain + processorInputParams.ToChain = path.ToChain + processorInputParams.FromToken = path.FromToken + processorInputParams.ToToken = path.ToToken + processorInputParams.AmountIn = path.AmountIn.ToInt() + processorInputParams.AmountOut = path.AmountOut.ToInt() - data, err := pathProcessors[path.ProcessorName].PackTxInputData(processorInputParams) + data, err := pathProcessors[path.ProcessorName].PackTxInputData(*processorInputParams) if err != nil { return nil, err } - addrTo := types.Address(params.AddressTo) sendArgs := &wallettypes.SendTxArgs{ Version: wallettypes.SendTxArgsVersion1, // tx fields - From: types.Address(params.AddressFrom), - To: &addrTo, + From: types.Address(processorInputParams.FromAddr), Value: path.AmountIn, Data: data, Gas: (*hexutil.Uint64)(&path.TxGasAmount), @@ -170,8 +153,16 @@ func buildTxForPath(transactor transactions.TransactorIface, path *routes.Path, ValueOut: path.AmountOut, FromChainID: path.FromChain.ChainID, ToChainID: path.ToChain.ChainID, - SlippagePercentage: params.SlippagePercentage, + SlippagePercentage: processorInputParams.SlippagePercentage, } + + isContractDeployment := path.ProcessorName == pathProcessorCommon.ProcessorCommunityDeployCollectiblesName || + path.ProcessorName == pathProcessorCommon.ProcessorCommunityDeployAssetsName + if !isContractDeployment { + addrTo := types.Address(processorInputParams.ToAddr) + sendArgs.To = &addrTo + } + if path.FromToken != nil { sendArgs.FromTokenID = path.FromToken.Symbol sendArgs.ToContractAddress = types.Address(path.FromToken.Address) @@ -193,6 +184,9 @@ func buildTxForPath(transactor transactions.TransactorIface, path *routes.Path, toContractAddr := types.Address(path.FromToken.Address) sendArgs.To = &toContractAddr } + } else if path.ProcessorName == pathProcessorCommon.ProcessorCommunityDeployOwnerTokenName { // special handling for community related txs, tokenID for those txs is ETH + toContractAddr := types.Address(*path.UsedContractAddress) + sendArgs.To = &toContractAddr } } if path.ToToken != nil { @@ -214,12 +208,12 @@ func buildTxForPath(transactor transactions.TransactorIface, path *routes.Path, } func (tm *TransactionManager) BuildTransactionsFromRoute(route routes.Route, pathProcessors map[string]pathprocessor.PathProcessor, - params BuildRouteExtraParams) (*responses.SigningDetails, uint64, uint64, error) { + processorInputParams *pathprocessor.ProcessorInputParams) (*responses.SigningDetails, uint64, uint64, error) { if len(route) == 0 { return nil, 0, 0, ErrNoRoute } - accFrom, err := tm.accountsDB.GetAccountByAddress(types.Address(params.AddressFrom)) + accFrom, err := tm.accountsDB.GetAccountByAddress(types.Address(processorInputParams.FromAddr)) if err != nil { return nil, 0, 0, err } @@ -244,7 +238,7 @@ func (tm *TransactionManager) BuildTransactionsFromRoute(route routes.Route, pat // always check for approval tx first for the path and build it if needed if path.ApprovalRequired && !tm.ApprovalPlacedForPath(path.ProcessorName) { - txDetails.ApprovalTxData, err = buildApprovalTxForPath(tm.transactor, path, params.AddressFrom, usedNonces, signer) + txDetails.ApprovalTxData, err = buildApprovalTxForPath(tm.transactor, path, processorInputParams.FromAddr, usedNonces, signer) if err != nil { return nil, path.FromChain.ChainID, path.ToChain.ChainID, err } @@ -257,7 +251,7 @@ func (tm *TransactionManager) BuildTransactionsFromRoute(route routes.Route, pat } // build tx for the path - txDetails.TxData, err = buildTxForPath(tm.transactor, path, pathProcessors, usedNonces, signer, params) + txDetails.TxData, err = buildTxForPath(path, pathProcessors, usedNonces, signer, processorInputParams) if err != nil { return nil, path.FromChain.ChainID, path.ToChain.ChainID, err } @@ -352,6 +346,11 @@ func addSignatureAndSendTransaction( txData.TxArgs.MultiTransactionID = multiTransactionID + if txWithSignature.To() == nil { + toAddr := crypto.CreateAddress(txData.TxArgs.From, txData.Tx.Nonce()) + txData.TxArgs.To = &toAddr + } + return responses.NewRouterSentTransaction(txData.TxArgs, txData.SentHash, isApproval), nil } diff --git a/signal/events_community_tokens.go b/signal/events_community_tokens.go index f89215f82b4..88631ee0c03 100644 --- a/signal/events_community_tokens.go +++ b/signal/events_community_tokens.go @@ -18,25 +18,25 @@ const ( ) type CommunityTokenTransactionSignal struct { - TransactionType string `json:"transactionType"` - Success bool `json:"success"` // transaction's status - Hash common.Hash `json:"hash"` // transaction hash - CommunityToken *token.CommunityToken `json:"communityToken,omitempty"` // community token changed by transaction - OwnerToken *token.CommunityToken `json:"ownerToken,omitempty"` // owner token emitted by deployment transaction - MasterToken *token.CommunityToken `json:"masterToken,omitempty"` // master token emitted by deployment transaction - ErrorString string `json:"errorString"` // information about failed operation + SendType int `json:"sendType"` + Success bool `json:"success"` // transaction's status + Hash common.Hash `json:"hash"` // transaction hash + CommunityToken *token.CommunityToken `json:"communityToken,omitempty"` // community token changed by transaction + OwnerToken *token.CommunityToken `json:"ownerToken,omitempty"` // owner token emitted by deployment transaction + MasterToken *token.CommunityToken `json:"masterToken,omitempty"` // master token emitted by deployment transaction + ErrorString string `json:"errorString"` // information about failed operation } -func SendCommunityTokenTransactionStatusSignal(transactionType string, success bool, hash common.Hash, +func SendCommunityTokenTransactionStatusSignal(sendType int, success bool, hash common.Hash, communityToken *token.CommunityToken, ownerToken *token.CommunityToken, masterToken *token.CommunityToken, errorString string) { send(EventCommunityTokenTransactionStatusChanged, CommunityTokenTransactionSignal{ - TransactionType: transactionType, - Success: success, - Hash: hash, - CommunityToken: communityToken, - OwnerToken: ownerToken, - MasterToken: masterToken, - ErrorString: errorString, + SendType: sendType, + Success: success, + Hash: hash, + CommunityToken: communityToken, + OwnerToken: ownerToken, + MasterToken: masterToken, + ErrorString: errorString, }) } diff --git a/transactions/transactor.go b/transactions/transactor.go index aefda2947a6..e9d99a38c94 100644 --- a/transactions/transactor.go +++ b/transactions/transactor.go @@ -191,13 +191,20 @@ func (t *Transactor) SendRawTransaction(chainID uint64, rawTx string) error { } func createPendingTransaction(from common.Address, symbol string, chainID uint64, multiTransactionID wallet_common.MultiTransactionIDType, tx *gethtypes.Transaction) (pTx *PendingTransaction) { + var toAddress common.Address + if tx.To() != nil { + toAddress = common.Address(*tx.To()) + } else { + toAddr := crypto.CreateAddress(types.Address(from), tx.Nonce()) + toAddress = common.Address(toAddr) + } pTx = &PendingTransaction{ Hash: tx.Hash(), Timestamp: uint64(time.Now().Unix()), Value: bigint.BigInt{Int: tx.Value()}, From: from, - To: *tx.To(), + To: toAddress, Nonce: tx.Nonce(), Data: string(tx.Data()), Type: WalletTransfer,