owned this note
owned this note
Published
Linked with GitHub
# ATOM Staking Guide with P2P.ORG API and Fireblocks Raw Signing
This guide will walk you through the process of staking ATOM tokens using P2P.ORG API in conjunction with Fireblocks Raw Signing feature.
> Disclaimer: This guide is written to help you navigate the flow and understand some data structures. Please handle errors, data validation checks, and the selection of appropriate vaults/addresses carefully on your own.
## Overview
The staking procedure using Fireblock's Raw Signing involves interaction with the P2P.ORG API to form, encode, and send transactions, and with the Fireblocks API to retrieve asset storage structures and extract the necessary addresses for staking delegation.
Here is the flow diagram

## Prerequisites
Before you begin, ensure you have:
1. P2P.org API key
2. Enabled Raw Signing in Fireblocks. [Here](https://developers.fireblocks.com/docs/raw-signing#enabling-raw-signing) how to enable raw signing and TAP policy
3. Fireblocks API key and secret. [Here](https://docs.google.com/document/d/1XXZIdFtRcrqMXiMbZ3Cq1M75bWMxNEjo55Ib_DRLSmc/edit?usp=sharing) is the instruction how to get an API key and download a secret
4. Installed [Fireblocks SDK](https://developers.fireblocks.com/reference/api-overview#language-specific-sdks--guides)
> Setting up Raw Signing and TAP policy in Fireblocks is a rather complex task and its description is beyond the scope of this document. You will definitely need to contact Fireblocks Sustomer Service for setup assistance. The manager can also explain how everything works.
# 1.Connect Fireblocks and Retrieve Wallet Address
The main goal of this step is to connect to Fireblocks, retrieve the list of vaults and wallets, and then select the appropriate public key for creating the transaction. Here's the sequence:
Let's begin with import required modules and constants from Fireblocks SDK
```
from fireblocks_sdk import (FireblocksSDK, TransferPeerPath, VAULT_ACCOUNT, TRANSACTION_STATUS_COMPLETED, PagedVaultAccountsRequestFilters)
```
And then create a Fireblocks SDK connection:
```
fireblocks = get_fireblocks_sdk(fireblocks_api_secret, fireblocks_api_key)
```
This code initializes the connection to Fireblocks using the provided API key and secret.
Retrieve the list of vaults:
```
vaults = fireblocks.get_vault_accounts_with_page_info(PagedVaultAccountsRequestFilters())
```
This method fetches all available vault accounts from Fireblocks. The vaults object returned has the following structure:
```
vaults
└── accounts (array)
└── account
├── id
├── name
└── assets (array)
└── asset
└── id
```
From this structure, you need to choose:
`['account']['id']`: The ID of the selected vault
`['asset']['id']`: The ID of the selected asset within the vault
```
account = vaults['accounts'][vault_index]
account_id = account['id']
asset_id = account['assets'][asset_index]['id']
```
> Fireblocks has a flexible structure of vaults/addresses/assets, so it's necessary to select the appropriate nodes in the structure for each specific case.
Then get the wallet address and public key for a specific vault and asset:
```
addresses = fireblocks.get_deposit_addresses(vault_account_id=account_id, asset_id=asset_id)
pub_key = fireblocks.get_public_key_info_for_vault_account(
asset_id=asset_id,
vault_account_id=account_id,
compressed=True,
change=0,
address_index=0)
```
`addresses` contains an array of deposit addresses of the specific vault account and asset
```
addresses (array)
└── address_object
└── address (string)
```
use `addresses[0]['address']` or whatever you need
`pub_key` structure
```
pub_key
└── publicKey (string)
```
use `pub_key["publicKey"]` to get a public key
```
pub_key = pub_key['publicKey']
address = addresses[0]['address']
```
# 2.Create ATOM Staking Transaction
Call the P2P.ORG API staking/stake method to form the staking request
```
headers = {
"accept": "application/json",
"content-type": "application/json",
"authorization": "Bearer " + p2p_api_key
}
payload = {
"stashAccountAddress": address,
"amount": amount
}
response = requests.post(p2p_api_url + "staking/stake/", headers=headers, json=payload)
response = response.json()
```
The `address` parameter used in the payload is the deposit address we obtained in the previous step (`addresses[0]['address']`). This is the address from which the staking transaction will be initiated.
`amount` is the amount of ATOM tokens to stake. You can use a fraction of ATOMs by specifying decimal numbers with decimal point. For example `amount = '0.5'`
The API responds with a JSON structure. The key fields we need from this response are:
```
encoded_body = response['result']['transactionData']['encodedBody']
encoded_auth_info = response['result']['transactionData']['encodedAuthInfo']
```
These fields contain the encoded transaction data that we'll use for signing.
Calculate the Sha256 hash of the `encoded_body`:
```
message_hash = hashlib.sha256(encoded_body.encode()).hexdigest()
```
This message hash will be used in the next step for signing the transaction with Fireblocks.
The `encoded_body`, `encoded_auth_info`, and `message_hash` are crucial for the subsequent steps in the staking process, particularly for signing the transaction and broadcasting it to the network.
# 3.Send Transaction to Fireblocks for Signature
In this step, we send the transaction to Fireblocks for signing. For ATOM staking transactions, Fireblocks requires only the transaction hash for signing.
To initiate the signing process, we use the `create_transaction` method from the Fireblocks SDK:
```
extra = {
"rawMessageData": {
"messages": [
{
"content": message_hash,
}
]
}
}
tx_id, status = fireblocks.create_transaction(
tx_type='RAW',
asset_id='ATOM',
source=TransferPeerPath(VAULT_ACCOUNT, "0"),
extra_parameters=extra
).values()
```
The method returns a transaction ID (`tx_id`) and initial status, which we capture.
This step initiates the signing process on the Fireblocks platform. The `tx_id` returned will be used in the next step to check the status of the signing process and retrieve the signature.
# 4.Poll Transaction Status
Let's poll for the transaction status until it's successful, and then retrieve the full signature.
> It's important to note that a successful status at this stage only means that the transaction was successfully signed, not that it has been executed on the blockchain.
```
while True:
tx_info = fireblocks.get_transaction_by_id(tx_id)
status = tx_info.get('status', 'unknown')
if status == TRANSACTION_STATUS_COMPLETED:
break
sleep(5)
```
Once we have a successful status, we can retrieve the full signature
```
full_sig = tx_info['signedMessages'][0]['signature']['fullSig']
```
The `full_sig` we'll use to encode and broadcast the transaction.
# 5.Encode Transaction
At this point, we have completed all the preliminary steps and now we're ready to assemble all these pieces into a transaction structure that's ready to be sent to the blockchain. To do this, we'll use the `transaction/encode` method from the P2P.ORG API
```
headers = {
"accept": "application/json",
"content-type": "application/json",
"authorization": "Bearer " + p2p_api_key
}
payload = {
"delegatorAddress": address,
"encodedBody": encoded_body,
"encodedAuthInfo": encoded_auth_info,
"signature": full_sig
}
response = requests.post(p2p_api_url + "transaction/encode/", headers=headers, json=payload)
response = response.json()
```
The `address` parameter used in the payload is the deposit address we obtained in the step 1 (`addresses[0]['address']`). This is the address from which the staking transaction will be initiated.
The API responds with a JSON structure. We need to get `encodedTx` field:
```
encoded_tx = response['result']['encodedTx']
```
# 6.Send to Blockchain
Finally, broadcast the encoded transaction to the blockchain using P2P.ORG API
```
headers = {
"accept": "application/json",
"content-type": "application/json",
"authorization": "Bearer " + p2p_api_key
}
payload = {
"signedTransaction": encoded_tx
}
response = requests.post(p2p_api_url + "transaction/send/", headers=headers, json=payload)
response = response.json()
```
If everything is done correctly, the transaction will be automatically executed after being sent, resulting in the delegation of the stake to the validator.
The unstake operation differs only in creating a different type of transaction by calling the `staking/unstake` method in Step 2.