github.com/status-im/status-go@v1.1.0/services/accounts/accounts.go (about)

     1  package accounts
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"strings"
     7  
     8  	"github.com/ethereum/go-ethereum/common"
     9  	"github.com/ethereum/go-ethereum/event"
    10  
    11  	"github.com/ethereum/go-ethereum/log"
    12  	"github.com/status-im/status-go/account"
    13  	"github.com/status-im/status-go/eth-node/types"
    14  	"github.com/status-im/status-go/multiaccounts/accounts"
    15  	walletsettings "github.com/status-im/status-go/multiaccounts/settings_wallet"
    16  	"github.com/status-im/status-go/params"
    17  	"github.com/status-im/status-go/protocol"
    18  	"github.com/status-im/status-go/services/accounts/accountsevent"
    19  )
    20  
    21  func NewAccountsAPI(manager *account.GethManager, config *params.NodeConfig, db *accounts.Database, feed *event.Feed, messenger **protocol.Messenger) *API {
    22  	return &API{manager, config, db, feed, messenger}
    23  }
    24  
    25  // API is class with methods available over RPC.
    26  type API struct {
    27  	manager   *account.GethManager
    28  	config    *params.NodeConfig
    29  	db        *accounts.Database
    30  	feed      *event.Feed
    31  	messenger **protocol.Messenger
    32  }
    33  
    34  type DerivedAddress struct {
    35  	Address        common.Address `json:"address"`
    36  	Path           string         `json:"path"`
    37  	HasActivity    bool           `json:"hasActivity"`
    38  	AlreadyCreated bool           `json:"alreadyCreated"`
    39  }
    40  
    41  func (api *API) SaveAccount(ctx context.Context, account *accounts.Account) error {
    42  	log.Info("[AccountsAPI::SaveAccount]")
    43  	err := (*api.messenger).SaveOrUpdateAccount(account)
    44  	if err != nil {
    45  		return err
    46  	}
    47  
    48  	api.feed.Send(accountsevent.Event{
    49  		Type:     accountsevent.EventTypeAdded,
    50  		Accounts: []common.Address{common.Address(account.Address)},
    51  	})
    52  	return nil
    53  }
    54  
    55  // Setting `Keypair` without `Accounts` will update keypair only, `Keycards` won't be saved/updated this way.
    56  func (api *API) SaveKeypair(ctx context.Context, keypair *accounts.Keypair) error {
    57  	log.Info("[AccountsAPI::SaveKeypair]")
    58  	err := (*api.messenger).SaveOrUpdateKeypair(keypair)
    59  	if err != nil {
    60  		return err
    61  	}
    62  
    63  	commonAddresses := []common.Address{}
    64  	for _, acc := range keypair.Accounts {
    65  		commonAddresses = append(commonAddresses, common.Address(acc.Address))
    66  	}
    67  
    68  	api.feed.Send(accountsevent.Event{
    69  		Type:     accountsevent.EventTypeAdded,
    70  		Accounts: commonAddresses,
    71  	})
    72  	return nil
    73  }
    74  
    75  func (api *API) HasPairedDevices(ctx context.Context) bool {
    76  	return (*api.messenger).HasPairedDevices()
    77  }
    78  
    79  // Setting `Keypair` without `Accounts` will update keypair only.
    80  func (api *API) UpdateKeypairName(ctx context.Context, keyUID string, name string) error {
    81  	return (*api.messenger).UpdateKeypairName(keyUID, name)
    82  }
    83  
    84  func (api *API) MoveWalletAccount(ctx context.Context, fromPosition int64, toPosition int64) error {
    85  	return (*api.messenger).MoveWalletAccount(fromPosition, toPosition)
    86  }
    87  
    88  func (api *API) UpdateTokenPreferences(ctx context.Context, preferences []walletsettings.TokenPreferences) error {
    89  	return (*api.messenger).UpdateTokenPreferences(preferences)
    90  }
    91  
    92  func (api *API) GetTokenPreferences(ctx context.Context) ([]walletsettings.TokenPreferences, error) {
    93  	return (*api.messenger).GetTokenPreferences()
    94  }
    95  
    96  func (api *API) UpdateCollectiblePreferences(ctx context.Context, preferences []walletsettings.CollectiblePreferences) error {
    97  	return (*api.messenger).UpdateCollectiblePreferences(preferences)
    98  }
    99  
   100  func (api *API) GetCollectiblePreferences(ctx context.Context) ([]walletsettings.CollectiblePreferences, error) {
   101  	return (*api.messenger).GetCollectiblePreferences()
   102  }
   103  
   104  func (api *API) GetAccounts(ctx context.Context) ([]*accounts.Account, error) {
   105  	return api.db.GetActiveAccounts()
   106  }
   107  
   108  func (api *API) GetWatchOnlyAccounts(ctx context.Context) ([]*accounts.Account, error) {
   109  	return api.db.GetActiveWatchOnlyAccounts()
   110  }
   111  
   112  func (api *API) GetKeypairs(ctx context.Context) ([]*accounts.Keypair, error) {
   113  	return api.db.GetActiveKeypairs()
   114  }
   115  
   116  func (api *API) GetAccountByAddress(ctx context.Context, address types.Address) (*accounts.Account, error) {
   117  	return api.db.GetAccountByAddress(address)
   118  }
   119  
   120  func (api *API) GetKeypairByKeyUID(ctx context.Context, keyUID string) (*accounts.Keypair, error) {
   121  	return api.db.GetKeypairByKeyUID(keyUID)
   122  }
   123  
   124  func (api *API) DeleteAccount(ctx context.Context, address types.Address) error {
   125  	err := (*api.messenger).DeleteAccount(address)
   126  	if err != nil {
   127  		return err
   128  	}
   129  
   130  	api.feed.Send(accountsevent.Event{
   131  		Type:     accountsevent.EventTypeRemoved,
   132  		Accounts: []common.Address{common.Address(address)},
   133  	})
   134  
   135  	return nil
   136  }
   137  
   138  func (api *API) DeleteKeypair(ctx context.Context, keyUID string) error {
   139  	keypair, err := api.db.GetKeypairByKeyUID(keyUID)
   140  	if err != nil {
   141  		return err
   142  	}
   143  
   144  	err = (*api.messenger).DeleteKeypair(keyUID)
   145  	if err != nil {
   146  		return err
   147  	}
   148  
   149  	var addresses []common.Address
   150  	for _, acc := range keypair.Accounts {
   151  		if acc.Chat {
   152  			continue
   153  		}
   154  		addresses = append(addresses, common.Address(acc.Address))
   155  	}
   156  
   157  	api.feed.Send(accountsevent.Event{
   158  		Type:     accountsevent.EventTypeRemoved,
   159  		Accounts: addresses,
   160  	})
   161  
   162  	return nil
   163  }
   164  
   165  func (api *API) AddKeypair(ctx context.Context, password string, keypair *accounts.Keypair) error {
   166  	if len(keypair.KeyUID) == 0 {
   167  		return errors.New("`KeyUID` field of a keypair must be set")
   168  	}
   169  
   170  	if len(keypair.Name) == 0 {
   171  		return errors.New("`Name` field of a keypair must be set")
   172  	}
   173  
   174  	if len(keypair.Type) == 0 {
   175  		return errors.New("`Type` field of a keypair must be set")
   176  	}
   177  
   178  	if keypair.Type != accounts.KeypairTypeKey {
   179  		if len(keypair.DerivedFrom) == 0 {
   180  			return errors.New("`DerivedFrom` field of a keypair must be set")
   181  		}
   182  	}
   183  
   184  	for _, acc := range keypair.Accounts {
   185  		if acc.KeyUID != keypair.KeyUID {
   186  			return errors.New("all accounts of a keypair must have the same `KeyUID` as keypair key uid")
   187  		}
   188  
   189  		err := api.checkAccountValidity(acc)
   190  		if err != nil {
   191  			return err
   192  		}
   193  	}
   194  
   195  	err := api.SaveKeypair(ctx, keypair)
   196  	if err != nil {
   197  		return err
   198  	}
   199  
   200  	if len(password) > 0 {
   201  		for _, acc := range keypair.Accounts {
   202  			if acc.Type == accounts.AccountTypeGenerated || acc.Type == accounts.AccountTypeSeed {
   203  				err = api.createKeystoreFileForAccount(keypair.DerivedFrom, password, acc)
   204  				if err != nil {
   205  					return err
   206  				}
   207  			}
   208  		}
   209  	}
   210  
   211  	return nil
   212  }
   213  
   214  // RemainingAccountCapacity returns the number of accounts that can be added.
   215  func (api *API) RemainingAccountCapacity(ctx context.Context) (int, error) {
   216  	return (*api.messenger).RemainingAccountCapacity()
   217  }
   218  
   219  // RemainingKeypairCapacity returns the number of keypairs that can be added.
   220  func (api *API) RemainingKeypairCapacity(ctx context.Context) (int, error) {
   221  	return (*api.messenger).RemainingKeypairCapacity()
   222  }
   223  
   224  // RemainingWatchOnlyAccountCapacity returns the number of watch-only accounts that can be added.
   225  func (api *API) RemainingWatchOnlyAccountCapacity(ctx context.Context) (int, error) {
   226  	return (*api.messenger).RemainingWatchOnlyAccountCapacity()
   227  }
   228  
   229  func (api *API) checkAccountValidity(account *accounts.Account) error {
   230  	if len(account.Address) == 0 {
   231  		return errors.New("`Address` field of an account must be set")
   232  	}
   233  
   234  	if len(account.Type) == 0 {
   235  		return errors.New("`Type` field of an account must be set")
   236  	}
   237  
   238  	if account.Wallet || account.Chat {
   239  		return errors.New("default wallet and chat account cannot be added this way")
   240  	}
   241  
   242  	if len(account.Name) == 0 {
   243  		return errors.New("`Name` field of an account must be set")
   244  	}
   245  
   246  	if len(account.Emoji) == 0 {
   247  		return errors.New("`Emoji` field of an account must be set")
   248  	}
   249  
   250  	if len(account.ColorID) == 0 {
   251  		return errors.New("`ColorID` field of an account must be set")
   252  	}
   253  
   254  	if account.Type != accounts.AccountTypeWatch {
   255  
   256  		if len(account.KeyUID) == 0 {
   257  			return errors.New("`KeyUID` field of an account must be set")
   258  		}
   259  
   260  		if len(account.PublicKey) == 0 {
   261  			return errors.New("`PublicKey` field of an account must be set")
   262  		}
   263  
   264  		if account.Type != accounts.AccountTypeKey {
   265  			if len(account.Path) == 0 {
   266  				return errors.New("`Path` field of an account must be set")
   267  			}
   268  		}
   269  	}
   270  
   271  	addressExists, err := api.db.AddressExists(account.Address)
   272  	if err != nil {
   273  		return err
   274  	}
   275  
   276  	if addressExists {
   277  		return errors.New("account already exists")
   278  	}
   279  
   280  	return nil
   281  }
   282  
   283  func (api *API) createKeystoreFileForAccount(masterAddress string, password string, account *accounts.Account) error {
   284  	if account.Type != accounts.AccountTypeGenerated && account.Type != accounts.AccountTypeSeed {
   285  		return errors.New("cannot create keystore file if account is not of `generated` or `seed` type")
   286  	}
   287  	if masterAddress == "" {
   288  		return errors.New("cannot create keystore file if master address is empty")
   289  	}
   290  	if password == "" {
   291  		return errors.New("cannot create keystore file if password is empty")
   292  	}
   293  
   294  	info, err := api.manager.AccountsGenerator().LoadAccount(masterAddress, password)
   295  	if err != nil {
   296  		return err
   297  	}
   298  
   299  	_, err = api.manager.AccountsGenerator().StoreDerivedAccounts(info.ID, password, []string{account.Path})
   300  	return err
   301  }
   302  
   303  func (api *API) AddAccount(ctx context.Context, password string, account *accounts.Account) error {
   304  	err := api.checkAccountValidity(account)
   305  	if err != nil {
   306  		return err
   307  	}
   308  
   309  	if account.Type != accounts.AccountTypeWatch {
   310  		kp, err := api.db.GetKeypairByKeyUID(account.KeyUID)
   311  		if err != nil {
   312  			if err == accounts.ErrDbKeypairNotFound {
   313  				return errors.New("cannot add an account for an unknown keypair")
   314  			}
   315  			return err
   316  		}
   317  
   318  		// we need to create local keystore file only if password is provided and the account is being added is of
   319  		// "generated" or "seed" type.
   320  		if (account.Type == accounts.AccountTypeGenerated || account.Type == accounts.AccountTypeSeed) && len(password) > 0 {
   321  			err = api.createKeystoreFileForAccount(kp.DerivedFrom, password, account)
   322  			if err != nil {
   323  				return err
   324  			}
   325  		}
   326  	}
   327  
   328  	if account.Type == accounts.AccountTypeGenerated {
   329  		account.AddressWasNotShown = true
   330  	}
   331  
   332  	return api.SaveAccount(ctx, account)
   333  }
   334  
   335  // Imports a new private key and creates local keystore file.
   336  func (api *API) ImportPrivateKey(ctx context.Context, privateKey string, password string) error {
   337  	info, err := api.manager.AccountsGenerator().ImportPrivateKey(privateKey)
   338  	if err != nil {
   339  		return err
   340  	}
   341  
   342  	kp, err := api.db.GetKeypairByKeyUID(info.KeyUID)
   343  	if err != nil && err != accounts.ErrDbKeypairNotFound {
   344  		return err
   345  	}
   346  
   347  	if kp != nil {
   348  		return errors.New("provided private key was already imported")
   349  	}
   350  
   351  	_, err = api.manager.AccountsGenerator().StoreAccount(info.ID, password)
   352  	return err
   353  }
   354  
   355  // Creates all keystore files for a keypair and mark it in db as fully operable.
   356  func (api *API) MakePrivateKeyKeypairFullyOperable(ctx context.Context, privateKey string, password string) error {
   357  	info, err := api.manager.AccountsGenerator().ImportPrivateKey(privateKey)
   358  	if err != nil {
   359  		return err
   360  	}
   361  
   362  	kp, err := api.db.GetKeypairByKeyUID(info.KeyUID)
   363  	if err != nil {
   364  		return err
   365  	}
   366  
   367  	if kp == nil {
   368  		return errors.New("keypair for the provided private key is not known")
   369  	}
   370  
   371  	_, err = api.manager.AccountsGenerator().StoreAccount(info.ID, password)
   372  	if err != nil {
   373  		return err
   374  	}
   375  
   376  	return (*api.messenger).MarkKeypairFullyOperable(info.KeyUID)
   377  }
   378  
   379  func (api *API) MakePartiallyOperableAccoutsFullyOperable(ctx context.Context, password string) (addresses []types.Address, err error) {
   380  	profileKeypair, err := api.db.GetProfileKeypair()
   381  	if err != nil {
   382  		return
   383  	}
   384  
   385  	if !profileKeypair.MigratedToKeycard() && !api.VerifyPassword(password) {
   386  		err = errors.New("wrong password provided")
   387  		return
   388  	}
   389  
   390  	keypairs, err := api.db.GetActiveKeypairs()
   391  	if err != nil {
   392  		return
   393  	}
   394  
   395  	for _, kp := range keypairs {
   396  		for _, acc := range kp.Accounts {
   397  			if acc.Operable != accounts.AccountPartiallyOperable {
   398  				continue
   399  			}
   400  			err = api.createKeystoreFileForAccount(kp.DerivedFrom, password, acc)
   401  			if err != nil {
   402  				return
   403  			}
   404  			err = api.db.MarkAccountFullyOperable(acc.Address)
   405  			if err != nil {
   406  				return
   407  			}
   408  			addresses = append(addresses, acc.Address)
   409  		}
   410  	}
   411  	return
   412  }
   413  
   414  // Imports a new mnemonic and creates local keystore file.
   415  func (api *API) ImportMnemonic(ctx context.Context, mnemonic string, password string) error {
   416  	mnemonicNoExtraSpaces := strings.Join(strings.Fields(mnemonic), " ")
   417  
   418  	generatedAccountInfo, err := api.manager.AccountsGenerator().ImportMnemonic(mnemonicNoExtraSpaces, "")
   419  	if err != nil {
   420  		return err
   421  	}
   422  
   423  	kp, err := api.db.GetKeypairByKeyUID(generatedAccountInfo.KeyUID)
   424  	if err != nil && err != accounts.ErrDbKeypairNotFound {
   425  		return err
   426  	}
   427  
   428  	if kp != nil {
   429  		return errors.New("provided mnemonic was already imported, to add new account use `AddAccount` endpoint")
   430  	}
   431  
   432  	_, err = api.manager.AccountsGenerator().StoreAccount(generatedAccountInfo.ID, password)
   433  	return err
   434  }
   435  
   436  // Creates all keystore files for a keypair and mark it in db as fully operable.
   437  func (api *API) MakeSeedPhraseKeypairFullyOperable(ctx context.Context, mnemonic string, password string) error {
   438  	mnemonicNoExtraSpaces := strings.Join(strings.Fields(mnemonic), " ")
   439  
   440  	generatedAccountInfo, err := api.manager.AccountsGenerator().ImportMnemonic(mnemonicNoExtraSpaces, "")
   441  	if err != nil {
   442  		return err
   443  	}
   444  
   445  	kp, err := api.db.GetKeypairByKeyUID(generatedAccountInfo.KeyUID)
   446  	if err != nil {
   447  		return err
   448  	}
   449  
   450  	if kp == nil {
   451  		return errors.New("keypair for the provided seed phrase is not known")
   452  	}
   453  
   454  	_, err = api.manager.AccountsGenerator().StoreAccount(generatedAccountInfo.ID, password)
   455  	if err != nil {
   456  		return err
   457  	}
   458  
   459  	var paths []string
   460  	for _, acc := range kp.Accounts {
   461  		paths = append(paths, acc.Path)
   462  	}
   463  
   464  	_, err = api.manager.AccountsGenerator().StoreDerivedAccounts(generatedAccountInfo.ID, password, paths)
   465  	if err != nil {
   466  		return err
   467  	}
   468  
   469  	return (*api.messenger).MarkKeypairFullyOperable(generatedAccountInfo.KeyUID)
   470  }
   471  
   472  // Creates a random new mnemonic.
   473  func (api *API) GetRandomMnemonic(ctx context.Context) (string, error) {
   474  	return account.GetRandomMnemonic()
   475  }
   476  
   477  func (api *API) VerifyKeystoreFileForAccount(address types.Address, password string) bool {
   478  	_, err := api.manager.VerifyAccountPassword(api.config.KeyStoreDir, address.Hex(), password)
   479  	return err == nil
   480  }
   481  
   482  func (api *API) VerifyPassword(password string) bool {
   483  	address, err := api.db.GetChatAddress()
   484  	if err != nil {
   485  		return false
   486  	}
   487  	return api.VerifyKeystoreFileForAccount(address, password)
   488  }
   489  
   490  func (api *API) MigrateNonProfileKeycardKeypairToApp(ctx context.Context, mnemonic string, password string) error {
   491  	mnemonicNoExtraSpaces := strings.Join(strings.Fields(mnemonic), " ")
   492  
   493  	generatedAccountInfo, err := api.manager.AccountsGenerator().ImportMnemonic(mnemonicNoExtraSpaces, "")
   494  	if err != nil {
   495  		return err
   496  	}
   497  
   498  	kp, err := api.db.GetKeypairByKeyUID(generatedAccountInfo.KeyUID)
   499  	if err != nil {
   500  		return err
   501  	}
   502  
   503  	if kp.Type == accounts.KeypairTypeProfile {
   504  		return errors.New("cannot migrate profile keypair")
   505  	}
   506  
   507  	if !kp.MigratedToKeycard() {
   508  		return errors.New("keypair being migrated is not a keycard keypair")
   509  	}
   510  
   511  	profileKeypair, err := api.db.GetProfileKeypair()
   512  	if err != nil {
   513  		return err
   514  	}
   515  
   516  	if !profileKeypair.MigratedToKeycard() && !api.VerifyPassword(password) {
   517  		return errors.New("wrong password provided")
   518  	}
   519  
   520  	_, err = api.manager.AccountsGenerator().StoreAccount(generatedAccountInfo.ID, password)
   521  	if err != nil {
   522  		return err
   523  	}
   524  
   525  	for _, acc := range kp.Accounts {
   526  		err = api.createKeystoreFileForAccount(kp.DerivedFrom, password, acc)
   527  		if err != nil {
   528  			return err
   529  		}
   530  	}
   531  
   532  	// this will emit SyncKeypair message
   533  	return (*api.messenger).DeleteAllKeycardsWithKeyUID(ctx, generatedAccountInfo.KeyUID)
   534  }
   535  
   536  // If keypair is migrated from keycard to app, then `accountsComingFromKeycard` should be set to true, otherwise false.
   537  // If keycard is new `Position` will be determined and set by the backend and `KeycardLocked` will be set to false.
   538  // If keycard is already added, `Position` and `KeycardLocked` will be unchanged.
   539  func (api *API) SaveOrUpdateKeycard(ctx context.Context, keycard *accounts.Keycard, accountsComingFromKeycard bool) error {
   540  	if len(keycard.AccountsAddresses) == 0 {
   541  		return errors.New("cannot migrate a keypair without accounts")
   542  	}
   543  
   544  	kpDb, err := api.db.GetKeypairByKeyUID(keycard.KeyUID)
   545  	if err != nil {
   546  		if err == accounts.ErrDbKeypairNotFound {
   547  			return errors.New("cannot migrate an unknown keypair")
   548  		}
   549  		return err
   550  	}
   551  
   552  	err = (*api.messenger).SaveOrUpdateKeycard(ctx, keycard)
   553  	if err != nil {
   554  		return err
   555  	}
   556  
   557  	if !accountsComingFromKeycard {
   558  		// Once we migrate a keypair, corresponding keystore files need to be deleted
   559  		// if the keypair being migrated is not already migrated (in case user is creating a copy of an existing Keycard)
   560  		// and if keypair operability is different from non operable (otherwise there are not keystore files to be deleted).
   561  		if !kpDb.MigratedToKeycard() && kpDb.Operability() != accounts.AccountNonOperable {
   562  			for _, acc := range kpDb.Accounts {
   563  				if acc.Operable != accounts.AccountFullyOperable {
   564  					continue
   565  				}
   566  				err = api.manager.DeleteAccount(acc.Address)
   567  				if err != nil {
   568  					return err
   569  				}
   570  			}
   571  
   572  			err = api.manager.DeleteAccount(types.Address(common.HexToAddress(kpDb.DerivedFrom)))
   573  			if err != nil {
   574  				return err
   575  			}
   576  		}
   577  
   578  		err = (*api.messenger).MarkKeypairFullyOperable(keycard.KeyUID)
   579  		if err != nil {
   580  			return err
   581  		}
   582  	}
   583  
   584  	return nil
   585  }
   586  
   587  func (api *API) GetAllKnownKeycards(ctx context.Context) ([]*accounts.Keycard, error) {
   588  	return api.db.GetAllKnownKeycards()
   589  }
   590  
   591  func (api *API) GetKeycardsWithSameKeyUID(ctx context.Context, keyUID string) ([]*accounts.Keycard, error) {
   592  	return api.db.GetKeycardsWithSameKeyUID(keyUID)
   593  }
   594  
   595  func (api *API) GetKeycardByKeycardUID(ctx context.Context, keycardUID string) (*accounts.Keycard, error) {
   596  	return api.db.GetKeycardByKeycardUID(keycardUID)
   597  }
   598  
   599  func (api *API) SetKeycardName(ctx context.Context, keycardUID string, kpName string) error {
   600  	return (*api.messenger).SetKeycardName(ctx, keycardUID, kpName)
   601  }
   602  
   603  func (api *API) KeycardLocked(ctx context.Context, keycardUID string) error {
   604  	return (*api.messenger).KeycardLocked(ctx, keycardUID)
   605  }
   606  
   607  func (api *API) KeycardUnlocked(ctx context.Context, keycardUID string) error {
   608  	return (*api.messenger).KeycardUnlocked(ctx, keycardUID)
   609  }
   610  
   611  func (api *API) DeleteKeycardAccounts(ctx context.Context, keycardUID string, accountAddresses []types.Address) error {
   612  	return (*api.messenger).DeleteKeycardAccounts(ctx, keycardUID, accountAddresses)
   613  }
   614  
   615  func (api *API) DeleteKeycard(ctx context.Context, keycardUID string) error {
   616  	return (*api.messenger).DeleteKeycard(ctx, keycardUID)
   617  }
   618  
   619  func (api *API) DeleteAllKeycardsWithKeyUID(ctx context.Context, keyUID string) error {
   620  	return (*api.messenger).DeleteAllKeycardsWithKeyUID(ctx, keyUID)
   621  }
   622  
   623  func (api *API) UpdateKeycardUID(ctx context.Context, oldKeycardUID string, newKeycardUID string) error {
   624  	return (*api.messenger).UpdateKeycardUID(ctx, oldKeycardUID, newKeycardUID)
   625  }
   626  
   627  func (api *API) AddressWasShown(address types.Address) error {
   628  	return api.db.AddressWasShown(address)
   629  }
   630  
   631  func (api *API) GetNumOfAddressesToGenerateForKeypair(keyUID string) (uint64, error) {
   632  	return api.db.GetNumOfAddressesToGenerateForKeypair(keyUID)
   633  }
   634  
   635  func (api *API) ResolveSuggestedPathForKeypair(keyUID string) (string, error) {
   636  	return api.db.ResolveSuggestedPathForKeypair(keyUID)
   637  }