Unit 1 · Lesson 02 · Web3 with Go & Polygon

Accounts, Balances & wei

Before an investor can buy a share of your client's property, your backend needs to answer a very ordinary question: how much does this address hold? Does the buyer have enough POL to pay the gas? Later — once shares are a token — the exact same call tells you how many shares an address owns. Reading a balance is the first read your product actually cares about.

The win A Go program that takes any Amoy address and prints its balance — first in raw wei, then converted to human POL. Plus the one skill that separates working token backends from subtly broken ones: doing money math with big.Int correctly.

What is an "account", really?

On Polygon (and every EVM chain) an account is just an entry in the chain's giant ledger, named by a 20-byte address written as 40 hex characters: 0x71C7…976F. There are two kinds — and the difference matters for your product:

EOA — externally owned account
A wallet controlled by a private key — like the one you generated for the faucet in Lesson 01. Your investors are EOAs. A human (or your Go signing code) authorizes its transactions. It has a balance and nothing else.
Contract account
An account controlled by code deployed on-chain, not a key. Your property-share token will live at a contract account. It has a balance and stored data (who owns which shares). We deploy one in Unit 4.

BalanceAt works the same on both. Today we read an EOA's native POL balance — the gas money.

wei: the only unit the chain uses

The chain never stores decimals. A balance is always a whole number of wei, the smallest unit, where 1 POL = 1,000,000,000,000,000,000 wei (that's 1018). Think of wei as "cents", except there are a quintillion of them per coin.

Why big.Int is not optional

Here's the trap most newcomers walk into. Go's biggest ordinary integer, int64, tops out at 9,223,372,036,854,775,807. In wei, that is about 9.2 POL. Any account holding ten coins overflows it — silently, into garbage numbers. That's why every on-chain quantity in go-ethereum is a *big.Int: an integer with no size limit. Three rules keep you safe:

Rule 1 — never compare with ==
*big.Int is a pointer; == compares addresses in your program's memory, not values. Two equal balances read at different times compare as "not equal". Use a.Cmp(b), which returns -1, 0, or 1 — so "does the buyer have enough gas?" is balance.Cmp(needed) >= 0.
Rule 2 — never put money in a float
float64 holds ~15–17 significant digits; wei amounts have up to 19+ digits per coin. Floats will round your investors' money. All arithmetic stays in integer wei; you convert to "1.9435 POL" only at the very edge, for display.
Rule 3 — arithmetic writes into the receiver
big.Int methods store their result in the object you call them on: sum := new(big.Int).Add(a, b) means "make a fresh big.Int, put a+b in it". This looks odd but avoids allocating on every operation. You'll see the new(big.Int).Op(x, y) shape everywhere.

Read the balance

Use your own faucet-funded address from Lesson 01 — or any active address copied from Amoy Polygonscan.

main.go

package main

import (
	"context"
	"fmt"
	"log"
	"math"
	"math/big"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/ethclient"
)

func main() {
	client, err := ethclient.Dial("https://rpc-amoy.polygon.technology/")
	if err != nil {
		log.Fatalf("could not connect: %v", err)
	}
	defer client.Close()

	// Turn the human hex string into a typed 20-byte address.
	account := common.HexToAddress("0x0000000000000000000000000000000000001010")

	// nil block number = "the latest block". Returns wei as *big.Int.
	wei, err := client.BalanceAt(context.Background(), account, nil)
	if err != nil {
		log.Fatalf("could not read balance: %v", err)
	}
	fmt.Printf("balance (wei): %s\n", wei)

	// Money LOGIC stays in integer wei: enough to cover 0.01 POL of gas?
	needed := big.NewInt(10_000_000_000_000_000) // 0.01 POL in wei
	if wei.Cmp(needed) >= 0 {
		fmt.Println("gas check: enough for ~0.01 POL of fees")
	} else {
		fmt.Println("gas check: too low — hit the faucet")
	}

	// DISPLAY is the only place floats are allowed: wei -> POL for humans.
	fbalance := new(big.Float).SetInt(wei)
	pol := new(big.Float).Quo(fbalance, big.NewFloat(math.Pow10(18)))
	fmt.Printf("balance (POL): %s\n", pol.Text('f', 6))
}

Run it:

terminal

go run main.go
balance (wei): 1943500000000000000
gas check: enough for ~0.01 POL of fees
balance (POL): 1.943500
You did it You just read live on-chain state for an arbitrary account — the same call, unchanged, will later tell you how many property shares an investor holds. Swap in your own faucet address and watch the number match Polygonscan exactly.

Two things worth noticing

common.HexToAddress is not just a cast. It parses the hex into a fixed 20-byte value and is the type every go-ethereum method expects. Passing a raw string won't compile — addresses are a real type here, which stops a whole class of typo bugs.

The nil block number means "latest". Because balances live in state, and state exists at every block, you can ask "what was this balance 10,000 blocks ago?" by passing a block number instead. That's how you'll later audit ownership at the moment a sale closed. Powerful, and free to read.

Zero is normal A brand-new address you've never funded reads 0 — that's correct, not an error. If your Lesson 01 wallet still reads zero, claim from the Amoy faucet (and remember: every study day).

Check yourself

No peeking — recall from memory. The effort is the point.

1. How many wei are in one POL?

2. An account controlled by a private key is called a…

3. Why can't balances live in an int64?

4. The correct way to check two *big.Int values are equal is…

5. Where is big.Float allowed in a token backend?

Primary source · read this next
Ethereum Development with Go — Account Balances. The canonical reference for BalanceAt and wei conversion, straight from our backbone book. For big.Int itself, the math/big GoDoc is authoritative.

I'm your teacher — ask me anything. Want to see the balance at a past block? Curious why the chain refuses to store decimals at all? Wondering how a token balance differs from this native balance? Ask away.