/
onflow.org
Flow Playground

Create an Account

How to create a Flow account with the Flow Go SDK


You can find a complete runnable example of account creation in the Flow Go SDK source repository.

On Flow, account creation happens inside a transaction. Because the network allows for a many-to-many relationship between public keys and accounts, it's not possible to derive a new account address from a public key offline.

The Flow VM uses a deterministic address generation algorithm to assign account addresses on chain. You can find more details about address generation in the accounts & keys documentation.

Prepare the public key

Flow uses ECDSA keys to control access to user accounts. Each key pair can be used in combination with the SHA2-256 or SHA3-256 hashing algorithms.

To control your newly-created account, you'll need to authorize at least one public key.

Public key format

Flow represents ECDSA public keys in raw form without additional metadata. Each key is a single byte slice containing a concatenation of its X and Y components in big-endian byte form.

For example, a public key on the ECDSA_P256 curve consists of two 32-byte integers X and Y:

publicKey = bigEndianBytes(X) + bigEndianBytes(Y)
import (
  "github.com/onflow/flow-go-sdk/crypto"
)

// Raw public key encoded as hex
const rawPublicKey = "9cd98d436d111aab0718ab008a466d636a22ac3679d335b77e33ef7c52d9c8ce47cf5ad71ba38cedd336402aa62d5986dc224311383383c09125ec0636c0b042"

// Decode this public key against the ECDSA_P256 curve
publicKey, err := crypto.DecodePublicKeyHex(crypto.ECDSA_P256, rawPublicKey)
if err != nil {
  panic("failed to decode raw public key")
}

Define an account key

A Flow account can contain zero or more public keys. These are referred to as account keys.

An account key contains the following data:

  • Raw public key
  • Signature algorithm
  • Hash algorithm
  • Weight (integer between 0-1000)

The weight field is used to support multi-sig functionality. For this example we'll used a single account key with full weight.

import (
  "github.com/onflow/flow-go-sdk"
  "github.com/onflow/flow-go-sdk/crypto"
)

accountKey := flow.NewAccountKey().
  SetPublicKey(publicKey). // The signature algorithm is inferred from the public key
  SetHashAlgo(crypto.SHA3_256). // This key will require SHA3 hashes
  SetWeight(flow.AccountKeyWeightThreshold) // Give this key full signing weight

Construct the transaction

Account creation happens inside a transaction, which means that somebody must pay to submit that transaction to the network. We'll call this person the account creator.

import (
  "github.com/onflow/flow-go-sdk"
  "github.com/onflow/flow-go-sdk/crypto"
  "github.com/onflow/flow-go-sdk/templates"
)

var (
  creatorAddress    flow.Address
  creatorAccountKey *flow.AccountKey
  creatorSigner     crypto.Signer
)

var accessAPIHost string

// Establish a connection to an access node
flowClient, err := client.New(accessAPIHost)
if err != nil {
  panic("failed to establish connection with Access API")
}

// Use the templates package to create a new account creation transaction
tx := templates.CreateAccount([]*flow.AccountKey{accountKey}, nil, creatorAddress)

// Set the transaction payer and proposal key
tx.SetPayer(creatorAddress)
tx.SetProposalKey(
  creatorAddress,
  creatorAccountKey.ID,
  creatorAccountKey.SequenceNumber,
)

// Get the latest sealed block to use as a reference block
latestBlock, err := flowClient.GetLatestBlockHeader(context.Background(), true)
if err != nil {
  panic("failed to fetch latest block")
}

tx.SetReferenceBlockID(latestBlock.ID)

// Sign and submit the transaction

err = tx.SignEnvelope(creatorAddress, creatorAccountKey.ID, creatorSigner)
if err != nil {
  panic("failed to sign transaction envelope")
}

err = flowClient.SendTransaction(context.Background(), *tx)
if err != nil {
  panic("failed to send transaction to network")
}

Fetch the result

The new account address will be emitted in a system-level flow.AccountCreated event.

This event is returned as part of the transaction result:

import (
  "github.com/onflow/flow-go-sdk"
)

result, err := flowClient.GetTransactionResult(ctx, tx.ID())
if err != nil {
    panic("failed to get transaction result")
}

var newAddress flow.Address

if result.Status != flow.TransactionStatusSealed {
  panic("address not known until transaction is sealed")
}

for _, event := range result.Events {
  if event.Type == flow.EventAccountCreated {
    newAddress = flow.AccountCreatedEvent(event).Address()
    break
  }
}
Edit on GitHub