Wallets
What Is a Wallet?
A wallet is a cryptographic identity on a ULedger chain. It consists of:
- A keypair (public key + private key) generated from BIP39 entropy.
- An address derived as
SHA256(lowercase(publicKey)): deterministic, not random. - A parent reference: the wallet that created this one (empty for root wallets).
- An auth group map: role-based CRUD permission sets.
- A capabilities list: fine-grained, scoped, expirable permissions.
- A satellite wallets list: addresses of all direct child wallets.
- An enabled flag: disabled wallets cannot sign or submit transactions.
- A key type: the cryptographic algorithm (fixed at creation, must match the chain).
Wallets do not store funds in the traditional sense. They hold permissions, sign transactions, and serve as the identity anchor for all actions on the chain.
Wallet Data Structure
UL_Wallet {
PublicKey string // Compressed hex public key
Address string // SHA256(lowercase(publicKey))
Parent string // Parent wallet address ("" = root)
Entity string // Primary entity affiliation backlink
Enabled bool
SatelliteWallets []string // Direct children
AuthGroups map[string]UL_AuthPermission // "admin" and "wallet" groups
Capabilities []Capability // Scoped permissions
KeyType string // secp256k1 | ed25519 | mldsa87 | bls12377
}
UL_AuthPermission {
Create bool
Read bool
Update bool
Delete bool
}
Wallet Key Types
Key type is set at chain creation and applies to all wallets on that chain.
| Key Type | Algorithm | Notes |
|---|---|---|
secp256k1 | ECDSA on secp256k1 curve | Bitcoin/Ethereum compatible; widely supported. |
ed25519 | EdDSA on Curve25519 | Faster verification; smaller signatures. |
mldsa87 | ML-DSA-87 (CRYSTALS-Dilithium level 5) | Post-quantum; recommended for long-term audit chains. |
bls12377 | BLS on BLS12-377 curve | Signature aggregation; specialized use cases. |
Wallet private keys are generated from BIP39 mnemonic phrases (optionally passphrase-protected) and stored in .ukey files. The private key never leaves the SDK process; only signatures and public keys are transmitted to the network.
Wallet Hierarchy
Wallets form a tree. Every wallet except root wallets has a Parent address pointing to the wallet that created it.
Root Wallet (Parent = "")
├── Child Wallet A (Parent = Root)
│ ├── Grandchild Wallet A1 (Parent = Child A)
│ └── Grandchild Wallet A2 (Parent = Child A)
└── Child Wallet B (Parent = Root)
└── Grandchild Wallet B1 (Parent = Child B)
Parent wallets record all direct children in their SatelliteWallets array. This allows a full subtree traversal starting from any wallet.
Hierarchy Rules
- A root wallet has
Parent == ""and, before being linked to an entity, must not already have an entity affiliation. - A child wallet inherits no more permissions than its parent holds. Capability inheritance is enforced at creation: a child's capabilities must be a strict subset of the parent's.
- Disabling a parent wallet via
ALTER_WALLETcascades via BFS across all descendants; every satellite wallet is disabled atomically. This is the only subtree operation that is atomic. - Re-enabling a parent does not automatically re-enable children. Re-enabling must be done explicitly per wallet.
Auth Groups
Every wallet carries two standard auth groups: admin and wallet.
admin Group
Controls administrative access to the wallet itself and its entity. A wallet with full admin permissions (Create+Read+Update+Delete) can:
- Alter the wallet's own settings.
- Link the wallet to an entity as an admin-role member.
- Create child wallets.
wallet Group
Controls wallet creation and update capabilities within the wallet's authority scope.
Full admin example:
AuthGroups = {
"admin": { Create: true, Read: true, Update: true, Delete: true },
"wallet": { Create: true, Read: true, Update: true, Delete: true }
}
Read-only wallet:
AuthGroups = {
"admin": { Create: false, Read: true, Update: false, Delete: false },
"wallet": { Create: false, Read: true, Update: false, Delete: false }
}
Important: Auth groups control administrative permissions. Fine-grained action permissions (e.g., "invoke this contract") are handled by Capabilities, not auth groups. See Capabilities.
Wallet Lifecycle
1. Generate
# Generate wallets with secp256k1 keys
go run ./cmd/generate_wallets --type=secp256k1
# Generate wallets with an authority group assigned
go run ./cmd/generate_wallets --type=mldsa87 --auth=admin
Generates .ukey files in the ./wallets directory. Each file contains the keypair for one wallet.
2. Register
go run ./cmd/register_wallets
Submits TX_CREATE_WALLET transactions for all wallets in ./wallets, on-boarding them to the blockchain. Until registered, a wallet address is not recognized by the chain.
3. Alter
go run ./cmd/alter_wallets
Submits ALTER_WALLET transactions to update auth groups, attach capabilities, enable/disable, or modify the wallet's configuration.
4. Create Child Wallets
A registered wallet with wallet.Create == true in its auth groups can create child wallets by submitting TX_CREATE_WALLET transactions that reference itself as parent.
5. Disable / Cascade
# Via SDK: submit ALTER_WALLET with Enabled = false
Disabling a wallet submits ALTER_WALLET. The processor performs a BFS cascade, disabling all satellite wallets atomically. This is the emergency stop for an entire agent subtree.
The Entity Backlink (wallet.Entity)
A wallet has a single Entity field (its primary entity affiliation). This field is set only in specific circumstances:
When wallet.Entity is set | Trigger |
|---|---|
| Root wallet creates entity | The creating wallet becomes the entity's owner |
| Admin-role wallet linked to entity | Admin-role link sets the backlink |
| Ownership transferred | New owner wallet's backlink is updated |
| Recovery executed | Recovered wallet's backlink is updated |
Member-role links do not set wallet.Entity. A wallet linked to an entity with a member role and specific permission bits has power within that entity, but its primary affiliation is not changed. This allows wallets to participate in multiple entities without conflating their primary home.
Wallet Files (.ukey Format)
Wallet private keys are stored as .ukey files. The filename is the wallet's address (public key hash). Example:
./wallets-uis-mldsa/8e620db359101eb28bf9db4f08c2677349f7747d1c59528774f2193318b8c1c7.ukey
The SDK loads these files when signing transactions. Keep .ukey files secure; they contain the raw private key material.