Before starting, some preparation work is required:
Before using the wallet API, create a project and generate an API key on the developer portal:
Next, some configuration work is needed to be done by the developer.
When sending REST requests, authentication is required. Please refer to the REST Request Authentication Guide.
Import the necessary Node.js libraries and set your environment variables Node.js Environment Setup.
You can use our Signing SDK to create wallet mnemonics and addresses.
First, you need to install the latest version of the Signing SDK,take EVM network as an example:
npm install @okxweb3/crypto-lib
npm install @okxweb3/coin-base
npm install @okxweb3/coin-ethereum
git clone https://github.com/okx/js-wallet-sdk.git
sh build.sh
Taking the ETH network as an example, you can use the Signing SDK to create an ETH wallet object and derive the corresponding address.
import { bip39, BigNumber } from "@okxweb3/crypto-lib";
import { EthWallet,MessageTypes } from "@okxweb3/coin-ethereum";
// eth wallet
let wallet = new EthWallet();
// get mnemonic
let mnemonic = await bip39.generateMnemonic();
console.log("generate mnemonic:", mnemonic);
// get derived key
const hdPath = await wallet.getDerivedPath({ index: 0 });
let derivePrivateKey = await wallet.getDerivedPrivateKey({ mnemonic: mnemonic, hdPath: hdPath });
console.log("generate derived private key:", derivePrivateKey, ",derived path: ", hdPath);
// get new address
let newAddress = await wallet.getNewAddress({ privateKey: derivePrivateKey });
console.log("generate new address:", newAddress.address);
// get the public key
let publicKey = newAddress.publicKey;
console.log("the corresponding public key:", publicKey);
We have prepared an open-source demo program to demonstrate the features of the Signing SDK mentioned above. Get the demo program source code here.
You have already generated the mnemonic and address in the previous step. Now you can create an account (AccountId) to aggregate multiple addresses for batch querying of token balances and transaction history.
let now = new Date();
let timestamp = now.getTime();
let timestampString = timestamp.toString();
console.log(timestampString);
let data = {
type: MessageTypes.PERSONAL_SIGN,
message: timestampString
};
let signParams = {
privateKey: derivePrivateKey,
data: data
}
let signature = await wallet.signMessage(signParams);
console.log(signature);
Using the obtained address, publicKey, signature, signMessage, and chainIndex information, call the POST /api/v5/wallet/account/create-account
interface to create a wallet account.
For example, for exchange scenarios, subscribing to multiple addresses on the same chain is implemented as follows:
// Define your parameters
const addresses = [
{
"chainIndex":"1",
"address":"0x68800215a2c08e5e65ff20ac0289566a42e2c18e",
"publicKey":"0xf30bae03d162d5ac2cce4678a244fa6c12ea20ffe1e32881e9e3fb016f3f8aeffe794c7173a8fd3fcff573736241681335bd779554961d729a93ce7efc8683c9",
"signature":"0xa3d53079890cb07a5fc2dac4afb5d183f2e5a45c2953f6676bc9fb84a7e4602747401e5904fce4470399e54db6dc437688a0bbe3943cbc53f15197db7f3dc1b81b"
},
{
"chainIndex":"1",
"address":"0x4c8f8089607ba1d370c7c62743a309bcb01d1eec",
"publicKey":"0xcbc322ae2148d436630e8884d927a13424075162580298f03c4664128122e982ca2d4b7f89bd5663cb41f2a6535b35adc384681ad2914e16eb413a046a39dba3",
"signature":"0xf189f73d8136c9d44cde11a4a55d3c5bb9817cfc9b7edf82e2ae69195ba986ee7d679d7a8ca9bd12f28545429cadc4ed7045dba9d478fcbc0fd28f232db3841e1b"
}
];
const getCreateAccountBody = {
addresses: addresses,
signMessage: '1718798213000', // UNIX Timestamp in milliseconds
};
// Define auxiliary function
const getCreateAccountData = async () => {
const apiRequestUrl = getRequestUrl(
apiBaseUrl,
'/api/v5/wallet/account/create-account'
);
return fetch(apiRequestUrl, {
method: 'post',
headers: headersParams,
body: JSON.stringify(getCreateAccountBody),
})
.then((res) => res.json())
.then((res) => {
return res;
});
};
const { data: createAccountData } = await getCreateAccountData();
Withdrawals can be achieved in three steps:
First, call the POST /api/v5/wallet/pre-transaction/sign-info
interface to query the data required for transaction, such as gas price, gas limit, nonce. Take ETH for an example:
// Define your parameters
const postSignInfoBody = {
chainIndex: '1',
fromAddr: '0xdf54b6c6195ea4d948d03bfd818d365cf175cfc2',
toAddr: '0x1e80c39051f078ee34763282cbb36ffd88b40c65',
txAmount: '123000000000000',
extJson: {
inputData: '041bbc6fa102394773c6d8f6d634320773af4'
}
};
// Define auxiliary function
const signInfoData = async () => {
const apiRequestUrl = getRequestUrl(
apiBaseUrl,
'/api/v5/wallet/pre-transaction/sign-info'
);
return fetch(apiRequestUrl, {
method: 'post',
headers: headersParams,
body: JSON.stringify(postSignInfoBody),
})
.then((res) => res.json())
.then((res) => {
return res;
});
};
const { data: signInfoData } = await signInfoData();
When the relevant information is queried, you will receive a response like this:
{
"code": 0,
"msg": "success",
"data": [
{
"gasLimit": "21000",
"nonce": "0",
"gasPrice": {
"normal": "12200500000",
"min": "9130000000",
"max": "16801000000",
"supportEip1559": true,
"erc1599Protocol": {
"suggestBaseFee": "8630000000",
"proposePriorityFee": "550000000",
"safePriorityFee": "500000000",
"fastPriorityFee": "2130000000",
"baseFee": "8630000000"
}
}
},
]
}
Use the Signing SDK to verify the address and build the transaction.
// Verify address
let valid = await wallet.validAddress({
address: newAddress.address
});
console.log("verify address isValid:", valid.isValid);
// Sign the transaction
let signParams = {
privateKey: derivePrivateKey,
data: {
to: newAddress.address,
value: new BigNumber(0),
nonce: 5,
gasPrice: new BigNumber(100 * 1000000000),
gasLimit: new BigNumber(21000),
chainId: 42
}
};
let signedTx = await wallet.signTransaction(signParams);
console.log("signed tx:", signedTx);
Call the POST /api/v5/wallet/pre-transaction/send-transaction
interface to broadcast the transaction to the chain.
// Define your parameters
const postSendTransactionBody = {
signedTx: "0xf8aa6a84ee6b280083012e389455d398326f99059ff775485246999027b319795580b844a9059cbb000000000000000000000000e0f7a45f1aa6cfff6fbbb1049f7a8c9248312c2e00000000000000000000000000000000000000000000000000000002540be4008193a0dfecbffac07db267e7438fe94f48c0d30bb17c3d86774bd445a9d70d06a974e9a0363c583a80dfc099d3a4186ce943d4bb38420dd52e23d26d30a73bfa8e291ed9",
chainId: "1",
accountId:"13886e05-1265-4b79-8ac3-b7ab46211001",
fromAddr: "0x238193be9e80e68eace3588b45d8cf4a7eae0fa3",
toAddr: "0xe0f7a45f1aa6cfff6fbbb1049f7a8c9248312c2e",
txHash: "0x585efb06371c2827a8da7b3dc2f0d3daf4f61cd14959c928bc698c43822881c3",
txAmount: "10000000000",
serviceCharge: "4128241194000",
tokenAddress: "0x55d398326f99059ff775485246999027b3197955",
txType: "transfer",
extJson: {
gasPrice: "196582914",
gasLimit: "21000",
nonce: "578"
}
};
// Define auxiliary function
const sendTransactionData = async () => {
const apiRequestUrl = getRequestUrl(
apiBaseUrl,
'api/v5/wallet/pre-transaction/send-transaction'
);
return fetch(apiRequestUrl, {
method: 'post',
headers: headersParams,
body: JSON.stringify(postSendTransactionBody),
})
.then((res) => res.json())
.then((res) => {
return res;
});
};
const { data: sendTransactionData } = await sendTransactionData();
After broadcasting, you will receive a response like this:
{
"code": 0,
"msg": "success",
"data": [{
"orderId": "469356614923743232"
}]
}
After sending the transaction, use the GET /api/v5/wallet/post-transaction/transaction-detail-by-ordid
interface and the orderId to get the transaction details. (You can also query by transaction hash, see API Reference).
// Define your parameters
const params = {
accountId: '13886e05-1265-4b79-8ac3-b7ab46211001',
orderId: '469356614923743232',
chainIndex: '1'
};
// Define auxiliary function
const getTransactionDetailData = async () => {
const apiRequestUrl = getRequestUrl(
apiBaseUrl,
'/api/v5/wallet/post-transaction/transaction-detail-by-ordid',
params
);
return fetch(apiRequestUrl, {
method: 'get',
headers: headersParams,
})
.then((res) => res.json())
.then((res) => {
return res;
});
};
const { data: transactionDetailData } = await getTransactionDetailData();
When the details are successfully queried, you will receive a response like this:
{
"code": 0,
"msg": "success",
"data": [
{
"chainIndex": "1",
"orderId": "469356614923743232",
"txhash": "0xcbf411766d65f3cf92839ababa73c4afec69a83442e8b67a68b5104b50a04ejb",
"blockHash" : "0x3ee63382b883fc40e35da6023fb341dd01cd2ec011f992bb54cf312f517740c9",
"blockHeight" : "19235519",
"blockTime" : "1708026551000",
"feeUsdValue" : "138.846919",
"fees" : "49102595635945621",
"gasLimit" : "2000000",
"gasPrice" : "87504603347",
"gasUsed" : "561143",
"txType": "0",
"txTime": "0",
"txStatus": "0",
"txDetail": [
{
"iType" : "0",
"txAmount": "0",
"tokenAddress":""
"contractCall" : true,
"fromAddr" : "0x3ee63382b883fc40e35da6023fb341dd01cd2ec011f992bb54cf312f517740c9",
"toAddr" : "0x3ee63382b883fc40e35da6023fb341dd01cd2ec011f992bb54cf312f517740c9",
"logIndex" : "-1",
"status" : "SUCCESS"
}
]
}
]
}
Additionally, you can use Webhook Subscription to get real-time and accurate information about the status of withdrawal transactions. This feature can also be used to discover deposit transactions in real-time.
Wallet accounts support aggregate balance queries for millions of addresses, which can be used for exchange reconciliation scenarios.
Before querying the token balance using a wallet account, you need to call /api/v5/wallet/asset/add-token
to add the token of interest.
// Define parameters
const postAddTokenBody = {
accountId: '13886e05-1265-4b79-8ac3-b7ab46211001',
chainIndex: '1',
tokenAddress: '0xdac17f958d2ee523a2206206994597c13d831ec7'
};
// Define auxiliary function
const postAddTokenData = async () => {
const apiRequestUrl = getRequestUrl(
apiBaseUrl,
'/api/v5/wallet/asset/add-token'
);
return fetch(apiRequestUrl, {
method: 'post',
headers: headersParams,
body: JSON.stringify(postAddTokenBody),
})
.then((res) => res.json())
.then((res) => {
return res;
});
};
const { data: addTokenData } = await postAddTokenData();
After successfully subscribing, you can call the POST /api/v5/wallet/asset/token-balances
interface to view the total balance of the token held by all addresses under the account.
// Define parameters
const getAssetsParams = {
accountId: '13886e05-1265-4b79-8ac3-b7ab46211001',
tokenAddresses: [
{
"chainIndex": "1",
"tokenAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7"
},
]
};
// Define auxiliary function
const getAssetsData = async () => {
const apiRequestUrl = getRequestUrl(
apiBaseUrl,
'/api/v5/wallet/asset/token-balances',
getAssetsParams
);
return fetch(apiRequestUrl, {
method: 'GET',
headers: headersParams,
})
.then((res) => res.json())
.then((res) => {
return res;
});
};
const { data: getAssetsData } = await getAssetsData();
When assets are queried, you will receive a response like this:
{
"code": "0",
"data": [
{
"chainIndex": "1",
"tokenAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"symbol": "USDT",
"balance": "0.688467",
"tokenPrice": "0.99977",
"tokenType": "1"
}
],
"msg": "success"
}
At this point, you have basically implemented the basic functions needed to develop an exchange wallet.
Additionally, Wallet API provides a rich range of interfaces to fully meet the needs of exchange wallet developers. For details, please refer to the API Reference.