For Developers: How to Integrate Circle’s Developer Controlled Wallet

Leon Do
G7_DAO
Published in
5 min readAug 24, 2023

--

Overview

Circle’s wallet is a developer option for storing, sending and spending cryptocurrencies and/or NFTs. It’s built on top of multiple-party computation (MPC) technology. This means the private key is split, encrypted and shared among multiple parties. This allows for a secure key management system without a single point of failure, making it ideal for consumer-focused wallets.

Circle has two MPC custodial models

  1. User Controlled Wallets where the user has full control of their wallet.
  2. Developer Controlled Wallets where the developer controls the user’s wallet.

In this tutorial, we’ll go over how to create a Developer Controlled Wallet. This type of wallet allows developers to retain control over the user’s wallet. The advantage is a frictionless user experience which is important for consumer-focused industries, like Web3 gaming. With Developer Controlled wallets, developers can manage blockchain interactions for users, like sending and receiving digital assets or minting NFTs; and users don’t need to be concerned with some of the complexities of Web3, like the repercussions of losing a seed phrase.

Tip

Circle’s roadmap will include an SDK to manage cipher texts (explained below) easily. The current tutorial will explain with curl commands. This can be translated to any language.

Create Circle Account

Visit API Keys and generate a new key

The API key should look like

TEST_API_KEY:3c49129618c3348dc7ceb4659b752df4:ed5a10f941af042f67b272433ad78d1a

To test, curl

curl --request GET \
--url https://api.circle.com/v1/w3s/wallets \
--header 'accept: application/json' \
--header 'authorization: Bearer TEST_API_KEY:3c49129618c3348dc7ceb4659b752df4:ed5a10f941af042f67b272433ad78d1a'

Response should be

{"data":{"wallets":[]}}

Register Developer Controlled Wallet

For critical API requests like create wallet and create transaction in developer-controlled wallet, an entity secret cipher text is required to be appended to the API request parameter.

To generate an secret cipher text

curl --request GET \
--url 'https://api.circle.com/v1/w3s/config/entity/publicKey' \
--header 'accept: application/json' \
--header 'authorization: Bearer TEST_API_KEY:3c49129618c3348dc7ceb4659b752df4:ed5a10f941af042f67b272433ad78d1a'

RSA response

{
"data": {
"publicKey": "-----BEGIN RSA PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA14m3HFbISuOdSKsMrRgV\nK971vBzzqqw+OlhQIIZK2DByoiNXwB00Zrnn2o2w5rCz/Ez1otStpYNK7QyzEtzd\nnCAx6kG24CegieJbRVCc2yRpEyHXnmTCudBtcb9LrgGdhrwjqyTt1u2e4faT/rCf\n3l7BAsy5m5BoLy+lurYgnYaYTXcx2SYRVSe/Ju9WKFag/oXxM8L/ItYgcylXKEaw\ntErY+W5aEbkHsBelgF5tO3AOHUBK19xSlgUneT/T/ygdjWS7t5TLWU0GtEaBYTMJ\nUhRlRGdgK1Xx6Gwejj0tJsIgLcTDvVRbEKl6kIFokslyD5R2c9h4YmzoxDhEMVpk\nTO+AvyN3nUmlm4S9QaPmqMiFuEJqYtpQoyBNpqcjnnZTO1Y/eMA6eENN4D1nFdHG\nSAtY3lDt1rgKmVxkdJfsVJ+ucMbbTJqeedHPBPqI3vzk+jqHW64/Q80mgs074dsN\nosJ5avNjwfByUX7jV22NLALMr3KTqyXMWwyHGMs1Je/77T9V4Oy8MrPAv92A/v4s\nZltBuS34KteX9sYIX8zZ9I5L0s5lYo6naXaOaKFGAg0Iht5jdLhHMZSAdGDK+tDr\n2Mse0GBzC2JPGtG8gV8NSmilJ9sZYiSsc+E9fK7dK4tLob4n1SpyhPqMzVo2mmf1\ndkJaB5v822zrjCpg2tpy2fcCAwEAAQ==\n-----END RSA PUBLIC KEY-----\n"
}
}

Clone this repo

Run

python python/generate_hex_encoded_entity_secret.py

Entity secret response

Hex encoded entity secret: 364ba801bab96e6daa7ef824842f27c08d81e73fea5276c76e5822e8a5e2b556

Edit the python/generate_entity_secret_ciphertext.py

# Paste your entity public key here.
public_key_string = '-----BEGIN RSA PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA14m3HFbISuOdSKsMrRgV\nK971vBzzqqw+OlhQIIZK2DByoiNXwB00Zrnn2o2w5rCz/Ez1otStpYNK7QyzEtzd\nnCAx6kG24CegieJbRVCc2yRpEyHXnmTCudBtcb9LrgGdhrwjqyTt1u2e4faT/rCf\n3l7BAsy5m5BoLy+lurYgnYaYTXcx2SYRVSe/Ju9WKFag/oXxM8L/ItYgcylXKEaw\ntErY+W5aEbkHsBelgF5tO3AOHUBK19xSlgUneT/T/ygdjWS7t5TLWU0GtEaBYTMJ\nUhRlRGdgK1Xx6Gwejj0tJsIgLcTDvVRbEKl6kIFokslyD5R2c9h4YmzoxDhEMVpk\nTO+AvyN3nUmlm4S9QaPmqMiFuEJqYtpQoyBNpqcjnnZTO1Y/eMA6eENN4D1nFdHG\nSAtY3lDt1rgKmVxkdJfsVJ+ucMbbTJqeedHPBPqI3vzk+jqHW64/Q80mgs074dsN\nosJ5avNjwfByUX7jV22NLALMr3KTqyXMWwyHGMs1Je/77T9V4Oy8MrPAv92A/v4s\nZltBuS34KteX9sYIX8zZ9I5L0s5lYo6naXaOaKFGAg0Iht5jdLhHMZSAdGDK+tDr\n2Mse0GBzC2JPGtG8gV8NSmilJ9sZYiSsc+E9fK7dK4tLob4n1SpyhPqMzVo2mmf1\ndkJaB5v822zrjCpg2tpy2fcCAwEAAQ==\n-----END RSA PUBLIC KEY-----\n'

# If you already have a hex encoded entity secret, you can paste it here. the length of the hex string should be 64.
hex_encoded_entity_secret = '364ba801bab96e6daa7ef824842f27c08d81e73fea5276c76e5822e8a5e2b556'

Run the script to get

Hex encoded entity secret: f976dfefeab13d8c706c8553b5eb0c54a4ca2d168dfd77835dcc8a806abc2b6f
Entity secret ciphertext: X3SmZsKFMaxvHyRozJwcjocgENO3kv6eq9tZqrdW4i+6dzWwHeQ7Jas7O4nBrf8bCnKMOke3omcuAdprJm+5ZYH+z7BYCgIZuUlrR3idJsMhmO2+poxUL/PvMF7M+z2bzGEBPZPif/beQUlQ8b1g2JAi7VcOddfTq9LRw8HWq9GHuwV4wjhnSoIZdklp84Gmv8N+jzDjEQbinyy4mlIHZlpfYmY2CO+Flkqdu/FGIz/YZUkITcGQ+xMxwL3NZ+adOOohFL+8q52zWIqQt/7Arw2wO9Cu5vo441tY0QnRsbxF6MybsJ/gGVP8S9NxOmYshVcpS8V0Ux5N5W8v9bBMB8Bsezlhb3I6VpHY3hElQseYwzNDAp3eA6hKBkveKmkJQW5Q5vbiRApIRBlpwlc4HtBNRFAE7pU7BLGViRcDoptAs0AKrloUOIO74iy3pqAYj3hjeFVCs+q9zftcmQ3kClD3rChWFDw95wlVlN8XKAj8WDBd2cYhCmFkqZzWRQU3Qmg1Z3Rub4VcNpGrLDsHIoLBrp72rkI08g/FaXJujnhdPM6ppTY9rBo51TjapHK33yIQyNsbYM0q/LdnH0kbTD07GHYF3qfMUqUiKAYC3Zh8B0tXJXNY/jR4qP7kcAUlsj/iYcAcFm3tJBfIOIqshjLcv5S8pXKKFJL3aM7xKPw=

Register

Create a Wallet Set

A wallet set is contains one or many wallets.

curl --request POST \
--url 'https://api.circle.com/v1/w3s/developer/walletSets' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--header 'authorization: Bearer TEST_API_KEY:3c49129618c3348dc7ceb4659b752df4:ed5a10f941af042f67b272433ad78d1a' \
--data '{
"idempotencyKey": "4e56ca1f-8ad8-4cd0-b9d5-3837539125f5",
"name": "My Wallet Set",
"entitySecretCiphertext": "X3SmZsKFMaxvHyRozJwcjocgENO3kv6eq9tZqrdW4i+6dzWwHeQ7Jas7O4nBrf8bCnKMOke3omcuAdprJm+5ZYH+z7BYCgIZuUlrR3idJsMhmO2+poxUL/PvMF7M+z2bzGEBPZPif/beQUlQ8b1g2JAi7VcOddfTq9LRw8HWq9GHuwV4wjhnSoIZdklp84Gmv8N+jzDjEQbinyy4mlIHZlpfYmY2CO+Flkqdu/FGIz/YZUkITcGQ+xMxwL3NZ+adOOohFL+8q52zWIqQt/7Arw2wO9Cu5vo441tY0QnRsbxF6MybsJ/gGVP8S9NxOmYshVcpS8V0Ux5N5W8v9bBMB8Bsezlhb3I6VpHY3hElQseYwzNDAp3eA6hKBkveKmkJQW5Q5vbiRApIRBlpwlc4HtBNRFAE7pU7BLGViRcDoptAs0AKrloUOIO74iy3pqAYj3hjeFVCs+q9zftcmQ3kClD3rChWFDw95wlVlN8XKAj8WDBd2cYhCmFkqZzWRQU3Qmg1Z3Rub4VcNpGrLDsHIoLBrp72rkI08g/FaXJujnhdPM6ppTY9rBo51TjapHK33yIQyNsbYM0q/LdnH0kbTD07GHYF3qfMUqUiKAYC3Zh8B0tXJXNY/jR4qP7kcAUlsj/iYcAcFm3tJBfIOIqshjLcv5S8pXKKFJL3aM7xKPw="
}'

Response

{
"data": {
"walletSet": {
"id": "0189f75a-6439-7a29-a3d4-ffc28c4411da",
"custodyType": "DEVELOPER",
"name": "My Wallet Set",
"updateDate": "2023-08-15T04:00:42Z",
"createDate": "2023-08-15T04:00:42Z"
}
}
}

Create a Wallet

Create a wallet inside the wallet set using the walletSet.id above

curl --request POST \
--url 'https://api.circle.com/v1/w3s/developer/wallets' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--header 'authorization: Bearer TEST_API_KEY:3c49129618c3348dc7ceb4659b752df4:ed5a10f941af042f67b272433ad78d1a' \
--data '{
"idempotencyKey": "e8105afa-29cf-46cf-802f-76d67a5a9adc",
"blockchains": [
"MATIC-MUMBAI"
],
"count": 1,
"entitySecretCiphertext": "X3SmZsKFMaxvHyRozJwcjocgENO3kv6eq9tZqrdW4i+6dzWwHeQ7Jas7O4nBrf8bCnKMOke3omcuAdprJm+5ZYH+z7BYCgIZuUlrR3idJsMhmO2+poxUL/PvMF7M+z2bzGEBPZPif/beQUlQ8b1g2JAi7VcOddfTq9LRw8HWq9GHuwV4wjhnSoIZdklp84Gmv8N+jzDjEQbinyy4mlIHZlpfYmY2CO+Flkqdu/FGIz/YZUkITcGQ+xMxwL3NZ+adOOohFL+8q52zWIqQt/7Arw2wO9Cu5vo441tY0QnRsbxF6MybsJ/gGVP8S9NxOmYshVcpS8V0Ux5N5W8v9bBMB8Bsezlhb3I6VpHY3hElQseYwzNDAp3eA6hKBkveKmkJQW5Q5vbiRApIRBlpwlc4HtBNRFAE7pU7BLGViRcDoptAs0AKrloUOIO74iy3pqAYj3hjeFVCs+q9zftcmQ3kClD3rChWFDw95wlVlN8XKAj8WDBd2cYhCmFkqZzWRQU3Qmg1Z3Rub4VcNpGrLDsHIoLBrp72rkI08g/FaXJujnhdPM6ppTY9rBo51TjapHK33yIQyNsbYM0q/LdnH0kbTD07GHYF3qfMUqUiKAYC3Zh8B0tXJXNY/jR4qP7kcAUlsj/iYcAcFm3tJBfIOIqshjLcv5S8pXKKFJL3aM7xKPw=",
"walletSetId": "0189f75a-6439-7a29-a3d4-ffc28c4411da"
}'

Response

{
"data": {
"wallets": [
{
"id": "74f423b5-0c14-47ee-bdf6-d1a87b6fb06f",
"state": "LIVE",
"walletSetId": "0189f75a-6439-7a29-a3d4-ffc28c4411da",
"custodyType": "DEVELOPER",
"address": "0xe5c73198f9bcdef8d995b377b5108c271799e692",
"addressIndex": 0,
"blockchain": "MATIC-MUMBAI",
"accountType": "EOA",
"updateDate": "2023-08-15T04:16:01Z",
"createDate": "2023-08-15T04:16:01Z"
}
]
}
}

Verify

Fund the wallet

Transfer Tokens

curl --request POST \
--url 'https://api.circle.com/v1/w3s/developer/transactions/transfer' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--header 'authorization: Bearer TEST_API_KEY:3c49129618c3348dc7ceb4659b752df4:ed5a10f941af042f67b272433ad78d1a' \
--data '{
"idempotencyKey": "1033769d-9592-440f-aa92-982c6e0077ff",
"walletId": "74f423b5-0c14-47ee-bdf6-d1a87b6fb06f",
"tokenId": "e4f549f9-a910-59b1-b5cd-8f972871f5db",
"destinationAddress": "0xdD4c825203f97984e7867F11eeCc813A036089D1",
"amounts": [
".01"
],
"feeLevel": "MEDIUM",
"entitySecretCiphertext": "X3SmZsKFMaxvHyRozJwcjocgENO3kv6eq9tZqrdW4i+6dzWwHeQ7Jas7O4nBrf8bCnKMOke3omcuAdprJm+5ZYH+z7BYCgIZuUlrR3idJsMhmO2+poxUL/PvMF7M+z2bzGEBPZPif/beQUlQ8b1g2JAi7VcOddfTq9LRw8HWq9GHuwV4wjhnSoIZdklp84Gmv8N+jzDjEQbinyy4mlIHZlpfYmY2CO+Flkqdu/FGIz/YZUkITcGQ+xMxwL3NZ+adOOohFL+8q52zWIqQt/7Arw2wO9Cu5vo441tY0QnRsbxF6MybsJ/gGVP8S9NxOmYshVcpS8V0Ux5N5W8v9bBMB8Bsezlhb3I6VpHY3hElQseYwzNDAp3eA6hKBkveKmkJQW5Q5vbiRApIRBlpwlc4HtBNRFAE7pU7BLGViRcDoptAs0AKrloUOIO74iy3pqAYj3hjeFVCs+q9zftcmQ3kClD3rChWFDw95wlVlN8XKAj8WDBd2cYhCmFkqZzWRQU3Qmg1Z3Rub4VcNpGrLDsHIoLBrp72rkI08g/FaXJujnhdPM6ppTY9rBo51TjapHK33yIQyNsbYM0q/LdnH0kbTD07GHYF3qfMUqUiKAYC3Zh8B0tXJXNY/jR4qP7kcAUlsj/iYcAcFm3tJBfIOIqshjLcv5S8pXKKFJL3aM7xKPw="
}'

Response

{
"data": {
"id": "f84f811d-a433-54ef-b300-027ee79adbab",
"state": "INITIATED"
}
}

Check Transfer State

curl --request GET \
--url 'https://api.circle.com/v1/w3s/transactions/f84f811d-a433-54ef-b300-027ee79adbab' \
--header 'accept: application/json' \
--header 'authorization: Bearer TEST_API_KEY:3c49129618c3348dc7ceb4659b752df4:ed5a10f941af042f67b272433ad78d1a'

Response

{
"data": {
"transaction": {
"id": "f84f811d-a433-54ef-b300-027ee79adbab",
"blockchain": "MATIC-MUMBAI",
"tokenId": "e4f549f9-a910-59b1-b5cd-8f972871f5db",
"walletId": "74f423b5-0c14-47ee-bdf6-d1a87b6fb06f",
"sourceAddress": "0xe5c73198f9bcdef8d995b377b5108c271799e692",
"destinationAddress": "0xc90e058234d4b2db799d787a855ec68d801a53a3",
"transactionType": "OUTBOUND",
"custodyType": "DEVELOPER",
"state": "COMPLETE",
"amounts": ["0.01"],
"nfts": null,
"txHash": "0x6804311addbfe75de2fe5a58186dd0ce19c5c9f51b82d7247c4d4bf67967d7f2",
"blockHash": "0xac05f40184fe4bbd0c22b285b972bf0b6a86b56deccc05b4a102c0f699163d0f",
"blockHeight": 39026255,
"networkFee": "0.000087493544775",
"firstConfirmDate": "2023-08-15T16:11:14Z",
"operation": "TRANSFER",
"feeLevel": "MEDIUM",
"estimatedFee": { "gasLimit": "21000", "baseFee": "0.000000015", "priorityFee": "4.16635926", "maxFee": "4.16635929" },
"refId": "",
"abiParameters": null,
"createDate": "2023-08-15T16:11:08Z",
"updateDate": "2023-08-15T16:12:02Z"
}
}
}

Wallets can also do contract execution

Conclusion

Once a Developer Controlled Wallet is registered and created, the user flow can be.

  • User signs up with OAuth or any web2 login
  • Once signed up, the developer can create a new wallet and fund it
  • For every user action, the developer can send transactions on the user’s behalf.

This allows the user to have a familiar experience and the developer is able to manage and track user activity in one dashboard.

--

--