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.

  1. Include userToken copied from the previous step in the header of a GET request to the /wallets endpoint.
  2. 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>'
    
  3. Using id which you just copied, send a GET request to the /wallet/{id}/balances endpoint to check your wallet’s token balance.
    From the response, copy and store the tokenId 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:

  1. Include walletId and destinationAddress in a POST request to the /user/transactions/transfer endpoint.
  2. From the response, copy challengeId for the next step.

Execute a contract

To create a transaction that executes a smart contract:

  1. Include walletId and contractAddress in a POST request to the /user/transactions/contractExecution endpoint.
  2. 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.

    ucw-sotec-execchall01

    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 .

    ucw-sotec-confirmtrx01

ucw-sotec-contractint01

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"
      }
    ]
  }
}