For Developers: How to Integrate Circle’s Developer Controlled Wallet
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
- User Controlled Wallets where the user has full control of their wallet.
- 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.