This article uses a simple development example of the exchange withdrawal function, including the preparation of the development environment and the deployment of BFChain, so that developers can easily master how to develop their own DApp on the BFChain network.
The development examples in this article are developed using TypeScript. In order to help developers get started quickly, some necessary tools need to be prepared before development.
Install Typescript npm install -g typescript
View Typescript version tsc -v
Developers can download the node installation program through the BFChain official website (http://www.bfchain.com), and start the local node after the installation is complete.
In mainstream exchanges, user deposit and withdrawal is a very common function. It includes the following functions:
The functions are shown in the figure:
In order to ensure the security of the data and the private key not to leak, the exchange must start the BFChain node locally, and generate transaction data through the local node to perform user withdrawal (transfer) functions.
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
);
}
/**
* Get the latest block of the local node
*/
async getLastBlock() {
return this.__bfchainSdk.api.basic.getLastBlock();
}
/**
* Get account balance
* @param:address: account address
* @param:assetType: asset type
*/
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;
}
}
In order to simplify the data model, in this example, we only use the two data models of Account and Transaction. Take MongoDB as an example.
import {Schema} from "mongoose"
const accountSchema = new Schema({
accountId: {type: String, require: true }, // account ID
address: {type: String, require: true }, // account address
coin: {type: String, require: true }, // currency
balance: {type: String, require: true} // balance
});
export const account = mongoose.model("account", accountSchema)
const transactionSchema = new Schema({
tranId: {type: String, require: true }, // transaction ID
amount: {type: Number, require: true }, // transaction amount
tranType: {type: String, require: true }, // transaction type
senderId: {type: String, require: true }, // sender ID
recipientId: {type: String, require: true }, // recipient ID
signature: {type: String, require: true} // transaction signature
});
export const transaction = mongoose.model("tansaction", transactionSchema)
export class Withdraw {
private _withDrawAmount: number; // Withdraw cash amount
private _address: string; // account address
private _withDrawAddress: string; // Withdrawal address
private _secret: string; // Withdraw private key
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"
});
// The account balance is greater than the amount of cash to be withdrawn
if (accountInfos.balance <this._withDrawAmount) {
throw new Error("Insufficient account balance")
}
const chainbalance = this.bfchainSdk.getAddressBalance(this._address);
// The balance on the chain is greater than the amount of cash to be withdrawn
if (chainbalance <this._withDrawAmount) {
throw new Error("The balance of the address on the chain is insufficient")
}
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
}
// Call the transfer interface
const trTranferAsset = await this.bfchainSdk.api.transaction.sendTransferAsset(data);
// The transfer is successful, record the data in the transaction table
if (trTranferAsset && trTranferAsset.success === true) {
// Generate withdrawal transaction table data
transaction.insertOne({
tranId: uuid.v4(),
amount: this._withDrawAmount, // Withdraw cash amount
tranType: "withdraw", // Withdraw cash
senderId: this._address, // sender address of withdrawal
recipientId: this._withDrawAddress, // Withdrawal address
signature: trTranferAsset.result.signature // transaction signature
})
}
}
}
(async () => {
const withdraw = new Withdraw(20000,"Account Address","Withdrawal Address","Account Address Private Key");
await withdraw.exec();
})().catch(err => {
console.error(err);
});