Module: Solrengine::Sdp

Defined in:
lib/solrengine/sdp.rb,
lib/solrengine/sdp/engine.rb,
lib/solrengine/sdp/errors.rb,
lib/solrengine/sdp/faucet.rb,
lib/solrengine/sdp/version.rb,
lib/solrengine/sdp/broadcaster.rb,
lib/solrengine/sdp/wallet_owner.rb,
lib/solrengine/sdp/configuration.rb,
app/models/solrengine/sdp/transfer.rb,
app/jobs/solrengine/sdp/track_transfer_job.rb,
app/jobs/solrengine/sdp/provision_wallet_job.rb,
lib/generators/solrengine/sdp/install_generator.rb

Overview

Rails engine for custodial Solana wallets backed by the Solana Developer Platform (SDP). Composes the solana-sdp client with the solrengine family.

Defined Under Namespace

Modules: WalletOwner Classes: Broadcaster, Configuration, ConfigurationError, Engine, Error, Faucet, InstallGenerator, InsufficientBalance, ProvisionWalletJob, TrackTransferJob, Transfer

Constant Summary collapse

EXEMPT_TASK_PREFIXES =

Rake task prefixes for which the boot-time api_key check is skipped: CI and Docker image builds run these without production secrets.

%w[assets: db: app: tmp: log:].freeze
REALTIME_SUBSCRIBER_NAME =

Name this engine registers under on the solrengine-realtime subscriber registry (start_realtime!/stop_realtime!). Apps can register their own subscribers alongside it under their own names.

:solrengine_sdp
VERSION =
"0.1.0"
COMPATIBLE_SDP_VERSION =

The SDP release this engine version is tested against. SDP breaks its API between minors; bump this (and re-verify) on every SDP upgrade.

"0.28"

Class Method Summary collapse

Class Method Details

.clientObject

Memoized SDP API client built from the configuration. Reset whenever ‘configure` runs, so reconfiguring always yields a fresh client.



41
42
43
44
45
46
# File 'lib/solrengine/sdp.rb', line 41

def client
  @client ||= ::Sdp::Client.new(
    api_key: configuration.validate!.api_key,
    base_url: configuration.base_url
  )
end

.configurationObject



29
30
31
# File 'lib/solrengine/sdp.rb', line 29

def configuration
  @configuration ||= Configuration.new
end

.configure {|configuration| ... } ⇒ Object

Yields:



33
34
35
36
37
# File 'lib/solrengine/sdp.rb', line 33

def configure
  yield(configuration)
  @client = nil
  configuration
end

.exempt_context?(task_names) ⇒ Boolean

Pure function over rake task names so the exemption logic is unit-testable: exempt_context?() => true.

Returns:

  • (Boolean)


55
56
57
58
59
# File 'lib/solrengine/sdp.rb', line 55

def exempt_context?(task_names)
  Array(task_names).any? do |task|
    EXEMPT_TASK_PREFIXES.any? { |prefix| task.to_s.start_with?(prefix) }
  end
end

.exempt_rake_context?Boolean

True when the current process is an exempt rake task (boot check skip).

Returns:

  • (Boolean)


62
63
64
65
66
67
68
69
# File 'lib/solrengine/sdp.rb', line 62

def exempt_rake_context?
  return false unless defined?(Rake) && Rake.respond_to?(:application)

  application = Rake.application
  return false unless application.respond_to?(:top_level_tasks)

  exempt_context?(application.top_level_tasks)
end

.price_for(mint) ⇒ Object

USD price for a mint via the optional tokens gem. Returns nil when the gem is absent or the price lookup fails — price must never gate money flows (the U9 broadcaster builds on this).



88
89
90
91
92
93
94
# File 'lib/solrengine/sdp.rb', line 88

def price_for(mint)
  return nil unless price_source_available?

  Solrengine::Tokens::JupiterClient.fetch_prices([ mint ])[mint]
rescue StandardError
  nil
end

.price_source_available?Boolean

solrengine-tokens is an optional price source — never a hard dependency. True when its JupiterClient is loadable.

Returns:

  • (Boolean)


73
74
75
76
77
78
79
80
81
82
83
# File 'lib/solrengine/sdp.rb', line 73

def price_source_available?
  return true if defined?(Solrengine::Tokens::JupiterClient)

  begin
    require "solrengine/tokens"
  rescue LoadError
    return false
  end

  defined?(Solrengine::Tokens::JupiterClient) ? true : false
end

.reset_configuration!Object



48
49
50
51
# File 'lib/solrengine/sdp.rb', line 48

def reset_configuration!
  @configuration = nil
  @client = nil
end

.start_realtime!Object

Registers the engine’s Broadcaster on the solrengine-realtime subscriber registry: every account-change dispatch re-fetches and broadcasts for that wallet. Called by the watcher process (bin/sdp_watcher); idempotent — re-subscribing replaces the block.



117
118
119
120
121
# File 'lib/solrengine/sdp.rb', line 117

def start_realtime!
  Solrengine::Realtime.subscribe(REALTIME_SUBSCRIBER_NAME) do |wallet_address|
    Broadcaster.call(wallet_address)
  end
end

.stop_realtime!Object



123
124
125
# File 'lib/solrengine/sdp.rb', line 123

def stop_realtime!
  Solrengine::Realtime.unsubscribe(REALTIME_SUBSCRIBER_NAME)
end

.usd_value_for(balance) ⇒ Object

USD value for an Sdp::Balance (AE3): SDP’s own usd_value when present (v0.29+ populates it), else derived from the optional tokens gem’s Jupiter price, else nil. Price data is decorative — every failure path degrades to nil so a price hiccup can NEVER fail a broadcaster fetch or gate a money-movement broadcast.



101
102
103
104
105
106
107
108
109
110
111
# File 'lib/solrengine/sdp.rb', line 101

def usd_value_for(balance)
  usd = balance.usd_value
  return usd unless usd.nil? || usd.to_s.empty?

  price = price_for(balance.mint)
  return nil unless price

  BigDecimal(balance.ui_amount.to_s) * BigDecimal(price.to_s)
rescue StandardError
  nil
end