Crypto-as-a-Service: Settle Between Go Accounts
Execute settlements between Go Accounts off chain. See the Guide.
-
Add connections to Go Accounts you find in the directory by passing the targetListingEntryId. If a Go Account isn't listed in the directory, you can still add connection if you know the wallet ID of the partner's Go Account.
-
The addressBookConnectionId and addressBookConnectionUpdatedAt timestamp identify your counterparty for the settlement. If you need to find these values, you can call the Lists counterparty Go Account connections endpoint.
-
If you're sending assets in the settlement, you must sign the settlement to confirm the transaction. However, if you're only receiving assets in a settlement, signing is optional. This step has two substeps:
Apply Signature
Use the JavaScript SDK to sign the settlement payload with your wallet passphrase. This generates a signature that proves you authorize the asset transfer.
Submit Signed Settlement
Submit the signed settlement with the payload and signature. Signing a settlement places the assets in your Go Account into a held state, preventing them from use in other transactions.
-
If you configure an approval requirement for settlements, you can't approve a settlement you initiated - another admin must always approve it. Update the pending approval status to approved.
// 1. Add a Connection
export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>"
export LISTING_ENTRY_ID="<LISTING_ENTRY_ID>"
export LOCAL_LISTING_ENTRY_DESCRIPTION="<LOCAL_LISTING_ENTRY_DESCRIPTION>"
export TARGET_LISTING_ENTRY_ID="<TARGET_LISTING_ENTRY_ID>"
curl -X POST \
https://app.bitgo-test.com/api/address-book/v1/connections \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-d '{
"listingEntryId": "'"$LISTING_ENTRY_ID"'",
"localListingEntryDescription": "'"$LOCAL_LISTING_ENTRY_DESCRIPTION"'",
"targetListingEntryId": "'"$TARGET_LISTING_ENTRY_ID"'"
}'
// 2. Create Settlement
export ENTERPRISE_ID="<YOUR_ENTERPRISE_ID>"
export ACCOUNT_ID="<YOUR_ACCOUNT_ID>"
export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>"
export EXTERNAL_ID="<YOUR_EXTERNAL_ID>"
export CONNECTION_ID="<CONNECTION_ID>"
export CONNECTION_UPDATED_AT="<CONNECTION_UPDATED_AT>"
export COUNTERPARTY_ACCOUNT_ID="<COUNTERPARTY_ACCOUNT_ID>"
export CURRENCY="<SENDING_ASSET>"
export QUANTITY="<SENDING_QUANTITY>"
curl -X POST \
https://app.bitgo-test.com/api/clearing/v1/enterprise/$ENTERPRISE_ID/account/$ACCOUNT_ID/settlement \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-d '{
"externalId": "'"$EXTERNAL_ID"'",
"addressBookConnectionId": "'"$CONNECTION_ID"'",
"addressBookConnectionUpdatedAt": "'"$CONNECTION_UPDATED_AT"'",
"assetTransfers": [
{
"currency": "'"$CURRENCY"'",
"quantity": "'"$QUANTITY"'"
}
]
}'
// 3. Sign Settlement (Optional)
// 3.1 Apply Signature
// Use JavaScript SDK to apply signature
// 3.2 Submit Signed Settlement
export ENTERPRISE_ID="<YOUR_ENTERPRISE_ID>"
export ACCOUNT_ID="<YOUR_ACCOUNT_ID>"
export SETTLEMENT_ID="<SETTLEMENT_ID>"
export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>"
export PAYLOAD="<SETTLEMENT_PAYLOAD>"
export SIGNATURE="<YOUR_PUBLIC_KEY>"
curl -X POST \
https://app.bitgo-test.com/api/clearing/v1/enterprise/$ENTERPRISE_ID/account/$ACCOUNT_ID/settlement/$SETTLEMENT_ID/signing \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-d '{
"payload": "'"$PAYLOAD"'",
"signature": "'"$SIGNATURE"'"
}'
// 4. Approve Settlement (Optional)
export APPROVAL_ID="<APPROVAL_ID>"
export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>"
export OTP="<YOUR_OTP>"
curl -X PUT \
https://app.bitgo-test.com/api/v2/pendingApprovals/$APPROVAL_ID \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-d '{
"state": "approved",
"otp": "'"$OTP"'"
}'
// 1. Add a Connection
import { BitGoAPI } from '@bitgo/sdk-api';
import { coins } from '@bitgo/sdk-core';
import { CreateAddressBookConnectionParams } from '@bitgo/sdk-core/src/bitgo/address-book';
import * as dotenv from 'dotenv';
const OFC_WALLET_ID = process.env.OFC_WALLET_ID;
const bitgo = new BitGoAPI({
accessToken: process.env.TESTNET_ACCESS_TOKEN,
env: 'test',
customRootURI: 'https://app.bitgo-test.com',
});
const coin = 'ofc';
bitgo.register(coin, coins.Ofc.createInstance);
async function main() {
const wallet = await bitgo.coin('ofc').wallets().get({ id: OFC_WALLET_ID });
const addressBook = wallet.toAddressBook();
const listing = await addressBook.getListing();
// Check if the wallet's listing exists and has entries
if (!listing.listingEntries || listing.listingEntries.length === 0) {
throw new Error('Wallet with ID ${OFC_WALLET_ID} does not have any address book listing entries.');
}
const listingEntry = listing.listingEntries[0];
const directory = await addressBook.getListingEntryDirectory();
// Check if the directory has any entries to connect with
if (!directory.listingEntries || directory.listingEntries.length === 0) {
throw new Error('The address book directory is empty. No available entries to connect with.');
}
const targetListingEntry = directory.listingEntries[0];
const body: CreateAddressBookConnectionParams = {
listingEntryId: listingEntry.id, // We now know this exists
targetListingEntryId: targetListingEntry.id, // We now know this exists
localListingEntryDescription: 'My description of the connection',
};
const connection = await addressBook.createConnection(body);
console.log('Address Book Connection:', connection);
}
// 2. Create Settlement
// Use the REST API for this step (see cURL tab)
// 3. Sign Settlement (Optional)
// 3.1 Apply Signature
import { BitGoAPI } from '@bitgo/sdk-api';
import { coins } from '@bitgo/sdk-core';
const OFC_WALLET_ID = process.env.OFC_WALLET_ID;
const payload = process.env.PAYLOAD;
const walletPassphrase = process.env.OFC_WALLET_PASSPHRASE;
const bitgo = new BitGoAPI({
accessToken: process.env.TESTNET_ACCESS_TOKEN,
env: 'test',
});
const coin = 'ofc';
bitgo.register(coin, coins.Ofc.createInstance);
async function main() {
const wallet = await bitgo.coin('ofc').wallets().get({ id: OFC_WALLET_ID });
console.log('Wallet:', wallet);
const tradingAccount = wallet.toTradingAccount();
const signature = tradingAccount.signPayload({payload, walletPassphrase});
console.log('Signature:', signature);
}
main().catch((e) => console.error(e));
// 3.2 Submit Signed Settlement
// Use the REST API for this step (see cURL tab)
// 4. Approve Settlement (Optional)
const baseCoin = this.bitgoSDK.coin(initialPendingApproval.coin);
const pendingApproval = await baseCoin.pendingApprovals().get({ id: initialPendingApproval.id });
const result = await pendingApproval.approve(params);
// 1. Add a Connection Response
{
"connections": [
{
"ownerListingEntry": {
"listing": {
"id": "string",
"name": "string",
"description": "string",
"editable": true
},
"id": "string",
"walletId": "string",
"coin": "string",
"type": "GO_ACCOUNT",
"description": "string",
"discoverable": true,
"featured": true,
"createdAt": "string",
"updatedAt": "string"
},
"targetListingEntry": {
"listing": {
"id": "string",
"name": "string",
"description": "string",
"editable": true
},
"id": "string",
"walletId": "string",
"coin": "string",
"type": "GO_ACCOUNT",
"description": "string",
"discoverable": true,
"featured": true,
"createdAt": "string",
"updatedAt": "string"
},
"id": "string", // this is the addressBookConnectionId for settlements
"type": "DVP",
"status": "PENDING_DEACTIVATION",
"label": "string",
"description": "string",
"createdBy": "string",
"updatedBy": "string",
"createdAt": "string",
"updatedAt": "string" // this is the addressBookConnectionUpdatedAt for settlements
}
]
}
// 2. Create Settlement Response
{
"approvalRequests": [
{
"createdAt": "2019-08-24",
"updatedAt": "2019-08-24",
"id": "23d3e6c5-72d2-41e8-9e79-8d7e09a19b74",
"accountId": "yourAccountId",
"status": "acknowledged",
"payload": "{\"nonce\":\"216c7312-5af8-463a-963d-8a91535b7ed5\",\"version\":\"3.0.0\",\"signerAccount\":\"yourAccountId\",\"settlementTransfers\":[{\"id\":\"b5f1ab55-b510-4bf6-92cd-214a7e23321\",\"sourceTradingAccountId\":\"yourAccountId\",\"destinationTradingAccountId\":\"counterpartiesAccountId\",\"currency\":\"$CURRENCY\",\"quantity\":\"$QUANTITY\"}]}",
},
{
"createdAt": "2019-08-24",
"updatedAt": "2019-08-24",
"id": "54586c2b-adfd-40bd-8115-ea3a37f3f4df",
"accountId": "counterpartiesAccountId",
"status": "initialized",
"payload": "{\"nonce\":\"d51a9909-0b25-4ebc-b864-eba6e76e77d1\",\"version\":\"3.0.0\",\"signerAccount\":\"counterpartiesAccountId\",\"settlementTransfers\":[{\"id\":\"b5f1ab55-b510-4bf6-92cd-214a7e23321\",\"sourceTradingAccountId\":\"yourAccountId\",\"destinationTradingAccountId\":\"counterpartiesAccountId\",\"currency\":\"$CURRENCY\",\"quantity\":\"$QUANTITY\"}]}",
}
],
"settlementTransfers": [
{
"id": "b5f1ab55-b510-4bf6-92cd-214a7e23321",
"sourceTradingAccountId": "yourAccountId",
"destinationTradingAccountId": "counterpartiesAccountId",
"currency":"$CURRENCY",
"quantity":"$QUANTITY"
}
],
"requesterAccountName": "string",
"finalizedAt": "2019-08-24",
"createdAt": "2019-08-24",
"updatedAt": "2019-08-24",
"id": "string", // settlementId for signing
"externalId": "string",
"notation": "string",
"requesterAccountId": "string",
"status": "canceled",
"type": "direct"
}
// 3. Sign Settlement (Optional)
// 3.1 Apply Signature Response
"1f2f62b22832940d82819eccc9e2fbcb5339ae863bc9fba4d6cc62f01550fc325a7e007fb8b753d0e862145e716d4a6054f75c2da2b7c2ac5d41679683a4996349"
// 3.2 Submit Signed Settlement Response
{}
// 4. Approve Settlement (Optional) Response
{
"id": "65529448bd87efe59c3b0156ddfce867",
"coin": "tbtc4",
"wallet": "654ec786c07fe8dc0dcfe03916ec5bb0",
"enterprise": "62c5ae8174ac860007aff138a2d74df7",
"creator": "62ab90e06dfda30007974f0a52a12995",
"createDate": "2023-11-13T21:25:28.022Z",
"info": {
"type": "transactionRequest",
"transactionRequest": {
"requestedAmount": "10000",
"fee": 20451,
"sourceWallet": "654ec786c07fe8dc0dcfe03916ec5bb0",
"policyUniqueId": "654ec786c07fe8dc0dcfe03f",
"recipients": [
{
"address": "2N1UvKhtSHLUa9YomSH7n9YMFCkMG2pbmsQ",
"amount": "10000",
"_id": "65529448bd87efe59c3b0158"
}
],
"coinSpecific": {
"tbtc4": {
"txHex": "010000000001010e4d3af014f9efe311062965d561b67f78a1759e7016605cd506ddd7041762d50000000023220020510ded26d712922bbb61bc68ef6766f836a03527820cbdc8b1551914eb467dafffffffff02102700000000000017a9145a581567fd2a630e61e34a696ab3bb887972886d87ad0f010000000000225120850d0ab466d15cb1565dd528d4d9709f3e46f41d41fe6d94aa01378e626983990500483045022100db45a8d94ee2144f7e29baa855d94a2bf0120707a7ab2fc93734ed94af972c460220558d00b91275aafc7805dfaaf9ab796adbe9f4e66dc467f7eb93b790671a323201000069522103c10ac628c880629ed0fd2a0563a898f4882baca45e15668a4d3064cf1ea379882103e56f84be4460080618ef869bb7b07096880760f748ba23efab533c2359f923bd21020ae81372264b5eac5c9dc7fe0b9a32bad00771c3a2c71f6e2c971823c3182ed653ae00000000"
}
},
"validTransaction": "010000000001010e4d3af014f9efe311062965d561b67f78a1759e7016605cd506ddd7041762d50000000023220020510ded26d712922bbb61bc68ef6766f836a03527820cbdc8b1551914eb467dafffffffff02102700000000000017a9145a581567fd2a630e61e34a696ab3bb887972886d87ad0f010000000000225120850d0ab466d15cb1565dd528d4d9709f3e46f41d41fe6d94aa01378e626983990400483045022100db45a8d94ee2144f7e29baa855d94a2bf0120707a7ab2fc93734ed94af972c460220558d00b91275aafc7805dfaaf9ab796adbe9f4e66dc467f7eb93b790671a323201473044022049e20c0073f42c9636408efc6c833ebf0e1a8ef13e8cb818dde2f4e2af7f7b7f022000cb3a321d65605b9d51d7f87e34306169607646c8d54a44011b021eff3dbe500169522103c10ac628c880629ed0fd2a0563a898f4882baca45e15668a4d3064cf1ea379882103e56f84be4460080618ef869bb7b07096880760f748ba23efab533c2359f923bd21020ae81372264b5eac5c9dc7fe0b9a32bad00771c3a2c71f6e2c971823c3182ed653ae00000000",
"validTransactionHash": "5ea8d2b93997ed9fa3597a2f3113817c8216f573a0278c2533bb5db50fdb0dff"
}
},
"state": "approved",
"scope": "wallet",
"userIds": [
"62ab90e06dfda30007974f0a52a12995",
"621d08a634ad8a0007fcddffd7c429cc"
],
"approvalsRequired": 1,
"singleRunResults": [
{
"ruleId": "Custody Enterprise Transaction ID Verification",
"triggered": false,
"_id": "65529448bd87efe59c3b0157"
}
],
"resolvers": [
{
"user": "621d08a634ad8a0007fcddffd7c429cc",
"date": "2023-11-13T21:44:27.794Z",
"resolutionType": "pending"
}
]
}