Lição 02 · Unit 1 · Web3 with Go and 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.
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 10^18). 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.Intis a pointer;==compares addresses in your program's memory, not values. Two equal balances read at different times compare as "not equal". Usea.Cmp(b), which returns-1,0, or1— so "does the buyer have enough gas?" isbalance.Cmp(needed) >= 0.- Rule 2 — never put money in a float
float64holds ~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.Intmethods 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 thenew(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.
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:
go run main.gobalance (wei): 1943500000000000000
gas check: enough for ~0.01 POL of fees
balance (POL): 1.943500Two 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.
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?
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.
Sou seu professor — traga suas perguntas difíceis de “mas por quê”. Exporte seu progresso na página do curso e cole no /teach.