Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

v0 source pure

Package bech32 implements the Bech32 and Bech32m string formats of BIP-173 and BIP-350 as a pure, reusable package.

Readme View source

gno.land/p/moul/x/daily/bech32/v0

Bech32 and Bech32m, BIP-173 and BIP-350: Encode, EncodeVariant, Decode, ConvertBits, EncodeBytes, DecodeBytes.

1import "gno.land/p/moul/x/daily/bech32/v0"
2
3hrp, payload, v, err := bech32.DecodeBytes("g1manfred47kzduec920z88wfr64ylksmdcedlf5")
4// hrp == "g", payload is the 20-byte address, v.String() == "bech32"
5
6bech32.EncodeBytes("g", payload)              // back to the same string
7bech32.EncodeVariant("a", nil, bech32.Bech32m) // "a1lqfn3a"

What the format buys. A human-readable part, the separator 1, and a data part in a 32-character alphabet that ends in a six-character BCH checksum. The checksum is the point: it detects every error of up to four characters, and the alphabet leaves out 1, b, i and o, the four that are easiest to misread. That is why a gno.land address, a Bitcoin segwit address and a Cosmos account address all look the way they do.

Two variants, one constant apart. Bech32 xors its checksum with 1, Bech32m with 0x2bc830a3. Decode accepts either and reports which one verified, so a caller that cares can check instead of assuming.

Why the second one exists, reproduced as a test rather than quoted: a Bech32 string ending in p keeps a valid checksum when q characters are inserted in front of that final p. a1q3g6mnsp and a1q3g6mnsqp both verify, and decode to different data. The same insertion on a Bech32m string is refused.

Verified against the BIP-173 and BIP-350 vectors, and against a live mainnet address, which is the externally anchored check: it decodes to 20 bytes and re-encodes to the same 40 characters.

Strict where it matters: mixed case is rejected rather than folded, the 90 character cap of BIP-173 is enforced (it is what keeps a wrong character locatable, and a decode bounded in gas), and ConvertBits going back to 8 bits refuses leftover bits rather than inventing a trailing zero byte. A Variant that is neither constant is refused rather than quietly encoded as Bech32, and EncodeBytes checks MaxPayloadLen(hrp) before it regroups anything, so an oversized payload costs a comparison and not a full scan.

Live demo: r/moul/x/daily/bech32demo · render it at /r/moul/x/daily/bech32demo/v0.


Part of moul/gno-contracts — moul's versioned gno.land contracts. See the repository for the full catalog, build/test tooling, and usage.

🧪 Highly experimental — potentially vibe-coded. Not audited; may break, change, or be removed at any time. Do not use with anything of value. Full disclaimer: DISCLAIMER.

Overview

Package bech32 implements the Bech32 and Bech32m string formats of BIP-173 and BIP-350 as a pure, reusable package.

A Bech32 string is a human-readable part ("g", "bc", "cosmos"), the separator '1', and a data part written in a 32-character alphabet that ends in a 6-character BCH checksum. The checksum is the point: it detects every error of up to four characters, and locates a single wrong character well enough for a wallet to say which one it is. That is why a gno.land address, a Bitcoin segwit address and a Cosmos account address all look the way they do.

Two variants, differing only in the constant the checksum is xored with:

  • Bech32 (constant 1), BIP-173. What gno.land addresses use.
  • Bech32m (constant 0x2bc830a3), BIP-350. Adopted after a length-extension weakness was found in the original: a Bech32 string ending in 'p' keeps its checksum valid when 'q' characters are inserted before that final 'p'.

Decode accepts either and reports which one it read, so a caller that cares (segwit v1 and later must be Bech32m) can check rather than assume.

The data part is a sequence of 5-bit groups. ConvertBits regroups an ordinary 8-bit payload into those groups and back, and EncodeBytes / DecodeBytes are the two wrapped together for the common case:

Example
1hrp, payload, _, err := bech32.DecodeBytes("g1manfred47kzduec920z88wfr64ylksmdcedlf5")
2// hrp == "g", payload is the 20-byte address

A live demo of this package is at r/moul/x/daily/bech32demo(/r/moul/x/daily/bech32demo/v0).

Constants 4

const Charset

1const Charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
source

Charset is the 32-character data alphabet. It omits '1', 'b', 'i' and 'o', the four that are easiest to misread.

const ChecksumLen

1const ChecksumLen = 6
source

ChecksumLen is the number of data characters the checksum occupies.

const MaxLen

1const MaxLen = 90
source

MaxLen is BIP-173's cap on the whole encoded string, separator and checksum included. It exists so that a wrong character can always be located, and it is what keeps a decode bounded in gas.

const Bech32, Bech32m

1const (
2	// Bech32 is BIP-173, checksum constant 1.
3	Bech32 Variant = iota
4	// Bech32m is BIP-350, checksum constant 0x2bc830a3.
5	Bech32m
6)
source

The two variants.

Variables 1

var ErrTooLong, ErrTooShort, ErrMixedCase, ErrNoSeparator, ErrInvalidHRP, ErrInvalidChar, ErrInvalidData, ErrChecksum, ErrInvalidBits, ErrInvalidPadding, ErrInvalidVariant

 1var (
 2	// ErrTooLong is returned when a string exceeds MaxLen.
 3	ErrTooLong = errors.New("bech32: string too long")
 4	// ErrTooShort is returned when a string is shorter than the minimum.
 5	ErrTooShort = errors.New("bech32: string too short")
 6	// ErrMixedCase is returned when a string mixes upper and lower case.
 7	ErrMixedCase = errors.New("bech32: mixed case")
 8	// ErrNoSeparator is returned when the '1' separator is missing.
 9	ErrNoSeparator = errors.New("bech32: no separator")
10	// ErrInvalidHRP is returned for an out-of-range human-readable part.
11	ErrInvalidHRP = errors.New("bech32: invalid human-readable part")
12	// ErrInvalidChar is returned when the data part holds a non-charset byte.
13	ErrInvalidChar = errors.New("bech32: invalid data character")
14	// ErrInvalidData is returned when a group does not fit its bit width.
15	ErrInvalidData = errors.New("bech32: invalid data value")
16	// ErrChecksum is returned when the trailing checksum does not verify.
17	ErrChecksum = errors.New("bech32: bad checksum")
18	// ErrInvalidBits is returned for an unsupported ConvertBits width.
19	ErrInvalidBits = errors.New("bech32: invalid bit width")
20	// ErrInvalidPadding is returned when unpadded conversion leaves bits over.
21	ErrInvalidPadding = errors.New("bech32: invalid padding")
22	// ErrInvalidVariant is returned for a Variant that is neither constant.
23	ErrInvalidVariant = errors.New("bech32: invalid variant")
24)
source

Functions 7

func ConvertBits

1func ConvertBits(data []byte, from, to uint8, pad bool) ([]byte, error)
source

ConvertBits regroups data from one bit width to another, most significant bit first. Going 8 to 5 needs pad set, since the last group is rarely full; coming back 5 to 8 must not pad, so that leftover bits are reported rather than turned into a trailing zero byte.

func Encode

1func Encode(hrp string, data []byte) (string, error)
source

Encode builds a Bech32 string from a human-readable part and 5-bit groups.

func EncodeBytes

1func EncodeBytes(hrp string, payload []byte) (string, error)
source

EncodeBytes encodes an 8-bit payload as Bech32, converting to 5-bit groups first. This is the call an address wants.

func EncodeVariant

1func EncodeVariant(hrp string, data []byte, v Variant) (string, error)
source

EncodeVariant is Encode with an explicit checksum variant.

hrp is lowercased; every data byte must be below 32. The result is always lowercase, which is the form BIP-173 requires anything machine-generated to use.

func MaxPayloadLen

1func MaxPayloadLen(hrp string) int
source

MaxPayloadLen is the largest 8-bit payload EncodeBytes can fit under hrp without passing MaxLen. It is 51 bytes for a gno.land address, whose human-readable part is one character, and 0 when hrp leaves no room at all.

func Decode

1func Decode(s string) (string, []byte, Variant, error)
source

Decode parses a Bech32 or Bech32m string into its human-readable part, its 5-bit data groups (checksum removed) and the variant whose checksum verified.

The input may be all upper or all lower case, never a mix; the returned human-readable part is lowercase.

func DecodeBytes

1func DecodeBytes(s string) (string, []byte, Variant, error)
source

DecodeBytes decodes a string and converts its data part back to 8-bit bytes.

Types 1

type Variant

ident
1type Variant int
source

Variant distinguishes the two checksum constants.

Methods on Variant

func String

method on Variant
1func (v Variant) String() string
source

String names the variant, "bech32" or "bech32m".

Dependencies

Imports 1

/p/g1magld5ptgzaktqcexxq0pgq5lmdf0kz6re8hlv/x/daily/bech32/v0

Imported by indexer

Source Files 4