istox 0.1.86 → 0.1.87

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 54f934711b93a3532841dd514bf6acf9339719df7c029b0f040bf75002659a4d
4
- data.tar.gz: f4ae4d0d97e732d911a805beacfcc2e6222b04bf0cdd35c1b720e3ac46561bc1
3
+ metadata.gz: b9516beb23b5b52b1a86c7a7ab8d03b7e7c377d74163b7a81400041c4501a4e0
4
+ data.tar.gz: fdaa33a8f434fc799eaa9416ef2287d384fd28e1610a4d68cbf637007c4d5af9
5
5
  SHA512:
6
- metadata.gz: 36f9db1bfa5ca342b0f49308b5d4e18f84f1021daf58273962fcf7255be2c458edc063b951847efed3fe6bb9f2398b6fb322357d92df4495b47f1b21118c13e7
7
- data.tar.gz: b8dcae36fa774e2dbe2c8fdcfa3f6d40854204b39dfe134730a4897a3c91ff4bb3a58394a77abfb7224d4139b2d1bfa427305cd6a68af329f7b836f3b2fceeed
6
+ metadata.gz: 957c7d9762478fa79949b747676a14361052ad9667175bd0824233fc1ac2dae72469b69e7addc8d47eb2b6ab75803edc36be3699e14cbbdbfd01daa81ffceef8
7
+ data.tar.gz: ab95640711cb08298c6a68e0b7a14a320d8fa38fbfebb9a44d1bf56752e1abc999459e982329addd2b48579ccd221b6565218f45afe47332571401cbd5e28928
data/.rubocop.yml CHANGED
@@ -34,7 +34,7 @@ Metrics/AbcSize:
34
34
  Max: 100
35
35
 
36
36
  Metrics/BlockLength:
37
- CountComments: false # count full line comments?
37
+ CountComments: false # count full line comments?
38
38
  Max: 25
39
39
  Exclude:
40
40
  - config/**/*
@@ -45,7 +45,7 @@ Metrics/BlockNesting:
45
45
  Max: 4
46
46
 
47
47
  Metrics/ClassLength:
48
- CountComments: false # count full line comments?
48
+ CountComments: false # count full line comments?
49
49
  Max: 200
50
50
 
51
51
  # Avoid complex methods.
@@ -53,15 +53,15 @@ Metrics/CyclomaticComplexity:
53
53
  Max: 20
54
54
 
55
55
  Metrics/MethodLength:
56
- CountComments: false # count full line comments?
56
+ CountComments: false # count full line comments?
57
57
  Max: 100
58
58
 
59
59
  Metrics/ModuleLength:
60
- CountComments: false # count full line comments?
60
+ CountComments: false # count full line comments?
61
61
  Max: 200
62
62
 
63
63
  Metrics/LineLength:
64
- Max: 130
64
+ Max: 150
65
65
  # To make it possible to copy or click on URIs in the code, we allow lines
66
66
  # containing a URI to be longer than Max.
67
67
  AllowURI: true
@@ -70,7 +70,7 @@ Metrics/LineLength:
70
70
  - https
71
71
 
72
72
  Metrics/ParameterLists:
73
- Max: 5
73
+ Max: 8
74
74
  CountKeywordArgs: true
75
75
 
76
76
  Metrics/PerceivedComplexity:
@@ -91,7 +91,6 @@ RSpec/ExampleLength:
91
91
  Lint/BigDecimalNew:
92
92
  Enabled: false
93
93
 
94
-
95
94
  RSpec/MessageSpies:
96
95
  Enabled: false
97
96
 
@@ -99,4 +98,4 @@ RSpec/AnyInstance:
99
98
  Enabled: false
100
99
 
101
100
  RSpec/VerifiedDoubles:
102
- Enabled: false
101
+ Enabled: false
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- istox (0.1.85.pre.test2)
4
+ istox (0.1.86)
5
5
  binding_of_caller
6
6
  bunny (>= 2.12.0)
7
7
  graphlient
data/lib/istox.rb CHANGED
@@ -16,7 +16,8 @@ module Istox
16
16
  require 'istox/helpers/publisher'
17
17
  require 'istox/helpers/bunny_boot'
18
18
  require 'istox/helpers/vault'
19
- require 'istox/helpers/order_book'
19
+ require 'istox/helpers/order_book_price_time'
20
+ require 'istox/helpers/order_book_prorate'
20
21
  require 'istox/helpers/grpc_client'
21
22
  require 'istox/helpers/graphql_client'
22
23
  require 'istox/helpers/gruf_listener_hook'
@@ -0,0 +1,167 @@
1
+ module Istox
2
+ class OrderBookPriceTime
3
+ class << self
4
+ def allocation(soft_cap, total_supply, investments)
5
+ # sort by token price desc, and id asc
6
+ investments = investments.sort do |a, b|
7
+ [b[:token_price], a[:id]] <=> [a[:token_price], b[:id]]
8
+ end
9
+
10
+ total_supply = ::BigDecimal.new(total_supply.to_s)
11
+
12
+ interests = investments
13
+ cutoff_price = 0.0
14
+ total_bid_token = 0.0
15
+ total_allocated = 0.0
16
+ total_unallocated = 0.0
17
+ total_investment = 0.0
18
+ cutoff_price = ::Istox::FMath.round_up(::Istox::FMath.div(soft_cap, total_supply), 2)
19
+ is_cutoff = false
20
+
21
+ # return result immediately if no interest
22
+ unless interests.count.positive?
23
+ return { total_supply: total_supply.to_s, total_investment: '0',
24
+ cutoff_price: cutoff_price, total_bid_token: '0',
25
+ total_allocated: '0', total_unallocated: total_supply.to_s,
26
+ interests: [] }
27
+ end
28
+
29
+ interests = interests.map do |item|
30
+ bid_token = Istox::FMath.round_down(::Istox::FMath.div(item[:fiat_amount], item[:token_price]), 0)
31
+ total_bid_token = ::Istox::FMath.add(total_bid_token, bid_token)
32
+ allocated = 0.0
33
+ unallocated = bid_token
34
+ unless is_cutoff
35
+ allocated = bid_token
36
+ unallocated = 0.0
37
+ if ::BigDecimal.new(::Istox::FMath.add(total_allocated, bid_token)) >= total_supply
38
+ unallocated = ::Istox::FMath.sub(::Istox::FMath.add(total_allocated, bid_token), total_supply)
39
+ allocated = ::Istox::FMath.sub(bid_token, unallocated)
40
+ cutoff_price = item[:token_price]
41
+ is_cutoff = true
42
+ end
43
+ total_allocated = ::Istox::FMath.add(total_allocated, allocated)
44
+ total_unallocated = ::Istox::FMath.add(total_unallocated, unallocated)
45
+ end
46
+ {
47
+ id: item[:id],
48
+ # user bidding total fiat amount
49
+ fiat_amount: item[:fiat_amount],
50
+ # user bidding price
51
+ bid_price: item[:token_price],
52
+ # total user bidding token derived from fiat_amount / token_price
53
+ bid_token: bid_token,
54
+ # round down allocated token to whole number
55
+ allocated: ::Istox::FMath.round_down(allocated, 0),
56
+ # round up unallocated token to whole number
57
+ unallocated: ::Istox::FMath.round_up(unallocated, 0)
58
+ }
59
+ end
60
+
61
+ interests = interests.map do |item|
62
+ investment = ::Istox::FMath.mul(item[:allocated], cutoff_price)
63
+ total_investment = ::Istox::FMath.add(total_investment, investment)
64
+
65
+ # each user maximum total investment amount in fiat
66
+ item.merge!(investment: investment)
67
+ end
68
+
69
+ {
70
+ # total token supply
71
+ total_supply: total_supply.to_s,
72
+ # total investment in fiat
73
+ total_investment: total_investment,
74
+ # cut off price
75
+ cutoff_price: cutoff_price.to_s,
76
+ # total bid tokens, more than total supply for oversubscribe case
77
+ total_bid_token: total_bid_token,
78
+ # total allocated tokens
79
+ total_allocated: total_allocated,
80
+ # total unallocated tokens
81
+ total_unallocated: total_unallocated,
82
+ # all investor interest results
83
+ interests: interests
84
+ }
85
+ end
86
+
87
+ # rubocop:disable Metrics/BlockNesting
88
+ def prorate_allocation(token_price, min_investment, bid_block,
89
+ invest_step, hard_cap, interests)
90
+ # sort by id asc
91
+ interests = interests.sort do |a, b|
92
+ a[:id] <=> b[:id]
93
+ end
94
+ total_interests = 0
95
+
96
+ max_allowed_investor = Istox::FMath.round_down(::Istox::FMath.div(
97
+ ::Istox::FMath.div(hard_cap, bid_block), token_price
98
+ ), 0).to_i
99
+
100
+ interests.each_with_index.map do |interest, i|
101
+ total_interests = ::Istox::FMath.add(total_investment, interest.fiat_amount) if i < max_allowed_investor
102
+ end
103
+
104
+ # only need to handle when oversubscribe
105
+ if total_interests.to_d > hard_cap.to_s.to_d
106
+
107
+ # prorate the interests
108
+ interests = interests.each_with_index.map do |interest, i|
109
+ {
110
+ id: interest.id,
111
+ investment: i < max_allowed_investor ? ::FMath.mul(::FMath.div(interest.fiat_amount, total_interests), hard_cap) : '0'
112
+ }
113
+ end
114
+
115
+ # do adjustments for interest if needed
116
+ final_interests = interests.map do |interest|
117
+ if interest[:investment].to_d < min_investment.to_d
118
+ # calculate the adjusted amount
119
+ lacking_amount = ::Istox::FMath.sub(min_investment, interest[:investment])
120
+ # if some investor has less than min_investment allocated, auto adjust to min investment
121
+ interest[:investment] = min_investment.to_s
122
+
123
+ # must deduct the adjust amount from later book building joiners who has more amount allocated
124
+ # than min-investment
125
+ if lacking_amount.to_d.positive?
126
+ interests.reverse_each do |inner_interest|
127
+ # skip the same interest from deduction candidate
128
+ next unless inner_interest[:id] != interest[:id]
129
+
130
+ # when investor investment is higher than min_investment, he will become a candidate to
131
+ # get investment deduction
132
+ while inner_interest[:investment].to_d > min_investment.to_d && lacking_amount.to_d.positive?
133
+ lacking_amount = ::Istox::FMath.sub(lacking_amount, invest_step)
134
+ inner_interest[:investment] = ::Istox::FMath.sub(inner_interest[:investment], invest_step)
135
+
136
+ # add back the difference if over deduct an investor invest amount
137
+ if lacking_amount.to_d.negative?
138
+ inner_interest[:investment] = ::Istox::FMath.add(inner_interest[:investment],
139
+ BigDecimal(lacking_amount).abs)
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
145
+ {
146
+ id: interest[:id],
147
+ investment: interest[:investment]
148
+ }
149
+ end
150
+ else
151
+ final_interests = interests.each_with_index.map do |interest, i|
152
+ {
153
+ id: interest.id,
154
+ investment: i < max_allowed_investor ? interest.fiat_amount : '0'
155
+ }
156
+ end
157
+ end
158
+
159
+ {
160
+ cutoff_price: token_price,
161
+ interests: final_interests
162
+ }
163
+ end
164
+ end
165
+ # rubocop:enable Metrics/BlockNesting
166
+ end
167
+ end
@@ -0,0 +1,80 @@
1
+ module Istox
2
+ class OrderBookProrate
3
+ class << self
4
+ def allocation(token_price:, min_investment:, bid_block:,
5
+ invest_step:, hard_cap:, investments:)
6
+
7
+ # sort by id asc
8
+ interests = investments.sort do |a, b|
9
+ a[:id] <=> b[:id]
10
+ end
11
+ total_interests = 0
12
+
13
+ max_allowed_investor = Istox::FMath.round_down(::Istox::FMath.div(hard_cap, bid_block), 0).to_i
14
+
15
+ interests.each_with_index.map do |interest, i|
16
+ total_interests = ::Istox::FMath.add(total_interests, interest[:fiat_amount]) if i < max_allowed_investor
17
+ end
18
+
19
+ # only need to handle when oversubscribe
20
+ if total_interests.to_d > hard_cap.to_s.to_d
21
+
22
+ # prorate the interests
23
+ interests = interests.each_with_index.map do |interest, i|
24
+ investment = if i < max_allowed_investor
25
+ ::Istox::FMath.round_down(::Istox::FMath.mul(::Istox::FMath.div(interest[:fiat_amount], total_interests), hard_cap), 0)
26
+ else
27
+ ::BigDecimal.new('0').to_s
28
+ end
29
+ investment = investment.to_d - investment.to_d.modulo(invest_step.to_s.to_d)
30
+
31
+ investment = min_investment.to_d if investment < min_investment.to_d
32
+
33
+ {
34
+ id: interest[:id],
35
+ investment: investment.to_s
36
+ }
37
+ end
38
+
39
+ new_total_interests = '0'
40
+ interests.each do |interest|
41
+ new_total_interests = ::Istox::FMath.add(new_total_interests, interest[:investment])
42
+ end
43
+
44
+ # need to do adjustment if summation of new rounded values still larger than
45
+ if new_total_interests.to_d > hard_cap.to_s.to_d
46
+ total_deducting = ::Istox::FMath.sub(new_total_interests, hard_cap)
47
+ interests.reverse_each do |interest|
48
+ next unless interest[:investment].to_d > min_investment.to_d
49
+
50
+ deductable = ::Istox::FMath.sub(interest[:investment], min_investment)
51
+
52
+ if deductable.to_d > total_deducting.to_d
53
+ interest[:investment] = ::Istox::FMath.sub(interest[:investment], total_deducting)
54
+ total_deducting = 0
55
+ else
56
+ interest[:investment] = ::Istox::FMath.sub(interest[:investment], deductable)
57
+ total_deducting = ::Istox::FMath.sub(total_deducting, deductable)
58
+ end
59
+ end
60
+ end
61
+
62
+ final_interests = interests
63
+
64
+ else
65
+ final_interests = interests.each_with_index.map do |interest, i|
66
+ {
67
+ id: interest[:id],
68
+ investment: i < max_allowed_investor ? ::BigDecimal.new(interest[:fiat_amount]).to_s : ::BigDecimal.new('0').to_s
69
+ }
70
+ end
71
+ end
72
+
73
+ {
74
+ cutoff_price: token_price.to_s,
75
+ interests: final_interests
76
+ }
77
+ end
78
+ end
79
+ end
80
+ end
@@ -1,4 +1,5 @@
1
1
  require 'date'
2
+ require 'rails'
2
3
 
3
4
  # https://en.wikipedia.org/wiki/Day_count_convention#30/360_Bond_Basis
4
5
  # For 360 basis: 30/360 Bond Basis
@@ -15,25 +16,25 @@ module Istox
15
16
  def initialize(coupon: nil, maturity_date: nil, years: nil, coupon_frequency: nil, coupon_payment_dates: nil, face_value: 100, days_of_year: 365)
16
17
  raise "Invalid coupon #{coupon}" if (coupon.nil? || !is_number?(coupon))
17
18
  raise "Invalid maturity_date #{maturity_date}" if (maturity_date.nil? || maturity_date.methods.include?("strftime"))
18
- raise "Invalid years #{years}" if (years.nil? || !years.is_a?(Integer))
19
+ raise "Invalid years #{years}" if (years.nil? || !is_number?(years))
19
20
  raise "Invalid coupon_frequency #{days_of_year}" if (coupon_frequency.nil? || !is_number?(coupon))
20
21
  raise "Invalid coupon_payment_dates #{coupon_payment_dates}" if (coupon_payment_dates.nil? || coupon_payment_dates.count == 0 || coupon_payment_dates.any? { |date| date > maturity_date })
21
22
  raise "Invalid days_of_year #{days_of_year}" if (days_of_year != 365 && days_of_year != 360)
22
23
 
23
- @coupon = coupon
24
+ @coupon = coupon.to_d
24
25
  @maturity_date = maturity_date
25
26
  @years = years
26
- @coupon_frequency = coupon_frequency
27
- @days_of_year = days_of_year
27
+ @coupon_frequency = coupon_frequency.to_d
28
+ @days_of_year = days_of_year.to_d
28
29
  @face_value = face_value
29
30
  @coupon_payment_dates = coupon_payment_dates.sort
30
- @start_date = (@maturity_date-@years.years)
31
+ @start_date = (@maturity_date-(years*12).to_i.months)
31
32
  end
32
33
 
33
34
  def price(ytm, date, ex_coupon_date: nil, fees: 0)
34
35
  # price_for_irr(irr_from_ytm(ytm), date, fees: fees)
35
36
  price = price_for_irr(ytm/@coupon_frequency, date, ex_coupon_date: ex_coupon_date, fees: fees)
36
- price.round(2)
37
+ price
37
38
  end
38
39
 
39
40
  # def interest_payments(from_date)
@@ -67,7 +68,7 @@ module Istox
67
68
  private
68
69
 
69
70
  def is_365?
70
- @days_of_year == 365
71
+ @days_of_year == 365.to_d
71
72
  end
72
73
 
73
74
  def add_month(mydate, n)
@@ -129,9 +130,9 @@ module Istox
129
130
  if payment_dates.count == 0
130
131
  # no more coupon payments, we do only 1 discount of accrued interest and face value to the current date
131
132
  if is_365?
132
- discount_factor = 1/(1+irr*(@maturity_date-date)/accrued_interest_discount_days)
133
+ discount_factor = 1.0/(1+irr*(@maturity_date-date)/accrued_interest_discount_days)
133
134
  else
134
- discount_factor = 1/(1+irr*@coupon_frequency*day_count_factor(date, @maturity_date, nil))
135
+ discount_factor = 1.0/(1+irr*@coupon_frequency*day_count_factor(date, @maturity_date, nil))
135
136
  end
136
137
  discounted_accrued_interest = @coupon*@face_value*accrued_interest_factor*discount_factor
137
138
  discounted_face_value = @face_value*discount_factor
@@ -139,14 +140,15 @@ module Istox
139
140
  else
140
141
  # we discount face value and coupons and accrued interest (coupon) at maturity (if any)
141
142
  # to the first coupon payment left, and then discount the total to current date
142
- value_at_first_coupon = 0
143
+ value_at_first_coupon = 0.to_d
143
144
  if payment_dates.include?(@maturity)
144
145
  # last coupon payment is at maturity
145
- discount_factor = 1/(1+irr)**(payment_dates.count-1)
146
+ discount_factor = 1.0/(1+irr)**(payment_dates.count-1)
146
147
  value_at_first_coupon = @face_value*discount_factor
147
148
  else
149
+ period = @coupon_payment_dates.include?(date) ? payment_dates.count : payment_dates.count-1
148
150
  # last coupon is not at maturity, need to add accrued interest (coupon)
149
- discount_factor = 1/((1+irr)**(payment_dates.count-1))/(1+irr*@coupon_frequency*accrued_interest_factor)
151
+ discount_factor = 1.0/((1+irr)**period)/(1+irr*@coupon_frequency*accrued_interest_factor)
150
152
  discounted_accrued_interest = @coupon*@face_value*accrued_interest_factor*discount_factor
151
153
  discounted_face_value = @face_value*discount_factor
152
154
  value_at_first_coupon = discounted_accrued_interest + discounted_face_value
@@ -154,26 +156,42 @@ module Istox
154
156
 
155
157
  # first coupon can be pro-rata
156
158
  value_at_first_coupon += first_coupon(date, ex_coupon_date)
157
- if payment_dates.count > 1
158
- for n in 1..payment_dates.count-1 do
159
- value_at_first_coupon += @coupon*@face_value/@coupon_frequency/(1+irr)**n
159
+ if payment_dates.count >= 1
160
+ if @coupon_payment_dates.include?(date)
161
+ # today is on one of the coupon payment date, the coupon will not be payed today
162
+ # discount all coupon payments to first coupon date
163
+ for n in 1..payment_dates.count do
164
+ value_at_first_coupon += @coupon*@face_value/@coupon_frequency/(1+irr)**n
165
+ end
166
+ else
167
+ # discount all coupon payments excluding first one to first coupon date
168
+ for n in 1..payment_dates.count-1 do
169
+ value_at_first_coupon += @coupon*@face_value/@coupon_frequency/(1+irr)**n
170
+ end
160
171
  end
161
172
  end
162
173
 
163
- # discount value at first coupon date to present value
164
- if is_365?
165
- price = value_at_first_coupon/(1+irr*(payment_dates.first-date)/first_coupon_discount_days(date))/(1+fees)
174
+ if @coupon_payment_dates.include?(date)
175
+ # today is one of the coupon payment, no need to discount
176
+ price = value_at_first_coupon
166
177
  else
167
- price = value_at_first_coupon/(1+irr*@coupon_frequency*day_count_factor(date, payment_dates.first, nil))/(1+fees)
168
- end
178
+ # discount value at first coupon date to present value
179
+ if is_365?
180
+ price = value_at_first_coupon/(1+irr*(payment_dates.first-date)/first_coupon_discount_days(date))/(1+fees)
181
+ else
182
+ price = value_at_first_coupon/(1+irr*@coupon_frequency*day_count_factor(date, payment_dates.first, nil))/(1+fees)
183
+ end
184
+ end
169
185
  end
170
186
 
171
187
  end
172
188
 
173
189
  def first_coupon(date, ex_coupon_date)
174
- if !ex_coupon_date.nil? && date > ex_coupon_date
190
+ if @coupon_payment_dates.include?(date) || (!ex_coupon_date.nil? && date > ex_coupon_date)
191
+ # if today is on coupon payment date, we won't include the coupon
192
+ # as by default it's passing ex_coupon_date in our calculation
175
193
  # after ex coupon date, buyer won't receive next coupon
176
- return 0
194
+ return 0.to_d
177
195
  end
178
196
 
179
197
  coupon = @coupon*@face_value/@coupon_frequency
@@ -223,7 +241,7 @@ module Istox
223
241
  if d1 == 30
224
242
  d2 = [d2, 30].min
225
243
  end
226
- (360*(date2.year - date1.year) + 30*(date2.month - date1.month) + d2 - d1)/360.to_d
244
+ (360*(date2.year - date1.year) + 30*(date2.month - date1.month) + d2 - d1)/@days_of_year
227
245
  end
228
246
  end
229
247
 
data/lib/istox/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Istox
2
- VERSION = '0.1.86'.freeze
2
+ VERSION = '0.1.87'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: istox
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.86
4
+ version: 0.1.87
5
5
  platform: ruby
6
6
  authors:
7
7
  - Siong Leng
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-09-17 00:00:00.000000000 Z
11
+ date: 2019-09-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bunny
@@ -299,7 +299,8 @@ files:
299
299
  - lib/istox/helpers/gruf_listener_hook.rb
300
300
  - lib/istox/helpers/message_service.rb
301
301
  - lib/istox/helpers/my_open_struct.rb
302
- - lib/istox/helpers/order_book.rb
302
+ - lib/istox/helpers/order_book_price_time.rb
303
+ - lib/istox/helpers/order_book_prorate.rb
303
304
  - lib/istox/helpers/publisher.rb
304
305
  - lib/istox/helpers/vault.rb
305
306
  - lib/istox/interfaces/chainhub/transaction.rb
@@ -1,88 +0,0 @@
1
- module Istox
2
- class OrderBook
3
- class << self
4
- def allocation(soft_cap, total_supply, investments)
5
- # sort by token price desc, and id asc
6
- investments = investments.sort do |a, b|
7
- [b[:token_price], a[:id]] <=> [a[:token_price], b[:id]]
8
- end
9
-
10
- total_supply = ::BigDecimal.new(total_supply.to_s)
11
-
12
- interests = investments
13
- cutoff_price = 0.0
14
- total_bid_token = 0.0
15
- total_allocated = 0.0
16
- total_unallocated = 0.0
17
- total_investment = 0.0
18
- cutoff_price = ::Istox::FMath.round_up(::Istox::FMath.div(soft_cap, total_supply), 2)
19
- is_cutoff = false
20
-
21
- # return result immediately if no interest
22
- unless interests.count.positive?
23
- return { total_supply: total_supply.to_s, total_investment: '0',
24
- cutoff_price: cutoff_price, total_bid_token: '0',
25
- total_allocated: '0', total_unallocated: total_supply.to_s,
26
- interests: [] }
27
- end
28
-
29
- interests = interests.map do |item|
30
- bid_token = Istox::FMath.round_down(::Istox::FMath.div(item[:fiat_amount], item[:token_price]), 0)
31
- total_bid_token = ::Istox::FMath.add(total_bid_token, bid_token)
32
- allocated = 0.0
33
- unallocated = bid_token
34
- unless is_cutoff
35
- allocated = bid_token
36
- unallocated = 0.0
37
- if ::BigDecimal.new(::Istox::FMath.add(total_allocated, bid_token)) >= total_supply
38
- unallocated = ::Istox::FMath.sub(::Istox::FMath.add(total_allocated, bid_token), total_supply)
39
- allocated = ::Istox::FMath.sub(bid_token, unallocated)
40
- cutoff_price = item[:token_price]
41
- is_cutoff = true
42
- end
43
- total_allocated = ::Istox::FMath.add(total_allocated, allocated)
44
- total_unallocated = ::Istox::FMath.add(total_unallocated, unallocated)
45
- end
46
- {
47
- id: item[:id],
48
- # user bidding total fiat amount
49
- fiat_amount: item[:fiat_amount],
50
- # user bidding price
51
- bid_price: item[:token_price],
52
- # total user bidding token derived from fiat_amount / token_price
53
- bid_token: bid_token,
54
- # round down allocated token to whole number
55
- allocated: ::Istox::FMath.round_down(allocated, 0),
56
- # round up unallocated token to whole number
57
- unallocated: ::Istox::FMath.round_up(unallocated, 0)
58
- }
59
- end
60
-
61
- interests = interests.map do |item|
62
- investment = ::Istox::FMath.mul(item[:allocated], cutoff_price)
63
- total_investment = ::Istox::FMath.add(total_investment, investment)
64
-
65
- # each user maximum total investment amount in fiat
66
- item.merge!(investment: investment)
67
- end
68
-
69
- {
70
- # total token supply
71
- total_supply: total_supply.to_s,
72
- # total investment in fiat
73
- total_investment: total_investment,
74
- # cut off price
75
- cutoff_price: cutoff_price.to_s,
76
- # total bid tokens, more than total supply for oversubscribe case
77
- total_bid_token: total_bid_token,
78
- # total allocated tokens
79
- total_allocated: total_allocated,
80
- # total unallocated tokens
81
- total_unallocated: total_unallocated,
82
- # all investor interest results
83
- interests: interests
84
- }
85
- end
86
- end
87
- end
88
- end