mirror of
https://github.com/we-promise/sure.git
synced 2026-04-08 06:44:52 +00:00
* Feat(CoinStats): Scaffold implementation, not yet functional * Feat(CoinStats): Implement crypto wallet balance and transactions * Feat(CoinStats): Add tests, Minor improvements * Feat(CoinStats): Utilize bulk fetch API endpoints * Feat(CoinStats): Migrate strings to i8n * Feat(CoinStats): Fix error handling in wallet link modal * Feat(CoinStats): Implement hourly provider sync job * Feat(CoinStats): Generate docstrings * Fix(CoinStats): Validate API Key on provider update * Fix(Providers): Safely handle race condition in merchance creation * Fix(CoinStats): Don't catch system signals in account processor * Fix(CoinStats): Preload before iterating accounts * Fix(CoinStats): Add no opener / referrer to API dashboard link * Fix(CoinStats): Use strict matching for symbols * Fix(CoinStats): Remove dead code in transactions importer * Fix(CoinStats): Avoid transaction fallback ID collisions * Fix(CoinStats): Improve Blockchains fetch error handling * Fix(CoinStats): Enforce NOT NULL constraint for API Key schema * Fix(CoinStats): Migrate sync status strings to i8n * Fix(CoinStats): Use class name rather than hardcoded string * Fix(CoinStats): Use account currency rather than hardcoded USD * Fix(CoinStats): Migrate from standalone to Provider class * Fix(CoinStats): Fix test failures due to string changes
69 lines
2.7 KiB
Ruby
69 lines
2.7 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
# Shared logic for extracting unique transaction IDs from CoinStats API responses.
|
|
# Different blockchains return transaction IDs in different locations:
|
|
# - Ethereum/EVM: hash.id (transaction hash)
|
|
# - Bitcoin/UTXO: transactions[0].items[0].id
|
|
module CoinstatsTransactionIdentifiable
|
|
extend ActiveSupport::Concern
|
|
|
|
private
|
|
|
|
# Extracts a unique transaction ID from CoinStats transaction data.
|
|
# Handles different blockchain formats and generates fallback IDs.
|
|
# @param transaction_data [Hash] Raw transaction data from API
|
|
# @return [String, nil] Unique transaction identifier or nil
|
|
def extract_coinstats_transaction_id(transaction_data)
|
|
tx = transaction_data.is_a?(Hash) ? transaction_data.with_indifferent_access : {}
|
|
|
|
# Try hash.id first (Ethereum/EVM chains)
|
|
hash_id = tx.dig(:hash, :id)
|
|
return hash_id if hash_id.present?
|
|
|
|
# Try transactions[0].items[0].id (Bitcoin/UTXO chains)
|
|
item_id = tx.dig(:transactions, 0, :items, 0, :id)
|
|
return item_id if item_id.present?
|
|
|
|
# Fallback: generate ID from multiple fields to reduce collision risk.
|
|
# Include as many distinguishing fields as possible since transactions
|
|
# with same date/type/amount are common (DCA, recurring purchases, batch trades).
|
|
fallback_id = build_fallback_transaction_id(tx)
|
|
return fallback_id if fallback_id.present?
|
|
|
|
nil
|
|
end
|
|
|
|
# Builds a fallback transaction ID from available fields.
|
|
# Uses a hash digest of combined fields to handle varying field availability
|
|
# while maintaining uniqueness across similar transactions.
|
|
# @param tx [HashWithIndifferentAccess] Transaction data
|
|
# @return [String, nil] Generated fallback ID or nil if insufficient data
|
|
def build_fallback_transaction_id(tx)
|
|
date = tx[:date]
|
|
type = tx[:type]
|
|
amount = tx.dig(:coinData, :count)
|
|
|
|
# Require minimum fields for a valid fallback
|
|
return nil unless date.present? && type.present? && amount.present?
|
|
|
|
# Collect additional distinguishing fields.
|
|
# Only use stable transaction data—avoid market-dependent values
|
|
# (currentValue, totalWorth, profit) that can change between API calls.
|
|
components = [
|
|
date,
|
|
type,
|
|
amount,
|
|
tx.dig(:coinData, :symbol),
|
|
tx.dig(:fee, :count),
|
|
tx.dig(:fee, :coin, :symbol),
|
|
tx.dig(:transactions, 0, :action),
|
|
tx.dig(:transactions, 0, :items, 0, :coin, :id),
|
|
tx.dig(:transactions, 0, :items, 0, :count)
|
|
].compact
|
|
|
|
# Generate a hash digest for a fixed-length, collision-resistant ID
|
|
content = components.join("|")
|
|
"fallback_#{Digest::SHA256.hexdigest(content)[0, 16]}"
|
|
end
|
|
end
|