Creating a BitClot post isn't that straightforward. If you have ever used Twitter API (aka Tweepy) you might recall that tweeting through the API was really easy and convenient. All you needed to do was to call the predefined functions of the twitter API. This isn't the case with BItClout. If you have to make post or any transaction (since post is also a transaction) you will have to go through 3 major steps:
Step 1: Get the post transaction Hex
Step 2: Sign the transaction Hex
Step 3: Submit the transaction.
While the step 1 and step 3 is fairly easy, the step 2 is where a lot of people find it difficult to get through. This is where this article can come handy for those people! You must note that this article is only for creating post from YOUR account only. You must not try to create any service (like schedule post or something) which will make post on other user's behalf. It is also recommended to use a test account to follow this tutorial because you will be using the seedHex of your account in your code. The seedHex in other language is as powerful as your BitClout seed phrase. If someone have access to your seedHex they can do ANYTHING with your account. If you are in crypto world from long time, you might know the risk of storing seed phrase (or seed Hex) on a computer or anywhere online. That's why you must use a test account to follow this tutorial (I repeat).
Before we get started, I want to let you know that this complete tutorial is in Python and it is must that you should be comfortable with the basics of python atleast.
-
Getting the post transaction Hex:
This step is fairly simple enough. All you need is to collect the transactionHex of the required post from the
https://bitclout.com/api/v0/submit-post
endpoint. Below is a python function that returns the transactionHex for a post
import json
import requests
def getPostTransaction(publicKey, body, imageUrl):
header = {
"content-type": "application/json"
}
payload= {"UpdaterPublicKeyBase58Check": publicKey,
"PostHashHexToModify": "",
"ParentStakeID": "",
"Title": "",
"BodyObj": {"Body": body, "ImageURLs": imageUrl},
"RecloutedPostHashHex": "",
"PostExtraData": {},
"Sub": "",
"IsHidden": False,
"MinFeeRateNanosPerKB": 1000}
res = requests.post(
"https://bitclout.com/api/v0/submit-post", json=payload, headers=header)
resJson = res.json()
transactionHex = resJson["TransactionHex"]
return transactionHex
In the above code snippet, publicKey
refers to the public key of the account through which you want to make the post, body
is a string referring to the post content and imageURL
is a list type variable that stores the image URLs which you want to post on BitClout. You can pass imageURL
to []
if you don't want top post image in your clout ( clout is to BitClout as tweet is to twitter )
- Signing the TransactionHex: The next and most important step is to sign the transaction. But wait, what does signing a transaction means ? Well, you are doing a transaction from an account so you can relate it as a bank account transaction where there must be a signature of the bank account owner. Does that makes sense ? The signature part is to make sure that the transaction is being done from the account owner himself and not from any thief*. BitClout has developed their own Identity service which is used to sign the transaction.
There are third party transaction signing APIs like this but as the author of the API himself says that the API could have vulnerabilities which can be a breach to your account security, you must NOT use the above mentioned API to sign the transaction. But don't worry, there are other ways to sign the transaction!
The TransactionHex can be signed without using Identity service as well. Below is a python script that can sign the TransactionHex in few milliseconds! Create a new python file sign.py
which contains the following script:
import hashlib
import hmac
def get_hmac(key, data):
return hmac.new(key, data, hashlib.sha256).digest()
def hmac_drbg(entropy, string):
material = entropy + string
K = b"\x00" * 32
V = b"\x01" * 32
K = get_hmac(K, V + b"\x00" + material)
V = get_hmac(K, V)
K = get_hmac(K, V + b"\x01" + material)
V = get_hmac(K, V)
temp = b""
while len(temp) < 32:
V = get_hmac(K, V)
temp += V
return temp[:32]
#######
g=(0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,
0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8)
p=0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f
n = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
def point_add(point1, point2):
# Returns the result of point1 + point2 according to the group law.
if point1 is None:
return point2
if point2 is None:
return point1
x1, y1 = point1
x2, y2 = point2
if x1 == x2 and y1 != y2:
return None
if x1 == x2:
m = (3 * x1 * x1) * pow(2 * y1, -1, p)
else:
m = (y1 - y2) * pow(x1 - x2, -1, p)
x3 = m * m - x1 - x2
y3 = y1 + m * (x3 - x1)
result = (x3 % p, -y3 % p)
return result
def scalar_mult(k, point):
# Returns k * point computed using the double and point_add algorithm.
result = None
addend = point
while k:
if k & 1:
# Add.
result = point_add(result, addend)
# Double.
addend = point_add(addend, addend)
k >>= 1
return result
#######
def to_DER(r, s): # Signature to DER format
r = bytes.fromhex(r)
s = bytes.fromhex(s)
if r[0] > 0x80:
r = bytes.fromhex("00")+r
if s[0] > 0x80:
s = bytes.fromhex("00")+s
res = bytes.fromhex("02"+hex(len(r))[2:]) + r + bytes.fromhex("02"+hex(len(s))[2:]) + s
res = bytes.fromhex("30"+hex(len(res))[2:]) + res
return res.hex()
def Sign_Transaction(seedHex, TransactionHex):
s256 = hashlib.sha256(hashlib.sha256(bytes.fromhex(TransactionHex)).digest()).digest()
drbg = hmac_drbg(entropy=bytes.fromhex(seedHex), string=s256)
k = int.from_bytes(drbg, 'big')
kp = scalar_mult(k, g)
kpX = kp[0]
r = kpX % n
s = pow(k, -1, n) * (r * int(seedHex, 16)+int(s256.hex(), 16))
s = s % n
signature = to_DER(hex(r)[2:].zfill(64), hex(s)[2:].zfill(64))
signed_transaction = TransactionHex[:-2] + hex(len(bytearray.fromhex(signature)))[2:] + signature
return signed_transaction
The method Sign_Transaction(seedHex, TransactionHex)
takes seedHex and TransactionHex as two argument. You can find the seedHex of your account in your browser storage.
Just open https://bitclout.com/
> Dev tools > Application > Storage > Local Storage > https://identity.bitclout.com
> users > Select the public key with which you want to post > seedHex
Note: Never share your seedHex with anyone. sharing seedHex is equivalent of sharing seed phrase
The second argument TransactionHex
is the transaction which you want to sign.
On a side note, I didn't create the above code but I can make sure that the above code is secure! You can check the repo here.
Note: You must use the code of this article ONLY and NOT from any other repo mentioned in this article. The code in this article is secure and none of your private info like seedHex is being shared with any third party. Being on the safe side, you must review the code of sign.py
on your own.
-
Submitting the signed post transaction:
The third and last step is to submit the signed post TransactionHex. Below is a python code that submits the transaction using
https://bitclout.com/api/v0/submit-transaction
import requests
def submitTransaction(signedTransactionHex):
payload= {
"TransactionHex": signedTransactionHex
}
response = requests.post(
"https://bitclout.com/api/v0/submit-transaction", json=payload)
return response.status_code
the submitTransaction
method only takes signedTransactionHex
as argument.
This is what the 3 required steps are to make post on BitClout. Summing up the above 3 steps, below is a 100% working code.
import os
import requests
import json
import hashlib
import hmac
def getPostTransaction(publicKey, body, imageUrl):
header = {
"content-type": "application/json"
}
payload= {"UpdaterPublicKeyBase58Check": publicKey,
"PostHashHexToModify": "",
"ParentStakeID": "",
"Title": "",
"BodyObj": {"Body": body, "ImageURLs": imageUrl},
"RecloutedPostHashHex": "",
"PostExtraData": {},
"Sub": "",
"IsHidden": False,
"MinFeeRateNanosPerKB": 1000}
res = requests.post(
"https://bitclout.com/api/v0/submit-post", json=payload, headers=header)
resJson = res.json()
transactionHex = resJson["TransactionHex"]
return transactionHex
def get_hmac(key, data):
return hmac.new(key, data, hashlib.sha256).digest()
def hmac_drbg(entropy, string):
material = entropy + string
K = b"\x00" * 32
V = b"\x01" * 32
K = get_hmac(K, V + b"\x00" + material)
V = get_hmac(K, V)
K = get_hmac(K, V + b"\x01" + material)
V = get_hmac(K, V)
temp = b""
while len(temp) < 32:
V = get_hmac(K, V)
temp += V
return temp[:32]
#######
g=(0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,
0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8)
p=0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f
n = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
def point_add(point1, point2):
# Returns the result of point1 + point2 according to the group law.
if point1 is None:
return point2
if point2 is None:
return point1
x1, y1 = point1
x2, y2 = point2
if x1 == x2 and y1 != y2:
return None
if x1 == x2:
m = (3 * x1 * x1) * pow(2 * y1, -1, p)
else:
m = (y1 - y2) * pow(x1 - x2, -1, p)
x3 = m * m - x1 - x2
y3 = y1 + m * (x3 - x1)
result = (x3 % p, -y3 % p)
return result
def scalar_mult(k, point):
# Returns k * point computed using the double and point_add algorithm.
result = None
addend = point
while k:
if k & 1:
# Add.
result = point_add(result, addend)
# Double.
addend = point_add(addend, addend)
k >>= 1
return result
#######
def to_DER(r, s): # Signature to DER format
r = bytes.fromhex(r)
s = bytes.fromhex(s)
if r[0] > 0x80:
r = bytes.fromhex("00")+r
if s[0] > 0x80:
s = bytes.fromhex("00")+s
res = bytes.fromhex("02"+hex(len(r))[2:]) + r + bytes.fromhex("02"+hex(len(s))[2:]) + s
res = bytes.fromhex("30"+hex(len(res))[2:]) + res
return res.hex()
def Sign_Transaction(seedHex, TransactionHex):
s256 = hashlib.sha256(hashlib.sha256(bytes.fromhex(TransactionHex)).digest()).digest()
drbg = hmac_drbg(entropy=bytes.fromhex(seedHex), string=s256)
k = int.from_bytes(drbg, 'big')
kp = scalar_mult(k, g)
kpX = kp[0]
r = kpX % n
s = pow(k, -1, n) * (r * int(seedHex, 16)+int(s256.hex(), 16))
s = s % n
signature = to_DER(hex(r)[2:].zfill(64), hex(s)[2:].zfill(64))
signed_transaction = TransactionHex[:-2] + hex(len(bytearray.fromhex(signature)))[2:] + signature
return signed_transaction
def submitTransaction(signedTransactionHex):
payload= {
"TransactionHex": signedTransactionHex
}
response = requests.post(
"https://bitclout.com/api/v0/submit-transaction", json=payload)
return response.status_code
if __name__ == "__main__":
postBody = "In retrospect, it was inevitable 💎🤲 " #don't dare change this body, Diamondhands won't like you
imageURL = []
seedHex = "YOUR_SEED_HEX" # It is recommended to store your seedHex as environment variable
publicKey = "YOUR_PUBLIC_KEY"
postTxn = getPostTransaction(publicKey, postBody, imageURL)
signedTxn = Sign_Transaction(seedHex,postTxn)
status = submitTransaction(signedTxn)
if status == 200:
print("Post done!")
else:
print("Error " + str(status))
Below is the result of the above code:
Congrats! You made a post on BitClout through your code.
If you like the article don't forget to let us know in the comments or maybe give a shout to DevsClout ? You can also join DevsClout discord server to chat with more devs who are building awesome projects on BitClout! We would love to hear back from you!
Top comments (0)