买入卖出RAM

课程目标

  1. 如何获取全网RAM相关的公共数据
  2. 如何使用Bancor算法计算实时的RAM价格
  3. 如何购买RAM
  4. 如何卖出RAM

前言

现在我们能获取到了基于测试网络的账号的网络资源数据,现在我们就介绍如何交易RAM。

交易RAM的前提是我们需要知道它的价格,即买1KB内存需要花费多少EOS,卖1个EOS能获取多少内存。这就需要我们去链上查询数据库获取相应的实时的数据,再进行计算拿到单价。下面我们来一步步实现,最后展示在项目中的相应源码。

一、获取全网RAM数量

全网RAM数量的数据是存在数据库中的,需要使用eosjs库的getTableRows方法获取,或者RPC的chain/get_table_rows接口获取。下面会有eosjs进行交易,所以本章我们统一使用eosjs访问数据,可以到官网查看eosjs-api接口文档。

首先访问主网中RAM的数量。


Eos = require('eosjs')

eosconfig = {
    httpEndpoint: 'https://node1.zbeos.com',
    chainId: 'aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906',

    // keyProvider: privatekeyList, // WIF string or array of keys..
    expireInSeconds: 60,
    broadcast: true,
    verbose: false, // API activity
    sign: true
},
eos = Eos(eosconfig)

eos.getTableRows(true,"eosio","eosio","global").then(result => {
    console.log(result)
})

输出如下

{ rows:
   [ { max_block_net_usage: 1048576,
       target_block_net_usage_pct: 1000,
       max_transaction_net_usage: 524288,
       base_per_transaction_net_usage: 12,
       net_usage_leeway: 500,
       context_free_discount_net_usage_num: 20,
       context_free_discount_net_usage_den: 100,
       max_block_cpu_usage: 200000,
       target_block_cpu_usage_pct: 2000,
       max_transaction_cpu_usage: 150000,
       min_transaction_cpu_usage: 100,
       max_transaction_lifetime: 3600,
       deferred_trx_expiration_window: 600,
       max_transaction_delay: 3888000,
       max_inline_action_size: 4096,
       max_inline_action_depth: 4,
       max_authority_depth: 6,
       max_ram_size: '88414727168',
       total_ram_bytes_reserved: '49777052734',
       total_ram_stake: '26261375993',
       last_producer_schedule_update: '2018-11-05T06:57:51.000',
       last_pervote_bucket_fill: '1541399606000000',
       pervote_bucket: 219209992,
       perblock_bucket: 36804696,
       total_unpaid_blocks: 95761,
       total_activated_stake: '4035546740321',
       thresh_activated_stake_time: '1529505892000000',
       last_producer_schedule_size: 21,
       total_producer_vote_weight: '24124687214114213888.00000000000000000',
       last_name_close: '2018-11-05T02:08:26.000' } ],
  more: false }
  

其中“max_ram_size“字段的数据就是全网RAM的数量,单位是bytes,那么转换为G,则结果为:88402589696/(102410241024)=82.34263134002686 G。获取方式如下:

eos.getTableRows(true,"eosio","eosio","global").then(result => {
    //ram总量,bytes转为G
    let ramTotal = result.rows[0].max_ram_size / (1024*1024*1024)  
})

二、获取RAM使用量、计算RAM价格

与获取RAM总量一样,我们使用eosjs库的getTableRows方法获取主网中RAM的使用量、消耗RAM的EOS数量,最后通过Bancor算法计算RAM价格。

eosconfig = {
    httpEndpoint: 'https://node1.zbeos.com',
    chainId: 'aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906',

    // keyProvider: privatekeyList, // WIF string or array of keys..
    expireInSeconds: 60,
    broadcast: true,
    verbose: false, // API activity
    sign: true
},
eos = Eos(eosconfig)

eos.getTableRows(true, "eosio", "eosio", "rammarket").then(ramData => {
    console.log(ramData)
})

输出如下

  • 其中“base“字段里的“balance”数据就是全网RAM的使用量,单位是bytes。
  • “quote“字段里的“balance”数据就是消耗RAM的EOS数量。

下面根据Bancor算法算出RAM的价格

ramPrice = quote.balance / (base.balance / 1024)

Bancor协议是底层货币协议,通过区块链技术和智能合约为加密数字货币提供连续流动性和实现异步价格。

所以ramPrice的获取方式如下:

eos.getTableRows(true, "eosio", "eosio", "rammarket").then(ramData => {
    console.log(ramData)
    //RAM消耗的EOS数量
    let eosAmount = ramData.rows[0].quote.balance.split(" ")[0];
    //RAM使用量
    let ramAmount = ramData.rows[0].base.balance.split(" ")[0] / 1024;
    //RAM价格
    let ramPriceWithEOS = eosAmount / ramAmount
    console.log(eosAmount, ramAmount, ramPriceWithEOS);
})

三、买入RAM

买入RAM的API其实我们已经使用过了,在创建账号的时候我们为别人创建的账号就需要为它购买一定的RAM,现在我们在来单独调用购买的API。由于现在需要交易,所以会使用资源,因此我们重新配置一下eosconfig切换到测试网络进行测试。

eosconfig = {
    httpEndpoint:"http://jungle.cryptolions.io:18888",
    chainId: "038f4b0fc8ff18a4f0842a8f0564611f6e96e8535901dd45e43ac8691a1c4dca", // 32 byte (64 char) hex string
    keyProvider: ["5HqCj7sg4K2xZ1KD5sSH38kuJkKGqfE1wSGiLL3M599ajacVmTs"], 
    expireInSeconds: 60,
    broadcast: true,
    verbose: false, // API activity
    sign: true
},

eos = Eos(eosconfig)

eos.getTableRows(true, "eosio", "eosio", "rammarket").then(ramData => {
    console.log(ramData)
    //RAM消耗的EOS数量
    let eosAmount = ramData.rows[0].quote.balance.split(" ")[0];
    //RAM使用量
    let ramAmount = ramData.rows[0].base.balance.split(" ")[0] / 1024;
    //RAM价格
    let ramPriceWithEOS = eosAmount / ramAmount
    console.log(eosAmount, ramAmount, ramPriceWithEOS);
})

let account = "lixutest1111"
let ramAmount = 8*1024
async function buyRAM() {
    result = await eos.transaction(tr => {
        tr.buyrambytes({
            payer: account,
            receiver: account,
            bytes: ramAmount
        })
    })
    console.log(result)
}
buyRAM()


在运行之前先记录下来账号“lixutest1111”的网络资源数据。

运行后看到价格是0.07576784159120188 EOS/KB

代码中是买了8KB的RAM,那我们计算一下,需要花费多少EOS,其实很简单

8KB * 0.07576784159120188 EOS/KB = 0.6061EOS

那我们再计算一下账号“lixutest1111”现在的EOS可用余额和RAM总量是多少

78.7959 - 0.6061 = 78.1898EOS
13.30 + 8 = 21.30KB

计算好以后我们再看看现在账号“lixutest1111”的在链上的资源数据是多少。

可以发现计算的结果和链上的结果有了一定的偏差,这是因为RAM价格会随着市场波动而变化,所以导致结果有一点点偏差,这是不可避免的。另外我们在购买RAM的时候,需要输入的是EOS数量,然后根据RAM价格转换成需要购买的RAM大小,再进行交易。

四、卖出RAM

卖出RAM需要用到另外一个方法 sellram

eosconfig = {
    httpEndpoint: "http://jungle.cryptolions.io:18888",
    chainId: "038f4b0fc8ff18a4f0842a8f0564611f6e96e8535901dd45e43ac8691a1c4dca", // 32 byte (64 char) hex string
    keyProvider: ["5HqCj7sg4K2xZ1KD5sSH38kuJkKGqfE1wSGiLL3M599ajacVmTs"],
    expireInSeconds: 60,
    broadcast: true,
    verbose: false, // API activity
    sign: true
},

    eos = Eos(eosconfig)

eos.getTableRows(true, "eosio", "eosio", "rammarket").then(ramData => {
    console.log(ramData)
    //RAM消耗的EOS数量
    let eosAmount = ramData.rows[0].quote.balance.split(" ")[0];
    //RAM使用量
    let ramAmount = ramData.rows[0].base.balance.split(" ")[0] / 1024;
    //RAM价格
    let ramPriceWithEOS = eosAmount / ramAmount
    console.log(eosAmount, ramAmount, ramPriceWithEOS);
})

let account = "lixutest1111"
let ramAmount = 8*1024
async function sellRAM() {
    result = await eos.transaction(tr => {
        tr.sellram({
            account: account,
            bytes: ramAmount
        })
    })
    console.log(result)
}
sellRAM()

运行后看到价格是0.0757692783033557 EOS/KB,确实与上一次获取到的价格有点偏差。

这里就不花时间作计算了,大家可以在根据价格与出售量计算在做比较。重新查看账号“lixutest1111”的EOS数量多了0.6,RAM内存减少了8KB。

五、项目源码

1. controllers/netResource.js

在controllers文件夹下新建netResource.js文件,实现获取全局RAM数据与买入卖出RAM的功能。

let { success, fail } = require("../utils/myUtils")
let myUtils = require("../utils/myUtils")
let walletModel = require("../models/wallet")

async function getRamInfo() {
    ramData = await eos.getTableRows(true, "eosio", "eosio", "rammarket")
    //RAM消耗的EOS数量
    let eosAmount = ramData.rows[0].quote.balance.split(" ")[0];
    //RAM使用量
    let ramAmount = ramData.rows[0].base.balance.split(" ")[0] / 1024;
    //RAM价格
    let ramPriceWithEOS = eosAmount / ramAmount
    console.log(eosAmount, ramAmount, ramPriceWithEOS);
    return {
        ramUsed: ramAmount / (1024 * 1024),
        ramPrice: ramPriceWithEOS,
    }
}

module.exports = {
    netResourceGetRAMInfo: async (ctx) => {
        console.log(ctx.request.body)
        let { wallet, password } = ctx.request.body
        //获取钱包里面所有的私钥配置EOSJS
        let privatekeyList = await walletModel.getWalletPrivatekeyList(wallet, password)
        eos = myUtils.getEOSJS(privatekeyList)

        let ramData = await eos.getTableRows(true, "eosio", "eosio", "global")
        //ram总量,bytes转为G
        let ramTotal = ramData.rows[0].max_ram_size / (1024 * 1024 * 1024)
        console.log(ramTotal);

        let ramInfo = await getRamInfo()
        ctx.body = success({
            ramPrice: ramInfo.ramPrice,
            ramTotal: ramTotal,
            ramAvailable: ramTotal - ramInfo.ramUsed,
        })
    },

    netResourceTransactionRAM: async (ctx) => {
        console.log(ctx.request.body)
        let { amount, transaction_type, account, wallet, password } = ctx.request.body
        //获取钱包里面所有的私钥配置EOSJS
        let privatekeyList = await walletModel.getWalletPrivatekeyList(wallet, password)
        eos = myUtils.getEOSJS(privatekeyList)

        let result
        if (transaction_type == '1') {
            //买RAM
            console.log("买RAM")
            let ramInfo = await getRamInfo()
            let ramAmount = parseInt((amount / ramInfo.ramPrice) * 1024)
            console.log("ramAmount:", ramAmount)
            result = await eos.transaction(tr => {
                tr.buyrambytes({
                    payer: account,
                    receiver: account,
                    bytes: ramAmount
                })
            })
        } else {
            //卖RAM
            console.log("卖RAM")
            let ramAmount = parseInt(amount*1024)
            result = await eos.transaction(tr => {
                tr.sellram({
                    account: account,
                    bytes: ramAmount 
                })
            })
        }

        console.log("data:", result)
        if (result.broadcast) {
            ctx.body = success("ok")
        } else {
            ctx.body = fail("error")
        }
    }

}

2. router/router.js

将获取全局RAM数据与买入卖出RAM的接口绑定到路由。

......
let netresourceController = require("../controllers/netResource")

//网络资源
router.post("/net_resource/ram/info", netresourceController.netResourceGetRAMInfo)
router.post("/net_resource/ram/transaction", netresourceController.netResourceTransactionRAM)

3. views/netResource.html

编辑views文件夹下的netResource.html文件,实现RAM交易的表单显示。

......

<div class="interactive">
    <form id="ram-transaction">
        <input type="radio" id="ram-transaction-buy" name="transaction_type" value="1" checked="checked">
        <label for="ram-transaction-buy">买入</label>

        <input type="radio" id="ram-transaction-sell" name="transaction_type" value="2"  checked>
        <label for="ram-transaction-sell">卖出</label>
        <br>

        <div>全网内存:<span id="ram-total"></span></div>
        <div>内存价格:<span id="ram-price"></span> EOS/KB</div>

        <label>数量:</label>
        <input type="text" name="amount" placeholder="请输入EOS数量"><br>

        <input type="text" name="account" hidden="hidden">
        <input type="text" name="wallet" hidden="hidden">
        <input type="text" name="password" hidden="hidden">
        <button type="submit" id="ram-transaction-button">买入</button>
    </form>
</div>

......

4. static/js/netResource.js

对全局RAM数据与买入卖出RAM的表单进行网络请求处理与页面渲染。

$(document).ready(function () {
    ......

    $("input[name=account][hidden=hidden]").val(currentAccount)
    let currentwallet = localStorage.getItem("currentwallet")
    let walletPassword = localStorage.getItem(currentwallet)
    $("input[name=wallet][hidden=hidden]").val(currentwallet)
    $("input[name=password][hidden=hidden]").val(walletPassword)

    $("input[name=transaction_type]").change(function () {
        if (this.value == 1) {
            $("#ram-transaction-button").text("买入")
            $("input[name=amount]").attr({"placeholder":"请输入EOS数量"})
        } else {
            $("#ram-transaction-button").text("卖出")
            $("input[name=amount]").attr({"placeholder":"请输入RAM(KB)数量"})

        }
    })

    //ram全局数据
    $.post("/net_resource/ram/info", { "account": currentAccount }, function (res, status) {
        console.log(status + JSON.stringify(res))
        if (res.code == 0) {
            $("#ram-total").text(res.data.ramAvailable.toFixed(2) + " GB / " + res.data.ramTotal.toFixed(2) + "GB")
            $("#ram-price").text(res.data.ramPrice.toFixed(6))
        }
    })

    //交易RAM
    $("#ram-transaction").validate({
        rules: {
            amount: {required: true,},
        },
        messages: {
            name: {required: "请输入要交易的数量",},
        },
        submitHandler: function (form) {
            $(form).ajaxSubmit({
                url: "/net_resource/ram/transaction",
                type: "post",
                dataType: "json",
                success: function (res, status) {
                    console.log(status + JSON.stringify(res))
                    if (res.code == 0) {
                        alert("交易成功")
                        location.reload() 
                    } else {
                        alert("交易失败")
                    }
                },
                error: function (res, status) {
                    console.log(status + JSON.stringify(res))
                    alert(res.data)
                }
            });
        }
    })
})

六、项目效果

项目源码Github地址

作者:李旭

来源:http://www.chaindesk.cn

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦