Files
sure/app/models/provider/coinstats_adapter.rb
Ethan 3b4ab735b0 Add (beta) CoinStats Crypto Wallet Integration with Balance and Transaction Syncing (#512)
* 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
2026-01-07 15:59:04 +01:00

120 lines
3.6 KiB
Ruby

# Provider adapter for CoinStats cryptocurrency wallet integration.
# Handles sync operations and institution metadata for crypto accounts.
class Provider::CoinstatsAdapter < Provider::Base
include Provider::Syncable
include Provider::InstitutionMetadata
# Register this adapter with the factory
Provider::Factory.register("CoinstatsAccount", self)
# @return [Array<String>] Account types supported by this provider
def self.supported_account_types
%w[Crypto]
end
# Returns connection configurations for this provider
# @param family [Family] The family to check connection eligibility
# @return [Array<Hash>] Connection config with name, description, and paths
def self.connection_configs(family:)
return [] unless family.can_connect_coinstats?
[ {
key: "coinstats",
name: "CoinStats",
description: "Connect to your crypto wallet via CoinStats",
can_connect: true,
new_account_path: ->(accountable_type, return_to) {
Rails.application.routes.url_helpers.new_coinstats_item_path(
accountable_type: accountable_type,
return_to: return_to
)
},
# CoinStats wallets are linked via the link_wallet action, not via existing account selection
existing_account_path: nil
} ]
end
# @return [String] Unique identifier for this provider
def provider_name
"coinstats"
end
# Build a Coinstats provider instance with family-specific credentials
# @param family [Family] The family to get credentials for (required)
# @return [Provider::Coinstats, nil] Returns nil if credentials are not configured
def self.build_provider(family: nil)
return nil unless family.present?
# Get family-specific credentials
coinstats_item = family.coinstats_items.where.not(api_key: nil).first
return nil unless coinstats_item&.credentials_configured?
Provider::Coinstats.new(coinstats_item.api_key)
end
# @return [String] URL path for triggering a sync
def sync_path
Rails.application.routes.url_helpers.sync_coinstats_item_path(item)
end
# @return [CoinstatsItem] The parent item containing API credentials
def item
provider_account.coinstats_item
end
# @return [Boolean] Whether holdings can be manually deleted
def can_delete_holdings?
false
end
# Extracts institution domain from metadata, deriving from URL if needed.
# @return [String, nil] Domain name or nil if unavailable
def institution_domain
metadata = provider_account.institution_metadata
return nil unless metadata.present?
domain = metadata["domain"]
url = metadata["url"]
# Derive domain from URL if missing
if domain.blank? && url.present?
begin
domain = URI.parse(url).host&.gsub(/^www\./, "")
rescue URI::InvalidURIError
Rails.logger.warn("Invalid institution URL for Coinstats account #{provider_account.id}: #{url}")
end
end
domain
end
# @return [String, nil] Institution display name
def institution_name
metadata = provider_account.institution_metadata
return nil unless metadata.present?
metadata["name"]
end
# @return [String, nil] Institution website URL
def institution_url
metadata = provider_account.institution_metadata
return nil unless metadata.present?
metadata["url"]
end
# @return [nil] CoinStats doesn't provide institution colors
def institution_color
nil # CoinStats doesn't provide institution colors
end
# @return [String, nil] URL for institution/token logo
def logo_url
metadata = provider_account.institution_metadata
return nil unless metadata.present?
metadata["logo"]
end
end