Direct RC delegations
Forewords:
You may have heard about RC pools, direct RC delegations are not the same. RC pools got deprecated in favor of RC delegations. Although they are quite similar in name, their function is very different. If you're interested in the difference between the two see this post: @howo/direct-rc-delegations-vs-rc-pools-and-tentative-direct-rc-delegations-spec
Direct RC delegations
Direct RC delegations are the simplest version of RC delegation you can think of:
Bob is out of RC, Alice has 200 RC, so she uses her posting key to delegate 100 RC to Bob. Bob now has 100 rc that he is free to use.
Now while this is a basic example, I'll dive into some more specifics.
Max RC vs RC
It's important to differentiate between the two resources we are dealing with:
Here I have 77 million RC out of 87 million max RC. When a user executes a direct RC delegation, they delegate max RC. This has two effects:
- Once spent, the delegated RC will regenerate on the delegatee's account
- Delegating RC will increase the amount of RC you regenerate. The more max RC you have the more you regenerate RC over time, in theory you could delegate 100 million RC on someone with 10k RC to make him regenerate his RC much more quickly and then undelegate. (Although this is unpractical due to the fact that undelegating burns unspent RC).
Constraints & details
- You cannot delegate delegated RC, this is to avoid expensive chain computations.
- You delegate max RC and RC, so you can't do a delegation when you are out of RC.
- When undelegating, the delegator only gets his max RC back, all unspent RC is burned. The delegator will regenerate the RC over time.
- Delegating is done over
custom_json
and requires posting authority (more on that later) - You can delegate to as many accounts as you want.
- You can't delegate all your RC (and end up with 0 RC), you have to always keep the RC equivalent of the account creation fee (3 HIVE as the time of writing).
- You can delegate up to 100 accounts in a single operation.
RC reserve
When an account is created, the account_creation_fee
is burned (as the time of writing, 3 HIVE), that amount is converted in RC, this is why even 0 HP account have some RC. (this is also true for accounts created with account creation tokens).
This "RC reserve" is not delegatable, this is to prevent a bad actor from delegating all your RC away effectively soft-locking your account.
RC delegations in action
There is only one operation used for everything. delegate_rc
the operations is sent in the form a custom json, here's an example where howo
delegates 100 max RC to alice
.
{
"id": "rc",
"json": [
"delegate_rc",
{
"from": "howo",
"delegatees": [
"alice"
],
"max_rc": 100
}
]
}
I will be basing the rest of this guide using hive-js, all the examples, also note that all the keys in this example are from a testnet, they are worthless.
Creating an RC delegation
The parameters are pretty straightforward:
from: Person who delegates the max RC
delegatees
: array of account names that you want to delegate to (max 100)
max_rc
: max RC amount you want to delegate.
function delegate_rc(delegator, posting_key, delegatees, max_rc) {
return new Promise(resolve => {
const json = JSON.stringify(['delegate_rc', {
from: delegator,
delegatees: delegatees,
max_rc: max_rc,
}]);
hive.broadcast.customJson(posting_key, [], [delegator], 'rc', json, function (err, result) {
resolve(err)
});
});
}
Updating a delegation
Updating a delegation is done with the same operation, just input a different max RC amount and the delegation will be increased/reduced.
Keep in mind that if you reduce the delegation, the max RC will come back to you but the RC will be burned.
Deleting a delegation
Deleting a delegation is done by calling delegate_rc with max_rc
set to 0.
Getting RC from an account
This api endpoint has existed since HF20 but has been updated with RC delegations, it's simply called by passing in an array of accounts
function get_rc(accounts) {
return new Promise(resolve => {
hive.api.call('rc_api.find_rc_accounts', {accounts: accounts}, function (err, result) {
return resolve(result)
})
});
}
async function main() {
let rc_accounts = await get_rc(["initminer", "miners"])
}
output is an array of rc_account
objects, note the new fields: delegated_rc
and received_delegated_rc
[
{
"account": "initminer",
"rc_manabar": {
"current_mana": 3153959569,
"last_update_time": 1660535262
},
"max_rc_creation_adjustment": {
"amount": "2020748973",
"precision": 6,
"nai": "@@000000037"
},
"max_rc": 3153959569,
"delegated_rc": 150,
"received_delegated_rc": 0
},
{
"account": "miners",
"rc_manabar": {
"current_mana": 2020748983,
"last_update_time": 1660535259
},
"max_rc_creation_adjustment": {
"amount": "2020748973",
"precision": 6,
"nai": "@@000000037"
},
"max_rc": 2020748983,
"delegated_rc": 0,
"received_delegated_rc": 10
}
]
Listing RC accounts:
If you don't have the full list, you can request the RC accounts:
function list_rc_accounts(start, limit) {
return new Promise(resolve => {
hive.api.call('rc_api.list_rc_accounts', {start:start, limit: limit}, function (err, result) {
return resolve(result)
})
});
}
async function main() {
let rc_accounts = await list_rc_accounts("initminer", 2)
}
The ordering is alphabetical, so you input the start and how many users you want to fetch (limited to 1000) and there you go, think of it as pagination.
If you reach the end of the 1000 and didn't find your account, put the last account as "start" and get the next 1000.
Listing all delegations
So this is where it gets a tad tricky, the start
param is an array.
- The first element of the array is
from
, who is delegating - The second element of the array is
to
, who is delegated to
The second parameter, limit
is pretty self explanatory. it's limited to 1000 elements per query.
Both parameters are used for pagination.
If you want to fetch a specific delegation, fill both from
and to
If you want to get all the delegations from an account, set from
as the account and leave to
as empty
If you only input from
and reach the end of limit
(for instance if an account delegated more than 1000 users), you can continue by inputting the last delegatee in the to
field.
Here's a few examples:
function list_rc_direct_delegations(from, to, limit) {
return new Promise(resolve => {
hive.api.call('rc_api.list_rc_direct_delegations', {start:[from, to], limit: limit}, function (err, result) {
return resolve(result)
})
});
}
async function main() {
// List the first 100 delegations from initminer
await list_rc_direct_delegations("initminer", "", 100)
// get the delegation from initminer to howo
await list_rc_direct_delegations("initminer", "howo", 1)
// List 100 delegations starting with the initminer -> howo delegation
await list_rc_direct_delegations("initminer", "howo", 100)
}
The output is an array of delegation objects:
[
{
"from": "initminer",
"to": "howo",
"delegated_rc": 70
},
{
"from": "initminer",
"to": "alice",
"delegated_rc": 70
}
]
Important tidbit about the ordering, you may be confused that this list is not in alphabetical order, this is because under the hood, we index with account numbers, not account names.
So the reason why howo
comes up before alice
in this testnet, is because if you look at the get_account api call:
[{
"id": 6,
"name": "howo",
...
},{
"id": 7,
"name": "alice",
....
}
]
alice
's id is 7 and howo
's id is 6. If you want to get a bit more in depth, I talk about it in this issue (among other things): https://gitlab.syncad.com/hive/hive/-/issues/271
Note that due to technical limitations, it's not possible to only input to
and get all the inbound delegations, this has to be built on an L2 api (eg: HAF/hivemind/hiveSQL).
Conclusion
I hope you found this documentation useful, if you want to check the direct RC delegation code yourself it's here: https://gitlab.syncad.com/hive/hive/-/merge_requests/245/diffs
I'll be glad to answer any questions that come up in the comments and hope to see a lot of use for rc delegations once hard fork 26 hits !