钱包 API
搭建 Web3 钱包

搭建 Web3 钱包#

第一步:创建项目及配置#

在开始搭建前,需要做一些准备工作:

创建项目和 API key#

在开始使用钱包 API 之前,先在开发者管理平台创建项目并生成 API key:

  1. 登录开发者管理平台
  2. 创建新的项目。
  3. 在项目设置中生成 API key。

接下来,需要开发者做一些配置工作。

REST 请求验证配置#

在发送 REST 请求时,需要进行验证。具体请查看 REST 请求验证指南

Node.js 环境设置#

导入必要的 Node.js 库并设置你的环境变量 Node.js 环境设置


第二步:生成钱包助记词及地址#

你可以使用我们提供的 签名 SDK 创建钱包助记词及地址。

npm install#

首先需要安装到最新的签名 SDK 版本,以 EVM 网络为例:

npm install @okxweb3/crypto-lib
npm install @okxweb3/coin-base
npm install @okxweb3/coin-ethereum

本地构建#

  1. 下载项目源码
git clone https://github.com/okx/js-wallet-sdk.git
  1. 运行构建脚本
sh build.sh

以 EVM 网络为例,可以依照下述代码,使用签名 SDK 创建一个 EVM 钱包对象,并且推导出所对应地址。

import { bip39, BigNumber } from "@okxweb3/crypto-lib";
import { EthWallet } 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) 来聚合多个地址,进行批量查询代币余额和交易历史。

说明
通常情况,Web3 钱包有三个概念:

- 钱包,谁拥有助记词,谁就拥有钱包。
- 账户,基于 BIP-44 规范,一套助记词下可以派生多个账户。
- 地址,每个账户下的每个链,对应一个地址。

生成 UNIX 时间戳的消息签名#

let now = new Date();

let timestamp = now.getTime();

let timestampString = timestamp.toString();

console.log(timestampString);

let signParams = {
    privateKey: derivePrivateKey,
    data: timestampString
}

let signature = await wallet.signMessage(signParams);
console.log(signature);

创建钱包账户#

通过上述获取的 address、publicKey、signature、signMessage,以及 chainIndex 信息,调用 POST /api/v5/waas/account/create-account 接口来创建钱包账户

例如,同时订阅不同链上的同一地址,实现如下:

//定义你的参数
const addresses = [
    {
        "chainIndex":"1",
        "address":"0x561815e02bac6128bbbbc551005ddfd92a5c24db",
        "publicKey":"02012db63bf0380294a6ecf87615fe869384b0510cb910a094254b6844af023ee2",
        "signature":"62acda5e471d9bf0099add50f4845256868d980821c161095651a918d3ef8a6b2286f512028172eabbe46ec2c9c2c20e5c40ff1fb23e1cdfdbed033ad924ce521b"
    },
    {
        "chainIndex":"10",
        "address":"0x561815e02bac6128bbbbc551005ddfd92a5c24db",
        "publicKey":"02012db63bf0380294a6ecf87615fe869384b0510cb910a094254b6844af023ee2",
        "signature":"62acda5e471d9bf0099add50f4845256868d980821c161095651a918d3ef8a6b2286f512028172eabbe46ec2c9c2c20e5c40ff1fb23e1cdfdbed033ad924ce521b"
    }
];

const getCreateAccountBody = {
    addresses: addresses,
    signMessage: '1717062864123', // UNIX Timestamp in millisecond
};

//定义辅助函数
const getCreateAccountData = async () => {
    const apiRequestUrl = getRequestUrl(
      apiBaseUrl,
      '/api/v5/waas/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/waas/account/create-watch-only-account 接口,点击此处查看详情。


第四步:构建并发送交易#

主要由三个步骤达成,分别为:

获取签名所需信息#

首先,调用 POST /api/v5/waas/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/waas/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/waas/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/waas/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"
    }]
}
说明
点击查看 orderId 的定义

第五步:查询交易详情#

当发送交易后,使用 GET /api/v5/waas/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/waas/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,实时准确地了解交易发送后的状态,查看详情


到这里,你基本上已经实现了开发一款 Web3 钱包所需的部分基础功能。

此外,钱包 API 提供了丰富接口,全方位满足 Web3 钱包开发者的需求,具体请查看 API 参考