istox 0.1.86 → 0.1.87

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 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