Unit 1 · Lesson 02 · Web3 with Go & Polygon
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.
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.
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:
BalanceAt works the same on both. Today we read an EOA's native
POL balance — the gas money.
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.
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:
==*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.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.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.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
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.
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).
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?
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.