Earn SPK by Delegating LARYNX | Share your Feedback and Review the Code

88

  • 538
  • 4
  • 70.777
  • Reply

  • Open in the desktop app ADD TO PLAYLIST

    spknetwork

    Published on Jun 21, 2022
    About :

    We've been humming along for a few months now, it's been mostly smooth but of course there have been a couple of hiccoughs. There have been more than 50 accounts running node services which provides decentralization as well as capital to collateralize the ad-hoc multi-signature decentralized exchange. The goal of the SPK network is to facilitate decentralized storage which means we have a few stops on the way.

    Proposing software version 1.1.0

    Here we are, for the first time, issuing some SPK tokens. The idea is to trickle out tokens to our users for governance voting so when the time comes to scale, it's not us making all the decisions. Initially we're setting our APY at a very low number so our first movers advantage will be more in the decision making and less in token accumulation.

    Proposed Rates:

    .1% APR to node operators
    .03% APR to Delegators to node operators(split between the delegator and the delegatee, 0.015% each)
    .01% APR to Non-Delegators

    This rate is calculated daily, and is non-compounding, based on Locked, or Powered LARYNX only.

    The incentive is for our users to provide infrastructure, or to select their infrastructure providers. The low amount for the delegation makes it more profitable to provide services than to game the delegation system. The lowest amount goes toward those interested enough to stake into the ecosystem, but not much else.

    Interest

    Those familiar with the HBD interest algorithms will find some nostalgic methods here. When an account sends SPK, powers up or delegates Larynx, or claims earnings will have their interest calculated first. The periods are based on whole days (28800 blocks). This keeps compute cycles low and makes scaling easier. The down-side is front ends will have to calculate balances.

    Code Review

    I welcome and strongly encourage code review. I'll explain some of the biggest pieces below.

    Interest Calc

    const simpleInterest = (p, t, r) => { 
      const amount = p * (1 + r / 365);
      const interest = amount - p;
      return parseInt(interest * t);
    };
    

    p => principal
    t => time in days
    r => rate (0.01, 0.0015, 0.001)

    SPK Earnings Calc

    const reward_spk = (acc, bn) => {
        return new Promise((res, rej) => {
            const Pblock = getPathNum(["spkb", acc]);
            const Pstats = getPathObj(["stats"]);
            const Ppow = getPathNum(["pow", acc]);
            const Pgranted = getPathNum(["granted", acc, "t"]);
            const Pgranting = getPathNum(["granting", acc, "t"]);
            const Pgov = getPathNum(["gov", acc]);
            const Pspk = getPathNum(['spk', acc])
            const Pspkt = getPathNum(['spk', 't'])
            Promise.all([Pblock, Pstats, Ppow, Pgranted, Pgranting, Pgov, Pspk, Pspkt]).then(
                (mem) => {
                    var block = mem[0],
                        diff = bn - block,
                        stats = mem[1],
                        pow = mem[2],
                        granted = mem[3],
                        granting = mem[4],
                        gov = mem[5],
                        spk = mem[6],
                        spkt = mem[7],
                        r = 0, a = 0, b = 0, c = 0, t = 0
                    if (!block){
                        store.batch(
                          [{ type: "put", path: ["spkb", acc], data: bn}],
                          [res, rej, 0]
                        );
                    } else if(diff < 28800){ //min claim period
                        res(r)
                    } else {
                        t = parseInt(diff/28800)
                        a = simpleInterest(gov, t, stats.spk_rate_lgov)
                        b = simpleInterest(pow, t, stats.spk_rate_lpow);
                        c = simpleInterest(
                          (granted + granting),
                          t,
                          stats.spk_rate_ldel
                        );
                        const i = a + b + c
                        if(i){
                            store.batch(
                              [{type: "put", path: ["spk", acc], data: spk + i}, 
                   {type: "put", path: ["spk", "t"], data: spkt + i}, 
                   {type: "put", path: ["spkb", acc], data: bn - (diff % 28800)
                               }],
                              [res, rej, i]
                            );
                        } else {
                            res(0)
                        }
                    }
    
                }
            );
        })
    }
    

    Here the different balances are accessed in memory interest is calculated, and the SPK balance and total SPK balance are adusted. The Interest is calculated for whole days stored as block numbers in ['spkb']

    SPK Send

    exports.spk_send = (json, from, active, pc) => {
        let Pinterest = reward_spk(from, json.block_num),
            Pinterest2 = reward_spk(json.to, json.block_num);
        Promise.all([Pinterest, Pinterest2])
            .then(interest => {
                let fbalp = getPathNum(["spk", from]),
                    tbp = getPathNum(["spk", json.to]); //to balance promise
                Promise.all([fbalp, tbp])
                    .then((bals) => {
                        let fbal = bals[0],
                            tbal = bals[1],
                            ops = [];
                        send = parseInt(json.amount);
                        if (
                            json.to &&
                            typeof json.to == "string" &&
                            send > 0 &&
                            fbal >= send &&
                            active &&
                            json.to != from
                        ) {
                            //balance checks
                            ops.push({
                                type: "put",
                                path: ["spk", from],
                                data: parseInt(fbal - send),
                            });
                            ops.push({
                                type: "put",
                                path: ["spk", json.to],
                                data: parseInt(tbal + send),
                            });
                            let msg = `@${from}| Sent @${json.to} ${parseFloat(
                                parseInt(json.amount) / 1000
                            ).toFixed(3)} SPK`;
                            if (config.hookurl || config.status)
                                postToDiscord(msg, `${json.block_num}:${json.transaction_id}`);
                            ops.push({
                                type: "put",
                                path: ["feed", `${json.block_num}:${json.transaction_id}`],
                                data: msg,
                            });
                        } else {
                            ops.push({
                                type: "put",
                                path: ["feed", `${json.block_num}:${json.transaction_id}`],
                                data: `@${from}| Invalid spk send operation`,
                            });
                        }
                        if (process.env.npm_lifecycle_event == "test") pc[2] = ops;
                        store.batch(ops, pc);
                    })
                    .catch((e) => {
                        console.log(e);
                    });
            })
    };
    

    Here you can see the interest is calculated and rewarded before the send operation occurs. In the future this will also happen for all smart contracts that rely on SPK balance or changes to locked Larynx balances.

    One concern here, if the approach to voting is space sensitive, changing SPK balances will require vote weights to be returned to the average. If votes are stored in the system the vote can be recalculated. I'm interested in hearing about clever ways to track votes with out keeping a whole accounting of them in memory.

    Power Up and Delegate

    exports.power_up = (json, from, active, pc) => {
        reward_spk(from, json.block_num).then(interest => {
            var amount = parseInt(json.amount),
                lpp = getPathNum(["balances", from]),
                tpowp = getPathNum(["pow", "t"]),
                powp = getPathNum(["pow", from]);
    
            Promise.all([lpp, tpowp, powp])
                .then((bals) => {
                    let lb = bals[0],
                        tpow = bals[1],
                        pow = bals[2],
                        lbal = typeof lb != "number" ? 0 : lb,
                        pbal = typeof pow != "number" ? 0 : pow,
                        ops = [];
                    if (amount <= lbal && active) {
                        ops.push({
                            type: "put",
                            path: ["balances", from],
                            data: lbal - amount,
                        });
                        ops.push({
                            type: "put",
                            path: ["pow", from],
                            data: pbal + amount,
                        });
                        ops.push({
                            type: "put",
                            path: ["pow", "t"],
                            data: tpow + amount,
                        });
                        const msg = `@${from}| Powered ${parseFloat(
                            json.amount / 1000
                        ).toFixed(3)} ${config.TOKEN}`;
                        if (config.hookurl || config.status)
                            postToDiscord(msg, `${json.block_num}:${json.transaction_id}`);
                        ops.push({
                            type: "put",
                            path: ["feed", `${json.block_num}:${json.transaction_id}`],
                            data: msg,
                        });
                    } else {
                        ops.push({
                            type: "put",
                            path: ["feed", `${json.block_num}:${json.transaction_id}`],
                            data: `@${from}| Invalid power up`,
                        });
                    }
                    store.batch(ops, pc);
                })
                .catch((e) => {
                    console.log(e);
                });
        })
    }
    
    exports.power_grant = (json, from, active, pc) => {
        var amount = parseInt(json.amount),
            to = json.to,
            Pgranting_from_total = getPathNum(["granting", from, "t"]),
            Pgranting_to_from = getPathNum(["granting", from, to]),
            Pgranted_to_from = getPathNum(["granted", to, from]),
            Pgranted_to_total = getPathNum(["granted", to, "t"]),
            Ppower = getPathNum(["pow", from]),
            Pup_from = getPathObj(["up", from]),
            Pdown_from = getPathObj(["down", from]),
            Pup_to = getPathObj(["up", to]),
            Pdown_to = getPathObj(["down", to]),
            Pgov = getPathNum(['gov', to])
            Pinterest = reward_spk(from, json.block_num), //interest calc before balance changes.
            Pinterest2 = reward_spk(json.to, json.block_num);
        Promise.all([
            Ppower,
            Pgranted_to_from,
            Pgranted_to_total,
            Pgranting_to_from,
            Pgranting_from_total,
            Pup_from,
            Pup_to,
            Pdown_from,
            Pdown_to,
            Pgov,
            Pinterest,
            Pinterest2
        ])
            .then((mem) => {
                let from_power = mem[0],
                    granted_to_from = mem[1],
                    granted_to_total = mem[2],
                    granting_to_from = mem[3],
                    granting_from_total = mem[4],
                    up_from = mem[5],
                    up_to = mem[6],
                    down_from = mem[7],
                    down_to = mem[8],
                    ops = [];
                if (amount < from_power && amount >= 0 && active && mem[9]) { //mem[9] checks for gov balance in to account. 
                    if (amount > granted_to_from) {
                        let more = amount - granted_to_from;
                        if (up_from.max) {
                            up_from.max -= more;
                        }
                        if (down_from.max) {
                            down_from.max -= more;
                        }
                        if (up_to.max) {
                            up_to.max += more;
                        }
                        if (down_to.max) {
                            down_to.max += more;
                        }
                        ops.push({
                            type: "put",
                            path: ["granting", from, "t"],
                            data: granting_from_total + more,
                        });
                        ops.push({
                            type: "put",
                            path: ["granting", from, to],
                            data: granting_to_from + more,
                        });
                        ops.push({
                            type: "put",
                            path: ["granted", to, from],
                            data: granted_to_from + more,
                        });
                        ops.push({
                            type: "put",
                            path: ["granted", to, "t"],
                            data: granted_to_total + more,
                        });
                        ops.push({
                            type: "put",
                            path: ["pow", from],
                            data: from_power - more,
                        }); //weeks wait? chron ops? no because of the power growth at vote
                        ops.push({
                            type: "put",
                            path: ["up", from],
                            data: up_from,
                        });
                        ops.push({
                            type: "put",
                            path: ["down", from],
                            data: down_from,
                        });
                        ops.push({ type: "put", path: ["up", to], data: up_to });
                        ops.push({
                            type: "put",
                            path: ["down", to],
                            data: down_to,
                        });
                        const msg = `@${from}| Has granted ${parseFloat(
                            amount / 1000
                        ).toFixed(3)} to ${to}`;
                        if (config.hookurl || config.status)
                            postToDiscord(
                                msg,
                                `${json.block_num}:${json.transaction_id}`
                            );
                        ops.push({
                            type: "put",
                            path: [
                                "feed",
                                `${json.block_num}:${json.transaction_id}`,
                            ],
                            data: msg,
                        });
                    } else if (amount < granted_to_from) {
                        let less = granted_to_from - amount;
                        if (up_from.max) {
                            up_from.max += less;
                        }
                        if (down_from.max) {
                            down_from.max += less;
                        }
                        if (up_to.max) {
                            up_to.max -= less;
                        }
                        if (down_to.max) {
                            down_to.max -= less;
                        }
                        ops.push({
                            type: "put",
                            path: ["granting", from, "t"],
                            data: granting_from_total - less,
                        });
                        ops.push({
                            type: "put",
                            path: ["granting", from, to],
                            data: granting_to_from - less,
                        });
                        ops.push({
                            type: "put",
                            path: ["granted", to, from],
                            data: granted_to_from - less,
                        });
                        ops.push({
                            type: "put",
                            path: ["granted", to, "t"],
                            data: granted_to_total - less,
                        });
                        ops.push({
                            type: "put",
                            path: ["pow", from],
                            data: from_power + less,
                        });
                        ops.push({
                            type: "put",
                            path: ["up", from],
                            data: up_from,
                        });
                        ops.push({
                            type: "put",
                            path: ["down", from],
                            data: down_from,
                        });
                        ops.push({ type: "put", path: ["up", to], data: up_to });
                        ops.push({
                            type: "put",
                            path: ["down", to],
                            data: down_to,
                        });
                        const msg = `@${from}| Has granted ${parseFloat(
                            amount / 1000
                        ).toFixed(3)} to ${to}`;
                        if (config.hookurl || config.status)
                            postToDiscord(
                                msg,
                                `${json.block_num}:${json.transaction_id}`
                            );
                        ops.push({
                            type: "put",
                            path: [
                                "feed",
                                `${json.block_num}:${json.transaction_id}`,
                            ],
                            data: msg,
                        });
                    } else {
                        const msg = `@${from}| Has already granted ${parseFloat(
                            amount / 1000
                        ).toFixed(3)} to ${to}`;
                        if (config.hookurl || config.status)
                            postToDiscord(
                                msg,
                                `${json.block_num}:${json.transaction_id}`
                            );
                        ops.push({
                            type: "put",
                            path: [
                                "feed",
                                `${json.block_num}:${json.transaction_id}`,
                            ],
                            data: msg,
                        });
                    }
                } else {
                    const msg = `@${from}| Invalid delegation`;
                    if (config.hookurl || config.status)
                        postToDiscord(
                            msg,
                            `${json.block_num}:${json.transaction_id}`
                        );
                    ops.push({
                        type: "put",
                        path: ["feed", `${json.block_num}:${json.transaction_id}`],
                        data: msg,
                    });
                }
                store.batch(ops, pc);
            })
            .catch((e) => {
                console.log(e);
            });
    }
    

    The only thing new here to note is delegation are only allowed to accounts with a gov balance, which only node operating accounts can have. Removing or lowering a delegation will also calculate the SPK balance before the change. As far as I can figure there is no way to "double spend" Larynx for rewards... please check me on this, it's important.

    API

    Stated previously front-ends will have to calculate SPK balances based on the same information, which means a little extra API is needed. This will need to be coupled with the interest rate stats and head block number.

    Thank You

    Thank you to the community of node runners and that help me and each other run and improve this and other Hive software. I appreciate your feedback here and on our Discord Server


    Vote for our Witness:



    Tags :

    spknetwork larynx spk leofinance ctp hive crypto web3

    Woo! This creator can upvote comments using 3speak's stake today because they are a top performing creator! Leave a quality comment relating to their content and you could receive an upvote worth at least a dollar.

    Their limit for today is $0!
    Comments:
    Time until spknetwork can give away $0 to their commenters.
    0 Days 0 Hours 0 Minutes 0 Seconds
    Reply:

    To comment on this video please connect a HIVE account to your profile: Connect HIVE Account

    01:35:15
    64 views 9 months ago $

    More Videos

    01:35
    2 views a year ago $
    00:55
    1 views 9 months ago $
    10:00
    3 views 2 years ago $