taunchpad 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +28 -0
- data/Rakefile +32 -0
- data/app/controllers/concerns/exception_handlers.rb +19 -0
- data/app/controllers/concerns/jwt_payload.rb +19 -0
- data/app/controllers/concerns/response.rb +25 -0
- data/app/controllers/launchpad/api/v2/admin/base_controller.rb +22 -0
- data/app/controllers/launchpad/api/v2/admin/ieo/orders_controller.rb +29 -0
- data/app/controllers/launchpad/api/v2/admin/ieo/sales_controller.rb +112 -0
- data/app/controllers/launchpad/api/v2/private/base_controller.rb +14 -0
- data/app/controllers/launchpad/api/v2/private/ieo/orders_controller.rb +97 -0
- data/app/controllers/launchpad/api/v2/private/ieo/sales_controller.rb +22 -0
- data/app/controllers/launchpad/api/v2/public/base_controller.rb +13 -0
- data/app/controllers/launchpad/api/v2/public/ieo/sales_controller.rb +56 -0
- data/app/controllers/launchpad/application_controller.rb +10 -0
- data/app/helpers/launchpad/application_helper.rb +4 -0
- data/app/models/launchpad/application_record.rb +5 -0
- data/app/models/launchpad/ieo.rb +21 -0
- data/app/models/launchpad/ieo/order.rb +576 -0
- data/app/models/launchpad/ieo/sale.rb +371 -0
- data/app/models/launchpad/ieo/sale_pair.rb +83 -0
- data/app/services/barong/management_api_v2/client.rb +33 -0
- data/app/services/management_api_v2/client.rb +73 -0
- data/app/services/management_api_v2/exception.rb +25 -0
- data/app/services/peatio/management_api_v2/client.rb +49 -0
- data/app/workers/launchpad/ieo/order_execute_worker.rb +26 -0
- data/app/workers/launchpad/ieo/order_refund_worker.rb +19 -0
- data/app/workers/launchpad/ieo/order_release_worker.rb +22 -0
- data/app/workers/launchpad/ieo/sale_cancel_worker.rb +20 -0
- data/app/workers/launchpad/ieo/sale_currency_list_worker.rb +19 -0
- data/app/workers/launchpad/ieo/sale_distribute_worker.rb +20 -0
- data/app/workers/launchpad/ieo/sale_finish_worker.rb +21 -0
- data/app/workers/launchpad/ieo/sale_pair_list_worker.rb +21 -0
- data/app/workers/launchpad/ieo/sale_release_funds_worker.rb +23 -0
- data/app/workers/launchpad/ieo/sale_start_worker.rb +21 -0
- data/config/initializers/active_model.rb +13 -0
- data/config/initializers/api_pagination.rb +33 -0
- data/config/initializers/inflections.rb +19 -0
- data/config/routes.rb +35 -0
- data/db/migrate/20191120145404_create_launchpad_ieo.rb +52 -0
- data/db/migrate/20200814114105_add_fees_policy_in_sale.rb +5 -0
- data/lib/launchpad.rb +10 -0
- data/lib/launchpad/engine.rb +17 -0
- data/lib/launchpad/precision_validator.rb +25 -0
- data/lib/launchpad/version.rb +3 -0
- data/lib/tasks/launchpad_tasks.rake +4 -0
- metadata +229 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_dependency "launchpad/api/v2/public/base_controller"
|
4
|
+
module Launchpad
|
5
|
+
module API
|
6
|
+
module V2
|
7
|
+
module Public
|
8
|
+
module IEO
|
9
|
+
class SalesController < BaseController
|
10
|
+
def index
|
11
|
+
ransack_params = {
|
12
|
+
state_in: params[:state],
|
13
|
+
currency_id_eq: params[:currency_id],
|
14
|
+
name_cont_all: params[:name]
|
15
|
+
}
|
16
|
+
|
17
|
+
sales = Launchpad::IEO::Sale.public_states.order(created_at: (params[:ordering] || :asc))
|
18
|
+
.ransack(ransack_params)
|
19
|
+
|
20
|
+
json_response(paginate(sales.result), 200)
|
21
|
+
end
|
22
|
+
|
23
|
+
def show
|
24
|
+
launchpad = Launchpad::IEO::Sale.find(params[:id])
|
25
|
+
additional_params = {
|
26
|
+
generic_bids: launchpad.orders.where("state = ? OR state = ?", 'active', 'completed').last(50).as_json(uid: true),
|
27
|
+
}
|
28
|
+
json_response(launchpad.as_json.merge(additional_params), 200)
|
29
|
+
end
|
30
|
+
|
31
|
+
def search
|
32
|
+
ransack_params = {
|
33
|
+
state_in: params[:state],
|
34
|
+
currency_id_eq: params[:currency_id],
|
35
|
+
name_cont_all: params[:name]
|
36
|
+
}
|
37
|
+
|
38
|
+
sales = Launchpad::IEO::Sale.includes(:pairs)
|
39
|
+
.public_states.order(Arel.sql("CASE state
|
40
|
+
WHEN 'ongoing' THEN '1'
|
41
|
+
WHEN 'preparing' THEN '2'
|
42
|
+
WHEN 'distributing' THEN '3'
|
43
|
+
WHEN 'refunding' THEN '4'
|
44
|
+
WHEN 'finished' THEN '5'
|
45
|
+
WHEN 'cancelled' THEN '6'
|
46
|
+
END")).order(created_at: :desc)
|
47
|
+
.ransack(ransack_params)
|
48
|
+
|
49
|
+
json_response(paginate(sales.result).map(&:sales_to_json), 200)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Launchpad
|
2
|
+
module IEO
|
3
|
+
TOKENS_AMOUNT_PRECISION = 4
|
4
|
+
RATIO_PRECISION = 4
|
5
|
+
COMMISSION_PRECISION = 6
|
6
|
+
|
7
|
+
LIABILITY_CODES = {
|
8
|
+
fiat: { main: 201, locked: 211 },
|
9
|
+
coin: { main: 202, locked: 212 }
|
10
|
+
}
|
11
|
+
|
12
|
+
REVENUE_CODES = {
|
13
|
+
fiat: { main: 301 },
|
14
|
+
coin: { main: 302 }
|
15
|
+
}
|
16
|
+
|
17
|
+
def self.table_name_prefix
|
18
|
+
'ieo_'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,576 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Launchpad
|
4
|
+
module IEO
|
5
|
+
# TODO: Add validation that orders are created only for ongoing sales.
|
6
|
+
# TODO: Add attr_readonly for all models.
|
7
|
+
class Order < ApplicationRecord
|
8
|
+
# == Constants ============================================================
|
9
|
+
|
10
|
+
include AASM
|
11
|
+
|
12
|
+
# == Attributes ===========================================================
|
13
|
+
|
14
|
+
# == Extensions ===========================================================
|
15
|
+
|
16
|
+
serialize :transfer_keys, Array
|
17
|
+
|
18
|
+
# NOTE: We use pessimistic locking for avoiding double transfer creation.
|
19
|
+
aasm requires_lock: true, column: :state do
|
20
|
+
state :preparing, initial: true
|
21
|
+
state :rejected
|
22
|
+
state :active
|
23
|
+
state :refunding
|
24
|
+
state :closed
|
25
|
+
state :cancelled
|
26
|
+
state :executing
|
27
|
+
state :completed
|
28
|
+
state :releasing
|
29
|
+
state :purchased
|
30
|
+
|
31
|
+
# Events docs:
|
32
|
+
# preparing -> active;
|
33
|
+
# preparing -> rejected;
|
34
|
+
# active -> executing;
|
35
|
+
# active -> refunfing;
|
36
|
+
# active -> cancelled;
|
37
|
+
# exucuting -> purchased;
|
38
|
+
# executing -> completed;
|
39
|
+
# executing -> refunding;
|
40
|
+
# executing -> cancelled;
|
41
|
+
# purchased -> releasing;
|
42
|
+
# releasing -> completed;
|
43
|
+
# refunding -> closed;
|
44
|
+
|
45
|
+
# dispatch! => preparing -> active;
|
46
|
+
#
|
47
|
+
# reject! => preparing -> rejected;
|
48
|
+
#
|
49
|
+
# execute! => active -> executing;
|
50
|
+
#
|
51
|
+
# - => executing -> purchased;
|
52
|
+
# /
|
53
|
+
# purchase! - - => executing -> completed;
|
54
|
+
# \
|
55
|
+
# - => executing -> refunding;
|
56
|
+
#
|
57
|
+
# refund! => active -> refunding;
|
58
|
+
#
|
59
|
+
# release! => purchased -> releasing;
|
60
|
+
#
|
61
|
+
# unlock! => releasing -> completed;
|
62
|
+
#
|
63
|
+
# close! => refunding -> closed;
|
64
|
+
#
|
65
|
+
# - => executing -> cancelled;
|
66
|
+
# /
|
67
|
+
# cancel! - |
|
68
|
+
# \
|
69
|
+
# - => active -> cancelled;
|
70
|
+
#
|
71
|
+
|
72
|
+
event :dispatch do
|
73
|
+
transitions from: :preparing, to: :active do
|
74
|
+
after do
|
75
|
+
perform_transfer(lock_transfer_params)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
after_commit do
|
79
|
+
sale.notify_ranger
|
80
|
+
execute! if sale.type.fcfs?
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
event :reject do
|
85
|
+
transitions from: :preparing, to: :rejected
|
86
|
+
end
|
87
|
+
|
88
|
+
event :execute, after_commit: :enqueue_execute_job do
|
89
|
+
transitions from: :active, to: :executing
|
90
|
+
end
|
91
|
+
|
92
|
+
event :purchase do
|
93
|
+
transitions from: :executing, to: :purchased do
|
94
|
+
guard do
|
95
|
+
calculate_share
|
96
|
+
sale.lockup_percentage.positive?
|
97
|
+
end
|
98
|
+
after do
|
99
|
+
perform_transfer(execute_transfer_params)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
transitions from: :executing, to: :completed do
|
104
|
+
guard do
|
105
|
+
calculate_share
|
106
|
+
sale.lockup_percentage.zero?
|
107
|
+
end
|
108
|
+
after do
|
109
|
+
perform_transfer(execute_transfer_params)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
transitions from: :executing, to: :refunding
|
114
|
+
|
115
|
+
after_commit do
|
116
|
+
sale.finish! if sale.type.fcfs? && sale.full?
|
117
|
+
enqueue_refund_job if refunding?
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
event :refund do
|
122
|
+
transitions from: :active, to: :refunding
|
123
|
+
|
124
|
+
after_commit do
|
125
|
+
enqueue_refund_job
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
event :release do
|
130
|
+
transitions from: :purchased, to: :releasing
|
131
|
+
|
132
|
+
after_commit do
|
133
|
+
enqueue_released_job
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
event :unlock do
|
138
|
+
transitions from: :releasing, to: :completed do
|
139
|
+
after do
|
140
|
+
perform_transfer(release_transfer_params)
|
141
|
+
calculate_release
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
event :close do
|
147
|
+
transitions from: :refunding, to: :closed do
|
148
|
+
after do
|
149
|
+
calculate_refunded
|
150
|
+
perform_transfer(cancel_transfer_params)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
event :cancel do
|
156
|
+
transitions from: :executing, to: :cancelled do
|
157
|
+
after do
|
158
|
+
calculate_refund
|
159
|
+
Rails.logger.info("Transition from executing to cancelled")
|
160
|
+
perform_transfer(cancel_transfer_params)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
transitions from: :active, to: :cancelled do
|
165
|
+
after do
|
166
|
+
calculate_refund
|
167
|
+
Rails.logger.info("Transition from active to cancelled")
|
168
|
+
perform_transfer(cancel_transfer_params)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
after_commit do
|
173
|
+
sale.notify_ranger
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# == Relationships ========================================================
|
179
|
+
|
180
|
+
belongs_to :sale_pair, class_name: "Launchpad::IEO::SalePair", required: true
|
181
|
+
|
182
|
+
has_one :sale, class_name: "Launchpad::IEO::Sale", through: :sale_pair
|
183
|
+
|
184
|
+
# == Validations ==========================================================
|
185
|
+
|
186
|
+
validates :uid, :contribution, :executed, :refunded, :tokens_received,
|
187
|
+
:commission_rate, :commission_amount, :state, presence: {
|
188
|
+
message: ->(_, _data) { "missing_" }
|
189
|
+
}
|
190
|
+
|
191
|
+
validates :contribution,
|
192
|
+
numericality: {
|
193
|
+
greater_than: 0,
|
194
|
+
message: ->(_, _data) { "non_positive_" }
|
195
|
+
}
|
196
|
+
|
197
|
+
validates :executed, :refunded, :tokens_received,
|
198
|
+
:commission_rate, :commission_amount,
|
199
|
+
numericality: {
|
200
|
+
greater_than_or_equal_to: 0,
|
201
|
+
message: ->(_, _data) { "negative_" }
|
202
|
+
}
|
203
|
+
|
204
|
+
validates :tokens_ordered,
|
205
|
+
numericality: {
|
206
|
+
greater_than_or_equal_to: ->(order) { order.sale.min_amount },
|
207
|
+
message: ->(_, _data) { "_less_than_min_amount" }
|
208
|
+
}
|
209
|
+
|
210
|
+
validates :tokens_ordered,
|
211
|
+
numericality: {
|
212
|
+
less_than_or_equal_to: ->(order) { order.sale.max_amount - order.sale.orders.active_and_prepared.where(uid: order.uid).tokens_ordered },
|
213
|
+
message: ->(_, _data) { "_greater_than_max_amount" }
|
214
|
+
}, if: ->(order) { order.sale.type.proportional? },
|
215
|
+
on: :create
|
216
|
+
|
217
|
+
validates :tokens_ordered,
|
218
|
+
numericality: {
|
219
|
+
less_than_or_equal_to: ->(order) { order.sale.max_amount - order.sale.orders.active_and_prepared_and_completed.where(uid: order.uid).tokens_ordered },
|
220
|
+
message: ->(_, _data) { "_greater_than_max_amount" }
|
221
|
+
}, if: ->(order) { order.sale.type.fcfs? },
|
222
|
+
on: :create
|
223
|
+
|
224
|
+
validates :tokens_ordered,
|
225
|
+
numericality: {
|
226
|
+
less_than_or_equal_to: ->(order) { order.sale.supply - order.sale.orders.executable.tokens_ordered },
|
227
|
+
message: ->(_, _) { "_greater_than_sale_supply" }
|
228
|
+
}, if: ->(order) { order.sale.type.fcfs? },
|
229
|
+
on: :create
|
230
|
+
|
231
|
+
# == Scopes ===============================================================
|
232
|
+
|
233
|
+
scope :active_and_prepared, -> { where(state: %i[active preparing]) }
|
234
|
+
|
235
|
+
scope :active_and_prepared_and_completed, -> { where(state: %i[active preparing purchased completed]) }
|
236
|
+
|
237
|
+
# Scope represents all orders which may be executed.
|
238
|
+
# So this orders influence tokens_ordered and ratio.
|
239
|
+
scope :executable, -> { where(state: %i[preparing active executing purchased completed]) }
|
240
|
+
|
241
|
+
# == Callbacks ============================================================
|
242
|
+
|
243
|
+
before_validation(on: :create) { self.commission_rate = sale.commission }
|
244
|
+
|
245
|
+
# == Class Methods ========================================================
|
246
|
+
|
247
|
+
class << self
|
248
|
+
def tokens_ordered(ndigits: Launchpad::IEO::TOKENS_AMOUNT_PRECISION)
|
249
|
+
# Include SalePair for accessing price.
|
250
|
+
if first&.add?
|
251
|
+
all.includes(:sale_pair).sum("contribution / ieo_sale_pairs.price").round(ndigits).to_d
|
252
|
+
else
|
253
|
+
all.includes(:sale_pair).sum("(1 - commission_rate) * contribution / ieo_sale_pairs.price").round(ndigits).to_d
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def release_fund(percent = 0)
|
258
|
+
return if percent.to_d == 0
|
259
|
+
|
260
|
+
all.each do |order|
|
261
|
+
next unless order.tokens_locked.positive?
|
262
|
+
|
263
|
+
funds = (order.tokens_locked + order.tokens_received) * percent.to_d
|
264
|
+
order.perform_transfer(order.release_funds_params(funds))
|
265
|
+
order.tokens_locked -= funds
|
266
|
+
order.tokens_received += funds
|
267
|
+
order.save!
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
# == Instance Methods =====================================================
|
273
|
+
|
274
|
+
delegate :price, to: :sale_pair
|
275
|
+
|
276
|
+
def as_json(_options={})
|
277
|
+
if _options[:uid]
|
278
|
+
super(except: [:transfer_keys, :uid],
|
279
|
+
methods: %i[tokens_ordered base_currency quote_currency ratio sale_name]).merge(sale_id: sale.id)
|
280
|
+
else
|
281
|
+
super(except: :transfer_keys,
|
282
|
+
methods: %i[tokens_ordered base_currency quote_currency ratio sale_name]).merge(sale_id: sale.id)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
def tokens_ordered(ndigits: Launchpad::IEO::TOKENS_AMOUNT_PRECISION)
|
287
|
+
if add?
|
288
|
+
(contribution / price).round(ndigits)
|
289
|
+
else
|
290
|
+
((1 - commission_rate) * contribution / price).round(ndigits)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
def executed_without_commission
|
295
|
+
add? ? executed : executed - commission_amount
|
296
|
+
end
|
297
|
+
|
298
|
+
def sale_name
|
299
|
+
sale.name
|
300
|
+
end
|
301
|
+
|
302
|
+
def base_currency
|
303
|
+
sale.currency_id
|
304
|
+
end
|
305
|
+
|
306
|
+
def quote_currency
|
307
|
+
sale_pair.quote_currency_id
|
308
|
+
end
|
309
|
+
|
310
|
+
def ratio
|
311
|
+
sale_ratio = sale.ratio < 1 ? 1 : sale.ratio
|
312
|
+
(1 / sale_ratio).to_d.round(Launchpad::IEO::RATIO_PRECISION, BigDecimal::ROUND_DOWN)
|
313
|
+
end
|
314
|
+
|
315
|
+
def calculate_share
|
316
|
+
self.executed = contribution * ratio
|
317
|
+
self.refunded = contribution - executed
|
318
|
+
|
319
|
+
self.commission_amount = executed * commission_rate
|
320
|
+
|
321
|
+
tokens_ordered = (executed_without_commission / sale_pair.price).round(Launchpad::IEO::TOKENS_AMOUNT_PRECISION, BigDecimal::ROUND_DOWN)
|
322
|
+
self.tokens_locked = tokens_ordered * sale.lockup_percentage.to_d
|
323
|
+
self.tokens_received = tokens_ordered * (1 - sale.lockup_percentage.to_d)
|
324
|
+
end
|
325
|
+
|
326
|
+
def calculate_refund
|
327
|
+
# This logic may become complex later.
|
328
|
+
self.refunded = calculate_contribution
|
329
|
+
end
|
330
|
+
|
331
|
+
def calculate_refunded
|
332
|
+
self.refunded = contribution - executed
|
333
|
+
self.refunded = calculate_execute_refunded
|
334
|
+
end
|
335
|
+
|
336
|
+
def add?
|
337
|
+
sale.fees_policy.add?
|
338
|
+
end
|
339
|
+
|
340
|
+
def calculate_contribution
|
341
|
+
add? ? contribution + contribution * commission_rate : contribution
|
342
|
+
end
|
343
|
+
|
344
|
+
def calculate_release
|
345
|
+
self.tokens_received += tokens_locked
|
346
|
+
self.tokens_locked = 0
|
347
|
+
end
|
348
|
+
|
349
|
+
def lock_transfer_params
|
350
|
+
type = fetch_currency(sale_pair.quote_currency_id)
|
351
|
+
.fetch(:type)
|
352
|
+
.to_sym
|
353
|
+
|
354
|
+
operations =
|
355
|
+
[
|
356
|
+
{
|
357
|
+
currency: sale_pair.quote_currency_id,
|
358
|
+
amount: calculate_contribution,
|
359
|
+
account_src: {code: Launchpad::IEO::LIABILITY_CODES[type][:main], uid: uid},
|
360
|
+
account_dst: {code: Launchpad::IEO::LIABILITY_CODES[type][:locked], uid: uid}
|
361
|
+
}
|
362
|
+
].delete_if { |op| op[:amount].zero? }
|
363
|
+
|
364
|
+
{
|
365
|
+
key: "IEO-lock-#{id}",
|
366
|
+
category: "purchases",
|
367
|
+
description: "Lock funds for IEO order #{id}",
|
368
|
+
operations: operations
|
369
|
+
}
|
370
|
+
end
|
371
|
+
|
372
|
+
def execute_transfer_params
|
373
|
+
base_currency_type = fetch_currency(sale.currency_id)
|
374
|
+
.fetch(:type)
|
375
|
+
.to_sym
|
376
|
+
quote_currency_type = fetch_currency(sale_pair.quote_currency_id)
|
377
|
+
.fetch(:type)
|
378
|
+
.to_sym
|
379
|
+
|
380
|
+
operations =
|
381
|
+
[
|
382
|
+
{ # Debit seller main liabilities & credit user locked liabilities with base currency.
|
383
|
+
currency: sale.currency_id,
|
384
|
+
amount: tokens_locked,
|
385
|
+
account_src: {code: Launchpad::IEO::LIABILITY_CODES[base_currency_type][:main],
|
386
|
+
uid: sale.owner_uid},
|
387
|
+
account_dst: {code: Launchpad::IEO::LIABILITY_CODES[base_currency_type][:locked],
|
388
|
+
uid: uid}
|
389
|
+
},
|
390
|
+
{ # Debit seller main liabilities & credit user main liabilities with base currency.
|
391
|
+
currency: sale.currency_id,
|
392
|
+
amount: tokens_received,
|
393
|
+
account_src: {code: Launchpad::IEO::LIABILITY_CODES[base_currency_type][:main],
|
394
|
+
uid: sale.owner_uid},
|
395
|
+
account_dst: {code: Launchpad::IEO::LIABILITY_CODES[base_currency_type][:main],
|
396
|
+
uid: uid}
|
397
|
+
},
|
398
|
+
{ # Debit user locked liabilities & credit seller main liabilities with quote currency.
|
399
|
+
currency: sale_pair.quote_currency_id,
|
400
|
+
amount: executed_without_commission,
|
401
|
+
account_src: {code: Launchpad::IEO::LIABILITY_CODES[quote_currency_type][:locked],
|
402
|
+
uid: uid},
|
403
|
+
account_dst: {code: Launchpad::IEO::LIABILITY_CODES[quote_currency_type][:main],
|
404
|
+
uid: sale.owner_uid}
|
405
|
+
},
|
406
|
+
{ # Debit user locked liabilities & credit user main liabilities with quote currency.
|
407
|
+
currency: sale_pair.quote_currency_id,
|
408
|
+
amount: calculate_execute_refunded,
|
409
|
+
account_src: {code: Launchpad::IEO::LIABILITY_CODES[quote_currency_type][:locked],
|
410
|
+
uid: uid},
|
411
|
+
account_dst: {code: Launchpad::IEO::LIABILITY_CODES[quote_currency_type][:main],
|
412
|
+
uid: uid}
|
413
|
+
},
|
414
|
+
{ # Debit user locked liabilities & credit main revenues with quote currency.
|
415
|
+
currency: sale_pair.quote_currency_id,
|
416
|
+
amount: commission_amount,
|
417
|
+
account_src: {code: Launchpad::IEO::LIABILITY_CODES[quote_currency_type][:locked],
|
418
|
+
uid: uid},
|
419
|
+
account_dst: {code: Launchpad::IEO::REVENUE_CODES[quote_currency_type][:main]}
|
420
|
+
}
|
421
|
+
].delete_if { |op| op[:amount].zero? }
|
422
|
+
|
423
|
+
{
|
424
|
+
key: "IEO-execute-#{id}",
|
425
|
+
category: :purchases,
|
426
|
+
description: "Execute IEO order #{id}",
|
427
|
+
operations: operations
|
428
|
+
}
|
429
|
+
end
|
430
|
+
|
431
|
+
def calculate_execute_refunded
|
432
|
+
add? ? refunded + refunded * commission_rate : refunded
|
433
|
+
end
|
434
|
+
|
435
|
+
def release_transfer_params
|
436
|
+
base_currency_type = fetch_currency(sale.currency_id)
|
437
|
+
.fetch(:type)
|
438
|
+
.to_sym
|
439
|
+
|
440
|
+
operations =
|
441
|
+
[
|
442
|
+
{ # Debit user locked liabilities & credit user main liabilities with base currency.
|
443
|
+
currency: sale.currency_id,
|
444
|
+
amount: tokens_locked,
|
445
|
+
account_src: {code: Launchpad::IEO::LIABILITY_CODES[base_currency_type][:locked],
|
446
|
+
uid: uid},
|
447
|
+
account_dst: {code: Launchpad::IEO::LIABILITY_CODES[base_currency_type][:main],
|
448
|
+
uid: uid}
|
449
|
+
}
|
450
|
+
].delete_if { |op| op[:amount].zero? }
|
451
|
+
|
452
|
+
{
|
453
|
+
key: "IEO-release-#{id}",
|
454
|
+
category: :purchases,
|
455
|
+
description: "Release IEO order #{id}",
|
456
|
+
operations: operations
|
457
|
+
}
|
458
|
+
end
|
459
|
+
|
460
|
+
def cancel_transfer_params
|
461
|
+
quote_currency_type = fetch_currency(sale_pair.quote_currency_id)
|
462
|
+
.fetch(:type)
|
463
|
+
.to_sym
|
464
|
+
|
465
|
+
operations =
|
466
|
+
[
|
467
|
+
{ # Debit user locked liabilities & credit user main liabilities with quote currency.
|
468
|
+
currency: sale_pair.quote_currency_id,
|
469
|
+
amount: refunded,
|
470
|
+
account_src: {code: Launchpad::IEO::LIABILITY_CODES[quote_currency_type][:locked],
|
471
|
+
uid: uid},
|
472
|
+
account_dst: {code: Launchpad::IEO::LIABILITY_CODES[quote_currency_type][:main],
|
473
|
+
uid: uid}
|
474
|
+
}
|
475
|
+
]
|
476
|
+
if add?
|
477
|
+
# Debit user locked liabilities & credit main revenues with quote currency.
|
478
|
+
operations << {
|
479
|
+
currency: sale_pair.quote_currency_id,
|
480
|
+
amount: commission_amount,
|
481
|
+
account_src: {code: Launchpad::IEO::LIABILITY_CODES[quote_currency_type][:locked],
|
482
|
+
uid: uid},
|
483
|
+
account_dst: {code: Launchpad::IEO::REVENUE_CODES[quote_currency_type][:main]}
|
484
|
+
}
|
485
|
+
end
|
486
|
+
|
487
|
+
operations.delete_if { |op| op[:amount].zero? }
|
488
|
+
|
489
|
+
{
|
490
|
+
key: "IEO-cancel-#{id}",
|
491
|
+
category: :purchases,
|
492
|
+
description: "Cancel IEO order #{id}",
|
493
|
+
operations: operations
|
494
|
+
}
|
495
|
+
end
|
496
|
+
|
497
|
+
def release_funds_params(funds)
|
498
|
+
base_currency_type = fetch_currency(sale.currency_id)
|
499
|
+
.fetch(:type)
|
500
|
+
.to_sym
|
501
|
+
operations =
|
502
|
+
[
|
503
|
+
{ # Debit user locked liabilities & credit user main liabilities with base currency.
|
504
|
+
currency: sale.currency_id,
|
505
|
+
amount: funds,
|
506
|
+
account_src: {code: Launchpad::IEO::LIABILITY_CODES[base_currency_type][:locked],
|
507
|
+
uid: uid},
|
508
|
+
account_dst: {code: Launchpad::IEO::LIABILITY_CODES[base_currency_type][:main],
|
509
|
+
uid: uid}
|
510
|
+
}
|
511
|
+
].delete_if { |op| op[:amount].zero? }
|
512
|
+
|
513
|
+
{
|
514
|
+
key: generate_key,
|
515
|
+
category: :purchases,
|
516
|
+
description: "Release IEO order locked funds #{id}",
|
517
|
+
operations: operations
|
518
|
+
}
|
519
|
+
end
|
520
|
+
|
521
|
+
def generate_key
|
522
|
+
"IEO-fund-release-#{id}-#{transfer_keys.grep(/IEO-fund-release-/).size}"
|
523
|
+
end
|
524
|
+
|
525
|
+
def perform_transfer(transfer_params)
|
526
|
+
Peatio::ManagementAPIV2::Client.new.create_transfer(transfer_params)
|
527
|
+
transfer_keys << transfer_params[:key]
|
528
|
+
end
|
529
|
+
|
530
|
+
def enqueue_execute_job
|
531
|
+
Launchpad::IEO::OrderExecuteWorker.perform_async(to_sgid(for: "order_execute"))
|
532
|
+
end
|
533
|
+
|
534
|
+
def enqueue_released_job
|
535
|
+
Launchpad::IEO::OrderReleaseWorker.perform_async(to_sgid(for: "order_release"))
|
536
|
+
end
|
537
|
+
|
538
|
+
def enqueue_refund_job
|
539
|
+
Launchpad::IEO::OrderRefundWorker.perform_async(to_sgid(for: "order_refund"))
|
540
|
+
end
|
541
|
+
|
542
|
+
private
|
543
|
+
|
544
|
+
# TODO: Move this method to helper or somewhere else.
|
545
|
+
def fetch_currency(code)
|
546
|
+
Rails.cache.fetch("currency_#{code}", expires_in: 1.day) do
|
547
|
+
Peatio::ManagementAPIV2::Client.new.currency(code: code)
|
548
|
+
end
|
549
|
+
end
|
550
|
+
end
|
551
|
+
end
|
552
|
+
end
|
553
|
+
|
554
|
+
# == Schema Information
|
555
|
+
# Schema version: 20191001145525
|
556
|
+
#
|
557
|
+
# Table name: ieo_orders
|
558
|
+
#
|
559
|
+
# id :bigint not null, primary key
|
560
|
+
# sale_pair_id :bigint not null
|
561
|
+
# uid :string(255) not null
|
562
|
+
# contribution :decimal(32, 16) not null
|
563
|
+
# executed :decimal(32, 16) default(0.0), not null
|
564
|
+
# refunded :decimal(32, 16) default(0.0), not null
|
565
|
+
# tokens_received :decimal(32, 16) default(0.0), not null
|
566
|
+
# commission_rate :decimal(16, 16) default(0.0), not null
|
567
|
+
# commission_amount :decimal(32, 16) default(0.0), not null
|
568
|
+
# transfer_keys :string(1000)
|
569
|
+
# state :string(32) not null
|
570
|
+
# created_at :datetime not null
|
571
|
+
# updated_at :datetime not null
|
572
|
+
#
|
573
|
+
# Indexes
|
574
|
+
#
|
575
|
+
# index_ieo_orders_on_sale_pair_id (sale_pair_id)
|
576
|
+
#
|