Send an Outbound Transfer or Execute Contract
Learn how to send USDC or execute a contract from a user-controlled wallet with social logins or email authentication
This guide outlines how to initiate a currency transfer or execute a contract from a previously created user-controlled wallet with social logins or email authentication. To create a wallet, see Create Your First Wallet with Social Logins or Create Your First Wallet with Email.
If you’re building with Wallet Signing Service, you must manage the transaction broadcasting and indexing with your blockchain infrastructure.
If it has been over 14 days or you end the last trial attempt since you first ran the sample app, your session is logged out. You must run the sample app and acquire a new user token to proceed further. For instructions, see the perform login part of Create Your First Wallet with Social Logins or Create Your First Wallet with Email Authentication.
Step 1. Check wallet balance and obtain token ID
Before making an outbound transfer, you must have a token ID and token balance greater than 0
.
- Include
userToken
copied from the previous step in the header of aGET
request to the/wallets
endpoint. - From the response, copy
id
which is your wallet ID.const response = await circleUserSdk.listWallets({ userToken: '<USER_TOKEN>', pageSize: 10 });
# create an api instance api_instance = user_controlled_wallets.WalletsApi(client) # list wallets try: response = api_instance.list_wallets("<USER_TOKEN>") print(response) except user_controlled_wallets.ApiException as e: print("Exception when calling WalletsApi->list_wallets: %s\n" % e)
curl --request GET \ --url 'https://api.circle.com/v1/w3s/wallets?pageSize=10' \ --header 'accept: application/json' \ --header 'authorization: Bearer <API_KEY>' \ --header 'X-User-Token: <USER_TOKEN>'
- Using
id
which you just copied, send aGET
request to the/wallet/{id}/balances
endpoint to check your wallet’s token balance.
From the response, copy and store thetokenId
that you intend to transfer.const response = await circleUserSdk.getWalletTokenBalance({ userToken: '<USER_TOKEN>', walletId: '<WALLET_ID>' });
# create an api instance api_instance = user_controlled_wallets.WalletsApi(client) try: response = api_instance.list_wallet_ballance("<USER_TOKEN>", "<WALLET_ID>") print(response) except user_controlled_wallets.ApiException as e: print("Exception when calling WalletsApi->list_wallet_ballance: %s\n" % e)
curl --request GET \ --url 'https://api.circle.com/v1/w3s/wallets/{id}/balances' \ --header 'accept: application/json' \ --header 'authorization: Bearer <API_KEY>' \ --header 'X-User-Token: <USER_TOKEN>'
Step 2. Estimate the cost of transaction (optional)
To estimate the fees for the transaction to transfer tokens, send a POST
request to the transactions/transfer/estimateFee
endpoint.
const response = await circleUserSdk.estimateTransferFee({
userToken: '<USER_TOKEN>',
amount: ['.01'],
destinationAddress: '0xEb9614D6d001391e22dDbbEA7571e9823A469c1f',
tokenId: '36b6931a-873a-56a8-8a27-b706b17104ee',
walletId: '01899cf2-d415-7052-a207-f9862157e546'
});
# create an api instance
api_instance = user_controlled_wallets.TransactionsApi(client)
# get user token
try:
request = user_controlled_wallets.EstimateTransferTransactionFeeRequest.from_dict({
"userToken": '<USER_TOKEN>',
"amounts": ['.01'],
"destinationAddress": '0xEb9614D6d001391e22dDbbEA7571e9823A469c1f',
"tokenId": '36b6931a-873a-56a8-8a27-b706b17104ee',
"walletId": '01899cf2-d415-7052-a207-f9862157e546'
})
response = api_instance.create_transfer_estimate_fee("<USER_TOKEN>", request)
print(response)
except user_controlled_wallets.ApiException as e:
print("Exception when calling TransactionsApi->create_transfer_estimate_fee: %s\n" % e)
curl --request POST \
--url 'https://api.circle.com/v1/w3s/transactions/transfer/estimateFee' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--header 'authorization: Bearer <API_KEY>' \
--header 'X-User-Token: <USER_TOKEN>' \
--data '
{
"amounts": [".01"],
"destinationAddress": "0xEb9614D6d001391e22dDbbEA7571e9823A469c1f",
"tokenId": "38f2ad29-a77b-5a44-be05-8d03923878a2",
"walletId": "01899cf2-d415-7052-a207-f9862157e546"
}
{
"data": {
"low": {
"gasLimit": "21000",
"baseFee": "2.456220277",
"priorityFee": "1.022783914",
"maxFee": "5.935224468"
},
"medium": {
"gasLimit": "21000",
"baseFee": "2.456220277",
"priorityFee": "2.655282857",
"maxFee": "7.567723411"
},
"high": {
"gasLimit": "21000",
"baseFee": "2.456220277",
"priorityFee": "15.986229693",
"maxFee": "20.898670247"
}
}
}
Step 3. Initiate a blockchain transaction
Execute an asset transfer
To initiate a blockchain transfer from a specified wallet to a blockchain address:
- Include
walletId
anddestinationAddress
in aPOST
request to the/user/transactions/transfer
endpoint. - From the response, copy
challengeId
for the next step.
Execute a contract
To create a transaction that executes a smart contract:
- Include
walletId
andcontractAddress
in aPOST
request to the/user/transactions/contractExecution
endpoint. - From the response, copy
challengeId
and enter it in the sample app.
If you do not have a wallet to use as a destination for the transfer, you can create another user-controlled wallet or send funds to any other blockchain wallet such as Metamask.
Step 4. Confirm execution
To authorize a transfer on the sample app's confirmation UI, ensure you have obtained an active user token and encryption key.
-
From the Execute Challenge screen in the sample app, paste the Challenge ID that you copied from the previous step and select Execute.
The sample app displays a confirmation UI that contains transaction details for the user to confirm. You can customize confirmation UIs. For more, see Confirmation UIs .
Step 5. Check transaction status
As the transfer transaction’s state
changes and ultimately completes, Circle sends webhook notifications to a subscriber endpoint. For a list of all possible states, see Asynchronous States and Statuses.
The following code sample shows an example of a webhook notification for a transfer.
{
"subscriptionId": "d4c07d5f-f05f-4fe4-853d-4dd434806dfb",
"notificationId": "acab8c14-92ae-481a-8335-6eb5271da014",
"notificationType": "transactions.outbound",
"notification": {
"id": "ad3f40ae-9c0e-52cf-816f-91838850572a",
"blockchain": "MATIC-AMOY",
"tokenId": "36b6931a-873a-56a8-8a27-b706b17104ee",
"walletId": "01899cf2-d415-7052-a207-f9862157e546",
"sourceAddress": "0x7b777eb80e82f73f118378b15509cb48cd2c2ac3",
"destinationAddress": "0x6e5eaf34c73d1cd0be4e24f923b97cf38e10d1f3",
"transactionType": "OUTBOUND",
"custodyType": "ENDUSER",
"state": "COMPLETE",
"amounts": [
"0.01"
],
"nfts": null,
"txHash": "0x535ff240984f54e755d67cdc9c79c88768fe5997955f09f3a66b4d1126810900",
"blockHash": "0xa4c5c79500240f3ae3f4e5c5f641198b7c698d83b7539ac4e8cf2d3f5f49bdfd",
"blockHeight": 41100000,
"networkFee": "0.07037500047405219",
"firstConfirmDate": "2023-10-11T21:08:28Z",
"operation": "TRANSFER",
"userId": "c266945c-f440-4537-85cf-a16b6e33b0cc",
"abiParameters": null,
"createDate": "2023-10-11T21:08:13Z",
"updateDate": "2023-10-11T21:08:37Z"
},
"timestamp": "2023-10-11T21:08:13Z",
"version": 2
}
You can also include your user's userId
or userToken
in a GET
request to the /transactions
endpoint.
const response = await circleUserSdk.listTransactions({
userToken: '<USER_TOKEN>'
});
# create an api instance
api_instance = user_controlled_wallets.TransactionsApi(client)
# get user token
try:
response = api_instance.list_transactions("<USER_TOKEN>")
print(response)
except user_controlled_wallets.ApiException as e:
print("Exception when calling TransactionsApi->list_transactions: %s\n" % e)
curl --request GET \
--url 'https://api.circle.com/v1/w3s/transactions' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--header 'authorization: Bearer <API_KEY>' \
--header 'X-User-Token: <USER_TOKEN>'
The following code sample shows the response.
{
"data": {
"transactions": [
{
"id": "ad3f40ae-9c0e-52cf-816f-91838850572a",
"blockchain": "MATIC-AMOY",
"tokenId": "36b6931a-873a-56a8-8a27-b706b17104ee",
"walletId": "01899cf2-d415-7052-a207-f9862157e546",
"sourceAddress": "0x7b777eb80e82f73f118378b15509cb48cd2c2ac3",
"destinationAddress": "0x6e5eaf34c73d1cd0be4e24f923b97cf38e10d1f3",
"transactionType": "OUTBOUND",
"custodyType": "ENDUSER",
"state": "COMPLETE",
"amounts": [
"0.01"
],
"nfts": null,
"txHash": "0x535ff240984f54e755d67cdc9c79c88768fe5997955f09f3a66b4d1126810900",
"blockHash": "0xa4c5c79500240f3ae3f4e5c5f641198b7c698d83b7539ac4e8cf2d3f5f49bdfd",
"blockHeight": 41100000,
"networkFee": "0.07037500047405219",
"firstConfirmDate": "2023-10-11T21:08:28Z",
"operation": "TRANSFER",
"userId": "c266945c-f440-4537-85cf-a16b6e33b0cc",
"abiParameters": null,
"createDate": "2023-10-11T21:08:13Z",
"updateDate": "2023-10-11T21:08:37Z"
},
{
"id": "81cf790a-ed95-5d41-b7bd-c4e15390eef6",
"blockchain": "MATIC-AMOY",
"tokenId": "36b6931a-873a-56a8-8a27-b706b17104ee",
"walletId": "01899cf2-d415-7052-a207-f9862157e546",
"sourceAddress": "0x48520ff9b32d8b5bf87abf789ea7b3c394c95ebe",
"destinationAddress": "0x7b777eb80e82f73f118378b15509cb48cd2c2ac3",
"transactionType": "INBOUND",
"custodyType": "ENDUSER",
"state": "COMPLETE",
"amounts": [
"10"
],
"nfts": null,
"txHash": "0x5121f9efec29d4d661ffb0b777727d1f5ba7b5bc286ac4891c82f7b1b80a9485",
"blockHash": "0xba7984dbe7423827b5fd175a636552ae85401c3f2a0c5cdda934a37d6652ac49",
"blockHeight": 41098635,
"networkFee": "0.001911870000955935",
"firstConfirmDate": "2023-10-11T20:13:33Z",
"operation": "TRANSFER",
"userId": "c266945c-f440-4537-85cf-a16b6e33b0cc",
"abiParameters": null,
"createDate": "2023-10-11T20:13:33Z",
"updateDate": "2023-10-11T20:13:45Z"
}
]
}
}
Updated 29 days ago