在开始搭建前,需要做一些准备工作:
在开始使用钱包 API 之前,先在开发者管理平台创建项目并生成 API key:
接下来,需要开发者做一些配置工作。
在发送 REST 请求时,需要进行验证。具体请查看 REST 请求验证指南。
导入必要的 Node.js 库并设置你的环境变量 Node.js 环境设置。
你可以使用我们提供的 签名 SDK 创建钱包助记词及地址。
首先需要安装到最新的签名 SDK 版本,以 EVM 网络为例:
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
以 EVM 网络为例,可以依照下述代码,使用签名 SDK 创建一个 EVM 钱包对象,并且推导出所对应地址。
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);
我们准备了一个开源演示程序,用于展示我们上述涉及到签名 SDK 的功能。 获取演示程序源码点击这里。
在上一步,你已经生成了助记词和地址。现在可以开始创建账户 (AccountId) 来聚合多个地址,进行批量查询代币余额和交易历史。
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);
通过上述获取的 address、publicKey、signature、signMessage,以及 chainIndex 信息,调用 POST /api/v5/wallet/account/create-account
接口来创建钱包账户。
例如,对于交易所场景,同时订阅同一链上的多个地址,实现如下:
//定义你的参数
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 millisecond
};
//定义辅助函数
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();
即提币,可以由三个步骤达成,分别为:
首先,调用 POST /api/v5/wallet/pre-transaction/sign-info
接口,查询签名所需要的一些数据,例如 EVM 链的 gas price、gas limit、nonce 等。
//定义你的参数
// EVM
const postSignInfoBody = {
chainIndex: '1',
fromAddr: '0xdf54b6c6195ea4d948d03bfd818d365cf175cfc2',
toAddr: '0x1e80c39051f078ee34763282cbb36ffd88b40c65',
txAmount: '123000000000000',
extJson: {
inputData: '041bbc6fa102394773c6d8f6d634320773af4'
}
};
//定义辅助函数
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();
当查询到相关信息后,你将收到如下的响应:
{
"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"
}
}
},
]
}
使用签名 SDK 先验证地址合法性再构建交易
// Verify address
let valid = await wallet.validAddress({
address: newAddress.address
});
console.log("verify address isValid:", valid.isValid);
// Sign a 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);
调用 POST /api/v5/wallet/pre-transaction/send-transaction
接口,交易广播到链上。
//定义你的参数
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"
}
};
//定义辅助函数
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();
广播后,你将收到如下的响应:
{
"code": 0,
"msg": "success",
"data": [{
"orderId": "469356614923743232"
}]
}
当发送交易后,使用 GET /api/v5/wallet/post-transaction/transaction-detail-by-ordid
接口并通过 orderId 来获取交易详情信息。(也可以通过交易哈希查询,查看 API 参考。)
//定义你的参数
const params = {
accountId: '13886e05-1265-4b79-8ac3-b7ab46211001',
orderId: '469356614923743232',
chainIndex: '1'
};
//定义辅助函数
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();
查询详情成功后,你将收到如下的响应:
{
"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"
}
]
}]
}
此外,你还可以通过订阅 Webhook,实时准确地了解提币交易发送后的状态。对于充币交易的实时发现,也可以使用该功能。
钱包账户可支持百万级地址的聚合余额查询,可以用于交易所的对账场景。
使用钱包账户查询币种的余额前,先要调用 /api/v5/wallet/asset/add-token
添加对于该币种的订阅。
//定义参数
const postAddTokenBody = {
accountId: '13886e05-1265-4b79-8ac3-b7ab46211001',
chainIndex: '1',
tokenAddress: '0xdac17f958d2ee523a2206206994597c13d831ec7'
};
//定义辅助函数
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();
成功订阅后,你可以调用 POST /api/v5/wallet/asset/token-balances
接口查看账户所有地址下持有的该币种的总余额。
//定义参数
const getAssetsParams = {
accountId: '13886e05-1265-4b79-8ac3-b7ab46211001',
tokenAddresses: [
{
"chainIndex": "1",
"tokenAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7"
},
]
};
//定义辅助函数
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();
当查询到资产时,你将收到如下的响应:
{
"code": "0",
"data": [
{
"chainIndex": "1",
"tokenAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"symbol": "USDT",
"balance": "0.688467",
"tokenPrice": "0.99977",
"tokenType": "1"
}
],
"msg": "success"
}
到这里,你基本上已经实现了开发一款交易所钱包所需的部分基础功能。
此外,钱包 API 提供了丰富接口,全方位满足交易所钱包开发者的需求,具体请查看 API 参考 。