使用智能合約實現自動分帳

自動分帳是不少平臺都會用到的支付功能。不少互聯網內容售賣平臺都會跟內容提供者分帳。好比:Apple 的 App Store 跟 App 開發者三七分紅。不少平臺都使用了支付寶、微信支付做爲支付手段,可是要同時實現給內容提供者分帳,倒是一件不太容易的事。使用 FIBOS 智能合約能夠很容易實現這個需求。javascript

文中代碼已在 GitHub 上開源。https://github.com/fengluo/fi...java

設計思路

在 FIBOS 轉帳是經過 token 合約的extransfer方法來實現的。extransfer方法在執行的時候會給轉帳方帳戶和入帳方帳戶發送通知。因此用戶給平臺方帳戶轉帳的時候,平臺帳戶就會收到通知。因此總體業務邏輯以下:git

quantity: 10 FO
        memo: 內容提供者帳戶           quantity: 8 FO
用戶帳戶 -------------------> 平臺帳戶 ----------------> 內容提供者帳戶
            extransfer      2/8 分紅   extransfer
  1. 用戶給平臺方帳戶轉帳,memo 中填寫內容提供者的帳戶名。
  2. 平臺方的帳戶合約監聽 extransfer 方法的通知,而後作出分帳計算,給對應內容提供者的帳戶轉帳對應金額。

總體邏輯很簡單,整個合約代碼邏輯差很少用20行就能夠寫完。github

編寫合約

FIBOS 的智能合約分爲 ABI 文件和 JS 合約兩部分。ABI 至關於合約接口,JS 合約則是功能實現。本案例目前沒有接口設計需求,不過 ABI 文件仍是合約不可缺乏的部分。因此咱們簡單建立一下就好。json

咱們先建立一個 contracts 文件夾,合約文件都會放在這裏。而後在此文件夾下,建立 subaccount.abi 文件,內容爲:微信

{
    "version": "eosio::abi/1.0"
}

JS 合約部分也沒有太複雜。在 contracts 文件夾下建立 subaccount.js 文件,代碼爲:ide

exports.on_extransfer = (from, to, quantity, memo) => {
    // 須要在開頭作一些判斷
    if (to === action.receiver && action.is_account(memo)) {
        const num = parseInt(quantity.quantity.split(' ')[0])
        // 假設咱們約定平臺方跟內容提供者是2/8分紅。
        const subnum = (num * 0.8).toFixed(4);
        trans.send_inline('eosio.token', 'extransfer', {
            from: to,
            to: memo,
            quantity: {
                quantity: `${subnum} ${quantity.quantity.split(' ')[1]}`,
                contract: quantity.contract
            },
            memo: 'sub account'
        },
        [
            {
                // 須要提供合約帳戶的 active 權限
                actor: action.receiver,
                permission: 'active'
            }
        ]);
    }
}

合約代碼開頭咱們須要作一些驗證。函數

  1. 收款方的帳戶爲合約帳戶,不然由於下面代碼執行給內容提供者轉帳時,由於轉賬方也是合約帳號會再次收到通知,形成無限遞歸,超出最大 send_inline 層數而報錯。
  2. 咱們用 memo 參數來放內容提供者的帳戶,因此咱們須要對此參數校驗一下該帳戶是否存在防止打錯。

合約代碼中咱們使用 send_inline 調用 eosio.token 合約來執行轉賬操做。轉賬操做須要對應帳戶的 active 權限才能執行。爲了解決權限濫用問題,FIBOS 定義了一個特殊權限 eosio.code。咱們須要在平臺合約帳戶中配置權限,在 active 權限下添加該合約帳戶的 eosio.code 受權。具體的配置操做會在下面說明。測試

在 FIBOS TestNet 上註冊帳號

爲方便測試,咱們在測試網 http://testnet.fibos.fo 上註冊三個帳戶。微信支付

  • 用戶帳號 helloworld11
  • 內容提供者帳號 helloworld22
  • 平臺合約帳號 helloworld33

咱們須要記錄這三個帳號的帳戶名以及公私鑰。以便下面的開發使用。建立一個統一的配置文件來記錄這些數據:

const config = {
    // 平臺合約帳戶的客戶端配置
    client: {
        chainId: '68cee14f598d88d340b50940b6ddfba28c444b46cd5f33201ace82c78896793a',
        httpEndpoint: 'http://testnet.fibos.fo',
        keyProvider: 'PRIVATE_KEY_OF_helloworld33'
    },
    // 用戶帳戶的客戶端配置
    callClient:{
        chainId: '68cee14f598d88d340b50940b6ddfba28c444b46cd5f33201ace82c78896793a',
        httpEndpoint: 'http://testnet.fibos.fo',
        keyProvider: 'PRIVATE_KEY_OF_helloworld11'
    },
    // 平臺合約帳戶信息
    contractAccount: {
        name: 'helloworld33',
        publicKey: 'PUBLIC_KEY_OF_helloworld33',
        privateKey: 'PRIVATE_KEY_OF_helloworld33'
    },
    // 用戶帳戶信息
    account1: {
        name: 'helloworld11',
        publicKey: 'PUBLIC_KEY_OF_helloworld11',
        privateKey: 'PRIVATE_KEY_OF_helloworld11'
    },
    // 內容提供者帳戶信息
    account2: {
        name: 'helloworld22',
        publicKey: 'PUBLIC_KEY_OF_helloworld22',
        privateKey: 'PRIVATE_KEY_OF_helloworld22'
    }
}

module.exports = config

配置權限

在合約代碼中,咱們調用了 trans.send_inline 函數調用合約 eosio.token 來實現轉賬操做,可是轉賬操做是須要帳戶的 active 權限。因此咱們須要更新一下合約帳戶的權限,須要添加調用者的 eosio.code 受權到它的 active 權限。這個調用者天然也是這個合約帳戶。

const FIBOS = require('fibos.js');
const config = require('./config');

const fibosClient = FIBOS(config.client);

let ctx = fibosClient.contractSync('eosio');

var r = ctx.updateauthSync({
  account: config.contractAccount.name,
  permission: 'active',
  parent: 'owner',
  auth: {
    threshold: 1,
    keys: [{
      key: config.contractAccount.publicKey,
      weight: 1
    }],
    accounts: [{
        permission: {
            // 將調用者帳號的 eosio.code 受權添加到它的 active 權限下。
            actor: config.contractAccount.name,
            permission: 'eosio.code'
        },
        weight: 1
    }]
  }
},{
    authorization: `${config.contractAccount.name}@owner` //更改帳戶權限須要使用 owner 權限
});
console.log(r);

部署合約

const FIBOS = require('fibos.js');
const config = require('./config');

const fibosClient = FIBOS(config.client);
const fs = require('fs');

// setcode
const jsCode = fs.readTextFile(`${__dirname}/contracts/subaccount.js`);
fibosClient.setcodeSync(config.contractAccount.name, 0, 0, fibosClient.compileCode(jsCode));

// getcode
const code = fibosClient.getCodeSync(config.contractAccount.name, true);
console.log('code:', code);

// setabi
const abi = JSON.parse(fs.readTextFile(`${__dirname}/contracts/subaccount.abi`));
fibosClient.setabiSync(config.contractAccount.name, abi);

轉帳測試

咱們先來寫一個腳本 account.js 來查看三個帳戶的餘額。

const FIBOS = require('fibos.js');
const config = require('./config');

const fibosClient = FIBOS(config.callClient);

const account1 = fibosClient.getTableRowsSync(true, 'eosio.token', config.account1.name, 'accounts');
console.log(config.account1.name);
console.log(account1);

const account2 = fibosClient.getTableRowsSync(true, 'eosio.token', config.account2.name, 'accounts');
console.log(config.account2.name);
console.log(account2);

const contractAccount = fibosClient.getTableRowsSync(true, 'eosio.token', config.contractAccount.name, 'accounts');
console.log(config.contractAccount.name);
console.log(contractAccount);

執行 fibos account.js 來查看三個帳戶信息。 目前咱們的帳戶尚未 FO,因此大體狀況是這樣的:

  • 用戶帳戶:helloworld11 金額:0.0000 FO
  • 內容提供者帳戶:helloworld22 金額:0.0000 FO
  • 平臺合約帳戶:helloworld33 金額:0.0000 FO

測試網會自動給每一個帳戶發放10 EOS 的通證用以測試使用。帳戶中還並無 FO 通證。因此咱們再來寫一個兌換腳本,用1 EOS 換一點 FO 通證。

const FIBOS = require('fibos.js');
const config = require('./config');

const fibosClient = FIBOS(config.callClient);

let ctx = fibosClient.contractSync('eosio.token');

const r = ctx.exchangeSync(
    config.account1.name,
    '1.0000 EOS@eosio',
    '0.0000 FO@eosio',
    'exchange FO to EOS',
    {
        authorization: config.account1.name
    }
);
console.log(r)

再次執行 fibos account.js 來查看帳戶信息。目前咱們的帳戶金額大體是這樣的:

  • 用戶帳戶:helloworld11 金額:146.4245 FO
  • 內容提供者帳戶:helloworld22 金額:0.0000 FO
  • 平臺合約帳戶:helloworld33 金額:0.0000 FO

下面寫個腳本 transfer.js 來執行轉賬操做。

const FIBOS = require('fibos.js');
const config = require('./config');

const fibosClient = FIBOS(config.callClient);

let ctx = fibosClient.contractSync('eosio.token');

const r = ctx.extransferSync(
    config.account1.name, // 用戶帳戶
    config.contractAccount.name, // 平臺合約帳戶
    '10.0000 FO@eosio', // 轉賬金額
    config.account2.name, // 附言填寫內容提供者的帳戶名,平臺合約會給它分帳
    {
        authorization: config.account1.name //提供用戶帳戶的受權
    }
)

console.log(r)

咱們要從用戶帳戶 account1 給平臺合約帳戶 account3 轉賬 10 FO。memo 參數爲要分紅的內容提供者帳戶 account2。根據合約中定的2/8分紅,平臺合約帳戶 account3 將會分得2 FO,而內容提供者帳戶 account2 將會得到8 FO。

使用命令 fibos transfer.js 執行該腳本完成轉賬操做。

下面咱們再來看一下目前三個帳戶狀況。執行命令 fibos account.js。三個帳戶金額大體以下。

  • 用戶帳戶:helloworld11 金額:136.4245 FO
  • 內容提供者帳戶:helloworld22 金額:8.0000 FO
  • 平臺合約帳戶:helloworld33 金額:2.0000 FO

結果顯示,分帳帳戶和平臺合約帳戶如預期那樣得到8 FO2 FO

綜上,咱們成功使用了智能合約實現了自動分帳。平臺方還能夠繼續根據本身業務須要定製本身的合約。

文中的代碼請參考:https://github.com/fengluo/fi...

相關文章
相關標籤/搜索