295 lines
8.8 KiB
Ruby
295 lines
8.8 KiB
Ruby
# encoding: UTF-8
|
|
# frozen_string_literal: true
|
|
|
|
class Deposit < ApplicationRecord
|
|
STATES = %i[submitted canceled rejected accepted collected skipped processing fee_processing].freeze
|
|
SUCCEED_PROCESSING_STATES = %i[accepted collected skipped processing fee_processing].freeze
|
|
|
|
serialize :spread, Array
|
|
serialize :from_addresses, Array
|
|
|
|
include AASM
|
|
include AASM::Locking
|
|
include TIDIdentifiable
|
|
include FeeChargeable
|
|
|
|
extend Enumerize
|
|
TRANSFER_TYPES = { fiat: 100, crypto: 200 }.freeze
|
|
|
|
belongs_to :currency, required: true
|
|
belongs_to :member, required: true
|
|
|
|
acts_as_eventable prefix: 'deposit', on: %i[create update]
|
|
|
|
validates :tid, presence: true, uniqueness: { case_sensitive: false }
|
|
validates :aasm_state, :type, presence: true
|
|
validates :completed_at, presence: { if: :completed? }
|
|
validates :block_number, allow_blank: true, numericality: { greater_than_or_equal_to: 0, only_integer: true }
|
|
validates :amount,
|
|
numericality: {
|
|
greater_than_or_equal_to:
|
|
-> (deposit){ deposit.currency.min_deposit_amount }
|
|
}
|
|
|
|
validate :verify_limits, on: :create
|
|
|
|
scope :recent, -> { order(id: :desc) }
|
|
|
|
before_validation { self.completed_at ||= Time.current if completed? }
|
|
before_validation { self.transfer_type ||= currency.coin? ? 'crypto' : 'fiat' }
|
|
|
|
aasm whiny_transitions: false do
|
|
state :submitted, initial: true
|
|
state :canceled
|
|
state :rejected
|
|
state :accepted
|
|
state :aml_processing
|
|
state :aml_suspicious
|
|
state :processing
|
|
state :skipped
|
|
state :collected
|
|
state :fee_processing
|
|
event(:cancel) { transitions from: :submitted, to: :canceled }
|
|
event(:reject) { transitions from: :submitted, to: :rejected }
|
|
event :accept do
|
|
transitions from: :submitted, to: :accepted
|
|
after do
|
|
if currency.coin? && Peatio::App.config.deposit_funds_locked
|
|
account.plus_locked_funds(amount)
|
|
else
|
|
account.plus_funds(amount)
|
|
end
|
|
record_submit_operations!
|
|
end
|
|
end
|
|
event :skip do
|
|
transitions from: :processing, to: :skipped
|
|
end
|
|
|
|
event :process do
|
|
if Peatio::AML.adapter.present?
|
|
transitions from: %i[aml_processing aml_suspicious accepted], to: :aml_processing do
|
|
after do
|
|
process_collect! if aml_check!
|
|
end
|
|
end
|
|
else
|
|
transitions from: %i[accepted skipped], to: :processing do
|
|
guard { currency.coin? }
|
|
end
|
|
end
|
|
end
|
|
|
|
event :fee_process do
|
|
transitions from: %i[accepted processing skipped], to: :fee_processing do
|
|
guard { currency.coin? }
|
|
end
|
|
end
|
|
|
|
event :process_collect do
|
|
transitions from: %i[aml_processing aml_suspicious], to: :processing do
|
|
guard { currency.coin? }
|
|
end
|
|
end if Peatio::AML.adapter.present?
|
|
|
|
event :aml_suspicious do
|
|
transitions from: :aml_processing, to: :aml_suspicious
|
|
end if Peatio::AML.adapter.present?
|
|
|
|
event :dispatch do
|
|
transitions from: %i[processing fee_processing], to: :collected
|
|
after do
|
|
if Peatio::App.config.deposit_funds_locked
|
|
account.unlock_funds(amount)
|
|
record_complete_operations!
|
|
end
|
|
end
|
|
end
|
|
|
|
event :refund do
|
|
transitions from: %i[aml_suspicious skipped], to: :refunding do
|
|
guard { currency.coin? }
|
|
end
|
|
end
|
|
end
|
|
|
|
def aml_check!
|
|
from_addresses.each do |address|
|
|
result = Peatio::AML.check!(address, currency_id, member.uid)
|
|
if result.risk_detected
|
|
aml_suspicious!
|
|
return nil
|
|
end
|
|
return nil if result.pending
|
|
end
|
|
true
|
|
end
|
|
|
|
def verify_limits
|
|
limits = DepositLimit.for(kyc_level: member.level, group: member.group, kind: transfer_type)
|
|
# If there are no limits in DB or current user withdraw limit
|
|
# has 0.0 for 24 hour and 1 month it will skip this checks
|
|
return true if limits.limit_24_hour.zero? && limits.limit_1_month.zero?
|
|
|
|
# Withdraw limits in USD and withdraw sum in currency.
|
|
# Convert withdraw sums with price from the currency model.
|
|
sum_24_hours, sum_1_month = ::Deposit.sanitize_execute_sum_queries(member_id)
|
|
errors.add(:member_id, 'reached 24 hours limitation') if sum_24_hours >= limits.limit_24_hour
|
|
errors.add(:member_id, 'reached 1 month limitation') if sum_1_month >= limits.limit_1_month
|
|
end
|
|
|
|
def blockchain_api
|
|
currency.blockchain_api
|
|
end
|
|
|
|
def confirmations
|
|
return 0 if block_number.blank?
|
|
return blockchain.processed_height - block_number if (blockchain.processed_height - block_number) >= 0
|
|
'N/A'
|
|
rescue StandardError => e
|
|
report_exception(e)
|
|
'N/A'
|
|
end
|
|
|
|
def spread_to_transactions
|
|
spread.map { |s| Peatio::Transaction.new(s) }
|
|
end
|
|
|
|
def spread_between_wallets!
|
|
return false if spread.present?
|
|
|
|
spread = WalletService.new(Wallet.deposit_wallet(currency_id)).spread_deposit(self)
|
|
update!(spread: spread.map(&:as_json))
|
|
end
|
|
|
|
def spread
|
|
super.map(&:symbolize_keys)
|
|
end
|
|
|
|
def account
|
|
member&.get_account(currency)
|
|
end
|
|
|
|
def uid
|
|
member&.uid
|
|
end
|
|
|
|
def uid=(uid)
|
|
self.member = Member.find_by_uid(uid)
|
|
end
|
|
|
|
def as_json_for_event_api
|
|
{ tid: tid,
|
|
user: { uid: member.uid, email: member.email },
|
|
uid: member.uid,
|
|
currency: currency_id,
|
|
amount: amount.to_s('F'),
|
|
state: aasm_state,
|
|
created_at: created_at.iso8601,
|
|
updated_at: updated_at.iso8601,
|
|
completed_at: completed_at&.iso8601,
|
|
blockchain_address: address,
|
|
blockchain_txid: txid }
|
|
end
|
|
|
|
def completed?
|
|
!submitted?
|
|
end
|
|
|
|
private
|
|
|
|
# Creates dependant operations for deposit.
|
|
def record_submit_operations!
|
|
transaction do
|
|
# Credit main fiat/crypto Asset account.
|
|
Operations::Asset.credit!(
|
|
amount: amount + fee,
|
|
currency: currency,
|
|
reference: self
|
|
)
|
|
|
|
# Credit main fiat/crypto Revenue account.
|
|
Operations::Revenue.credit!(
|
|
amount: fee,
|
|
currency: currency,
|
|
reference: self,
|
|
member_id: member_id
|
|
)
|
|
|
|
kind = currency.coin? && Peatio::App.config.deposit_funds_locked ? :locked : :main
|
|
# Credit locked fiat/crypto Liability account.
|
|
Operations::Liability.credit!(
|
|
amount: amount,
|
|
currency: currency,
|
|
reference: self,
|
|
member_id: member_id,
|
|
kind: kind
|
|
)
|
|
end
|
|
end
|
|
|
|
# Creates dependant operations for complete deposit.
|
|
def record_complete_operations!
|
|
transaction do
|
|
Operations::Liability.transfer!(
|
|
amount: amount,
|
|
currency: currency,
|
|
reference: self,
|
|
from_kind: :locked,
|
|
to_kind: :main,
|
|
member_id: member_id
|
|
)
|
|
end
|
|
end
|
|
|
|
class << self
|
|
def sum_query
|
|
'SELECT sum(d.amount) as sum FROM deposits as d ' \
|
|
'where d.transfer_type = 100 ' \
|
|
'AND d.member_id = ? AND d.aasm_state IN (?) AND d.created_at > ?;'
|
|
end
|
|
|
|
def sanitize_execute_sum_queries(member_id)
|
|
squery_24h = ActiveRecord::Base.sanitize_sql_for_conditions([sum_query, member_id, SUCCEED_PROCESSING_STATES, 24.hours.ago])
|
|
squery_1m = ActiveRecord::Base.sanitize_sql_for_conditions([sum_query, member_id, SUCCEED_PROCESSING_STATES, 1.month.ago])
|
|
|
|
sum_withdraws_24_hours = ActiveRecord::Base.connection.exec_query(squery_24h).to_hash.first['sum'].to_d
|
|
sum_withdraws_1_month = ActiveRecord::Base.connection.exec_query(squery_1m).to_hash.first['sum'].to_d
|
|
[sum_withdraws_24_hours, sum_withdraws_1_month]
|
|
end
|
|
end
|
|
end
|
|
|
|
# == Schema Information
|
|
# Schema version: 20200827105929
|
|
#
|
|
# Table name: deposits
|
|
#
|
|
# member_id :integer not null
|
|
# currency_id :string(10) not null
|
|
# amount :decimal(32, 16) not null
|
|
# fee :decimal(32, 16) not null
|
|
# address :string(95)
|
|
# from_addresses :string(1000)
|
|
# txid :string(128)
|
|
# txout :integer
|
|
# aasm_state :string(30) not null
|
|
# block_number :integer
|
|
# type :string(30) not null
|
|
# transfer_type :integer
|
|
# tid :string(64) not null
|
|
# spread :string(1000)
|
|
# created_at :datetime not null
|
|
# updated_at :datetime not null
|
|
# completed_at :datetime
|
|
#
|
|
# Indexes
|
|
#
|
|
# index_deposits_on_aasm_state_and_member_id_and_currency_id (aasm_state,member_id,currency_id)
|
|
# index_deposits_on_currency_id (currency_id)
|
|
# index_deposits_on_currency_id_and_txid_and_txout (currency_id,txid,txout) UNIQUE
|
|
# index_deposits_on_member_id_and_txid (member_id,txid)
|
|
# index_deposits_on_tid (tid)
|
|
# index_deposits_on_type (type)
|
|
#
|