Class: Istox::OrderBookPriceTime

Inherits:
Object
  • Object
show all
Defined in:
lib/istox/helpers/order_book_price_time.rb

Class Method Summary collapse

Class Method Details

.allocation(soft_cap, total_supply, investments, decimal_place: 2) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/istox/helpers/order_book_price_time.rb', line 4

def allocation(soft_cap, total_supply, investments, decimal_place: 2)
  # sort by token price desc, and id asc
  investments = investments.sort do |a, b|
    [b[:token_price], a[:id]] <=> [a[:token_price], b[:id]]
  end

  total_supply = ::BigDecimal.new(total_supply.to_s)

  interests = investments
  cutoff_price = 0.0
  total_bid_token = 0.0
  total_allocated = 0.0
  total_unallocated = 0.0
  total_investment = 0.0
  cutoff_price = ::Istox::FMath.round_up(::Istox::FMath.div(soft_cap, total_supply), decimal_place)
  is_cutoff = false

  # return result immediately if no interest
  unless interests.count.positive?
    return { total_supply: total_supply.to_s, total_investment: '0',
             cutoff_price: cutoff_price, total_bid_token: '0',
             total_allocated: '0', total_unallocated: total_supply.to_s,
             interests: [] }
  end

  interests = interests.map do |item|
    bid_token = Istox::FMath.round_down(::Istox::FMath.div(item[:fiat_amount], item[:token_price]), 0)
    total_bid_token = ::Istox::FMath.add(total_bid_token, bid_token)
    allocated = 0.0
    unallocated = bid_token
    unless is_cutoff
      allocated = bid_token
      unallocated = 0.0
      if ::BigDecimal.new(::Istox::FMath.add(total_allocated, bid_token)) >= total_supply
        unallocated = ::Istox::FMath.sub(::Istox::FMath.add(total_allocated, bid_token), total_supply)
        allocated = ::Istox::FMath.sub(bid_token, unallocated)
        cutoff_price = item[:token_price]
        is_cutoff = true
      end
      total_allocated = ::Istox::FMath.add(total_allocated, allocated)
      total_unallocated = ::Istox::FMath.add(total_unallocated, unallocated)
    end
    {
      id: item[:id],
      # user bidding total fiat amount
      fiat_amount: item[:fiat_amount],
      # user bidding price
      bid_price: item[:token_price],
      # total user bidding token derived from fiat_amount / token_price
      bid_token: bid_token,
      # round down allocated token to whole number
      allocated: ::Istox::FMath.round_down(allocated, 0),
      # round up unallocated token to whole number
      unallocated: ::Istox::FMath.round_up(unallocated, 0)
    }
  end

  interests = interests.map do |item|
    investment = ::Istox::FMath.mul(item[:allocated], cutoff_price)
    total_investment = ::Istox::FMath.add(total_investment, investment)

    # each user maximum total investment amount in fiat
    item.merge!(investment: investment)
  end

  {
    # total token supply
    total_supply: total_supply.to_s,
    # total investment in fiat
    total_investment: total_investment,
    # cut off price
    cutoff_price: cutoff_price.to_s,
    # total bid tokens, more than total supply for oversubscribe case
    total_bid_token: total_bid_token,
    # total allocated tokens
    total_allocated: total_allocated,
    # total unallocated tokens
    total_unallocated: total_unallocated,
    # all investor interest results
    interests: interests
  }
end

.prorate_allocation(token_price, min_investment, bid_block, invest_step, hard_cap, interests) ⇒ Object

rubocop:disable Metrics/BlockNesting



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/istox/helpers/order_book_price_time.rb', line 88

def prorate_allocation(token_price, min_investment, bid_block,
                       invest_step, hard_cap, interests)
  # sort by id asc
  interests = interests.sort do |a, b|
    a[:id] <=> b[:id]
  end
  total_interests = 0

  max_allowed_investor = Istox::FMath.round_down(::Istox::FMath.div(
                                                   ::Istox::FMath.div(hard_cap, bid_block), token_price
                                                 ), 0).to_i

  interests.each_with_index.map do |interest, i|
    total_interests = ::Istox::FMath.add(total_investment, interest.fiat_amount) if i < max_allowed_investor
  end

  # only need to handle when oversubscribe
  if total_interests.to_d > hard_cap.to_s.to_d

    # prorate the interests
    interests = interests.each_with_index.map do |interest, i|
      {
        id: interest.id,
        investment: i < max_allowed_investor ? ::FMath.mul(::FMath.div(interest.fiat_amount, total_interests), hard_cap) : '0'
      }
    end

    # do adjustments for interest if needed
    final_interests = interests.map do |interest|
      if interest[:investment].to_d < min_investment.to_d
        # calculate the adjusted amount
        lacking_amount = ::Istox::FMath.sub(min_investment, interest[:investment])
        # if some investor has less than min_investment allocated, auto adjust to min investment
        interest[:investment] = min_investment.to_s

        # must deduct the adjust amount from later book building joiners who has more amount allocated
        # than min-investment
        if lacking_amount.to_d.positive?
          interests.reverse_each do |inner_interest|
            # skip the same interest from deduction candidate
            next unless inner_interest[:id] != interest[:id]

            # when investor investment is higher than min_investment, he will become a candidate to
            # get investment deduction
            while inner_interest[:investment].to_d > min_investment.to_d && lacking_amount.to_d.positive?
              lacking_amount = ::Istox::FMath.sub(lacking_amount, invest_step)
              inner_interest[:investment] = ::Istox::FMath.sub(inner_interest[:investment], invest_step)

              # add back the difference if over deduct an investor invest amount
              if lacking_amount.to_d.negative?
                inner_interest[:investment] = ::Istox::FMath.add(inner_interest[:investment],
                                                                 BigDecimal(lacking_amount).abs)
              end
            end
          end
        end
      end
      {
        id: interest[:id],
        investment: interest[:investment]
      }
    end
  else
    final_interests = interests.each_with_index.map do |interest, i|
      {
        id: interest.id,
        investment: i < max_allowed_investor ? interest.fiat_amount : '0'
      }
    end
  end

  {
    cutoff_price: token_price,
    interests: final_interests
  }
end