Files
zeekayandHanzo Dev da299df77d the corporate name is Lux Industries Inc
Lux Partners Limited is not the entity behind this work. Every place the
old name stood — copyright lines, site footers, brand config, legal and
compliance text — now names the company that actually holds it.

lux.fund is the exception and is not renamed to match: that site belongs
to Lux Assets Management LLC, a different company, and it says so.

Two kinds of line were left for a person rather than rewritten. A dated
news item recording that Lux Partners Limited was established in the Isle
of Man is a historical fact, and renaming it would falsify the record. And
a sentence asserting that the company is domiciled in the Isle of Man is a
claim about a jurisdiction, not a name — an Inc or an LLC is not an Isle
of Man company, so swapping the name there would assert something untrue.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-28 13:05:15 -07:00

268 lines
5.7 KiB
Go

// Copyright (C) 2020-2025, Lux Industries Inc All rights reserved.
// See the file LICENSE for licensing terms.
// Package wallet provides multi-chain wallet implementation.
package wallet
import (
"context"
"errors"
"fmt"
"github.com/luxfi/consensus/utils/set"
"github.com/luxfi/crypto/bls"
"github.com/luxfi/ids"
"github.com/luxfi/sdk/chain"
"github.com/luxfi/sdk/crypto"
"github.com/luxfi/sdk/key"
)
var (
ErrInsufficientFunds = errors.New("insufficient funds")
ErrNoUTXOs = errors.New("no UTXOs available")
ErrInvalidAddress = errors.New("invalid address")
)
// UTXO represents an unspent transaction output
type UTXO struct {
ID ids.ID
AssetID ids.ID
Amount uint64
Owner ids.ShortID
Locktime uint64
}
// Wallet manages keys and transactions for personal usage
type Wallet struct {
// Network configuration
networkID uint32
chainID ids.ID
// Key management
keychain *key.Keychain
addresses set.Set[ids.ShortID]
// UTXO management
utxos map[ids.ID]*UTXO
// BLS key for validator operations
blsKey *bls.SecretKey
}
// New creates a new wallet instance
func New(networkID uint32, chainID ids.ID) *Wallet {
return &Wallet{
networkID: networkID,
chainID: chainID,
keychain: key.NewKeychain(),
addresses: set.NewSet[ids.ShortID](10),
utxos: make(map[ids.ID]*UTXO),
}
}
// ImportKey imports a private key into the wallet
func (w *Wallet) ImportKey(privateKey crypto.PrivateKey) (ids.ShortID, error) {
pubKey := privateKey.PublicKey()
// Generate address from public key
address := ids.ShortID{}
copy(address[:], pubKey[:20])
// Check if already imported
if w.addresses.Contains(address) {
return address, nil // Return the existing address
}
if err := w.keychain.Add(privateKey); err != nil {
return ids.ShortID{}, err
}
w.addresses.Add(address)
return address, nil
}
// GenerateKey generates a new key and adds it to the wallet
func (w *Wallet) GenerateKey() (ids.ShortID, error) {
privateKey, err := crypto.GeneratePrivateKey()
if err != nil {
return ids.ShortID{}, err
}
return w.ImportKey(privateKey)
}
// GetAddress returns a wallet address
func (w *Wallet) GetAddress() (ids.ShortID, error) {
addresses := w.addresses.List()
if len(addresses) == 0 {
return ids.ShortID{}, errors.New("no addresses in wallet")
}
return addresses[0], nil
}
// GetAllAddresses returns all wallet addresses
func (w *Wallet) GetAllAddresses() []ids.ShortID {
return w.addresses.List()
}
// GetBalance returns the balance for a specific asset
func (w *Wallet) GetBalance(assetID ids.ID) uint64 {
var balance uint64
for _, utxo := range w.utxos {
if utxo.AssetID == assetID && w.addresses.Contains(utxo.Owner) {
balance += utxo.Amount
}
}
return balance
}
// AddUTXO adds a UTXO to the wallet
func (w *Wallet) AddUTXO(utxo *UTXO) {
w.utxos[utxo.ID] = utxo
}
// RemoveUTXO removes a UTXO from the wallet
func (w *Wallet) RemoveUTXO(utxoID ids.ID) {
delete(w.utxos, utxoID)
}
// GetUTXOs returns UTXOs for spending
func (w *Wallet) GetUTXOs(assetID ids.ID, amount uint64) ([]*UTXO, uint64, error) {
var (
utxos []*UTXO
totalAmt uint64
)
for _, utxo := range w.utxos {
if utxo.AssetID != assetID {
continue
}
// Check if we own this UTXO
if !w.addresses.Contains(utxo.Owner) {
continue
}
utxos = append(utxos, utxo)
totalAmt += utxo.Amount
if totalAmt >= amount {
return utxos, totalAmt, nil
}
}
if totalAmt < amount {
return nil, 0, ErrInsufficientFunds
}
return utxos, totalAmt, nil
}
// Sign signs a transaction with the wallet's keys
func (w *Wallet) Sign(ctx context.Context, tx chain.Transaction) error {
// Get addresses that can sign
addresses := w.keychain.Addresses()
// Sign the transaction with available signers
if err := tx.Sign(addresses); err != nil {
return fmt.Errorf("failed to sign transaction: %w", err)
}
return nil
}
// SetBLSKey sets the BLS key for validator operations
func (w *Wallet) SetBLSKey(key *bls.SecretKey) {
w.blsKey = key
}
// GetBLSKey returns the BLS key for validator operations
func (w *Wallet) GetBLSKey() (*bls.SecretKey, error) {
if w.blsKey == nil {
return nil, errors.New("no BLS key set")
}
return w.blsKey, nil
}
// TransferInput represents an input to a transfer transaction
type TransferInput struct {
UTXOID ids.ID
AssetID ids.ID
Amount uint64
}
// TransferOutput represents an output from a transfer transaction
type TransferOutput struct {
AssetID ids.ID
Amount uint64
Recipient ids.ShortID
Locktime uint64
}
// CreateTransferTx creates a transfer transaction
func (w *Wallet) CreateTransferTx(
to ids.ShortID,
assetID ids.ID,
amount uint64,
memo []byte,
) (*TransferTx, error) {
// Get UTXOs for the transfer
utxos, totalAmt, err := w.GetUTXOs(assetID, amount)
if err != nil {
return nil, err
}
// Create inputs
var inputs []TransferInput
for _, utxo := range utxos {
input := TransferInput{
UTXOID: utxo.ID,
AssetID: assetID,
Amount: utxo.Amount,
}
inputs = append(inputs, input)
}
// Create outputs
outputs := []TransferOutput{
{
AssetID: assetID,
Amount: amount,
Recipient: to,
},
}
// Add change output if necessary
if totalAmt > amount {
from, err := w.GetAddress()
if err != nil {
return nil, err
}
changeOutput := TransferOutput{
AssetID: assetID,
Amount: totalAmt - amount,
Recipient: from,
}
outputs = append(outputs, changeOutput)
}
return &TransferTx{
NetworkID: w.networkID,
ChainID: w.chainID,
Inputs: inputs,
Outputs: outputs,
Memo: memo,
}, nil
}
// TransferTx represents a transfer transaction
type TransferTx struct {
NetworkID uint32
ChainID ids.ID
Inputs []TransferInput
Outputs []TransferOutput
Memo []byte
}