How to make post on BitClout using BItClout API

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!

    30

    This website collects cookies to deliver better user experience

    How to make post on BitClout using BItClout API