Table of contents
Building your request body
Sending Bitcoin is made easy with the BitGo Express. All you need is some identifying information for the originating wallet. As well as the destination wallet address and the amount that is to be sent.
Note: If your access token doesn't have a spending limit, it will lock regularly, preventing you from sending a transaction. If your access token is locked, Unlock the Session to proceed. You can set a spending limit. Check the first blog to see how.
On your terminal run this to send BTC to a destination wallet address
curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $ACCESS_TOKEN"
-d '{ "address": "2MszopwgnnoV7WLouQyQz9U9DkaEVpcTByF",
"amount": 50000000,
"walletPassphrase": "superlongpassword" }'
http://localhost:3080/api/v2/btc/wallet/<WALLET_ID>/sendcoins
Apart from the three parameters in your Request Body, you may finely adjust your transaction by adding other info such as blockchain fee information, add multiple addresses for bulk withdrawals.
{
"address": {
"value": "Adress 1"
},
"amount": {},
"walletPassphrase": "My Wallet Password",
"prv": "My Private Key If available",
"numBlocks": 247,
"feeRate": {},
"maxFeeRate": {},
"feeMultiplier": {},
"minConfirms": -46419653,
"enforceMinConfirmsForChange": false,
"gasPrice": {},
"eip1559": {
"maxPriorityFeePerGas": {
"value": "<Error: Too many levels of nesting to fake this schema>"
},
"maxFeePerGas": {
"value": "<Error: Too many levels of nesting to fake this schema>"
}
},
"gasLimit": {},
"targetWalletUnspents": 1000,
"minValue": {},
"maxValue": {},
"sequenceId": "dolore occaecat aliqua nostrud magn",
"nonce": {},
"noSplitChange": false,
"unspents": [
"12b147dd8b4f73c01f72bdbf5b589eea614f3de609ffdbdac84852d6505cf8a3:1",
"12b147dd8b4f73c01f72bdbf5b589eea614f3de609ffdbdac84852d6505cf8a3:1"
],
"changeAddress": {},
"instant": true,
"memo": {
"type": "eiusmod al",
"value": "magna cillum nostrud enim"
},
"comment": "ut culpa",
"addressType": "rep",
"startTime": "ea",
"consolidateId": "59cd72485007a239fb00282ed480da1f",
"lastLedgerSequence": -92846303,
"ledgerSequenceDelta": 48422319,
"cpfpTxIds": [
"ex",
"dolore amet veniam fugiat irure"
],
"cpfpFeeRate": -42568202,
"maxFee": 68424188,
"strategy": "deserunt sunt c",
"validFromBlock": 50509883,
"validToBlock": 22214801,
"type": "laborum quis sit Excepteur nulla",
"trustlines": [
{
"value": "<Error: Too many levels of nesting to fake this schema>"
},
{
"value": "<Error: Too many levels of nesting to fake this schema>"
}
],
"stakingOptions": {
"value": "<Error: Too many levels of nesting to fake this schema>"
},
"unstakingOptions": {
"from": "amet u",
"receiver": "cupidatat enim",
"unstakeCpuQuantity": "sint",
"unstakeNetQuantity": "commodo esse in"
},
"refundOptions": {
"address": "dolor labore cupidatat ut Lorem"
},
"messageKey": "amet",
"reservation": {
"expireTime": "1947-10-19T14:13:13.415Z"
},
"data": "laboris ut sunt do",
"hop": false
}
Any language capable of issuing HTTP requests can easily build on Bitgo Express through the RESTful interface. In the snippet below I used PHP to build the requested body and curl
it on http://locahost:3080
where we installed the Bitgo express instance. Therefore you can manage client withdrawals in code directly by calling on your request wrapper 🤓
/**
* This API call allows you to create and send cryptocurrency to a destination address.
*
* @param string $address Recipient address
* @param int $amount Amount to be sent to the recipient
* @param string $walletPassphrase The passphrase to be used to decrypt the user key on this wallet
* @param string $prv The private key in string form if the walletPassphrase is not available
* @param int $numBlocks Estimates the approximate fee per kilobyte necessary for a transaction confirmation within 'numBlocks' blocks.
* @param int $feeRate Fee rate in satoshis/litoshis/atoms per kilobyte.
* @param string $comment Any additional comment to attach to the transaction
* @param array $unspents The unspents to use in the transaction. Each unspent should be in the form prevTxId:nOutput.
* @param int $minConfirms Minimum number of confirmations unspents going into this transaction should have.
* @param bool $enforceMinConfirmsForChange Enforce minimum number of confirmations on change (internal) inputs.
* @param int $targetWalletUnspents The desired count of unspents in the wallet. If the wallet’s current unspent count is lower than the target, up to four additional change outputs will be added to the transaction. To reduce unspent count in your wallet see 'Consolidate Unspents’.
* @param bool $noSplitChange Set to true to disable automatic change splitting for purposes of unspent management.
* @param int $minValue Ignore unspents smaller than this amount of satoshis
* @param int $maxValue Ignore unspents larger than this amount of satoshis
* @param int $gasPrice Custom gas price to be used for sending the transaction
* @param int $gasLimit Custom gas limit for the transaction
* @param int $sequenceId The sequence ID of the transaction
* @param bool $segwit Allow SegWit unspents to be used, and create SegWit change.
* @param int $lastLedgerSequence Absolute max ledger the transaction should be accepted in, whereafter it will be rejected.
* @param string $ledgerSequenceDelta Relative ledger height (in relation to the current ledger) that the transaction should be accepted in, whereafter it will be rejected.
* @return array
*/
public function sendTransaction(string $address, int $amount, string $walletPassphrase, string $prv = null, int $numBlocks = null, int $feeRate = null,int $maxfeeRate = null, string $comment = null, array $unspents = null, int $minConfirms = null, bool $enforceMinConfirmsForChange = null, int $targetWalletUnspents = null, bool $noSplitChange = null, int $minValue = null, int $maxValue = null, int $gasPrice = null, int $gasLimit = null, int $sequenceId = null, bool $segwit = null, int $lastLedgerSequence = null, string $ledgerSequenceDelta = null) {
$this->url = $this->APIEndpoint . '/wallet/' . $this->walletId . '/sendcoins';
$this->params = [
'address' => $address,
'amount' => "$amount",
'walletPassphrase' => $walletPassphrase,
'prv' => $prv,
'numBlocks' => in_array($this->coin, $this->UTXObased) ? $numBlocks : null,
'feeRate' => in_array($this->coin, $this->UTXObased) ? $feeRate : null,
'maxFeeRate' => $maxFeeRate,
'comment' => $comment,
'unspents' => in_array($this->coin, $this->UTXObased) ? $unspents : null,
'minConfirms' => in_array($this->coin, $this->UTXObased) ? $minConfirms : null,
'enforceMinConfirmsForChange' => in_array($this->coin, $this->UTXObased) ? $enforceMinConfirmsForChange : null,
'targetWalletUnspents' => in_array($this->coin, $this->UTXObased) ? $targetWalletUnspents : null,
'noSplitChange' => in_array($this->coin, $this->UTXObased) ? $noSplitChange : null,
'minValue' => in_array($this->coin, $this->UTXObased) ? $minValue : null,
'maxValue' => in_array($this->coin, $this->UTXObased) ? $maxValue : null,
'gasPrice' => $this->coin === CurrencyCode::ETHEREUM ? $gasPrice : null,
'gasLimit' => $this->coin === CurrencyCode::ETHEREUM ? $gasLimit : null,
'sequenceId' => $this->coin === CurrencyCode::ETHEREUM ? $sequenceId : null,
'segwit' => in_array($this->coin, [CurrencyCode::BITCOIN, CurrencyCode::LITECOIN, CurrencyCode::BITCOIN_GOLD]) ? $segwit : null,
'lastLedgerSequence' => $this->coin === CurrencyCode::RIPPLE ? $lastLedgerSequence : null,
'ledgerSequenceDelta' => $this->coin === CurrencyCode::RIPPLE ? $ledgerSequenceDelta : null
];
return $this->__execute();
}
private function __execute(string $requestType = 'POST', bool $array = true) {
$ch = curl_init($this->url);
if ($requestType === 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(array_filter($this->params)));
} elseif ($requestType === 'PUT') {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(array_filter($this->params)));
}
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
if (isset($this->accessToken) && !$this->login) {
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Authorization: Bearer ' . $this->accessToken
]);
}
curl_setopt($ch, CURLOPT_CAINFO, dirname(__FILE__) . '/cacert.pem');
if (defined('CURLOPT_IPRESOLVE') && defined('CURL_IPRESOLVE_V4')) {
curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
}
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, $array);
}
Your transaction will be processed. BitGo uses the BitGo key to sign the half-signed transaction in a hardware security module (HSM), creating a fully-signed transaction. Using a BitGo node, Bitgo broadcasts the transaction to the network for confirmation.
Here is a sample result from a successful withdrawal to another wallet
{
"transfer": {
"coin": "btc",
"id": "59cd72485007a239fb00282ed480da1f",
"wallet": "2NBYMZ9QYufZZTmgXbXKJ8Mh5oMNEpNHVm6",
"enterprise": "62c5ae8174ac860007aff138a2d74df7",
"txid": "b8a828b98dbf32d9fd1875cbace9640ceb8c82626716b4a64203fdc79bb46d26",
"height": 0,
"heightId": "string",
"date": "2019-08-24T14:15:22Z",
"type": "send",
"value": 0,
"valueString": "string",
"baseValue": 0,
"baseValueString": "string",
"feeString": "string",
"payGoFee": 0,
"payGoFeeString": "string",
"usd": 0,
"usdRate": 0,
"state": "confirmed",
"tags": [
"59cd72485007a239fb00282ed480da1f"
],
"history": [
{
"date": "2019-08-24T14:15:22Z",
"user": "59cd72485007a239fb00282ed480da1f",
"action": "created",
"comment": "string"
}
],
"comment": "string",
"vSize": 0,
"nSegwitInputs": 0,
"coinSpecific": {},
"sequenceId": "string",
"entries": [
{
"address": "2NAUwNgXaoFj2VVnSEvNLGuez8CfdU2UCMZ",
"wallet": "string",
"value": 0,
"valueString": "string",
"isChange": true,
"isPayGo": true,
"token": "omg"
}
],
"usersNotified": true
},
"txid": "string",
"tx": "string",
"status": "confirmed"
}
Conclusion
Sending Bitcoin is made easy with the BitGo Express and since the transaction signing, occurs in a trusted environment i.e. your server, it offers a secure layer for you to conduct your withdrawals.
I'm excited to see how you can use Bitgo express to help power the next generation of Bitcoin apps. Let me know what you think of this on Twitter!