Module: Solrengine::Sdp::WalletOwner

Extended by:
ActiveSupport::Concern
Defined in:
lib/solrengine/sdp/wallet_owner.rb

Overview

Mixin for the host app’s user (wallet-owner) model. Expects four columns (the install generator adds them):

sdp_wallet_id           :string  — SDP's walletId once provisioned
wallet_address          :string  — the wallet's Solana public key
sdp_provisioning_state  :string, default: "pending", null: false
sdp_provisioning_error  :string  — renderable failure reason

Provisioning is a state machine, not a fire-and-forget job:

pending → provisioning → ready | failed
failed  → pending                          (retry_provisioning!)
provisioning (stale) → pending             (retry_provisioning! — a
  row whose updated_at is past Configuration#provisioning_lease was
  abandoned by a dead worker, never settled by a live one)

ProvisionWalletJob drives every transition; “still provisioning” and “permanently wallet-less” are always distinguishable, and a failure carries a reason the app can render and re-trigger.

Provisioning timing is the app’s call — the concern deliberately wires NO callback. To provision on signup, add one line to the host model:

after_create_commit :provision_wallet!

States are plain strings rather than a Rails enum so the mixin cannot collide with the host model’s own enums or generated methods.

Constant Summary collapse

PROVISIONING_STATES =
%w[pending provisioning ready failed].freeze

Instance Method Summary collapse

Instance Method Details

#provision_wallet!Object

Enqueues provisioning. No-op (with a log line) when the wallet is already ready or a live job currently owns the row; from failed it re-enqueues — the job’s claim accepts failed rows, so an explicit reset via retry_provisioning! is equivalent but also clears the error. A STALE provisioning row (lease lapsed — the claiming worker died before settling) also re-enqueues: the job’s claim takes over expired leases, and label adoption makes the re-run double-provision-safe.



90
91
92
93
94
95
96
97
98
99
100
# File 'lib/solrengine/sdp/wallet_owner.rb', line 90

def provision_wallet!
  if wallet_ready? || (wallet_provisioning? && !wallet_provisioning_stale?)
    Solrengine::Sdp.configuration.logger&.info(
      "[Solrengine::Sdp] provision_wallet! no-op for #{self.class.name}##{id}: " \
      "state is #{wallet_provisioning_state}"
    )
    return false
  end

  ProvisionWalletJob.perform_later(self)
end

#retry_provisioning!Object

Re-arms a failed row — or a STALE provisioning row abandoned by a dead worker: clears the stored reason, resets to pending, and enqueues a fresh job. Anything else (including provisioning rows a live job still owns) is a no-op returning false.



106
107
108
109
110
111
# File 'lib/solrengine/sdp/wallet_owner.rb', line 106

def retry_provisioning!
  return false unless wallet_failed? || wallet_provisioning_stale?

  update!(sdp_provisioning_state: "pending", sdp_provisioning_error: nil)
  ProvisionWalletJob.perform_later(self)
end

#sdp_wallet_labelObject

SDP wallet label this user provisions under. The namespace prefix guards against cross-app collisions when several apps share one SDP project (see Configuration#label_namespace).



69
70
71
# File 'lib/solrengine/sdp/wallet_owner.rb', line 69

def sdp_wallet_label
  "#{Solrengine::Sdp.configuration.label_namespace}-user-#{id}"
end

#wallet_failed?Boolean

Returns:

  • (Boolean)


62
63
64
# File 'lib/solrengine/sdp/wallet_owner.rb', line 62

def wallet_failed?
  wallet_provisioning_state == "failed"
end

#wallet_pending?Boolean

Returns:

  • (Boolean)


50
51
52
# File 'lib/solrengine/sdp/wallet_owner.rb', line 50

def wallet_pending?
  wallet_provisioning_state == "pending"
end

#wallet_provisioning?Boolean

Returns:

  • (Boolean)


54
55
56
# File 'lib/solrengine/sdp/wallet_owner.rb', line 54

def wallet_provisioning?
  wallet_provisioning_state == "provisioning"
end

#wallet_provisioning_stale?Boolean

A provisioning row whose lease has lapsed: no live job has touched it within Configuration#provisioning_lease (every claim/retry/settle renews updated_at), so the worker that claimed it died before settling. Stale rows are re-enqueueable; fresh ones belong to a live job and must be left alone.

Returns:

  • (Boolean)


78
79
80
81
# File 'lib/solrengine/sdp/wallet_owner.rb', line 78

def wallet_provisioning_stale?
  wallet_provisioning? &&
    updated_at <= Time.current - Solrengine::Sdp.configuration.provisioning_lease
end

#wallet_provisioning_stateObject

Tolerates NULL (rows predating the column default): no state is pending.



46
47
48
# File 'lib/solrengine/sdp/wallet_owner.rb', line 46

def wallet_provisioning_state
  self[:sdp_provisioning_state].presence || "pending"
end

#wallet_ready?Boolean

Returns:

  • (Boolean)


58
59
60
# File 'lib/solrengine/sdp/wallet_owner.rb', line 58

def wallet_ready?
  wallet_provisioning_state == "ready"
end