mirror of
https://github.com/luxfi/sdk.git
synced 2026-10-30 15:19:23 +00:00
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]>
347 lines
9.9 KiB
Go
347 lines
9.9 KiB
Go
// Copyright (C) 2025, Lux Industries Inc All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package contract
|
|
|
|
import (
|
|
"fmt"
|
|
"math/big"
|
|
|
|
"github.com/luxfi/crypto"
|
|
"github.com/luxfi/evm/precompile/contracts/nativeminter"
|
|
"github.com/luxfi/genesis/pkg/genesis"
|
|
"github.com/luxfi/geth/common"
|
|
"github.com/luxfi/sdk/application"
|
|
"github.com/luxfi/sdk/key"
|
|
"github.com/luxfi/sdk/models"
|
|
"github.com/luxfi/sdk/utils/fs"
|
|
)
|
|
|
|
// GetDefaultBlockchainAirdropKeyInfo returns information for the blockchain default allocation key.
|
|
// If found, returns key name, address, private key.
|
|
func GetDefaultBlockchainAirdropKeyInfo(
|
|
app *application.Lux,
|
|
blockchainName string,
|
|
) (string, string, string, error) {
|
|
keyName := genesis.GetDefaultBlockchainAirdropKeyName(blockchainName)
|
|
keyPath := app.GetKeyPath(keyName)
|
|
if fs.FileExists(keyPath) {
|
|
k, err := key.LoadSoft(models.NewLocalNetwork().ID(), keyPath)
|
|
if err != nil {
|
|
return "", "", "", err
|
|
}
|
|
return keyName, k.C(), k.PrivKeyHex(), nil
|
|
}
|
|
return "", "", "", nil
|
|
}
|
|
|
|
// GetBlockchainAirdropKeyInfo looks for known private keys inside a given genesis,
|
|
// giving preference to the ones expected to be default.
|
|
// It searches for:
|
|
// 1) default CLI allocation key for blockchains
|
|
// 2) ewoq
|
|
// 3) all other stored keys managed by CLI
|
|
// Returns address + private key when found.
|
|
func GetBlockchainAirdropKeyInfo(
|
|
app *application.Lux,
|
|
network models.Network,
|
|
blockchainName string,
|
|
genesisData []byte,
|
|
) (string, string, string, error) {
|
|
genesis, err := genesis.ByteSliceToEVMGenesis(genesisData)
|
|
if err != nil {
|
|
return "", "", "", err
|
|
}
|
|
if blockchainName != "" {
|
|
airdropKeyName, airdropAddress, airdropPrivKey, err := GetDefaultBlockchainAirdropKeyInfo(app, blockchainName)
|
|
if err != nil {
|
|
return "", "", "", err
|
|
}
|
|
for address := range genesis.Alloc {
|
|
if address == airdropAddress {
|
|
return airdropKeyName, airdropAddress, airdropPrivKey, nil
|
|
}
|
|
}
|
|
}
|
|
// Try to load ewoq key
|
|
ewoqPath := app.GetKeyPath("ewoq")
|
|
if fs.FileExists(ewoqPath) {
|
|
ewoq, err := key.LoadSoft(network.ID(), ewoqPath)
|
|
if err == nil {
|
|
for address := range genesis.Alloc {
|
|
if address == ewoq.C() {
|
|
return "ewoq", ewoq.C(), ewoq.PrivKeyHex(), nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
maxBalance := big.NewInt(0)
|
|
maxBalanceKeyName := ""
|
|
maxBalanceAddr := ""
|
|
maxBalancePrivKey := ""
|
|
for address, alloc := range genesis.Alloc {
|
|
balance := alloc.GetBalance()
|
|
if balance == nil || balance.Cmp(big.NewInt(0)) == 0 {
|
|
continue
|
|
}
|
|
// address is already a string
|
|
found, keyName, addressStr, privKey, err := SearchForManagedKey(app, network, address, false)
|
|
if err != nil {
|
|
return "", "", "", err
|
|
}
|
|
if found && balance.Cmp(maxBalance) > 0 {
|
|
maxBalance = balance
|
|
maxBalanceKeyName = keyName
|
|
maxBalanceAddr = addressStr
|
|
maxBalancePrivKey = privKey
|
|
}
|
|
}
|
|
return maxBalanceKeyName, maxBalanceAddr, maxBalancePrivKey, nil
|
|
}
|
|
|
|
func SearchForManagedKey(
|
|
app *application.Lux,
|
|
network models.Network,
|
|
address string,
|
|
includeEwoq bool,
|
|
) (bool, string, string, string, error) {
|
|
keyNames, err := genesis.GetKeyNames(app.GetKeyDir(), includeEwoq)
|
|
if err != nil {
|
|
return false, "", "", "", err
|
|
}
|
|
for _, keyName := range keyNames {
|
|
keyPath := app.GetKeyPath(keyName)
|
|
if k, err := key.LoadSoft(network.ID(), keyPath); err != nil {
|
|
return false, "", "", "", err
|
|
} else if address == k.C() {
|
|
return true, keyName, k.C(), k.PrivKeyHex(), nil
|
|
}
|
|
}
|
|
return false, "", "", "", nil
|
|
}
|
|
|
|
// GetEVMChainPrefundedKey gets the deployed blockchain genesis, and then looks for known
|
|
// private keys inside it.
|
|
// Returns address + private key when found.
|
|
func GetEVMChainPrefundedKey(
|
|
app *application.Lux,
|
|
network models.Network,
|
|
chainSpec ChainSpec,
|
|
) (string, string, error) {
|
|
genesisData, err := GetBlockchainGenesis(
|
|
app,
|
|
network,
|
|
chainSpec,
|
|
)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
if !genesis.ByteSliceIsEVMGenesis(genesisData) {
|
|
return "", "", fmt.Errorf("search for prefunded key is only supported on EVM based vms")
|
|
}
|
|
_, genesisAddress, genesisPrivateKey, err := GetBlockchainAirdropKeyInfo(
|
|
app,
|
|
network,
|
|
chainSpec.BlockchainName,
|
|
genesisData,
|
|
)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
return genesisAddress, genesisPrivateKey, nil
|
|
}
|
|
|
|
// GetBlockchainGenesis gets the deployed blockchain genesis.
|
|
func GetBlockchainGenesis(
|
|
app *application.Lux,
|
|
network models.Network,
|
|
chainSpec ChainSpec,
|
|
) ([]byte, error) {
|
|
blockchainID, err := GetBlockchainID(app, network, chainSpec)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Retrieve the blockchain creation transaction to get the genesis data
|
|
createChainTx, err := GetBlockchainTx(network.Endpoint(), blockchainID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get blockchain tx: %w", err)
|
|
}
|
|
return createChainTx.GenesisData, nil
|
|
}
|
|
|
|
func sumGenesisSupply(
|
|
genesisData []byte,
|
|
) (*big.Int, error) {
|
|
sum := new(big.Int)
|
|
genesis, err := genesis.ByteSliceToEVMGenesis(genesisData)
|
|
if err != nil {
|
|
return sum, err
|
|
}
|
|
for _, allocation := range genesis.Alloc {
|
|
sum.Add(sum, allocation.GetBalance())
|
|
}
|
|
return sum, nil
|
|
}
|
|
|
|
func GetEVMChainGenesisSupply(
|
|
app *application.Lux,
|
|
network models.Network,
|
|
chainSpec ChainSpec,
|
|
) (*big.Int, error) {
|
|
genesisData, err := GetBlockchainGenesis(
|
|
app,
|
|
network,
|
|
chainSpec,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !genesis.ByteSliceIsEVMGenesis(genesisData) {
|
|
return nil, fmt.Errorf("genesis supply calculation is only supported on EVM based vms")
|
|
}
|
|
return sumGenesisSupply(genesisData)
|
|
}
|
|
|
|
func getGenesisNativeMinterAdmin(
|
|
app *application.Lux,
|
|
network models.Network,
|
|
genesisData []byte,
|
|
) (bool, bool, string, string, string, error) {
|
|
genesis, err := genesis.ByteSliceToEVMGenesis(genesisData)
|
|
if err != nil {
|
|
return false, false, "", "", "", err
|
|
}
|
|
|
|
// Check if native minter is configured in the genesis
|
|
// Parse the genesis config to check for native minter precompile
|
|
if genesis.Config != nil {
|
|
// Check for native minter in the genesis allocations
|
|
// The native minter precompile is at a specific address
|
|
nativeMinterAddress := common.HexToAddress("0x0200000000000000000000000000000000000001")
|
|
|
|
if alloc, exists := genesis.Alloc[nativeMinterAddress.Hex()]; exists && len(alloc.GetCode()) > 0 {
|
|
// Native minter is configured
|
|
// Look for admin addresses in the genesis state
|
|
for addr := range genesis.Alloc {
|
|
// Check if this address has admin permissions
|
|
// Admin addresses typically have a non-zero balance or specific code
|
|
if addr != nativeMinterAddress.Hex() {
|
|
adminStr := addr
|
|
found, keyName, addressStr, privKey, err := SearchForManagedKey(app, network, adminStr, true)
|
|
if err != nil {
|
|
return false, false, "", "", "", err
|
|
}
|
|
if found {
|
|
return true, true, keyName, addressStr, privKey, nil
|
|
}
|
|
}
|
|
}
|
|
// Native minter exists but no managed key found
|
|
if len(genesis.Alloc) > 1 {
|
|
for addr := range genesis.Alloc {
|
|
if addr != nativeMinterAddress.Hex() {
|
|
return true, false, "", addr, "", nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false, false, "", "", "", nil
|
|
}
|
|
|
|
func getGenesisNativeMinterManager(
|
|
app *application.Lux,
|
|
network models.Network,
|
|
genesisData []byte,
|
|
) (bool, bool, string, string, string, error) {
|
|
_, err := genesis.ByteSliceToEVMGenesis(genesisData)
|
|
if err != nil {
|
|
return false, false, "", "", "", err
|
|
}
|
|
// TODO: Fix GenesisPrecompiles access - it's not in params.ChainConfig
|
|
// Need to use extras.ChainConfig or another approach
|
|
if false { // Placeholder - GenesisPrecompiles not accessible from params.ChainConfig
|
|
var allowListCfg *nativeminter.Config
|
|
_ = allowListCfg
|
|
if len(allowListCfg.ManagerAddresses) == 0 {
|
|
return false, false, "", "", "", nil
|
|
}
|
|
for _, admin := range allowListCfg.ManagerAddresses {
|
|
// Convert address to string
|
|
adminStr := fmt.Sprintf("0x%x", admin.Bytes())
|
|
found, keyName, addressStr, privKey, err := SearchForManagedKey(app, network, adminStr, true)
|
|
if err != nil {
|
|
return false, false, "", "", "", err
|
|
}
|
|
if found {
|
|
return true, true, keyName, addressStr, privKey, nil
|
|
}
|
|
}
|
|
return true, false, "", allowListCfg.ManagerAddresses[0].Hex(), "", nil
|
|
}
|
|
return false, false, "", "", "", nil
|
|
}
|
|
|
|
func GetEVMChainGenesisNativeMinterAdmin(
|
|
app *application.Lux,
|
|
network models.Network,
|
|
chainSpec ChainSpec,
|
|
) (bool, bool, string, string, string, error) {
|
|
genesisData, err := GetBlockchainGenesis(
|
|
app,
|
|
network,
|
|
chainSpec,
|
|
)
|
|
if err != nil {
|
|
return false, false, "", "", "", err
|
|
}
|
|
if !genesis.ByteSliceIsEVMGenesis(genesisData) {
|
|
return false, false, "", "", "", fmt.Errorf("genesis native minter admin query is only supported on EVM based vms")
|
|
}
|
|
return getGenesisNativeMinterAdmin(app, network, genesisData)
|
|
}
|
|
|
|
func GetEVMChainGenesisNativeMinterManager(
|
|
app *application.Lux,
|
|
network models.Network,
|
|
chainSpec ChainSpec,
|
|
) (bool, bool, string, string, string, error) {
|
|
genesisData, err := GetBlockchainGenesis(
|
|
app,
|
|
network,
|
|
chainSpec,
|
|
)
|
|
if err != nil {
|
|
return false, false, "", "", "", err
|
|
}
|
|
if !genesis.ByteSliceIsEVMGenesis(genesisData) {
|
|
return false, false, "", "", "", fmt.Errorf("genesis native minter manager query is only supported on EVM based vms")
|
|
}
|
|
return getGenesisNativeMinterManager(app, network, genesisData)
|
|
}
|
|
|
|
func ContractAddressIsInGenesisData(
|
|
genesisData []byte,
|
|
contractAddress crypto.Address,
|
|
) (bool, error) {
|
|
genesis, err := genesis.ByteSliceToEVMGenesis(genesisData)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
// Check if it's a valid EVM genesis
|
|
if genesis.Config == nil {
|
|
return false, fmt.Errorf("invalid EVM genesis: missing config")
|
|
}
|
|
// Convert contractAddress to string for comparison
|
|
// Try both with and without 0x prefix
|
|
contractAddrStr := fmt.Sprintf("%x", contractAddress.Bytes())
|
|
contractAddrStrWithPrefix := fmt.Sprintf("0x%x", contractAddress.Bytes())
|
|
for address, allocation := range genesis.Alloc {
|
|
if address == contractAddrStr || address == contractAddrStrWithPrefix {
|
|
code := allocation.GetCode()
|
|
return len(code) > 0, nil
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|