Skip to content

Latest commit

 

History

History
301 lines (232 loc) · 17.3 KB

how_to_add_new_wallet_type.md

File metadata and controls

301 lines (232 loc) · 17.3 KB

Guide to adding a new wallet type in Cake Wallet

Wallet Integration

N:B Throughout this guide, walletx refers to the specific wallet type you want to add. If you're adding BNB to CakeWallet, then walletx for you here is bnb.

Core Folder/Files Setup

  • Idenitify your core component/package (major project component), which would power the integration e.g web3dart, solana, onchain etc

  • Add a new entry to WalletType class in cw_core/wallet_type.dart.

  • Fill out the necessary information int he various functions in the files, concerning the wallet name, the native currency type, symbol etc.

  • Go to cw_core/lib/currency_for_wallet_type.dart, in the currencyForWalletType function, add a case for walletx, returning the native cryptocurrency for walletx.

  • If the cryptocurrency for walletx is not available among the default cryptocurrencies, add a new cryptocurrency entry in cw_core/lib/cryptocurrency.dart.

  • Add the newly created cryptocurrency name to the list named all in this file.

  • Create a package for the wallet specific integration, name it. cw_walletx

  • Add the following initial common files and replicate to fit the wallet

    • walletx_transaction_history.dart
    • walletx_transaction_info.dart
    • walletx_mnemonics_exception.dart
    • walletx_tokens.dart
    • walletx_wallet_service.dart:
    • walletx_wallet.dart
    • etc.
  • Add the code to run the code generation needed for the files in the cw_walletx package to the model_generator.sh script

       cd cw_walletx && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
    
  • Add the relevant dev_dependencies for generating the files also

    • build_runner
    • mobx_codegen
    • hive_generator

WalletX Proxy Setup

A Proxy class is used to communicate with the specific wallet package we have. Instead of directly making use of methods and parameters in cw_walletx within the lib directory, we use a proxy to access these data. All important functions, calls and interactions we want to make with our cw_walletx package would be defined and done through the proxy class. The class would define the import

  • Create a proxy folder titled walletx to handle the wallet operations. It would contain 2 files: cw_walletx.dart and walletx.dart.
  • cw_walletx.dart file would hold an implementation class containing major operations to be done in the lib directory. It serves as the link between the cw_walletx package and the rest of the codebase(lib directory files and folders).
  • walletx.dart would contain the abstract class highlighting the methods that would bring the functionalities and features in the cw_walletx package to the rest of the lib directory.
  • Add walletx.dart to .gitignore as we won’t be pushing it: lib/tron/tron.dart.
  • walletx.dart would always be generated based on the configure files we would be setting up in the next step.

Configuration Files Setup

  • Before we populate the field, head over to tool/configure.dart to setup the necessary configurations for the walletx proxy.

  • Define the output path, it’ll follow the format lib/walletx/walletx.dart.

  • Add the variable to check if walletx is to be activated

  • Define the function that would generate the abstract class for the proxy.(We will flesh out this function in the next steps).

  • Add the defined variable in step 2 to the generatePubspec and generateWalletTypes.

  • Next, modify the following functions:

    • generatePubspec function
    1. Add the parameters to the method params (i.e required bool hasWalletX)

    2. Define a variable to hold the entry for the pubspec.yaml file

       const cwWalletX = """
       cw_tron:
         path: ./cw_walletx
         """;
      
    3. Add an if block that takes in the passed parameter and adds the defined variable(inn the previous step) to the list of outputs

       if (hasWalletX) {
       	      output += '\n$cwWalletX’;
         }
      
    • generateWalletTypes function
    1. Add the parameters to the method params (i.e required bool hasWalletX)

    2. Add an if block to add the wallet type to the list of outputs this function generates

       if (hasWalletX) {
       		outputContent += '\tWalletType.walletx,\n’;
         }
      
  • Head over to scripts/android/pubspec.sh script, and modify the CONFIG_ARGS under $CAKEWALLET. Add "—walletx” to the end of the passed in params.

  • Repeat this in scripts/ios/app_config.sh and scripts/macos/app_config.sh

  • Open a terminal and cd into scripts/android/. Run the following commands to run setup configuration scripts(proxy class, add walletx to list of wallet types and add cw_walletx to pubspec).

      source ./app_env.sh cakewallet
    
      ./app_config.sh
    
      cd cw_walletx && flutter pub get && flutter packages pub run build_runner build
    
      flutter packages pub run build_runner build --delete-conflicting-outputs
    

Moving forward, our interactions with the cw_walletx package would be through the proxy class and its methods.

Pre-Wallet Creation for WalletX

  • Go to di.dart and locate the block to registerWalletService. In this, add the case to handle creating the WalletXWalletService

    case WalletType.walletx:
      return walletx!.createWalletXWalletService(_walletInfoSource);
    
  • Go to lib/view_model/wallet_new_vm.dart, in the getCredentials method, which gets the new wallet credentials for walletX add the case for the new wallet

    case WalletType.walletx:
      return walletx!.createWalletXNewWalletCredentials(name: name);
    

Node Setup

  • Before we can be able to successfully create a new wallet of wallet type walletx we need to setup the node that the wallet would use:

  • In the assets directory, create a new file and name it walletx_node_list.yml. This yml file would contain the details for nodes to be used for walletX. An example structure for each node entry

    uri: "api.nodeurl.io" 
    is_default: true
    useSSL: true
    

You can add as many node entries as desired.

  • Add the path to the yml file created to the pubspec_base.yaml file (“assets/walletx_node_list.yml”)

  • Go to lib/entities/node_list.dart, add a function to load the node entries we made in walletx_node_list.yml for walletx.

  • Name your function loadDefaultWalletXNodes(). The function would handle loading the yml file as a string and parsing it into a Node Object to be used within the app. Here’s a template for the function.

      Future<List<Node>> loadDefaultWalletXNodes() async {
        	final nodesRaw = await rootBundle.loadString('assets/tron_node_list.yml');
      	final loadedNodes = loadYaml(nodesRaw) as YamlList;
      	final nodes = <Node>[];
      	for (final raw in loadedNodes) {
      		if (raw is Map) {
      			final node = Node.fromMap(Map<String, Object>.from(raw));
      			node.type = WalletType.tron;
      			nodes.add(node);
      			}
      	}
         return nodes;
    

    }

  • Inside the resetToDefault function, call the function you created and add the result to the nodes result variable.

  • Go to lib/entities/default_settings_migration.dart file, we’ll be adding the following to the file.

  • At the top of the file, after the imports, define the default nodeUrl for wallet-name.

  • Next, write a function to fetch the node for this default uri you added above.

      Node? getWalletXDefaultNode({required Box<Node> nodes}) {
      	return nodes.values.firstWhereOrNull((Node node) => node.uriRaw == walletXDefaultNodeUri) ??
      		nodes.values.firstWhereOrNull((node) => node.type == WalletType.walletx);
      }
    
  • Next, write a function that will add the list of nodes we declared in the walletx_node_list.yml file to the Nodes Box, to be used in the app. Here’s the format for this function

    Future<void> addWalletXNodeList({required Box<Node> nodes}) async {
      final nodeList = await loadDefaultWalletXNodes();
      for (var node in nodeList) {
      		if (nodes.values.firstWhereOrNull((element) => element.uriRaw == node.uriRaw) == null) {
      			await nodes.add(node);
      		}
      	}
      }
    
  • Next, we’ll write the function to change walletX current node to default. An handy function we would make use of later on. Add a new preference key in lib/entities/preference_key.dart with the format PreferencesKey.currentWalletXNodeIdKey, we’ll use it to identify the current node id.

      Future<void> changeWalletXCurrentNodeToDefault(
      		{required SharedPreferences sharedPreferences, required Box<Node> nodes}) async {
      	final node = getWalletXDefaultNode(nodes: nodes);
      	final nodeId = node?.key as int? ?? 0;
      	await sharedPreferences.setInt(PreferencesKey.currentWalletXNodeIdKey, nodeId);
      }
    
  • Next, in the defaultSettingsMigration function at the top of the file, add a new case to handle both addWalletXNodeList and changeWalletXCurrentNodeToDefault

      case “next-number-increment”:
      	await addWalletXNodeList(nodes: nodes);
      	await changeWalletXCurrentNodeToDefault(sharedPreferences: sharedPreferences, nodes: nodes);
      	break;
    
  • Next, increase the initialMigrationVersion number in main.dart to be the new case entry number you entered in the step above for the defaultSettingsMigration function.

  • Next, go to lib/view_model/node_list/node_list_view_model.dart

  • In the reset function, add a case for walletX:

      case WalletType.tron:
      	node = getTronDefaultNode(nodes: _nodeSource)!;
      	break;
    
  • Lastly, go to cw_core/lib/node.dart,

  • In the uri getter, add a case to handle the uri setup for walletX. If the node uses http, return Uri.http, if not, return Uri.https

      case WalletType.walletX:
      		return Uri.https(uriRaw, ‘’);
    
  • Also, in the requestNode method, add a case for WalletType.walletx

  • Next is the modifications to lib/store/settings_store.dart file:

  • In the load function, create a variable to fetch the currentWalletxNodeId using the PreferencesKey.currentWalletXNodeIdKey we created earlier.

  • Create another variable walletXNode which gets the walletx node using the nodeId variable assigned in the step above.

  • Add a check to see if walletXNode is not null, if it’s not null, assign the created tronNode variable to the nodeMap with a type of walletX

      final walletXNode = nodeSource.get(walletXNodeId);
      final walletXNodeId = sharedPreferences.getInt(PreferencesKey.currentWalletXNodeIdKey);								
      if (walletXNode != null) {
      	nodes[WalletType.walletx] = walletXNode;
      }
    
  • Repeat the steps above in the reload function

  • Next, add a case for walletX in the _saveCurrentNode function.

  • Run the following commands after to generate modified files in cw_core and lib

      cd cw_core && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
    
      flutter packages pub run build_runner build --delete-conflicting-outputs
    
  • Lastly, before we run the app to test what we’ve done so far,

  • Go to lib/src/dashboard/widgets/menu_widget.dart and add an icon for walletX to be used within the app.

  • Go to lib/src/screens/wallet_list/wallet_list_page.dart and add an icon for walletx, add a case for walletx also in the imageFor method.

  • Do the same thing in lib/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart

  • One last thing before we can create a wallet for walletx, go to lib/view_model/wallet_new_vm.dart

  • Modify the seedPhraseWordsLength getter by adding a case for WalletType.walletx

Now you can run the codebase and successfully create a wallet for type walletX successfully.

Display Seeds/Keys

  • Next, we want to set up our wallet to display the seeds and/or keys in the security page of the app.
  • Go to lib/view_model/wallet_keys_view_model.dart
  • Modify the populateItems function by adding a case for WalletType.walletx in it.
  • Now your seeds and/or keys should display when you go to Security and Backup -> Show seed/keys page within the app.

Restore Wallet

  • Go to lib/core/seed_validator.dart
  • In the getWordList method, add a case to handle WalletType.walletx which would return the word list to be used to validate the passed in seeds.
  • Next, go to lib/restore_view_model.dart
  • Modify the hasRestoreFromPrivateKey to reflect if walletx supports restore from Key
  • Add a switch case to handle the various restore modes that walletX supports
  • Modify the getCredential method to handle the restore flows for WalletType.walletx
  • Run the build_runner code generation command

Receive

  • Go to lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
  • Create an implementation of PaymentUri for type WalletX.
  • In the uri getter, add a case for WalletType.walletx returning the implementation class for PaymentUri
  • Modify the addressList getter to return the address/addresses for walletx

Balance Screen

  • Go to lib/view_model/dashboard/balance_view_model.dart
  • Modify the function to adjust the way the balance is being display on the app: isHomeScreenSettingsEnabled
  • Add a case to the availableBalanceLabel getter to modify the text being displayed (Available or confirmed)
  • Same for additionalBalanceLabel
  • Next, go to lib/reactions/fiat_rate_update.dart
  • Modify the startFiatRateUpdate function and add a check for WalletType.walletx to return all the token currencies
  • Next, go to lib/reactions/on_current_wallet_change.dart
  • Modify the startCurrentWalletChangeReaction function and add a check for WalletType.walletx to return all the token currencies
  • Lastly, go to lib/view_model/dashboard/transaction_list_item.dart
  • In the formattedFiatAmount getter, add a case to handle the fiat amount conversion for WalletType.walletx

Send ViewModel

  • Go to lib/view_model/send/send_view_model.dart
  • Modify the _credentials function to reflect WalletType.walletx
  • Modify hasMultipleTokens to reflect wallets

Exchange

  • Go to lib/view_model/exchange/exchange_view_model.dart
  • First, add a case for WalletType.walletx in the initialPairBasedOnWallet method.
  • If WalletX supports tokens, go to lib/view_model/exchange/exchange_trade_view_model.dart
  • Modify the _checkIfCanSend method by creating a _isWalletXToken that checks if the from currency is WalletX and if its tag is for walletx
  • Add _isWalletXToken to the return logic for the method.

Secrets

  • Create a json file named wallet-secrets-config.json and put an empty curly bracket “{}” in it
  • Add a new entry to tool/utils/secret_key.dart for walletx
  • Modify the tool/generate_secrets_config.dart file for walletx, don’t forget to call secrets.clear() before adding a new set of generation logic
  • Modify the tool/import_secrets_config.dart file for walletx
  • In the .gitignore file, add **/tool/.walletx-secrets-config.json and **/cw_walletx/lib/.secrets.g.dart

HomeSettings: WalletX Tokens Display and Management

  • Go to lib/view_model/dashboard/home_settings_view_model.dart
  • Modify the _updateTokensList method to add all walletx tokens if the wallet type is WalletType.walletx.
  • Modify the getTokenAddressBasedOnWallet method to include a case to fetch the address for a WalletX token.
  • Modify the getToken method to return a specific walletx token
  • Modify the addToken, deleteToken and changeTokenAvailability methods to handle cases where the walletType is walletx

Buy and Sell WalletX

  • Go to lib/entities/provider_types.dart
  • Add a case for WalletType.walletx in the getAvailableBuyProviderTypes method. Return a list of providers that support buying WalletX.
  • Add a case for WalletType.walletx in the getAvailableSellProviderTypes method. Return a list of providers that support selling WalletX.

Restore QR setup

  • Go to lib/view_model/restore/wallet_restore_from_qr_code.dart
  • Add the scheme for walletx in _walletTypeMap
  • Also modify _determineWalletRestoreMode to include a case for walletx
  • Go to lib/view_model/restore/restore_from_qr_vm.dart
  • Modify getCredentialsFromRestoredWallet method
  • Go to lib/core/address_validator.dart
  • Modify the getAddressFromStringPattern method to add a case for WalletType.walletx
  • and if it has tokens (ex. erc20, trc20, spl tokens) then add them to the switch case as well
  • Add the scheme for walletx for both Android in AndroidManifestBase.xml and iOS in InfoBase.plist

Transaction History

  • Go to lib/view_model/transaction_details_view_model.dart
  • Add a case for WalletType.walletx to add the items to be displayed on the detailed view
  • Modify the _explorerUrl method to add the blockchain explorer link for WalletX in order to view the more info on a transaction
  • Modify the _explorerDescription to display the name of the explorer

Points to note when adding the new wallet type

  1. if it has tokens (ex. ERC20, SPL, etc...) make sure to add that to this function _checkIfCanSend in exchange_trade_view_model.dart
  2. if it has tokens (ex. ERC20, SPL, etc...) make sure to add a check for the tags as well in the
  3. Check On/Off ramp providers that support the new wallet currency and add them accordingly in provider_types.dart
  4. Add support for wallet uri scheme to restore from QR for both Android in AndroidManifestBase.xml and iOS in InfoBase.plist
  5. Make sure no imports are using the wallet internal package files directly, instead use the proxy layers that is created in the main lib lib/cw_ethereum.dart for example. (i.e try building Monero.com if you get compilation errors, then you probably missed something)

Copyright (C) 2018-2023 Cake Labs LLC