本文通过一个简单的交易所提现功能的开发示例,包括开发环境的准备和BFChain的部署,使开发者能够轻松掌握如何在BFChain网络上开发自己的DApp
本文的开发示例使用TypeScript进行开发,为了帮助开发者迅速上手,开发之前需要准备一些必备工具。
安装Typescript npm install -g typescript
查看Typescript版本 tsc -v
开发人员可以通过BFChain官网(http://www.bfchain.com)下载节点的安装程序,进行安装完成之后启动本地节点。
在主流的交易所中,用户充值提现是一个很常见的功能。 它包括以下几个功能:
功能展示如图:
为了保证数据的安全性和私钥不泄漏,交易所必须在本地启动BFChain节点,通过本地节点生成交易数据进行用户提现(转账)功能。
import { Sdk } from "@bfchain/pc-sdk-core";
export class BFChainPcSdk {
private __bfchainSdk: Sdk;
constructor() {
this.__bfchainSdk = new Sdk({
ip: "127.0.0.1",
port: "9003",
requestTimeOut: 10000,
requestProtocol: "websocket"
}, 9999
);
}
/**
* 获取本地节点当前最新区块
*/
async getLastBlock() {
return this.__bfchainSdk.api.basic.getLastBlock();
}
/**
* 获得账户余额
* @param:address:账户地址
* @param:assetType:资产类型
*/
async getAddressBalance(address: string, assetType?: string) {
if (!assetType) assetType = "BFT";
const accountAsset = await this.__bfchainSdk.api.basic.getAccountLastTransaction({
address: address,
assetType: assetType
})
if (accountAsset && accountAsset.success === true) {
return this.getAssetBalance(address, accountAsset.result.transactionInBlock)
} else {
return "0";
}
}
async getAssetBalance(address: string, tr) {
const assetChange = tr.transactionAssetChanges;
if (assetChange && assetChange.length > 0) {
const isSender = address === tr.transaction.senderId;
const isRecipient = address === tr.transaction.recipientId;
let assetType = "";
if(tr.transaction && tr.transaction.storageKey === "assetType" && tr.transaction.storageKey){
assetType = tr.transaction.storageKey
};
for(const change of assetChange){
if((isSender && change.accountType ===0) || (isRecipient && change.accountType === 1)){
if(!assetType){
return change.assetBalance;
}
if(assetType !== "BFT" && change.assetTypes === 0){
return change.assetBalance;
}
}
}
}
return "0"
}
get bfchainSdk() {
return this.__bfchainSdk;
}
}
为了简化数据模型,在这个实例中,我们只使用Account和Transaction这2个数据模型。以MongoDB为例。
import { Schema } from "mongoose"
const accountSchema = new Schema({
accountId: { type: String, require: true }, // 账户ID
address: { type: String, require: true }, // 账户地址
coin: { type: String, require: true }, // 币种
balance: { type: String, require: true } // 余额
});
export const account = mongoose.model("account", accountSchema)
const transactionSchema = new Schema({
tranId: { type: String, require: true }, // 交易ID
amount: { type: Number, require: true }, // 交易金额
tranType: { type: String, require: true }, // 交易类型
senderId: { type: String, require: true }, // 发送者ID
recipientId: { type: String, require: true }, // 接受者ID
signature: { type: String, require: true } // 交易签名
});
export const transaction = mongoose.model("tansaction", transactionSchema)
export class Withdraw {
private _withDrawAmount: number; // 提现金额
private _address: string; // 账户地址
private _withDrawAddress: string; // 提现地址
private _secret: string; // 提现私钥
private bfchainSdk: Sdk = new BFChainPcSdk().bfchainSdk;
constructor(amount: number, address:string, withDrawAddress: string, secret: string) {
this._withDrawAmount = amount;
this._address = address;
this._withDrawAddress = withDrawAddress;
this._secret = secret;
}
async exec() {
const accountInfos = account.find({
address: this._address,
coin: "BFT"
});
// 账户余额大于要提现金额
if (accountInfos.balance < this._withDrawAmount) {
throw new Error("账户余额不足")
}
const chainbalance = this.bfchainSdk.getAddressBalance(this._address);
// 链上余额大于要提现金额
if (chainbalance < this._withDrawAmount) {
throw new Error("链上地址余额不足")
}
const lastBlock = this.bfchainSdk.getLastBlock();
const height = lastBlock.result.height ? lastBlock.result.height : 1;
const data = {
secret: this._secret,
amount: this._withDrawAmount.toString(),
fee: 100,
assetType: "BFT",
recipientId: this._withDrawAddress,
height: height
}
// 调用转账接口
const trTranferAsset = await this.bfchainSdk.api.transaction.sendTransferAsset(data);
// 转账成功,在交易表中记录数据
if (trTranferAsset && trTranferAsset.success === true) {
// 生成提现交易表数据
transaction.insertOne({
tranId: uuid.v4(),
amount: this._withDrawAmount, // 提现金额
tranType: "withdraw", // 提现
senderId: this._address, // 提现的发送者地址
recipientId: this._withDrawAddress, // 提现的地址
signature: trTranferAsset.result.signature // 交易签名
})
}
}
}
(async () => {
const withdraw = new Withdraw(20000,"账户地址","提现地址","账户地址私钥");
await withdraw.exec();
})().catch(err => {
console.error(err);
});