Skip to main content
This guide gets you started with using MPC Wallets (Organization-Controlled Wallets). By following this guide, you will be able to use the WaaS 2.0 API to:
  1. Query the chains and tokens you can use
  2. Set up a wallet
  3. Deposit and withdraw tokens
  4. Query wallet balances
This guide uses the development environment in all its code samples. It is recommended that you use the development environment to test your new features first before deploying them to the production environment.

Technical architecture

The following diagram illustrates the components within a fully-functioning MPC vault: The components within a fully-functioning MPC vault
  • Each vault has two root extended public keys, one derived from the Secp256k1 curve and the other derived from the EdDSA curve.
  • Each root extended public key can be used to derive multiple addresses.
  • Each vault can have multiple key share holder groups. Each holder group has a set of TSS Nodes storing the private key shares that can collectively sign a transaction or recover the root private key. For Main Groups and Signing Groups, one key share holder will always be Cobo. To learn more about key share holder groups, see Create holder groups.
  • Each TSS key share group consists of two or three private key shares, depending on the number of holders. Each key share group can form a key pair with the root extended public key derived from the same curve. Each key share is unique.

Prerequisites

  • Follow the instructions in Get started with WaaS 2.0 to set up your account and send your first API request to the WaaS 2.0 service.
  • If you choose to use a WaaS SDK instead of manually writing the API requests, refer to the SDK guide corresponding to the programming language of your choice (Python, Java, Go, JavaScript) to integrate the SDK into your project.
  • Prepare some test tokens as you will need them when testing the deposit feature. To know which test tokens you can use, refer to Query chain and token information. In most cases, you can use XTN as the test token.
  • It is highly recommended that you set up a callback endpoint to receive and approve withdrawal requests and a webhook endpoint to receive real-time notifications regarding transaction status updates and other events of your concern. To learn how to set up and register webhook and callback endpoints, refer to Introduction to webhooks and callbacks.

1. Query chain and token information

Before you begin setting up your wallet and generating deposit addresses, it’s important to know which chains and tokens you can use.

Query enabled chains

To retrieve the chains you can use for Organization-Controlled Wallets and their corresponding chain IDs, call List enabled chains and specify the query parameters as follows:
  • wallet_type: MPC.
  • wallet_subtype: Org-Controlled.
import json

import cobo_waas2
from cobo_waas2 import (
   WalletType,
   WalletSubtype,
)

configuration = cobo_waas2.Configuration(
   # Replace `<YOUR_PRIVATE_KEY>` with your private key
   api_private_key="<YOUR_PRIVATE_KEY>",
   # Use the development environment
   host="https://api.dev.cobo.com/v2"
)

# Enter a context with an instance of the API client
with cobo_waas2.ApiClient(configuration) as api_client:
   # Create an instance of the API class
   wallet_api_instance = cobo_waas2.WalletsApi(api_client)
   try:
       # Query enabled chains
       api_response = wallet_api_instance.list_enabled_chains(
           wallet_type=WalletType.MPC,
           wallet_subtype=WalletSubtype.ORG_MINUS_CONTROLLED
       )
       print(f"The response of WalletsApi->list_enabled_chains:")
       print(json.dumps(api_response.to_dict(), indent=2))

   except Exception as e:
       print("Exception when calling WalletsApi->list_enabled_chains, %s\n", e)
import com.cobo.waas2.ApiClient;
import com.cobo.waas2.ApiException;
import com.cobo.waas2.Configuration;
import com.cobo.waas2.Env;
import com.cobo.waas2.api.WalletsApi;
import com.cobo.waas2.model.ListSupportedChains200Response;
import com.cobo.waas2.model.WalletSubtype;
import com.cobo.waas2.model.WalletType;

public class ListEnabledChainsExample {
    public static void main(String[] args) {
        ApiClient defaultClient = Configuration.getDefaultApiClient();
        // Use the development environment
        defaultClient.setEnv(Env.DEV);
        // Replace `<YOUR_PRIVATE_KEY>` with your private key
        defaultClient.setPrivKey("<YOUR_PRIVATE_KEY>");
        WalletsApi apiInstance = new WalletsApi();
        try {
            Integer limit = 50;
            String before = null;
            String after = null;
            ListSupportedChains200Response result = apiInstance.listEnabledChains(WalletType.MPC, WalletSubtype.ORG_CONTROLLED, limit, before, after);
            System.out.println(result);
        } catch (ApiException e) {
            System.err.println("Exception when calling WalletsApi#listEnabledChains");
            System.err.println("Status code: " + e.getCode());
            System.err.println("Reason: " + e.getResponseBody());
            System.err.println("Response headers: " + e.getResponseHeaders());
            e.printStackTrace();
        }
    }
}

If you want to use a chain that is not already on the list, you need to enable the chain first on Cobo Portal. To learn how to do so, see Transfer (deposit / withdraw). If you have reached the limit on the number of chains you can use, consider upgrading your pricing plan.

Query enabled tokens

To retrieve the tokens you can use for Organization-Controlled Wallets and their corresponding token IDs, call List enabled tokens and specify the query parameters as follows:
  • wallet_type: MPC.
  • wallet_subtype: Org-Controlled.
  • chain_ids: Specify the chain of your choice. You can also leave this parameter empty to query enabled tokens on all chains.
import json

import cobo_waas2
from cobo_waas2 import (
   WalletType,
   WalletSubtype,
)

configuration = cobo_waas2.Configuration(
   # Replace `<YOUR_PRIVATE_KEY>` with your private key
   api_private_key="<YOUR_PRIVATE_KEY>",
   # Use the development environment
   host="https://api.dev.cobo.com/v2"
)

# Enter a context with an instance of the API client
with cobo_waas2.ApiClient(configuration) as api_client:
   # Create an instance of the API class
   wallet_api_instance = cobo_waas2.WalletsApi(api_client)
   try:
       # Query enabled tokens
       api_response = wallet_api_instance.list_enabled_tokens(
           wallet_type=WalletType.MPC,
           wallet_subtype=WalletSubtype.ORG_MINUS_CONTROLLED
       )
       print(f"The response of WalletsApi->list_enabled_tokens:")
       print(json.dumps(api_response.to_dict(), indent=2))

   except Exception as e:
       print("Exception when calling WalletsApi->list_enabled_tokens, %s\n", e)
import com.cobo.waas2.ApiClient;
import com.cobo.waas2.ApiException;
import com.cobo.waas2.Configuration;
import com.cobo.waas2.Env;
import com.cobo.waas2.api.WalletsApi;
import com.cobo.waas2.model.ListSupportedTokens200Response;
import com.cobo.waas2.model.WalletSubtype;
import com.cobo.waas2.model.WalletType;


public class ListEnabledTokensExample {
    public static void main(String[] args) {
        ApiClient defaultClient = Configuration.getDefaultApiClient();
        // Use the development environment
        defaultClient.setEnv(Env.DEV);
        // Replace `<YOUR_PRIVATE_KEY>` with your private key
        defaultClient.setPrivKey("<YOUR_PRIVATE_KEY>");
        WalletsApi apiInstance = new WalletsApi();
        try {
            String chainIds = null;
            Integer limit = 50;
            String before = null;
            String after = null;
            ListSupportedTokens200Response result = apiInstance.listEnabledTokens(WalletType.MPC, WalletSubtype.ORG_CONTROLLED, chainIds, limit, before, after);
            System.out.println(result);
        } catch (ApiException e) {
            System.err.println("Exception when calling WalletsApi#listEnabledTokens");
            System.err.println("Status code: " + e.getCode());
            System.err.println("Reason: " + e.getResponseBody());
            System.err.println("Response headers: " + e.getResponseHeaders());
            e.printStackTrace();
        }
    }
}

2. Set up a wallet

The setup of an Organization-Controlled Wallet usually involves the following steps:
  1. Create a vault.
  2. Create a key share holder group.
  3. Generate root extended public keys and private key shares.
  4. Create an Organization-Controlled Wallet.

Create a vault

Now that you have confirmed which chains and tokens you can use, the next step is to set up a vault. To create an vault, call the Create vault operation and specify the properties in the request body as follows:
  • vault_type: Org-Controlled.
  • name: The vault’s name.
Upon successful completion of the request, the response will include the vault ID, which is the unique identifier of the vault you have just created. Store this vault ID as you will need it for subsequent steps.
import json
import cobo_waas2
from cobo_waas2.models.create_mpc_vault_request import CreateMpcVaultRequest
from cobo_waas2.models.mpc_vault import MPCVault
from cobo_waas2.models.mpc_vault_type import MPCVaultType
from cobo_waas2.rest import ApiException
from pprint import pprint

# Defining the host is optional and defaults to https://api.dev.cobo.com/v2
# See configuration.py for a list of all supported configuration parameters.
configuration = cobo_waas2.Configuration(
    api_private_key="<YOUR_PRIVATE_KEY>",
    host="https://api.dev.cobo.com/v2",
)
# Enter a context with an instance of the API client
with cobo_waas2.ApiClient(configuration) as api_client:
    # Create an instance of the API class
    api_instance = cobo_waas2.WalletsMPCWalletsApi(api_client)
    create_mpc_vault_request = cobo_waas2.CreateMpcVaultRequest(
        name="<YOUR_VAULT_NAME>",
        vault_type=MPCVaultType.ORG_MINUS_CONTROLLED
    )

    try:
        # Create a vault
        api_response = api_instance.create_mpc_vault(create_mpc_vault_request=create_mpc_vault_request)
        print("The response of WalletsMPCWalletsApi->create_mpc_vault:\n")
        print(json.dumps(api_response.to_dict(), indent=2))
    except Exception as e:
        print("Exception when calling WalletsMPCWalletsApi->create_mpc_vault: %s\n" % e)
import com.cobo.waas2.ApiClient;
import com.cobo.waas2.ApiException;
import com.cobo.waas2.Configuration;
import com.cobo.waas2.Env;
import com.cobo.waas2.api.WalletsMpcWalletsApi;
import com.cobo.waas2.model.*;

public class CreateVaultExample {
    public static void main(String[] args) {
        ApiClient defaultClient = Configuration.getDefaultApiClient();
        // Use the development environment
        defaultClient.setEnv(Env.DEV);
        // Replace `<YOUR_PRIVATE_KEY>` with your private key
        defaultClient.setPrivKey("<YOUR_PRIVATE_KEY>");
        WalletsMpcWalletsApi apiInstance = new WalletsMpcWalletsApi();
        try {
            CreateMpcVaultRequest params = new CreateMpcVaultRequest()
                    .name("OCW vault Demo(Java)")
                    .vaultType(MPCVaultType.ORG_CONTROLLED);
            MPCVault result = apiInstance.createMpcVault(params);
            System.out.println(result);
        } catch (ApiException e) {
            System.err.println("Exception when calling WalletsMpcWalletsApi#createMpcVault");
            System.err.println("Status code: " + e.getCode());
            System.err.println("Reason: " + e.getResponseBody());
            System.err.println("Response headers: " + e.getResponseHeaders());
            e.printStackTrace();
        }
    }
}

Create a key share holder group

A key share holder group has a set of TSS Nodes storing the private key shares that can collectively sign a transaction or recover the root private key. In this step, you will set up to be a key share holder, get the TSS Node ID, and call the WaaS API to create a holder group. A vault with a key share holder group

Become a key share holder

A Main Group consists of two key share holders. One holder is always Cobo, so you now need to find another holder to make up a Main Group. If you already have Cobo Guard installed on your phone, you can become a key share holder by using your phone as a mobile co-signer. As an alternative, you can set up a TSS node and use it as an API co-signer. To learn the differences between the two co-signer types, how to complete the setup, and how to acquire their TSS Node IDs, see Co-signer Type.

Create a Main Group

Creating a Main Group is mandatory before you can create an MPC Wallet. To help you get started quickly, this guide will only introduces how to create a Main Group. However, in your actual projects, it is highly recommended that you also create a Signing Group and Recovery Group, as well as to back up your key shares. For more information, see Create holder groups and Back up key shares. To create a Main Group, call the Create key share holder group operation and specify the parameters and properties as follows:
  • Path:
    • vault_id: The ID of the vault you have just created.
  • Request body:
    • key_share_holder_group_type: MainGroup.
    • participants: 2.
    • threshold: 2.
    • key_share_holders: The information about the key share holder. You do not need to provide the information about Cobo as the key share holder, as it will be automatically filled by the WaaS service.
      • name: Use a name that can help you easily identify the key share holder.
      • type: Mobile if you use a mobile co-signer, or API if you use an API co-signer.
      • tss_node_id: The TSS Node ID you have prepared in the previous step.
      • signer: true
Upon successful completion of the request, the response will include the key share holder group ID. Store this ID as you will need it for subsequent steps.
import json
import cobo_waas2
from cobo_waas2.models.create_key_share_holder_group_request import CreateKeyShareHolderGroupRequest
from cobo_waas2.models.key_share_holder_group import KeyShareHolderGroup
from cobo_waas2.models.key_share_holder_group_type import KeyShareHolderGroupType
from cobo_waas2.models.create_key_share_holder import CreateKeyShareHolder
from cobo_waas2.models.key_share_holder_type import KeyShareHolderType
from cobo_waas2.rest import ApiException
from pprint import pprint

# Defining the host is optional and defaults to https://api.dev.cobo.com/v2
# See configuration.py for a list of all supported configuration parameters.
configuration = cobo_waas2.Configuration(
    api_private_key="<YOUR_PRIVATE_KEY>",
    host="https://api.dev.cobo.com/v2"
)
# Enter a context with an instance of the API client
with cobo_waas2.ApiClient(configuration) as api_client:
    # Create an instance of the API class
    api_instance = cobo_waas2.WalletsMPCWalletsApi(api_client)
    vault_id = '<YOUR_VAULT_ID>'
    create_key_share_holder_group_request = cobo_waas2.CreateKeyShareHolderGroupRequest(
        key_share_holder_group_type=KeyShareHolderGroupType.MAINGROUP,
        participants=2,
        threshold=2,
        key_share_holders=[
            CreateKeyShareHolder(
                name="<YOUR_HOLDER_NAME>",
                # Set the share holder type to either API or Mobile
                type=KeyShareHolderType.API,
                tss_node_id="<YOUR_TSS_NODE_ID>",
                signer=True,
            )]
    )

    try:
        # Create key share holder group
        api_response = api_instance.create_key_share_holder_group(vault_id, create_key_share_holder_group_request=create_key_share_holder_group_request)
        print("The response of WalletsMPCWalletsApi->create_key_share_holder_group:\n")
        print(json.dumps(api_response.to_dict(), indent=2))
    except Exception as e:
        print("Exception when calling WalletsMPCWalletsApi->create_key_share_holder_group: %s\n" % e)
import com.cobo.waas2.ApiClient;
import com.cobo.waas2.ApiException;
import com.cobo.waas2.Configuration;
import com.cobo.waas2.Env;
import com.cobo.waas2.api.WalletsMpcWalletsApi;
import com.cobo.waas2.model.*;

import java.util.Arrays;
import java.util.List;
import java.util.UUID;

public class CreateKeyShareHolderGroupExample {
    public static void main(String[] args) {
        ApiClient defaultClient = Configuration.getDefaultApiClient();
        // for dev environment
        defaultClient.setEnv(Env.DEV);
        // Replace `<YOUR_PRIVATE_KEY>` with your private key
        defaultClient.setPrivKey("<YOUR_PRIVATE_KEY>");
        WalletsMpcWalletsApi apiInstance = new WalletsMpcWalletsApi();
        try {
            UUID vaultId = UUID.fromString("<YOUR_VAULT_ID>");
            List<CreateKeyShareHolder> keyShareHolderList =  Arrays.asList(
                    new CreateKeyShareHolder().name("<YOUR_HOLDER_NAME>")
                            // Set the share holder type to either API or Mobile
                            .type(KeyShareHolderType.MOBILE)
                            .tssNodeId("<YOUR_TSS_NODE_ID>")
                            .signer(true)
            );
            CreateKeyShareHolderGroupRequest request = new CreateKeyShareHolderGroupRequest()
                    .keyShareHolderGroupType(KeyShareHolderGroupType.MAINGROUP)
                    .participants(2)
                    .threshold(2)
                    .keyShareHolders(keyShareHolderList);
            KeyShareHolderGroup result = apiInstance.createKeyShareHolderGroup(vaultId, request);
            System.out.println(result);
        } catch (ApiException e) {
            System.err.println("Exception when calling WalletsMpcWalletsApi#createKeyShareHolderGroup");
            System.err.println("Status code: " + e.getCode());
            System.err.println("Reason: " + e.getResponseBody());
            System.err.println("Response headers: " + e.getResponseHeaders());
            e.printStackTrace();
        }
    }
}

Generate a root extended public key and key shares

After you create the Main Group, you need to generate root extended public keys and private key shares for your Main Group. A vault with a key share holder group, two root extended public keys and four private key shares To do so, perform the following steps:
  1. Call the Create TSS request operation and specify the parameters and properties as follows:
    • Path:
      • vault_id: The ID of the vault you have just created.
    • Request body:
      • type: KeyGen.
      • target_key_share_holder_group_id: The ID of the key share holder group you have just created.
  2. Generate key shares.
    • If you choose to use a mobile co-signer, you need to approve the “Approve Become Key Holder” request on Cobo Guard, and click Generate Key Shares to initiate the key generation process. Wait a brief moment for the key shares to be generated.
    • If you choose to use an API co-signer, the key share generation process will be automatically initiated unless you have also configured a TSS Node callback server, in which case the generation process will be initiated upon approval from the TSS Node callback server.
Once the root extended public key and key shares have been successfully generated, the TSS request status will become Success. You can subscribe to TSS request-related event types to receive real-time status updates or call Get TSS request to query the latest request status.
import json
import cobo_waas2
from cobo_waas2.models.create_tss_request_request import CreateTssRequestRequest
from cobo_waas2.models.tss_request import TSSRequest
from cobo_waas2.models.tss_request_type import TSSRequestType
from cobo_waas2.rest import ApiException
from pprint import pprint

# Defining the host is optional and defaults to https://api.dev.cobo.com/v2
# See configuration.py for a list of all supported configuration parameters.
configuration = cobo_waas2.Configuration(
    api_private_key="<YOUR_PRIVATE_KEY>",
    host="https://api.dev.cobo.com/v2"
)
# Enter a context with an instance of the API client
with cobo_waas2.ApiClient(configuration) as api_client:
    # Create an instance of the API class
    api_instance = cobo_waas2.WalletsMPCWalletsApi(api_client)
    vault_id = '<YOUR_VAULT_ID>'
    create_tss_request_request = cobo_waas2.CreateTssRequestRequest(
        type=TSSRequestType.KEYGEN,
        target_key_share_holder_group_id='<YOUR_KEY_SHARE_HOLDER_GROUP_ID>'
    )

    try:
        # Create TSS request
        api_response = api_instance.create_tss_request(vault_id, create_tss_request_request=create_tss_request_request)
        print("The response of WalletsMPCWalletsApi->create_tss_request:\n")
        print(json.dumps(api_response.to_dict(), indent=2))
    except Exception as e:
        print("Exception when calling WalletsMPCWalletsApi->create_tss_request: %s\n" % e)
import com.cobo.waas2.ApiClient;
import com.cobo.waas2.ApiException;
import com.cobo.waas2.Configuration;
import com.cobo.waas2.Env;
import com.cobo.waas2.api.WalletsMpcWalletsApi;
import com.cobo.waas2.model.CreateTssRequestRequest;
import com.cobo.waas2.model.TSSRequest;
import com.cobo.waas2.model.TSSRequestType;

import java.util.UUID;

public class CreateTssRequestExample {
    public static void main(String[] args) {
        ApiClient defaultClient = Configuration.getDefaultApiClient();
        // Use the development environment
        defaultClient.setEnv(Env.DEV);
        // Replace `<YOUR_PRIVATE_KEY>` with your private key
        defaultClient.setPrivKey("<YOUR_PRIVATE_KEY>");
        WalletsMpcWalletsApi apiInstance = new WalletsMpcWalletsApi();
        try {
            UUID vaultId = UUID.fromString("<YOUR_VAULT_ID>");
            CreateTssRequestRequest request = new CreateTssRequestRequest()
                    .type(TSSRequestType.KEYGEN)
                    .targetKeyShareHolderGroupId("YOUR_KEY_SHARE_HOLDER_GROUP_ID");
            TSSRequest result = apiInstance.createTssRequest(vaultId, request);
            System.out.println(result);
        } catch (ApiException e) {
            System.err.println("Exception when calling WalletsMpcWalletsApi#createTssRequest");
            System.err.println("Status code: " + e.getCode());
            System.err.println("Reason: " + e.getResponseBody());
            System.err.println("Response headers: " + e.getResponseHeaders());
            e.printStackTrace();
        }
    }
}

Create a wallet

Now that you have created a vault, created a key share holder group, and generated the key shares, the next step is to set up a wallet. To create an Organization-Controlled Wallet, call the Create wallet operation and specify the properties in the request body as follows:
  • name: Your wallet name.
  • wallet_type: MPC.
  • wallet_subtype:Org-Controlled.
  • vault_id: The ID of the vault you have just created.
Upon successful completion of the request, the response will include the wallet ID, which is the unique identifier of the wallet you have just created. Store this wallet ID as you will need it for subsequent steps.
import json
import cobo_waas2
from cobo_waas2.models.create_wallet_params import CreateWalletParams
from cobo_waas2.models.created_wallet_info import CreatedWalletInfo
from cobo_waas2.models.wallet_subtype import WalletSubtype
from cobo_waas2.models.wallet_type import WalletType
from cobo_waas2.rest import ApiException
from pprint import pprint

# Defining the host is optional and defaults to https://api.dev.cobo.com/v2
# See configuration.py for a list of all supported configuration parameters.
configuration = cobo_waas2.Configuration(
    api_private_key="<YOUR_PRIVATE_KEY>",
    host="https://api.dev.cobo.com/v2"
)
# Enter a context with an instance of the API client
with cobo_waas2.ApiClient(configuration) as api_client:
    # Create an instance of the API class
    api_instance = cobo_waas2.WalletsApi(api_client)
    create_wallet_params = cobo_waas2.CreateWalletParams(
        actual_instance=cobo_waas2.CreateMpcWalletParams(
            name="<YOUR_WALLET_NAME>",
            wallet_type=WalletType.MPC,
            wallet_subtype=WalletSubtype.ORG_MINUS_CONTROLLED,
            vault_id="<YOUR_VAULT_ID>"
        )
    )

    try:
        # Create a wallet
        api_response = api_instance.create_wallet(create_wallet_params=create_wallet_params)
        print("The response of WalletsApi->create_wallet:\n")
        print(json.dumps(api_response.to_dict(), indent=2))
    except Exception as e:
        print("Exception when calling WalletsApi->create_wallet: %s\n" % e)
import com.cobo.waas2.ApiClient;
import com.cobo.waas2.ApiException;
import com.cobo.waas2.Configuration;
import com.cobo.waas2.Env;
import com.cobo.waas2.api.WalletsApi;
import com.cobo.waas2.model.*;

public class CreateWalletExample {
    public static void main(String[] args) {
        ApiClient defaultClient = Configuration.getDefaultApiClient();
        // Use the development environment
        defaultClient.setEnv(Env.DEV);
        // Replace `<YOUR_PRIVATE_KEY>` with your private key
        defaultClient.setPrivKey("<YOUR_PRIVATE_KEY>");
        WalletsApi apiInstance = new WalletsApi();
        try {
            CreateMpcWalletParams params = new CreateMpcWalletParams()
                    .name("OCW Example Wallet Demo(Java)")
                    .walletType(WalletType.MPC)
                    .walletSubtype(WalletSubtype.ORG_CONTROLLED)
                    .vaultId("<YOUR_VAULT_ID>");
            CreatedWalletInfo result = apiInstance.createWallet(new CreateWalletParams(params));
            System.out.println(result);
        } catch (ApiException e) {
            System.err.println("Exception when calling WalletsApi#createWallet");
            System.err.println("Status code: " + e.getCode());
            System.err.println("Reason: " + e.getResponseBody());
            System.err.println("Response headers: " + e.getResponseHeaders());
            e.printStackTrace();
        }
    }
}

3. Deposit and withdraw tokens

After setting up an Organization-Controlled Wallet, it is time to deposit some tokens into it and learn how to make withdrawals.

Generate deposit addresses

To receive tokens, you need to generate deposit addresses within the wallet. To do so, call the Create addresses in wallet operation and specify the parameters and properties as follows:
  • Path:
    • wallet_id: The ID of the wallet you have just created.
  • Request body:
    • chain_id: The ID of the blockchain.
    • count: Use this parameter to specify the number of addresses you want to create.
Upon successful completion of the request, the response will include the addresses you have just created. You can now proceed to deposit tokens into these addresses.
import json

import cobo_waas2
from cobo_waas2 import (
   CreateAddressRequest,
   AddressEncoding,
)

configuration = cobo_waas2.Configuration(
   # Replace `<YOUR_PRIVATE_KEY>` with your private key
   api_private_key="<YOUR_PRIVATE_KEY>",
   # Use the development environment
   host="https://api.dev.cobo.com/v2"
)

# Enter a context with an instance of the API client
with cobo_waas2.ApiClient(configuration) as api_client:
   # Create an instance of the API class
   wallet_api_instance = cobo_waas2.WalletsApi(api_client)
   try:
       # Generate two addresses on the Bitcoin testnet3 (XTN) chain using P2TR encoding
       api_response = wallet_api_instance.create_address(
           wallet_id="<Your Wallet ID>",
           create_address_request=CreateAddressRequest(
               chain_id="XTN", count=2, encoding=AddressEncoding.ENCODING_P2_TR
           ),
       )
       print("The response of WalletsApi->create_address:")
       for address_info in api_response:
           print(json.dumps(address_info.to_dict(), indent=2))

   except Exception as e:
       print("Exception when calling WalletsApi->create_address, %s\n", e)
import com.cobo.waas2.ApiClient;
import com.cobo.waas2.ApiException;
import com.cobo.waas2.Configuration;
import com.cobo.waas2.Env;
import com.cobo.waas2.api.WalletsApi;
import com.cobo.waas2.model.AddressEncoding;
import com.cobo.waas2.model.AddressInfo;
import com.cobo.waas2.model.CreateAddressRequest;

import java.util.List;
import java.util.UUID;

public class CreateAddressExample {
    public static void main(String[] args) {
        ApiClient defaultClient = Configuration.getDefaultApiClient();
        // Use the development environment
        defaultClient.setEnv(Env.DEV);
        // Replace `<YOUR_PRIVATE_KEY>` with your private key
        defaultClient.setPrivKey("<YOUR_PRIVATE_KEY>");
        WalletsApi apiInstance = new WalletsApi();
        try {
            UUID wallet_id = UUID.fromString("<YOUR_WALLET_ID>");
            CreateAddressRequest params = new CreateAddressRequest()
                    .chainId("XTN")
                    .count(2)
                    .encoding(AddressEncoding.BECH32);
            List<AddressInfo> result = apiInstance.createAddress(wallet_id, params);
            for (AddressInfo addressInfo : result)
                System.out.println(addressInfo);
        } catch (ApiException e) {
            System.err.println("Exception when calling WalletsApi#createAddress");
            System.err.println("Status code: " + e.getCode());
            System.err.println("Reason: " + e.getResponseBody());
            System.err.println("Response headers: " + e.getResponseHeaders());
            e.printStackTrace();
        }
    }
}

Process deposit

After depositing tokens to the addresses you have generated, you can track the status of your deposit using one of the following two options. Compared with using API to query the transaction status, webhooks can give you real-time notifications and are thus the recommended option.

Option 1: Use webhooks for real-time notifications

Webhook is an essential mechanism for the WaaS service to communicate with your application. After you register a webhook endpoint on Cobo Portal, the WaaS service sends push messages to the designated URL when an event occurs. To learn how to set up a webhook endpoint and register it on Cobo Portal, refer to Introduction to webhooks and callbacks. To track the status of your deposit, you can subscribe to the following webhook event types:
  • wallets.transaction.created
  • wallets.transaction.updated
  • wallets.transaction.succeeded
  • wallets.transaction.failed
To learn the trigger condition and data structure of each event type, refer to Webhook event types and data types.

Option 2: Get transaction status by API call

To query the status of a deposit transaction, call the List all transactions operation and set the query parameters as follows:
  • types: Deposit.
  • statuses: Confirming, Completed. If you are depositing from an external address, you will be able to query the transaction details when the transaction is waiting for the required number of confirmations or when it is successfully executed.
  • wallet_ids: The ID of the wallet you have created in the first step.
import json
import uuid


import cobo_waas2
from cobo_waas2 import (
   CreateAddressRequest,
   AddressEncoding,
   TransferParams,
   TransferSource,
   MpcTransferSource,
   WalletSubtype,
   TransferDestination, AddressTransferDestination, TransferDestinationType, AddressTransferDestinationAccountOutput,
)


configuration = cobo_waas2.Configuration(
   # Replace `<YOUR_PRIVATE_KEY>` with your private key.
   api_private_key="<YOUR_PRIVATE_KEY>",
   # Use the development environment.
   host="https://api.dev.cobo.com/v2"
)


# Enter a context with an instance of the API client
with cobo_waas2.ApiClient(configuration) as api_client:
   # Create an instance of the API class
   transaction_api_instance = cobo_waas2.TransactionsApi(api_client)
   try:
       # List deposit transactions
       api_response = transaction_api_instance.list_transactions(
           types="Deposit",
           statuses="Confirming, Completed",
           wallet_ids="<YOUR_WALLET_ID>"
       )
       print("The response of TransactionsApi->list_transactions:")
       print(json.dumps(api_response.to_dict(), indent=2))


   except Exception as e:
       print("Exception when calling TransactionsApi->list_transactions, %s\n", e)
import com.cobo.waas2.ApiClient;
import com.cobo.waas2.ApiException;
import com.cobo.waas2.Configuration;
import com.cobo.waas2.Env;
import com.cobo.waas2.api.TransactionsApi;
import com.cobo.waas2.model.ListTransactions200Response;


import java.util.UUID;


public class ListTransactionsExample {
   public static void main(String[] args) {
       ApiClient defaultClient = Configuration.getDefaultApiClient();
       // Use the development environment
       defaultClient.setEnv(Env.DEV);
       // Replace `<YOUR_PRIVATE_KEY>` with your private key
       defaultClient.setPrivKey("<YOUR_PRIVATE_KEY>");
       TransactionsApi apiInstance = new TransactionsApi();
       try {
           String requestId = null;
           String coboIds = null;
           String transactionIds = null;
           String transactionHashes = "";
           String types = "Deposit";
           String statuses = "Confirming, Completed";
           String walletIds = "<YOUR_WALLET_ID>";
           String chainIds = null;
           String tokenIds = null;
           String assetIds = null;
           UUID vaultId = null;
           UUID projectId = null;
           Long minCreatedTimestamp = null;
           Long maxCreatedTimestamp = null;
           Integer limit = 50;
           String before = null;
           String after = null;


           ListTransactions200Response result = apiInstance.listTransactions(
                   requestId, coboIds, transactionIds, transactionHashes, types, statuses, walletIds, chainIds,
                   tokenIds, assetIds, vaultId, projectId, minCreatedTimestamp, maxCreatedTimestamp, limit, before, after);
           System.out.println(result);
       } catch (ApiException e) {
           System.err.println("Exception when calling WalletsApi#createAddress");
           System.err.println("Status code: " + e.getCode());
           System.err.println("Reason: " + e.getResponseBody());
           System.err.println("Response headers: " + e.getResponseHeaders());
           e.printStackTrace();
       }
   }
}

Withdraw tokens

Now that you have tokens in your wallet, it’s time to try withdrawing them.

Set up a callback endpoint

To enhance the security of your transactions, it is highly recommended that you set up a callback endpoint to receive and approve withdrawal requests. Once you initiate a withdrawal using the WaaS 2.0 API, the callback endpoint will receive a callback message containing the transaction details. The transaction will proceed only if you approve the withdrawal request. To learn how to set up a callback endpoint and register it on Cobo Portal, refer to Introduction to webhooks and callbacks.

Withdraw tokens

To withdraw tokens from the Asset Wallet, call the Transfer token operation and set the properties in the request body as follows:
  • request_id: Your request ID.
  • source.source_type: Org-Controlled.
  • source.wallet_id: The ID of the wallet you have just created.
  • token_id: The ID of the token you want to withdraw.
  • destination.destination_type: Address.
  • destination.account_output: The receiving address and memo (if applicable), and the amount you want to withdraw.
  • category_names: The custom category for you to identify your transactions.
  • description: The description of the transfer.
import json
import uuid

import cobo_waas2
from cobo_waas2 import (
   CreateAddressRequest,
   AddressEncoding,
   TransferParams,
   TransferSource,
   MpcTransferSource,
   WalletSubtype,
   TransferDestination, AddressTransferDestination, TransferDestinationType, AddressTransferDestinationAccountOutput,
)

configuration = cobo_waas2.Configuration(
   # Replace `<YOUR_PRIVATE_KEY>` with your private key
   api_private_key="<YOUR_PRIVATE_KEY>",
   # Use the development environment
   host="https://api.dev.cobo.com/v2"
)

# Enter a context with an instance of the API client
with cobo_waas2.ApiClient(configuration) as api_client:
   # Create an instance of the API class
   transaction_api_instance = cobo_waas2.TransactionsApi(api_client)
   try:
       # Withdraw Bitcoin testnet3(XTN) tokens from the wallet
       api_response = transaction_api_instance.create_transfer_transaction(
           transfer_params=TransferParams(
               request_id=str(uuid.uuid4()),
               source=TransferSource(
                   actual_instance=MpcTransferSource
                   (
                       source_type=WalletSubtype.ORG_MINUS_CONTROLLED,
                       wallet_id="<YOUR_WALLET ID>",
                   )
               ),
               token_id="XTN",
               destination=TransferDestination(
                   actual_instance=AddressTransferDestination(
                       destination_type=TransferDestinationType.ADDRESS,
                       account_output=AddressTransferDestinationAccountOutput(
                           address="<TARGET_ADDRESS>",
                           amount="<TRANSFER_AMOUNT>"
                       )
                   )
               ),
               category_names=["<CATEGORY_NAME>"],
               description="<DESCRIPTION>",
           )
       )
       print("The response of TransactionsApi->create_transfer_transaction:")
       print(json.dumps(api_response.to_dict(), indent=2))

   except Exception as e:
       print("Exception when calling TransactionsApi->create_transfer_transaction, %s\n", e)
import com.cobo.waas2.ApiClient;
import com.cobo.waas2.ApiException;
import com.cobo.waas2.Configuration;
import com.cobo.waas2.Env;
import com.cobo.waas2.api.TransactionsApi;
import com.cobo.waas2.model.*;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class TransferExample {
    public static void main(String[] args) {
        ApiClient defaultClient = Configuration.getDefaultApiClient();
        // Use the development environment
        defaultClient.setEnv(Env.DEV);
        // Replace `<YOUR_PRIVATE_KEY>` with your private key
        defaultClient.setPrivKey("<YOUR_PRIVATE_KEY>");
        TransactionsApi apiInstance = new TransactionsApi();
        try {
            UUID walletId = UUID.fromString("<YOUR_WALLET_ID>");

            TransferParams params = new TransferParams();
            params.setRequestId("Demo" + UUID.randomUUID());

            MpcTransferSource transferSource = new MpcTransferSource().sourceType(WalletSubtype.ORG_CONTROLLED).walletId(walletId);
            params.setSource(new TransferSource(transferSource));

            params.setTokenId("XTN");
            AddressTransferDestination addressTransferDestination = new AddressTransferDestination()
                    .destinationType(TransferDestinationType.ADDRESS)
                    .accountOutput(new AddressTransferDestinationAccountOutput()
                            .address("<TARGET_ADDRESS>")
                            .amount("<TRANSFER_AMOUNT>"));
            params.setDestination(new TransferDestination(addressTransferDestination));

            List<String> categoryNames = new ArrayList<>();
            categoryNames.add("<Category Example>");
            params.categoryNames(categoryNames).description("<Description Example>");

            CreateTransferTransaction201Response result = apiInstance.createTransferTransaction(params);
            System.out.println(result);
        } catch (ApiException e) {
            System.err.println("Exception when calling TransactionsApi#createTransferTransaction");
            System.err.println("Status code: " + e.getCode());
            System.err.println("Reason: " + e.getResponseBody());
            System.err.println("Response headers: " + e.getResponseHeaders());
            e.printStackTrace();
        }
    }
}
The response of the withdrawal request is as follows. Record the transaction ID as you will use it in the following steps.
{
   "request_id": "<YOUR_REQUEST_ID>",
   "transaction_id": "<THE_GENERATED_TRANSACTION_ID>",
   "status": "Submitted"
}

Confirm the withdrawal

If you have set up a callback endpoint, after you initiate the withdrawal transaction, your callback endpoint will receive a message containing the transaction details. Check if the transaction meets expectations, and respond with a success status code (200 or 201) and a response body of ok to approve the transaction. To learn more about handling a callback message, see Handle messages.

Monitor the withdrawal status

In addition to webhook events, you can also call the Get transaction information operation to query the status of the transaction. Set the path parameter transaction_id to the transaction ID returned in the response of the previous withdrawal request.
import json
import uuid


import cobo_waas2
from cobo_waas2 import (
   CreateAddressRequest,
   AddressEncoding,
   TransferParams,
   TransferSource,
   MpcTransferSource,
   WalletSubtype,
   TransferDestination, AddressTransferDestination, TransferDestinationType, AddressTransferDestinationAccountOutput,
)


configuration = cobo_waas2.Configuration(
   # Replace `<YOUR_PRIVATE_KEY>` with your private key.
   api_private_key="<YOUR_PRIVATE_KEY>",
   # Use the development environment.
   host="https://api.dev.cobo.com/v2"
)


# Enter a context with an instance of the API client
with cobo_waas2.ApiClient(configuration) as api_client:
   # Create an instance of the API class
   transaction_api_instance = cobo_waas2.TransactionsApi(api_client)
   try:
       # Get transaction by ID
       api_response = transaction_api_instance.get_transaction_by_id(
           transaction_id="<YOUR_TRANSACTION_ID>"
       )
       print("The response of TransactionsApi->get_transaction_by_id:")
       print(json.dumps(api_response.to_dict(), indent=2))


   except Exception as e:
       print("Exception when calling TransactionsApi->get_transaction_by_id, %s\n", e)
import com.cobo.waas2.ApiClient;
import com.cobo.waas2.ApiException;
import com.cobo.waas2.Configuration;
import com.cobo.waas2.Env;
import com.cobo.waas2.api.TransactionsApi;
import com.cobo.waas2.model.TransactionDetail;


import java.util.UUID;


public class GetTransactionExample {
   public static void main(String[] args) {
       ApiClient defaultClient = Configuration.getDefaultApiClient();
       // Use the development environment
       defaultClient.setEnv(Env.DEV);
       // Replace `<YOUR_PRIVATE_KEY>` with your private key
       defaultClient.setPrivKey("<YOUR_PRIVATE_KEY>");
       TransactionsApi apiInstance = new TransactionsApi();
       try {
           UUID transactionId = UUID.fromString("<YOUR_TRANSACTION_ID>");


           TransactionDetail result = apiInstance.getTransactionById(transactionId);
           System.out.println(result);
       } catch (ApiException e) {
           System.err.println("Exception when calling TransactionsApi#getTransactionById");
           System.err.println("Status code: " + e.getCode());
           System.err.println("Reason: " + e.getResponseBody());
           System.err.println("Response headers: " + e.getResponseHeaders());
           e.printStackTrace();
       }
   }
}

4. Query wallet balances

After successfully withdrawing tokens from your wallet, you can call List token balances by wallet to query the wallet balances. Specify the path and query parameters as follows:
  • wallet_id: The ID of the wallet you have just created.
  • token_ids: You can leave it empty to query the balances of all tokens, or set it to the specific token you want to query.
import json


import cobo_waas2
from cobo_waas2 import (
   WalletType,
   WalletSubtype,
)


configuration = cobo_waas2.Configuration(
   # Replace `<YOUR_PRIVATE_KEY>` with your private key.
   api_private_key="<YOUR_PRIVATE_KEY>",
   # Use the development environment.
   host="https://api.dev.cobo.com/v2"
)


# Enter a context with an instance of the API client
with cobo_waas2.ApiClient(configuration) as api_client:
   # Create an instance of the API class
   wallet_api_instance = cobo_waas2.WalletsApi(api_client)
   try:
       # List token balances
       api_response = wallet_api_instance.list_token_balances_for_wallet(
           wallet_id="<YOUR_WALLET_ID>",
       )
       print(f"The response of WalletsApi->list_token_balances_for_wallet:")
       print(json.dumps(api_response.to_dict(), indent=2))


   except Exception as e:
       print("Exception when calling WalletsApi->list_token_balances_for_wallet, %s\n", e)
import com.cobo.waas2.ApiClient;
import com.cobo.waas2.ApiException;
import com.cobo.waas2.Configuration;
import com.cobo.waas2.Env;
import com.cobo.waas2.api.WalletsApi;
import com.cobo.waas2.model.ListTokenBalancesForAddress200Response;

import java.util.UUID;

public class ListTokenBalancesExample {
    public static void main(String[] args) {
        ApiClient defaultClient = Configuration.getDefaultApiClient();
        // Use the development environment
        defaultClient.setEnv(Env.DEV);
        // Replace `<YOUR_PRIVATE_KEY>` with your private key
        defaultClient.setPrivKey("<YOUR_PRIVATE_KEY>");
        WalletsApi apiInstance = new WalletsApi();
        try {
            UUID wallet_id = UUID.fromString("<YOUR_WALLET_ID>");
            String tokenIds = null;
            Integer limit = 50;
            String before = null;
            String after = null;
            ListTokenBalancesForAddress200Response result = apiInstance.listTokenBalancesForWallet(wallet_id, tokenIds, limit, before, after);
            System.out.println(result);
        } catch (ApiException e) {
            System.err.println("Exception when calling WalletsApi#listTokenBalancesForWallet");
            System.err.println("Status code: " + e.getCode());
            System.err.println("Reason: " + e.getResponseBody());
            System.err.println("Response headers: " + e.getResponseHeaders());
            e.printStackTrace();
        }
    }
}