Tutorials
No Results
Tutorial Image

Beginner · 15 minutes or less

Creating a Go Transaction with the PureStake API

Creating a transaction and sending it in to the network programmatically are foundational tasks for working with a blockchain. Using the PureStake API allows a developer full access to the complete Algorand MainNet/Testnet ledgers without running any infrastructure, but does require a few minor changes to the code.

Requirements

1. Set Configuration Values for the PureStake API

First, you’ll need to set the default configuration values for PureStake’s API. This process varies from the Algorand-published examples in a couple of ways:

  1. You’ll need to set the token in a slice of the Header struct
  2. Later, you will explicitly set the ‘Content-Type’ for the transaction POST

To do that, let’s define our imports and the connection constants:

import (
    "fmt"
    "github.com/algorand/go-algorand-sdk/client/algod"
    "github.com/algorand/go-algorand-sdk/crypto"
    "github.com/algorand/go-algorand-sdk/mnemonic"
    "github.com/algorand/go-algorand-sdk/transaction"
)

const algodAddress = "https://testnet-algorand.api.purestake.io/ps1"
const psToken = "YOUR API KEY HERE"

Then, in the main function we can build our Headers slice:

var headers []*algod.Header
headers = append(headers, &algod.Header{"X-API-Key", psToken})

2. Recover Private Key

Now you’ll need to recover your private key using your mnemonic.

Important Note: Mnemonics control your account. Never share them or store insecurely. The code below is for demonstration purposes only.

const fromAddr = "YOUR ACCOUNT ADDRESS HERE"
const mn = "YOUR MNEMONIC HERE"
fromAddrPvtKey, err := mnemonic.ToPrivateKey(mn)
if err != nil {
    fmt.Printf("error getting suggested tx params: %s\n", err)
    return
}

3. Create the Client

To instantiate the client, enter:

var headers []*algod.Header
headers = append(headers, &algod.Header{"X-API-Key", psToken})
algodClient, err := algod.MakeClientWithHeaders(algodAddress, "", headers)
if err != nil {
    fmt.Printf("failed to make algod client: %s\n", err)
    return
}

4. Get and Set Transaction Parameters

Get the transaction parameters from the blockchain using the client object and set the amount and destination address.

txParams, err := algodClient.SuggestedParams()
if err != nil {
    fmt.Printf("error getting suggested tx params: %s\n", err)
    return
}
const toAddr = "AEC4WDHXCDF4B5LBNXXRTB3IJTVJSWUZ4VJ4THPU2QGRJGTA3MIDFN3CQA"

5. Create and Sign Transaction

Create a new transaction using the parameters defined above and sign it.

genID := txParams.GenesisID
tx, err := transaction.MakePaymentTxn(fromAddr, toAddr, 1, 100000, txParams.LastRound, txParams.LastRound+100, nil, "", genID, txParams.GenesisHash)
if err != nil {
    fmt.Printf("Error creating transaction: %s\n", err)
    return
}

_, bytes, err := crypto.SignTransaction(fromAddrPvtKey, tx)
if err != nil {
    fmt.Printf("Failed to sign transaction: %s\n", err)
    return
}

6. Include Verification Function

After the transaction is complete and sent in, this function is called to verify that the transaction has been included in the blockchain:

// Function from Algorand Inc. - utility for waiting on a transaction confirmation
func waitForConfirmation(algodClient algod.Client, txID string) {
    for {
        pt, err := algodClient.PendingTransactionInformation(txID)
        if err != nil {
            fmt.Printf("waiting for confirmation... (pool error, if any): %s\n", err)
            continue
        }
        if pt.ConfirmedRound > 0 {
            fmt.Printf("Transaction "+pt.TxID+" confirmed in round %d\n", pt.ConfirmedRound)
            break
        }
        nodeStatus, err := algodClient.Status()
        if err != nil {
            fmt.Printf("error getting algod status: %s\n", err)
            return
        }
        algodClient.StatusAfterBlock(nodeStatus.LastRound + 1)
    }
}

7. Submit and Verify Transaction

Lastly, send the signed transaction to the blockchain. Note the inclusion of a custom headers slice holding the ‘content-type’ of ‘application/x-binary’.

txHeaders := append([]*algod.Header{}, &algod.Header{"Content-Type", "application/x-binary"})
sendResponse, err := algodClient.SendRawTransaction(bytes, txHeaders...)
if err != nil {
    fmt.Printf("failed to send transaction: %s\n", err)
    return
}
fmt.Printf("Transaction successfully with ID: %s\n", sendResponse.TxID)
fmt.Printf("Waiting for confirmation...\n")
waitForConfirmation(algodClient, sendResponse.TxID)

You should see a response similar to this one:

Waiting for confirmation...
Transaction YFMPNMI773YHBM765ZZLA5PMBMQBRZNB3MCUFSYVRVGUW3DCZ6PQ confirmed in round 6007773.

8. Run All of the Code Together

If you’d rather just drop in the code in its entirety, copy below. You’ll need to enter your API key, your mnemonic, and some other specifics in order for it to function properly.

package main

import (
    "fmt"
    "github.com/algorand/go-algorand-sdk/client/algod"
    "github.com/algorand/go-algorand-sdk/crypto"
    "github.com/algorand/go-algorand-sdk/mnemonic"
    "github.com/algorand/go-algorand-sdk/transaction"
)

const algodAddress = "https://testnet-algorand.api.purestake.io/ps1"
const psToken = "YOU API KEY HERE"

// Initalize throw-away account for this example - check that is has funds before running the program.
const fromAddr = "YOUR ACCOUNT ADDRESS HERE"
const mn = "YOU MNEMONIC HERE"

const toAddr = "AEC4WDHXCDF4B5LBNXXRTB3IJTVJSWUZ4VJ4THPU2QGRJGTA3MIDFN3CQA"

// Function from Algorand Inc. - utility for waiting on a transaction confirmation
func waitForConfirmation(algodClient algod.Client, txID string) {
    for {
        pt, err := algodClient.PendingTransactionInformation(txID)
        if err != nil {
            fmt.Printf("waiting for confirmation... (pool error, if any): %s\n", err)
            continue
        }
        if pt.ConfirmedRound > 0 {
            fmt.Printf("Transaction "+pt.TxID+" confirmed in round %d\n", pt.ConfirmedRound)
            break
        }
        nodeStatus, err := algodClient.Status()
        if err != nil {
            fmt.Printf("error getting algod status: %s\n", err)
            return
        }
        algodClient.StatusAfterBlock(nodeStatus.LastRound + 1)
    }
}

func main() {
    // Create an algod client
    var headers []*algod.Header
    headers = append(headers, &algod.Header{"X-API-Key", psToken})
    algodClient, err := algod.MakeClientWithHeaders(algodAddress, "", headers)
    if err != nil {
        fmt.Printf("failed to make algod client: %s\n", err)
        return
    }
    fmt.Println("Algod client created")

    // Recover private key from the mnemonic
    fromAddrPvtKey, err := mnemonic.ToPrivateKey(mn)
    if err != nil {
        fmt.Printf("error getting suggested tx params: %s\n", err)
        return
    }
    fmt.Println("Private key recovered from mnemonic")

    // Get the suggested transaction parameters
    txParams, err := algodClient.SuggestedParams()
    if err != nil {
        fmt.Printf("error getting suggested tx params: %s\n", err)
        return
    }

    // Make transaction
    genID := txParams.GenesisID
    tx, err := transaction.MakePaymentTxn(fromAddr, toAddr, 1, 100000, txParams.LastRound, txParams.LastRound+100, nil, "", genID, txParams.GenesisHash)
    if err != nil {
        fmt.Printf("Error creating transaction: %s\n", err)
        return
    }

    // Sign the Transaction
    _, bytes, err := crypto.SignTransaction(fromAddrPvtKey, tx)
    if err != nil {
        fmt.Printf("Failed to sign transaction: %s\n", err)
        return
    }

    // Broadcast the transaction to the network
    txHeaders := append([]*algod.Header{}, &algod.Header{"Content-Type", "application/x-binary"})
    sendResponse, err := algodClient.SendRawTransaction(bytes, txHeaders...)
    if err != nil {
        fmt.Printf("failed to send transaction: %s\n", err)
        return
    }
    fmt.Printf("Transaction successfully with ID: %s\n", sendResponse.TxID)
    fmt.Printf("Waiting for confirmation...\n")
    waitForConfirmation(algodClient, sendResponse.TxID)
}

transactions

go

PureStake

v1 api

April 11, 2020