Module: BSV::Wallet::CLI

Defined in:
lib/bsv/wallet/cli.rb

Overview

Shared boot sequence for CLI utilities.

Each bin/ tool runs in its own OS process, so the global Sequel::Model.db is safe — only one wallet per process.

Examples:

wallet_name, args = BSV::Wallet::CLI.extract_wallet_name(ARGV)
ctx = BSV::Wallet::CLI.boot(wallet_name: wallet_name)
engine = ctx[:engine]

Defined Under Namespace

Modules: Output

Class Method Summary collapse

Class Method Details

.boot(wallet_name: nil, network: :mainnet) ⇒ Hash

Boot a wallet engine for the named wallet.

Connects to the database, runs migrations, and constructs all Layer 2a components + the Engine.

Parameters:

  • wallet_name (String, nil) (defaults to: nil)

    e.g. “alice”, “bob”, or nil for default

  • network (Symbol) (defaults to: :mainnet)

    :mainnet or :testnet

Returns:

  • (Hash)

    { engine:, key_deriver:, proof_store:, db:, identity_key:, private_key: }



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/bsv/wallet/cli.rb', line 25

def boot(wallet_name: nil, network: :mainnet)
  begin
    require 'dotenv/load'
  rescue LoadError
    # dotenv is optional — env vars can come from shell profile or CI
  end
  require 'sequel'
  require 'logger'
  require 'bsv-wallet'
  require 'bsv-wallet-postgres'

  unless BSV.logger
    BSV.logger = Logger.new($stderr)
    BSV.logger.level = Logger::DEBUG
  end

  wif = env_fetch('WIF', wallet_name)
  db_url = env_fetch('DATABASE_URL', wallet_name)

  db = Sequel.connect(db_url)
  db.extension :pg_enum
  db.extension :pg_array
  db.extension :pg_json
  BSV::Wallet::Postgres.connect(db)

  Sequel.extension :migration
  migrations_path = File.join(
    Gem::Specification.find_by_name('bsv-wallet-postgres').gem_dir,
    'db', 'migrations'
  )
  Sequel::Migrator.run(db, migrations_path)

  private_key = BSV::Primitives::PrivateKey.from_wif(wif)
  key_deriver = BSV::Wallet::KeyDeriver.new(private_key: private_key)

  store = BSV::Wallet::Postgres::Store.new(db: db)
  proof_store = BSV::Wallet::Postgres::ProofStore.new(db: db)
  utxo_pool = BSV::Wallet::Postgres::UTXOPool.new(store: store)

  network_provider = BSV::Network::Providers::WhatsOnChain.send(network)
  services = BSV::Network::Services.new(providers: [network_provider])
  chain_tracker = BSV::Network::ChainTracker.new(db: db, services: services)

  limp_threshold_raw = ENV.fetch('LIMP_THRESHOLD', BSV::Wallet::Engine::LIMP_THRESHOLD)
  begin
    limp_threshold = Integer(limp_threshold_raw)
  rescue ArgumentError
    abort "LIMP_THRESHOLD must be a valid integer (got #{limp_threshold_raw.inspect})"
  end

  engine = BSV::Wallet::Engine.new(
    store: store,
    utxo_pool: utxo_pool,
    broadcast_queue: BSV::Wallet::Postgres::BroadcastQueue.new(db: db),
    proof_store: proof_store,
    key_deriver: key_deriver,
    chain_tracker: chain_tracker,
    network_provider: network_provider,
    network: network,
    limp_threshold: limp_threshold
  )

  {
    engine: engine,
    utxo_pool: utxo_pool,
    key_deriver: key_deriver,
    proof_store: proof_store,
    db: db,
    identity_key: key_deriver.identity_key,
    private_key: private_key
  }
end

.env_fetch(base_name, wallet_name) ⇒ String

Resolve an environment variable with optional wallet-name suffix.

Examples:

env_fetch('WIF', 'alice')  # => ENV['BSV_WALLET_WIF_ALICE'] || ENV['WIF_ALICE'] || ENV['WIF'] || abort
env_fetch('WIF', nil)      # => ENV['WIF'] || abort

Parameters:

  • base_name (String)

    e.g. “WIF”, “DATABASE_URL”

  • wallet_name (String, nil)

Returns:

  • (String)


124
125
126
127
128
129
130
131
132
# File 'lib/bsv/wallet/cli.rb', line 124

def env_fetch(base_name, wallet_name)
  if wallet_name
    prefixed = "BSV_WALLET_#{base_name}_#{wallet_name.upcase}"
    suffixed = "#{base_name}_#{wallet_name.upcase}"
    ENV.fetch(prefixed) { ENV.fetch(suffixed) { ENV.fetch(base_name) { abort "Set #{prefixed} or #{suffixed}" } } }
  else
    ENV.fetch(base_name) { abort "Set #{base_name}" }
  end
end

.extract_wallet_name(argv) ⇒ Array(String, Array<String>)

Extract wallet name from the argument list.

The wallet name is the first argument if it matches a simple identifier pattern (letters/digits/underscores, starts with a letter). Flags (–foo) and hex strings (64-char txids) are not wallet names.

Parameters:

  • argv (Array<String>)

Returns:

  • (Array(String, Array<String>))
    wallet_name_or_nil, remaining_args


106
107
108
109
110
111
112
113
# File 'lib/bsv/wallet/cli.rb', line 106

def extract_wallet_name(argv)
  first = argv.first
  if first && !first.start_with?('-') && first.match?(/\A[a-zA-Z]\w{0,31}\z/)
    [first, argv[1..]]
  else
    [nil, argv.dup]
  end
end