I've spent the better part of this weekend working on dsteem and just published version 0.6.0 to npm. It's pretty much feature complete at this point, but I'm going to wait a while to tag a stable release. I want to get a chance to use it in a couple of projects before cementing the APIs.
One of the reasons I created dsteem was because I felt that the current ecosystem really needs more documentation (that's what the d in dsteem stands for). Starting out I found it difficult to understand how to use the protocol and how the calls should be formatted, especially for the account creation process. So I thought that documenting that process would be useful, as well as a good showcase of what dsteem can do.
There's two ways to create accounts on the steem blockchain, you can either pay the entire account creation fee up front (6 STEEM at the time of writing, more on that later) or you can pay a smaller fee and delegate some of your Steem Power to the new account. This is the method Steemit Inc. uses to create new accounts for users that sign up.
Delegated Steem Power is like a loan, the new account gets to use a portion of your Steem Power but they can not power it down and sell it.
The minimum creation fee is decided by the witnesses, they publish a price that is used to determine the final cost of the account. The median account creation price at the time of writing is 0.2 STEEM. Yes, I just wrote that it cost 6 STEEM but that's because to get the full price you need to multiply the base fee by 30. This can seem arbitrary at first but it will make sense when you understand how the base fee is used to calculate the discount when delegating steem.
Let's start by creating an account without delegation, to do that you first need to prepare some keys for the new account:
const username = 'foo'
const password = 'barman' // ā ļøšš©
const ownerKey = PrivateKey.fromLogin(username, password, 'owner')
const activeKey = PrivateKey.fromLogin(username, password, 'active')
const postingKey = PrivateKey.fromLogin(username, password, 'posting')
const memoKey = PrivateKey.fromLogin(username, password, 'memo')
As you can see the keys are derived from your username and password, that is why it is a good idea to login on steemit.com with just your posting key.
Steem has three auth levels: owner, active and posting. Posting lets you post content and vote, active lets you transfer money and owner is only used to change the other keys (and itself). The memo key meant for message encryption and is currently not used on steemit.com.
Now we need to wrap the keys in something called an Authority
, they can be used for lots of cool stuff like multisig and shared usage of an account, but for now we will just create the most basic authority objects possible for the keys.
const ownerAuth = {
weight_threshold: 1,
account_auths: [],
key_auths: [[ownerKey.createPublic(), 1]]
}
const activeAuth = {
weight_threshold: 1,
account_auths: [],
key_auths: [[activeKey.createPublic(), 1]]
}
const postingAuth = {
weight_threshold: 1,
account_auths: [],
key_auths: [[postingKey.createPublic(), 1]]
}
ā ļø
Take extra note that those are the public versions of the keys, the private keys are only ever used to sign transactions!
The first rule of steem is: You don't share your private key. The second rule of steem is: YOU DON'T SHARE YOUR PRIVATE KEY! Third rule of steem: Use strong passwords. Fourth rule: don't store your paper-wallet in a damp space.
That sorted we need to figure out what fee to pay for the new account, we could simply hard-code it to 6 STEEM but that could break if the witnesses arrive at a new consensus or an update to the protocol changes the constants.
So let's do it the proper way. To do that we need to connect to a steem rpc node and get the current witness consensus along with the configuration constants:
const client = new Client('wss://steemd.steemit.com')
const constants = await client.getConfig()
const chainProps = await client.getChainProperties()
const ratio = constants['STEEMIT_CREATE_ACCOUNT_WITH_STEEM_MODIFIER']
const fee = Asset.from(chainProps.account_creation_fee).multiply(ratio)
The steem chain properties are median values of what the top 21 witnesses has voted for and besides the account creation fee it also contains the maximum block size and the Steem Dollar interest rate.
There we go, 6 STEEM. Now we have everything we need to create a new account, let's construct an operation we can broadcast to the network:
const op: AccountCreateOperation = ['account_create', {
creator: 'this-is-you',
new_account_name: username,
owner: ownerAuth,
active: activeAuth,
posting: postingAuth,
memo_key: memoKey,
json_metadata: '',
}]
Now we need to package the operation in a transaction, serialize it and finally sign it with the active
authority of creator
. That's a bit out of scope for this article so let's just use dsteem's sendOperations
helper.
const creatorKey = PrivateKey.from('5rule1rule1rule1rule1')
await client.broadcast.sendOperations([op], creatorKey)
š
Account created! Easy! š Let's create another one, with delegation this time. But first I have to let you in on a little secret, Steem Power does not actually exist, it is just a representation of how many STEEM you would have if you withdrew your vesting shares.
The steem blockchain uses the VESTS symbol to represent your vesting shares in the platform.
So to figure out how much delegation is needed we need to get the current vesting share price:
const props = await client.database.getDynamicGlobalProperties()
const sharePrice = Price.from({
base: props.total_vesting_shares,
quote: props.total_vesting_fund_steem
})
š°
The creation fee is discounted on a 1 to 5 basis for delegated steem, e.g. 30 delegated steem is worth 6 steem. With that info we can calculate how many VESTS we need to delegate, let's say we want to pay 0.5 STEEM as a creation fee and the rest as delegation:
const fee = Asset.from('0.500 STEEM')
const ratio = constants['STEEMIT_CREATE_ACCOUNT_DELEGATION_RATIO']
const modifier = constants['STEEMIT_CREATE_ACCOUNT_WITH_STEEM_MODIFIER']
const targetDelegation = sharePrice.convert(
creationFee.multiply(modifier * ratio)
)
const delegation = targetDelegation.subtract(
sharePrice.convert(fee.multiply(ratio))
)
Now we can broadcast the operation just like before, just using the account_create_with_delegation
operation instead of account_create
. I'll leave that as an exercise for the reader š.
But you don't really need to know all this to use dsteem, it does it for you. Creating a new account is as simple as:
await client.broadcast.createAccount({
username: 'foo', password: 'barman', creator: 'someone'
}, someonesActiveKey)
That creates the keys, figures out the correct fee and delegation amounts and broadcasts the operation. See the method's documentation for more info.
The more you know... š¢