feat: Add hasActivity param to derived addresses (#2663)
Added functionality to find target address when 6th param in path is added for ex: "m'/44'/60'/0'/0/500" reperents the Address at the 500th index Added a api to get the Address derived from a private key
This commit is contained in:
parent
714c03c635
commit
15e5584ed2
|
@ -443,7 +443,7 @@ func (b *StatusNode) appmetricsService() common.StatusService {
|
||||||
|
|
||||||
func (b *StatusNode) walletService(accountsDB *accounts.Database, accountsFeed *event.Feed, openseaAPIKey string) common.StatusService {
|
func (b *StatusNode) walletService(accountsDB *accounts.Database, accountsFeed *event.Feed, openseaAPIKey string) common.StatusService {
|
||||||
if b.walletSrvc == nil {
|
if b.walletSrvc == nil {
|
||||||
b.walletSrvc = wallet.NewService(b.appDB, accountsDB, b.rpcClient, accountsFeed, openseaAPIKey)
|
b.walletSrvc = wallet.NewService(b.appDB, accountsDB, b.rpcClient, accountsFeed, openseaAPIKey, b.gethAccountManager)
|
||||||
}
|
}
|
||||||
return b.walletSrvc
|
return b.walletSrvc
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,13 +32,6 @@ type API struct {
|
||||||
feed *event.Feed
|
feed *event.Feed
|
||||||
}
|
}
|
||||||
|
|
||||||
type DerivedAddress struct {
|
|
||||||
Address common.Address `json:"address"`
|
|
||||||
Path string `json:"path"`
|
|
||||||
HasActivity bool `json:"hasActivity"`
|
|
||||||
AlreadyCreated bool `json:"alreadyCreated"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *API) SaveAccounts(ctx context.Context, accounts []accounts.Account) error {
|
func (api *API) SaveAccounts(ctx context.Context, accounts []accounts.Account) error {
|
||||||
log.Info("[AccountsAPI::SaveAccounts]")
|
log.Info("[AccountsAPI::SaveAccounts]")
|
||||||
err := api.db.SaveAccounts(accounts)
|
err := api.db.SaveAccounts(accounts)
|
||||||
|
@ -201,26 +194,6 @@ func (api *API) GenerateAccountWithDerivedPath(
|
||||||
return api.generateAccount(ctx, password, name, color, emoji, path, derivedFrom)
|
return api.generateAccount(ctx, password, name, color, emoji, path, derivedFrom)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) GetDerivedAddressesForPath(password string, derivedFrom string, path string, pageSize int, pageNumber int) ([]*DerivedAddress, error) {
|
|
||||||
info, err := api.manager.AccountsGenerator().LoadAccount(derivedFrom, password)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return api.getDerivedAddresses(info.ID, path, pageSize, pageNumber)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *API) GetDerivedAddressesForMenominicWithPath(mnemonic string, path string, pageSize int, pageNumber int) ([]*DerivedAddress, error) {
|
|
||||||
mnemonicNoExtraSpaces := strings.Join(strings.Fields(mnemonic), " ")
|
|
||||||
|
|
||||||
info, err := api.manager.AccountsGenerator().ImportMnemonic(mnemonicNoExtraSpaces, "")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return api.getDerivedAddresses(info.ID, path, pageSize, pageNumber)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *API) verifyPassword(password string) error {
|
func (api *API) verifyPassword(password string) error {
|
||||||
address, err := api.db.GetChatAddress()
|
address, err := api.db.GetChatAddress()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -230,49 +203,6 @@ func (api *API) verifyPassword(password string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) getDerivedAddresses(id string, path string, pageSize int, pageNumber int) ([]*DerivedAddress, error) {
|
|
||||||
addedAccounts, err := api.db.GetAccounts()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
derivedAddresses := make([]*DerivedAddress, 0)
|
|
||||||
|
|
||||||
if pageNumber <= 0 || pageSize <= 0 {
|
|
||||||
return nil, fmt.Errorf("pageSize and pageNumber should be greater than 0")
|
|
||||||
}
|
|
||||||
|
|
||||||
var startIndex = ((pageNumber - 1) * pageSize)
|
|
||||||
var endIndex = (pageNumber * pageSize)
|
|
||||||
|
|
||||||
for i := startIndex; i < endIndex; i++ {
|
|
||||||
derivedPath := fmt.Sprint(path, "/", i)
|
|
||||||
|
|
||||||
info, err := api.manager.AccountsGenerator().DeriveAddresses(id, []string{derivedPath})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
alreadyExists := false
|
|
||||||
for _, account := range addedAccounts {
|
|
||||||
if types.Address(common.HexToAddress(info[derivedPath].Address)) == account.Address {
|
|
||||||
alreadyExists = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
address := &DerivedAddress{
|
|
||||||
Address: common.HexToAddress(info[derivedPath].Address),
|
|
||||||
Path: derivedPath,
|
|
||||||
HasActivity: false,
|
|
||||||
AlreadyCreated: alreadyExists,
|
|
||||||
}
|
|
||||||
|
|
||||||
derivedAddresses = append(derivedAddresses, address)
|
|
||||||
}
|
|
||||||
return derivedAddresses, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *API) addAccountWithMnemonic(
|
func (api *API) addAccountWithMnemonic(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
mnemonic string,
|
mnemonic string,
|
||||||
|
|
|
@ -2,11 +2,16 @@ package wallet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
"github.com/status-im/status-go/params"
|
"github.com/status-im/status-go/params"
|
||||||
|
"github.com/status-im/status-go/services/wallet/async"
|
||||||
"github.com/status-im/status-go/services/wallet/chain"
|
"github.com/status-im/status-go/services/wallet/chain"
|
||||||
"github.com/status-im/status-go/services/wallet/transfer"
|
"github.com/status-im/status-go/services/wallet/transfer"
|
||||||
)
|
)
|
||||||
|
@ -30,6 +35,13 @@ func (api *API) GetWallet(ctx context.Context, chainIDs []uint64) (*Wallet, erro
|
||||||
return api.r.GetWallet(ctx, chainIDs)
|
return api.r.GetWallet(ctx, chainIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DerivedAddress struct {
|
||||||
|
Address common.Address `json:"address"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
HasActivity bool `json:"hasActivity"`
|
||||||
|
AlreadyCreated bool `json:"alreadyCreated"`
|
||||||
|
}
|
||||||
|
|
||||||
// SetInitialBlocksRange sets initial blocks range
|
// SetInitialBlocksRange sets initial blocks range
|
||||||
func (api *API) SetInitialBlocksRange(ctx context.Context) error {
|
func (api *API) SetInitialBlocksRange(ctx context.Context) error {
|
||||||
return api.s.transferController.SetInitialBlocksRange([]uint64{api.s.rpcClient.UpstreamChainID})
|
return api.s.transferController.SetInitialBlocksRange([]uint64{api.s.rpcClient.UpstreamChainID})
|
||||||
|
@ -303,3 +315,144 @@ func (api *API) GetSuggestedFees(ctx context.Context, chainID uint64) (*Suggeste
|
||||||
log.Debug("call to GetSuggestedFees")
|
log.Debug("call to GetSuggestedFees")
|
||||||
return api.s.feesManager.suggestedFees(ctx, chainID)
|
return api.s.feesManager.suggestedFees(ctx, chainID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *API) GetDerivedAddressesForPath(ctx context.Context, password string, derivedFrom string, path string, pageSize int, pageNumber int) ([]*DerivedAddress, error) {
|
||||||
|
info, err := api.s.gethManager.AccountsGenerator().LoadAccount(derivedFrom, password)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return api.getDerivedAddresses(ctx, info.ID, path, pageSize, pageNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *API) GetDerivedAddressesForMenominicWithPath(ctx context.Context, mnemonic string, path string, pageSize int, pageNumber int) ([]*DerivedAddress, error) {
|
||||||
|
mnemonicNoExtraSpaces := strings.Join(strings.Fields(mnemonic), " ")
|
||||||
|
|
||||||
|
info, err := api.s.gethManager.AccountsGenerator().ImportMnemonic(mnemonicNoExtraSpaces, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return api.getDerivedAddresses(ctx, info.ID, path, pageSize, pageNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *API) GetDerivedAddressForPrivateKey(ctx context.Context, privateKey string) ([]*DerivedAddress, error) {
|
||||||
|
var derivedAddresses = make([]*DerivedAddress, 0)
|
||||||
|
info, err := api.s.gethManager.AccountsGenerator().ImportPrivateKey(privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return derivedAddresses, err
|
||||||
|
}
|
||||||
|
|
||||||
|
addressExists, err := api.s.accountsDB.AddressExists(types.Address(common.HexToAddress(info.Address)))
|
||||||
|
if err != nil {
|
||||||
|
return derivedAddresses, err
|
||||||
|
}
|
||||||
|
if addressExists {
|
||||||
|
return derivedAddresses, fmt.Errorf("account already exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
transactions, err := api.s.transferController.GetTransfersByAddress(ctx, api.s.rpcClient.UpstreamChainID, common.HexToAddress(info.Address), nil, (*hexutil.Big)(big.NewInt(1)), false)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return derivedAddresses, err
|
||||||
|
}
|
||||||
|
|
||||||
|
hasActivity := int64(len(transactions)) > 0
|
||||||
|
|
||||||
|
derivedAddress := &DerivedAddress{
|
||||||
|
Address: common.HexToAddress(info.Address),
|
||||||
|
Path: "",
|
||||||
|
HasActivity: hasActivity,
|
||||||
|
AlreadyCreated: addressExists,
|
||||||
|
}
|
||||||
|
derivedAddresses = append(derivedAddresses, derivedAddress)
|
||||||
|
|
||||||
|
return derivedAddresses, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *API) getDerivedAddresses(ctx context.Context, id string, path string, pageSize int, pageNumber int) ([]*DerivedAddress, error) {
|
||||||
|
var (
|
||||||
|
group = async.NewAtomicGroup(ctx)
|
||||||
|
derivedAddresses = make([]*DerivedAddress, 0)
|
||||||
|
unorderedDerivedAddresses = map[int]*DerivedAddress{}
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
splitPathValues := strings.Split(path, "/")
|
||||||
|
if len(splitPathValues) == 6 {
|
||||||
|
derivedAddress, err := api.getDerivedAddress(id, path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
derivedAddresses = append(derivedAddresses, derivedAddress)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if pageNumber <= 0 || pageSize <= 0 {
|
||||||
|
return nil, fmt.Errorf("pageSize and pageNumber should be greater than 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
var startIndex = ((pageNumber - 1) * pageSize)
|
||||||
|
var endIndex = (pageNumber * pageSize)
|
||||||
|
|
||||||
|
for i := startIndex; i < endIndex; i++ {
|
||||||
|
derivedPath := fmt.Sprint(path, "/", i)
|
||||||
|
index := i
|
||||||
|
group.Add(func(parent context.Context) error {
|
||||||
|
derivedAddress, err := api.getDerivedAddress(id, derivedPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
unorderedDerivedAddresses[index] = derivedAddress
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-group.WaitAsync():
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
}
|
||||||
|
for i := startIndex; i < endIndex; i++ {
|
||||||
|
derivedAddresses = append(derivedAddresses, unorderedDerivedAddresses[i])
|
||||||
|
}
|
||||||
|
err = group.Error()
|
||||||
|
}
|
||||||
|
return derivedAddresses, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *API) getDerivedAddress(id string, derivedPath string) (*DerivedAddress, error) {
|
||||||
|
addedAccounts, err := api.s.accountsDB.GetAccounts()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := api.s.gethManager.AccountsGenerator().DeriveAddresses(id, []string{derivedPath})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
alreadyExists := false
|
||||||
|
for _, account := range addedAccounts {
|
||||||
|
if types.Address(common.HexToAddress(info[derivedPath].Address)) == account.Address {
|
||||||
|
alreadyExists = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ctx context.Context
|
||||||
|
transactions, err := api.s.transferController.GetTransfersByAddress(ctx, api.s.rpcClient.UpstreamChainID, common.HexToAddress(info[derivedPath].Address), nil, (*hexutil.Big)(big.NewInt(1)), false)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
hasActivity := int64(len(transactions)) > 0
|
||||||
|
|
||||||
|
address := &DerivedAddress{
|
||||||
|
Address: common.HexToAddress(info[derivedPath].Address),
|
||||||
|
Path: derivedPath,
|
||||||
|
HasActivity: hasActivity,
|
||||||
|
AlreadyCreated: alreadyExists,
|
||||||
|
}
|
||||||
|
|
||||||
|
return address, nil
|
||||||
|
}
|
||||||
|
|
|
@ -8,13 +8,14 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
gethrpc "github.com/ethereum/go-ethereum/rpc"
|
gethrpc "github.com/ethereum/go-ethereum/rpc"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/account"
|
||||||
"github.com/status-im/status-go/multiaccounts/accounts"
|
"github.com/status-im/status-go/multiaccounts/accounts"
|
||||||
"github.com/status-im/status-go/rpc"
|
"github.com/status-im/status-go/rpc"
|
||||||
"github.com/status-im/status-go/services/wallet/transfer"
|
"github.com/status-im/status-go/services/wallet/transfer"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewService initializes service instance.
|
// NewService initializes service instance.
|
||||||
func NewService(db *sql.DB, accountsDB *accounts.Database, rpcClient *rpc.Client, accountFeed *event.Feed, openseaAPIKey string) *Service {
|
func NewService(db *sql.DB, accountsDB *accounts.Database, rpcClient *rpc.Client, accountFeed *event.Feed, openseaAPIKey string, gethManager *account.GethManager) *Service {
|
||||||
cryptoOnRampManager := NewCryptoOnRampManager(&CryptoOnRampOptions{
|
cryptoOnRampManager := NewCryptoOnRampManager(&CryptoOnRampOptions{
|
||||||
dataSourceType: DataSourceStatic,
|
dataSourceType: DataSourceStatic,
|
||||||
})
|
})
|
||||||
|
@ -36,6 +37,7 @@ func NewService(db *sql.DB, accountsDB *accounts.Database, rpcClient *rpc.Client
|
||||||
cryptoOnRampManager: cryptoOnRampManager,
|
cryptoOnRampManager: cryptoOnRampManager,
|
||||||
openseaAPIKey: openseaAPIKey,
|
openseaAPIKey: openseaAPIKey,
|
||||||
feesManager: &FeeManager{rpcClient},
|
feesManager: &FeeManager{rpcClient},
|
||||||
|
gethManager: gethManager,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,6 +55,7 @@ type Service struct {
|
||||||
feesManager *FeeManager
|
feesManager *FeeManager
|
||||||
started bool
|
started bool
|
||||||
openseaAPIKey string
|
openseaAPIKey string
|
||||||
|
gethManager *account.GethManager
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start signals transmitter.
|
// Start signals transmitter.
|
||||||
|
|
Loading…
Reference in New Issue