By Wallet Type

Withdraw from Wallet - Self-Custody MPC (Simple)

Overview

The simple withdrawal flow for self-custody MPC wallets enables you to build, sign, and send transactions, all in one call, using the BitGo JavaScript SDK. The simple flow suffices for most use cases. If you require more granular control, see the integration guide for the manual flow.

The simple flow also supports sending to many recipients in 1 transaction, lowering the aggregate amount of blockchain fees when compared to creating multiple transactions. Sending to many aligns with the simple transaction flow, where in one call you build, sign, and send a half-signed transaction to BitGo. BitGo then uses the BitGo key to create a fully signed send-to-many transaction.

Just like with the manual flow, you can configure wallet policies to require approvals on withdrawals. Once a wallet admin half signs and approves the transaction, you send it to BitGo for final signing and broadcasting to the blockchain.

Prerequisites

Cookbook

Need just the steps? Expand the cookbook below to get started:

Withdraw - Self-Custody MPC (Simple)Open Cookbook

1. Build, Sign, and Send Transaction

Build and sign the transaction and send it to BitGo, all in one call.

Additionally, you can create a send-to-many transaction that automatically invokes a batcher contract to send assets to multiple recipients. Currently, send-to-many transactions are available for ETH and MATIC and support up to 200 recipients.

Endpoints:

import * as dotenv from "dotenv";
import {EnvironmentName} from "bitgo";
import {BitGoAPI} from "@bitgo/sdk-api";
import {Hteth} from "@bitgo/sdk-coin-eth";

dotenv.config();

const config = {
    USERNAME: process.env.USERNAME as string,
    PASSWORD: process.env.PASSWORD as string,
    ENV: process.env.ENV as EnvironmentName,
    OTP: process.env.OTP as string,
    ENTERPRISE_ID: process.env.ENTERPRISE_ID as string,
    WALLET_ID: process.env.WALLET_ID as string,
    WALLET_PASS_PHRASE: process.env.WALLET_PASS_PHRASE as string
};

const bitgo = new BitGoAPI({env: config.ENV});
bitgo.register("hteth", Hteth.createInstance);
const coin = bitgo.coin("hteth");

async function auth() {
    await bitgo.authenticate({
        username: config.USERNAME,
        password: config.PASSWORD,
        otp: config.OTP,
    });
    await bitgo.lock();
    await bitgo.unlock({otp: "000000", duration: 3600});
}

async function main() {
    await auth();
    const destinatonAddress = "0x1037c88b10fbd0754b9fbd3ba7be41fe7cb61f59";
    const wallet = await coin.wallets().get({id: config.WALLET_ID});
    const sendAmount = wallet.balanceString() ?? 0;
    const res = await wallet.sendMany({
        walletPassphrase: config.WALLET_PASS_PHRASE,
        recipients: [{address: destinatonAddress, amount: sendAmount}],
        type: "transfer",
    });
    console.log(res);
}

main().catch((err) => console.error(err));

Step Result

BitGo uses the data you pass to build a half-signed transaction. The transaction remains in a pending-approval status until a wallet admin approves it.

{
  "transfer": {
    "entries": [
      {
        "address": "0x1a52b70e708f4aec9d92260aa035b3c71e51df84",
        "wallet": "665e33f768f9b4b1cc2ca87a6b87cdfb",
        "value": -1,
        "valueString": "-1"
      },
      {
        "address": "0xe2c5b494162bd9033283af31e35be27c6ee4bbf7",
        "value": 1,
        "valueString": "1"
      }
    ],
    "id": "665e352e29c225381225e8f5f74f429d",
    "coin": "hteth",
    "wallet": "665e33f768f9b4b1cc2ca87a6b87cdfb",
    "walletType": "hot",
    "enterprise": "66325011d4b1ff58feae3b311d669aae",
    "organization": "66325011d4b1ff58feae3b76c2d30987",
    "txid": "0x43f7bef95dd02eea33d447afc625908a429902fbd35a35f0da6b7befddc69d9b",
    "txidType": "transactionHash",
    "txRequestId": "2c7e52a6-a422-49d3-876c-c050fb4cbf36",
    "height": 999999999,
    "heightId": "999999999-665e352e29c225381225e8f5f74f429d",
    "date": "2024-06-03T21:27:10.071Z",
    "type": "send",
    "value": -1,
    "valueString": "-1",
    "intendedValueString": "-1",
    "baseValue": -1,
    "baseValueString": "-1",
    "baseValueWithoutFees": -1,
    "baseValueWithoutFeesString": "-1",
    "feeString": "0",
    "payGoFee": 0,
    "payGoFeeString": "0",
    "usd": 0,
    "usdRate": 3775.4311802956,
    "state": "signed",
    "instant": false,
    "isReward": false,
    "isUnlock": false,
    "isFee": false,
    "tags": [
      "665e33f768f9b4b1cc2ca87a6b87cdfb",
      "66325011d4b1ff58feae3b311d669aae"
    ],
    "history": [
      {
        "date": "2024-06-03T21:27:10.071Z",
        "action": "signed"
      },
      {
        "date": "2024-06-03T21:27:10.020Z",
        "user": "660d7c9fa6fbf0d00b415146cf70405a",
        "action": "created"
      }
    ],
    "signedDate": "2024-06-03T21:27:10.071Z",
    "coinSpecific": {
      "isMev": false
    },
    "metadata": [],
    "signedTime": "2024-06-03T21:27:10.071Z",
    "createdTime": "2024-06-03T21:27:10.020Z"
  },
  "txRequest": {
    "apiVersion": "full",
    "txRequestId": "2c7e52a6-a422-49d3-876c-c050fb4cbf36",
    "walletId": "665e33f768f9b4b1cc2ca87a6b87cdfb",
    "walletType": "hot",
    "version": 10,
    "enterpriseId": "66325011d4b1ff58feae3b311d669aae",
    "organizationId": "66325011d4b1ff58feae3b76c2d30987",
    "userId": "660d7c9fa6fbf0d00b415146cf70405a",
    "initiatedBy": "660d7c9fa6fbf0d00b415146cf70405a",
    "updatedBy": "660d7c9fa6fbf0d00b415146cf70405a",
    "policiesChecked": true,
    "date": "2024-06-03T21:27:10.203Z",
    "createdDate": "2024-06-03T21:27:01.066Z",
    "intent": {
      "intentType": "payment",
      "recipients": [
        {
          "address": {
            "address": "0xE2C5b494162bd9033283Af31e35Be27C6Ee4Bbf7"
          },
          "amount": {
            "value": "1",
            "symbol": "hteth"
          }
        }
      ],
      "feeOptions": {
        "baseFee": "1005178132",
        "gasUsedRatio": "0.2743939",
        "safeLowMinerTip": "10896128",
        "normalMinerTip": "22751361",
        "standardMinerTip": "498266940",
        "fastestMinerTip": "437486468",
        "ludicrousMinerTip": "1500000000",
        "maxPriorityFeePerGas": "498266940",
        "maxFeePerGas": "2508623204"
      },
      "senderAddressIndex": 0
    },
    "intents": [
      {
        "intentType": "payment",
        "recipients": [
          {
            "address": {
              "address": "0xE2C5b494162bd9033283Af31e35Be27C6Ee4Bbf7"
            },
            "amount": {
              "value": "1",
              "symbol": "hteth"
            }
          }
        ],
        "feeOptions": {
          "baseFee": "1005178132",
          "gasUsedRatio": "0.2743939",
          "safeLowMinerTip": "10896128",
          "normalMinerTip": "22751361",
          "standardMinerTip": "498266940",
          "fastestMinerTip": "437486468",
          "ludicrousMinerTip": "1500000000",
          "maxPriorityFeePerGas": "498266940",
          "maxFeePerGas": "2508623204"
        },
        "senderAddressIndex": 0
      }
    ],
    "state": "delivered",
    "latest": true,
    "transactions": [
      {
        "state": "delivered",
        "signatureShares": [],
        "commitmentShares": [],
        "unsignedTx": {
          "parsedTx": {
            "minerFee": "0",
            "spendAmount": "1",
            "spendAmounts": [
              {
                "coinName": "hteth",
                "amountString": "1"
              }
            ],
            "outputs": [
              {
                "address": "0xe2c5b494162bd9033283af31e35be27c6ee4bbf7",
                "wallets": [],
                "enterprises": [],
                "valueString": "1",
                "coinName": "hteth"
              }
            ],
            "inputs": [
              {
                "value": 1,
                "address": "0x1a52b70e708f4aec9d92260aa035b3c71e51df84",
                "valueString": "1"
              }
            ],
            "hasUnvalidatedData": false
          },
          "serializedTxHex": "02ed82426880841db2f33c8495868d648301637894e2c5b494162bd9033283af31e35be27c6ee4bbf70180c0808080",
          "signableHex": "02ea82426880841db2f33c8495868d648301637894e2c5b494162bd9033283af31e35be27c6ee4bbf70180c0",
          "feeInfo": {
            "fee": 136813501552000,
            "feeString": "136813501552000"
          },
          "derivationPath": "m"
        },
        "txHash": "0x43f7bef95dd02eea33d447afc625908a429902fbd35a35f0da6b7befddc69d9b",
        "signedTx": {
          "id": "0x43f7bef95dd02eea33d447afc625908a429902fbd35a35f0da6b7befddc69d9b",
          "tx": "0x02f86d82426880841db2f33c8495868d648301637894e2c5b494162bd9033283af31e35be27c6ee4bbf70180c080a0631c4464bcb29519ac7e3998955c1bda374d058868557661c3711d531cb37e62a04836e3cf75969a2ddd9c38981d1b6fade02733059544c74784f9a85e62da8719"
        },
        "updatedDate": "2024-06-03T21:27:10.215Z",
        "createdDate": "2024-06-03T21:27:01.072Z"
      }
    ],
    "messages": [],
    "isCanceled": false
  },
  "txid": "0x43f7bef95dd02eea33d447afc625908a429902fbd35a35f0da6b7befddc69d9b",
  "tx": "0x02f86d82426880841db2f33c8495868d648301637894e2c5b494162bd9033283af31e35be27c6ee4bbf70180c080a0631c4464bcb29519ac7e3998955c1bda374d058868557661c3711d531cb37e62a04836e3cf75969a2ddd9c38981d1b6fade02733059544c74784f9a85e62da8719",
  "status": "signed"
}

2. Approve Transaction (Optional)

Note: You can't approve your own transactions - another admin must approve them.

Endpoint: Update Pending Approval

export BITGO_EXPRESS_HOST="<YOUR_LOCAL_HOST>"
export COIN="<ASSET_ID>"
export APPROVAL_ID="<APPROVAL_ID>"
export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>"
export WALLET_PASSPHRASE="<YOUR_WALLET_PASSPHRASE>"

curl -X PUT \
  http://$BITGO_EXPRESS_HOST/api/v2/$COIN/pendingApprovals/$APPROVAL_ID \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -d '{
    "state": "approved",
    "walletPassphrase": "'"$WALLET_PASSPHRASE"'"
  }'

Step Result

After a wallet admin approves the transaction, BitGo rebuilds the half-signed transaction, applying the most up-to-date fees. BitGo then applies the final signature using the BitGo key and broadcasts the transaction to the blockchain.

{
  "id": "66b66201c45ab3125fc2d8a77b541d12",
  "coin": "tsol",
  "wallet": "669569464cec3c82c9b524faf0d42087",
  "wallets": [],
  "enterprise": "6672220dd9e61ce3d62e9e2d710cbd6f",
  "organization": "66722210d9e61ce3d62e9e997100b40a",
  "bitgoOrg": "BitGo Trust",
  "creator": "6672175d79369c8ad556deaca06abeea",
  "createDate": "2024-08-09T18:37:53.078Z",
  "approvedDate": "2024-08-09T18:38:47.371Z",
  "info": {
    "type": "transactionRequestFull",
    "transactionRequestFull": {
      "txRequestId": "0b259f23-d5a8-47d0-8666-47a3df07e244",
      "videoApprovers": [],
      "intent": {
        "intentType": "payment",
        "recipients": [
          {
            "address": {
              "address": "ESda41265eU4typ7Q7MFnBuaYUvV3rYsJyrGQzqo6YZn"
            },
            "amount": {
              "value": "500000",
              "symbol": "tsol"
            }
          }
        ]
      }
    }
  },
  "approvers": [],
  "state": "approved",
  "scope": "wallet",
  "userIds": [
    "6672175d79369c8ad556deaca06abeea",
    "6672292d89bd038e41cfb93b6ee482f1"
  ],
  "approvalsRequired": 1,
  "singleRunResults": [],
  "txRequestId": "0b259f23-d5a8-47d0-8666-47a3df07e244",
  "resolvers": [
    {
      "user": "6672292d89bd038e41cfb93b6ee482f1",
      "date": "2024-08-09T18:38:45.245Z",
      "resolutionType": "pending",
      "resolutionAction": "approve"
    }
  ],
  "policyEvaluationId": "8dc3ffdd-50ea-4ab6-9742-963a12eb4f6c",
  "actions": [
    {
      "id": "d0bcc3af-b341-4b1c-9c7e-723ff4bc7259",
      "status": "COMPLETE",
      "name": "approvals.customer.walletAdmin",
      "parameters": {
        "minRequired": "1",
        "userIds": []
      },
      "resolvers": [
        {
          "user": "6672292d89bd038e41cfb93b6ee482f1",
          "date": "2024-08-09T18:38:45.245Z",
          "resolutionType": "pending",
          "resolutionAction": "approve"
        }
      ],
      "approvers": []
    }
  ],
  "resolutionOrder": [
    {
      "actions": [
        "d0bcc3af-b341-4b1c-9c7e-723ff4bc7259"
      ]
    }
  ]
}

Next

You can view your completed withdrawal in BitGo or on a blockchain explorer.

See Also