Prerequisites
- Contact your CSM to enable this feature and get access to:
- Make sure to prepare your workspace and perform all the steps necessary here.
- Install all the prerequisites for our TypeScript and Python SDK.
Add an Exchange Account via SDK
You can use the following code to add an exchange accountThis endpoint currently only supports the following exchanges
INDEPENDENT_RESERVE,BIT, BITHUMB, BITSO, CRYPTOCOM, BYBIT_V2, WHITEBIT , HITBTC, GEMINI, HUOBI, GATEIO, COINHAKO, BULLISH, BITGET, and LUNOIf you’re connecting a sub-account (for example, on exchanges that support account hierarchies), include
subAccountId in the credentials object. This field is required for sub-account connections and should be set to your exchange sub-account ID.import base64
import json
from fireblocks.client import Fireblocks
from fireblocks.client_configuration import ClientConfiguration
from fireblocks.base_path import BasePath
from fireblocks.models.add_exchange_account_request import AddExchangeAccountRequest
from cryptography.hazmat.primitives.serialization import load_pem_public_key
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes
from pprint import pprint
my_api_key="your_api_key"
with open('your_secret_key_file_path', 'r') as file:
secret_key_value = file.read()
configuration = ClientConfiguration(
api_key=my_api_key,
secret_key=secret_key_value,
base_path=BasePath.US #Make sure to use the correct environment. Please see the note
)
with Fireblocks(configuration) as fireblocks:
tenant_id = None
public_key = None
try:
# Get exchange accounts credentials public key
api_response = fireblocks.exchange_accounts.get_exchange_accounts_credentials_public_key().result()
tenant_id = api_response.data.tenant_id
public_key = api_response.data.public_key
except Exception as e:
print("Exception when calling ExchangeAccountAPI->get_exchange_accounts_credentials_public_key: %s\n" % e)
exit(1)
# Credentials encryption
exchange_api_key = "your_exchange_account_api_key"
exchange_api_secret = 'your_exchange_account_secret_key'
credentials = {
"apiKey": exchange_api_key,
"secret": exchange_api_secret,
"tenantId": tenant_id,
# Optional for sub-accounts:
# "subAccountId": sub_account_id,
}
pem_public_key = load_pem_public_key(bytearray(public_key, 'utf-8'), default_backend())
credentials_str = bytes(json.dumps(credentials, separators=(',', ':')), 'utf-8')
ciphertext = pem_public_key.encrypt(
credentials_str,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
encrypted_creds = bytes.decode(base64.b64encode(ciphertext))
# Prepare add exchange account request
add_exchange_account_request: AddExchangeAccountRequest = AddExchangeAccountRequest(
exchange_type='BIT',
name='My BIT account',
creds=encrypted_creds,
key=exchange_api_key
)
try:
# Add an exchange account
future = fireblocks.exchange_accounts.add_exchange_account(add_exchange_account_request=add_exchange_account_request)
api_response = future.result() # Wait for the response
print("The response of ExchangeAccountAPI->add_exchange_account:\n")
pprint(api_response.data.to_json())
except Exception as e:
print("Exception when calling ExchangeAccountAPI->add_exchange_account: %s\n" % e)
import { readFileSync } from 'fs';
import { Fireblocks, BasePath, ExchangeAccountsApiAddExchangeAccountRequest } from "@fireblocks/ts-sdk";
import { webcrypto } from "crypto";
const crypto = webcrypto;
const EXCHANGE_API_KEY = "exchange_api_key";
const EXCHANGE_API_SECRET = "exchange_api_secret";
const FIREBLOCKS_API_SECRET_PATH = "./fireblocks_secret.key";
const fireblocks = new Fireblocks({
apiKey: "my-api-key",
basePath: BasePath.US, //make sure to use the correct environment. Please see the note
secretKey: readFileSync(FIREBLOCKS_API_SECRET_PATH, "utf8"),
});
async function getExchangeAccountsCredentialsPublicKey() {
return (await fireblocks.exchangeAccounts.getExchangeAccountsCredentialsPublicKey()).data;
}
function str2ab(str: string): ArrayBuffer {
const buf = new ArrayBuffer(str.length);
const bufView = new Uint8Array(buf);
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
};
function arrayBufferToBase64(buffer: ArrayBuffer): string {
const bytes = new Uint8Array(buffer);
let binary = '';
for (let i = 0; i < bytes.length; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary);
}
async function encryptCredentials(publicKey: string, payload: any) {
const pemHeader = '-----BEGIN PUBLIC KEY-----\n';
const pemFooter = '\n-----END PUBLIC KEY-----';
const extractedCode = publicKey.substring(pemHeader.length, publicKey.length - pemFooter.length);
const binaryDerString = atob(extractedCode);
const binaryDer = str2ab(binaryDerString);
const cryptoPublicKey = await crypto.subtle.importKey(
'spki',
binaryDer,
{
name: 'RSA-OAEP',
hash: 'SHA-256',
},
true,
['encrypt'],
);
const payloadStr = JSON.stringify(payload);
const encodedDataString = new TextEncoder().encode(payloadStr);
const encryptedData = await crypto.subtle.encrypt(
{
name: 'RSA-OAEP',
},
cryptoPublicKey,
encodedDataString,
);
const encryptedDatab64 = arrayBufferToBase64(encryptedData);
return encryptedDatab64;
}
async function main() {
try {
const credsPublicKey = await getExchangeAccountsCredentialsPublicKey();
let credentials_payload = {
"apiKey": EXCHANGE_API_KEY,
"secret": EXCHANGE_API_SECRET,
"tenantId": credsPublicKey.tenantId,
// Optional for sub-accounts:
// subAccountId: SUB_ACCOUNT_ID,
};
const encryptedCredentials = await encryptCredentials(credsPublicKey.publicKey, credentials_payload);
const request: ExchangeAccountsApiAddExchangeAccountRequest = {
addExchangeAccountRequest: {
name: "My BIT account",
exchangeType: "BIT",
creds: encryptedCredentials,
key: EXCHANGE_API_KEY
}
};
const addExchangeResponse = await fireblocks.exchangeAccounts.addExchangeAccount(request);
console.log(addExchangeResponse);
} catch (e) {
console.log(e)
}
}
(async () => {
await main();
})();
Make sure you’re using the correct value for the API base URL for your environment:
BasePath.US- for production workspacesBasePath.Sandbox- for sandbox workspaces