mirror of
https://github.com/we-promise/sure.git
synced 2026-04-18 11:34:13 +00:00
* feat: Support multiple crypto wallets with same token Allows users to import multiple wallets containing the same cryptocurrency (e.g., ETH on different wallet addresses). Changes: - Add wallet_address column to coinstats_accounts - Update uniqueness validation to include wallet_address - Extract and store wallet address in WalletLinker - Add composite unique index on [item_id, account_id, wallet_address] - Add tests for multi-wallet support and backwards compatibility Users can now have: - ETH (0xAAA...) → "Ethereum (0xAA...AA)" - ETH (0xBBB...) → "Ethereum (0xBB...BB)" Backwards compatible: existing accounts with wallet_address: nil continue to work. * style: Fix array bracket spacing in migration * chore: Update schema.rb with wallet_address column and index Add the missing wallet_address column and composite unique index to db/schema.rb for CI compatibility with db:schema:load * test: Add test for wallet deletion with same token different addresses Verifies that deleting one wallet does not affect other wallets that share the same token but have different addresses. Addresses review comment from @EthanC via @jjmata --------- Signed-off-by: Juan José Mata <juanjo.mata@gmail.com> Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
72 lines
2.5 KiB
Ruby
72 lines
2.5 KiB
Ruby
# Represents a single crypto token/coin within a CoinStats wallet.
|
|
# Each wallet address may have multiple CoinstatsAccounts (one per token).
|
|
class CoinstatsAccount < ApplicationRecord
|
|
include CurrencyNormalizable
|
|
|
|
belongs_to :coinstats_item
|
|
|
|
# Association through account_providers (standard pattern for all providers)
|
|
has_one :account_provider, as: :provider, dependent: :destroy
|
|
has_one :account, through: :account_provider, source: :account
|
|
|
|
validates :name, :currency, presence: true
|
|
validates :account_id, uniqueness: { scope: [ :coinstats_item_id, :wallet_address ], allow_nil: true }
|
|
|
|
# Alias for compatibility with provider adapter pattern
|
|
alias_method :current_account, :account
|
|
|
|
# Updates account with latest balance data from CoinStats API.
|
|
# @param account_snapshot [Hash] Normalized balance data from API
|
|
def upsert_coinstats_snapshot!(account_snapshot)
|
|
# Convert to symbol keys or handle both string and symbol keys
|
|
snapshot = account_snapshot.with_indifferent_access
|
|
|
|
# Build attributes to update
|
|
attrs = {
|
|
current_balance: snapshot[:balance] || snapshot[:current_balance],
|
|
currency: parse_currency(snapshot[:currency]) || "USD",
|
|
name: snapshot[:name],
|
|
account_status: snapshot[:status],
|
|
provider: snapshot[:provider],
|
|
institution_metadata: {
|
|
logo: snapshot[:institution_logo]
|
|
}.compact,
|
|
raw_payload: account_snapshot
|
|
}
|
|
|
|
# Only set account_id if provided and not already set (preserves ID from initial creation)
|
|
if snapshot[:id].present? && account_id.blank?
|
|
attrs[:account_id] = snapshot[:id].to_s
|
|
end
|
|
|
|
update!(attrs)
|
|
end
|
|
|
|
# Stores transaction data from CoinStats API for later processing.
|
|
# @param transactions_snapshot [Hash, Array] Raw transactions response or array
|
|
def upsert_coinstats_transactions_snapshot!(transactions_snapshot)
|
|
# CoinStats API returns: { meta: { page, limit }, result: [...] }
|
|
# Extract just the result array for storage, or use directly if already an array
|
|
transactions_array = if transactions_snapshot.is_a?(Hash)
|
|
snapshot = transactions_snapshot.with_indifferent_access
|
|
snapshot[:result] || []
|
|
elsif transactions_snapshot.is_a?(Array)
|
|
transactions_snapshot
|
|
else
|
|
[]
|
|
end
|
|
|
|
assign_attributes(
|
|
raw_transactions_payload: transactions_array
|
|
)
|
|
|
|
save!
|
|
end
|
|
|
|
private
|
|
|
|
def log_invalid_currency(currency_value)
|
|
Rails.logger.warn("Invalid currency code '#{currency_value}' for CoinstatsAccount #{id}, defaulting to USD")
|
|
end
|
|
end
|