We refreshed our doc site!

Bookmarked links may have changed

Read release notes

Web3 Services

Create Your First Developer-Controlled Wallet

Create two developer-controlled wallets and send tokens between them.

Circle Programmable Wallets provide a comprehensive developer solution to storing, sending, and spending Web3 digital currencies and NFTs. You or your users can manage asset infrastructure. Circle provides a one-stop-shop experience with all the tools and services to handle the complex parts, including security, transaction monitoring, account recovery flows, and more.

This guide will assist you in creating a developer-controlled wallet. You will learn how to register an Entity Secret Ciphertext, create a wallet set, and ultimately establish a wallet. Throughout this comprehensive guide, you will utilize both command line and API requests. These can be achieved by referring to Circle's API references or utilizing cURL requests. For how to navigate the API references, see the Testing API References guide.

You can create wallets for both Smart Contract Accounts (SCA) and Externally Owned Accounts (EOA). To learn more, see the Account Types guide.

Prerequisites

  1. Create a Developer Account and acquire an API key in the Console.
  2. Install the Web3 Services SDKs, which is currently only available for Node.js. (optional)
View sequence diagram

1. Register an Entity Secret Ciphertext

The Entity Secret is a randomly generated 32-byte key designed to enhance security in developer-controlled wallets. After being created, the hex-encoded Entity Secret key is encrypted as a ciphertext for even greater data safety. To create a wallet or perform a transaction, you must append the ciphertext to the API request parameters. The ciphertext must be re-encrypted (rotated) whenever an API requires it.

You should generate the Entity Secret, encrypt it, and then register the ciphertext in the developer dashboard.

a. Generate the Entity Secret

First, generate the Entity Secret and store it somewhere safe where your server-side app can access it. For testing purposes, you can store it in your environment variables. This will be used in the following steps to create the Entity Secret Ciphertext.

When you follow the provided methods below, you will receive a 32-byte string value similar to 7ae43b03d7e48795cbf39ddad2f58dc8e186eb3d2dab3a5ec5bb3b33946639a4.

openssl rand -hex 32

b. Fetch your Entity's Public Key

To proceed with the Entity Secret Ciphertext creation, the next essential element is your entity's public key. This public key plays an important role in generating the Entity Secret Ciphertext in the upcoming step. To obtain the required public key, initiate a request to the GET /config/entity/publicKey API endpoint. Remember, this API endpoint can be accessed by providing your valid API key for authentication.

// Import and configure the developer-controlled wallet SDK
const { initiateDeveloperControlledWalletsClient } = require('@circle-fin/developer-controlled-wallets');
const circleDeveloperSdk = initiateDeveloperControlledWalletsClient({
  apiKey: '<API_KEY>',
  entitySecret: '<ENTITY_SECRET>' // Make sure to enter the entity secret from the step above.
});

const response = await circleDeveloperSdk.getPublicKey({});

Response Body

JSON
{
  "data": {
    "publicKey": "-----BEGIN RSA PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsL4dzMMQX8pYbiyj0g5H\nFGwdygAA2xDm2VquY8Sk0xlOC0yKr+rqUrqnZCj09vLuXbg1BreO/BP4F4GEIHgR\nBNT+o5Q8k0OqxLXmcm5sz6CPlsFCom+MiOj6s7RD0SXg91WF8MrN88GyN53xkemA\nOYU1AlIt4dVIrFyGY8aQ57sbWHyIjim+do1kBX+svIA/FLHG/sycoGiPU1E+Kydf\nlEDga4iR2DSbW6Zte9cGDg9Ivw/seNd0TLzJz6oC9XgSK5Et6/ZpOmqJgvISQ6rT\nK15DJ8EzIOzZZuEVOefgy1S7rLdSH7DexuR4W7T+KpP/f8Px0bxd4N6MT5V5kBYa\ngYHHIvqlJvXe5EzwidIWk1rg1X+YJt2M48h3Pr9HeECcmrnEYOgp32m/9lJ8vKp9\nhNh0rEKww/ULd1HqCEm/I0QGuji13XcGxVo5+7KCb/C76CNdW3pdRMn6fwFh4WVu\nu99iRc9OZhlkphysWm44hs1ZPpMCAkKttWjhnLZwIatN27x2JUqoCEUOho19iT+F\nwlPFA7E0Ju9Rqm68AkCXxHsJsAuGT8m6FLQZLHv4JyO/QEVzD7vY08A2I5dz1mVt\ngVam1/05Axju6poRomx/DUxiR0QH1+0Kg15+2A0fRkBggTTn7kvGsgz0cqk9cTm0\nEITpIVGcSGrVNRrmSye2OW0CAwEAAQ==\n-----END RSA PUBLIC KEY-----\n"
  }
}

c. Generate the Entity Secret Ciphertext

Once you have the public key, you'll use RSA encryption to secure your Entity Secret and generate the Ciphertext. Immediately after, you'll transform this encrypted data into the Base64 format. The output Ciphertext will be exactly 684 characters long.

const forge = require('node-forge')

const entitySecret = forge.util.hexToBytes('YOUR_ENTITY_SECRET')
const publicKey = forge.pki.publicKeyFromPem('YOUR_PUBLIC_KEY')
const encryptedData = publicKey.encrypt(entitySecret, 'RSA-OAEP', {
  md: forge.md.sha256.create(),
  mgf1: {
    md: forge.md.sha256.create(),
  },
})

console.log(forge.util.encode64(encryptedData))

NOTE: You can also refer to the provided sample code in Python and Go for encrypting and encoding the Entity Secret.

d. Register the Entity Secret Ciphertext

After generating, encrypting, and encoding the Entity Secret, register the Entity Secret Ciphertext within the developer console. You must register ciphertext in the console only once.

  1. Access the Configurator Page in the developer console.
  2. Enter the Entity Secret Ciphertext generated in the previous step.
  3. Click Register to complete the Entity Secret Ciphertext registration.

Once registered, you are provided with a file to facilitate recovery in cases where the Entity Secret is lost. This file is used in subsequent Entity Secret reset procedures.

Re-Encrypt the Entity Secret to create a new ciphertext for each API request

Circle's APIs Requiring Entity Secret Ciphertext enforce a different Entity Secret Ciphertext for each API request. One-time-use Entity Secret Ciphertext tokens ensure that even if an attacker captures the ciphertext from previous communication, they cannot exploit it in subsequent interactions.

To create a different Entity Secret Ciphertext token, repeat Step 1.c: Generate the Entity Secret Ciphertext. As long as the Entity Secret Ciphertext comes from the same registered entity secret, it is valid for an API request.

Using the same Ciphertext for multiple requests will lead to rejection.

2. Create a Wallet Set

A wallet set refers to a unified set of wallets, all managed by a single cryptographic private key. This makes it possible to have wallets from different blockchains sharing the same address.

To create a wallet set, make a request to POST /developer/walletSets and create a wallet set providing a unique Entity Secret Ciphertext as described in How to Re-Encrypt the Entity Secret.

const response = await circleDeveloperSdk.createWalletSet({
  name: 'Entity WalletSet A'
});

Response Body

JSON
{
  "data": {
    "walletSet": {
      "id": "0189bc61-7fe4-70f3-8a1b-0d14426397cb",
      "custodyType": "DEVELOPER",
      "updateDate": "2023-08-03T17:10:51Z",
      "createDate": "2023-08-03T17:10:51Z"
    }
  }
}

3. Create a Wallet

In Web3, a wallet isn't just a storage mechanism for digital tokens or NFTs; it’s the core structure of all user interactions on the blockchain. A wallet is comprised of a unique address and accompanying metadata stored on the blockchain.

To create a wallet, make a POST request to /developer/wallets using the walletSet.id from step 2 and a count of 2 as request parameters. We'll use the second wallet in the following quickstart to transfer tokens from wallet to wallet. NOTE: Don't forget to generate a new Entity Secret Ciphertext.

Amoy example

The following code samples show how to create an SCA wallet on Amoy and the response.

const response = await circleDeveloperSdk.createWallets({
  accountType: 'SCA',
  blockchains: ['MATIC-AMOY'],
  count: 2,
  walletSetId: '<wallet-set-id>'
});

Response Body

JSON
{
  "data": {
    "wallets": [
      {
        "id": "ce714f5b-0d8e-4062-9454-61aa1154869b",
        "state": "LIVE",
        "walletSetId": "0189bc61-7fe4-70f3-8a1b-0d14426397cb",
        "custodyType": "DEVELOPER",
        "address": "0xf5c83e5fede8456929d0f90e8c541dcac3d63835",
        "blockchain": "MATIC-AMOY",
        "accountType": "SCA",
        "updateDate": "2023-08-03T19:33:14Z",
        "createDate": "2023-08-03T19:33:14Z"
      },
      {
        "id": "703a83de-4851-47b8-ad08-94aa2271bfa6",
        "state": "LIVE",
        "walletSetId": "0189bc61-7fe4-70f3-8a1b-0d14426397cb",
        "custodyType": "DEVELOPER",
        "address": "0x7b777eb80e82f73f118378b15509cb48cd2c2ac3",
        "blockchain": "MATIC-AMOY",
        "accountType": "SCA",
        "updateDate": "2023-08-03T19:33:14Z",
        "createDate": "2023-08-03T19:33:14Z"
      }
    ]
  }
}

Solana example

The following code samples show how to create an EOA wallet on Solana and the response

const response = await circleDeveloperSdk.createWallets({
  accountType: 'EOA',
  blockchains: ['SOL-DEVNET'],
  count: 1,
  walletSetId: '<wallet-set-id>'
});

Response Body

JSON
{
  "data": {
    "wallets": [
      {
        "id": "a7b8c2d1-1c1e-4f7d-b2c3-7f5b9e8c4a9d",
        "state": "LIVE",
        "walletSetId": "0189bc61-7fe4-70f3-8a1b-0d14426397cb",
        "custodyType": "DEVELOPER",
        "address": "9FMYUH1mcQ9F12yjjk6BciTuBC5kvMKadThs941v5vk7",
        "blockchain": "SOL-DEVNET",
        "accountType": "EOA",
        "updateDate": "2023-08-03T19:34:15Z",
        "createDate": "2023-08-03T19:34:15Z"
      },
         ]
  }
}

Next Steps

You have successfully created two developer-controlled wallets! Jump into the next guide, where you will learn how to acquire Testnet tokens and transfer them from wallet to wallet.

  1. Transfer Tokens from Wallet to Wallet: Try out your first transfer from two on-chain wallets!
  2. Deploy a Smart Contract: Use your newly created wallet to deploy your first Smart Contract on-chain!
  3. Infrastructure Models: Learn more about the difference between User-Controlled and Developer-Controlled wallets!
Did this page help you?
© 2023-2024 Circle Technology Services, LLC. All rights reserved.