/
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 key pairs to control access to user accounts. Each key pair can be used in combination with the SHA2-256 or SHA3-256 hashing algorithms.

You'll need to authorize at least one public key to control your new account.

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, referred to as account keys.

An account key contains the following data:

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

The weight field is used to support multi-sig functionality. This example uses 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 (
  "context"
  "github.com/onflow/flow-go-sdk"
  "github.com/onflow/flow-go-sdk/client"
  "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 with 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