-
-
Notifications
You must be signed in to change notification settings - Fork 7
Rewrite math using bigDecimal and Antelope Integers #16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 10 commits
8c5c070
7670438
0302c44
e2d0ba4
628c865
66eee78
fb3ec73
adaa025
c183978
eb7d7a3
8113844
baef0d9
3983499
329f10c
facd652
303c5c6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -9,10 +9,10 @@ import { | |||||
UInt64, | ||||||
UInt8, | ||||||
} from '@wharfkit/antelope' | ||||||
|
||||||
import BN from 'bn.js' | ||||||
|
||||||
import {intToBigDecimal} from '..' | ||||||
import {PowerUpStateOptions} from './options' | ||||||
import bigDecimal from 'js-big-decimal' | ||||||
|
||||||
export abstract class PowerUpStateResource extends Struct { | ||||||
@Struct.field('uint8') version!: UInt8 | ||||||
|
@@ -37,92 +37,155 @@ export abstract class PowerUpStateResource extends Struct { | |||||
abstract per_day(options?: PowerUpStateOptions): number | ||||||
|
||||||
// Get the current number of allocated units (shift from REX -> PowerUp) | ||||||
public get allocated() { | ||||||
public get allocated_legacy() { | ||||||
return 1 - Number(this.weight_ratio) / Number(this.target_weight_ratio) / 100 | ||||||
} | ||||||
|
||||||
public get allocated(): number { | ||||||
return 1 - Number(this.weight_ratio.dividing(this.target_weight_ratio)) / 100 | ||||||
} | ||||||
|
||||||
// Get the current percentage of reserved units | ||||||
public get reserved() { | ||||||
public get reserved_legacy() { | ||||||
return new BN(String(this.utilization)) / new BN(String(this.weight)) | ||||||
} | ||||||
|
||||||
public get reserved(): Int64 { | ||||||
return this.utilization.dividing(this.weight) | ||||||
} | ||||||
|
||||||
// Get the symbol definition for the token | ||||||
public get symbol() { | ||||||
return this.min_price.symbol | ||||||
} | ||||||
|
||||||
// Common casting for typed values to numbers | ||||||
cast() { | ||||||
return { | ||||||
adjusted_utilization: Number(this.adjusted_utilization), | ||||||
decay_secs: Number(this.decay_secs.value), | ||||||
exponent: Number(this.exponent), | ||||||
utilization: Number(this.utilization), | ||||||
utilization_timestamp: Number(this.utilization_timestamp.value), | ||||||
weight: new BN(String(this.weight)), | ||||||
weight_ratio: Number(this.weight_ratio), | ||||||
} | ||||||
} | ||||||
|
||||||
// Mimic: https://github.com/EOSIO/eosio.contracts/blob/d7bc0a5cc8c0c2edd4dc61b0126517d0cb46fd94/contracts/eosio.system/src/powerup.cpp#L358 | ||||||
utilization_increase(sample: UInt128, frac) { | ||||||
utilization_increase_legacy(frac) { | ||||||
const {weight} = this | ||||||
const frac128 = UInt128.from(frac) | ||||||
const utilization_increase = | ||||||
new BN(weight.value.mul(new BN(frac128.value))) / Math.pow(10, 15) | ||||||
return Math.ceil(utilization_increase) | ||||||
} | ||||||
|
||||||
utilization_increase(frac: UInt128) { | ||||||
const base = intToBigDecimal(frac) | ||||||
const weight = intToBigDecimal(this.weight) | ||||||
const multiplier = intToBigDecimal(Math.pow(10, 15)) | ||||||
return UInt128.from(base.multiply(weight).divide(multiplier, 15).ceil().getValue()) | ||||||
} | ||||||
|
||||||
// Mimic: https://github.com/EOSIO/eosio.contracts/blob/d7bc0a5cc8c0c2edd4dc61b0126517d0cb46fd94/contracts/eosio.system/src/powerup.cpp#L284-L298 | ||||||
price_function(utilization: number): number { | ||||||
const {exponent, weight} = this.cast() | ||||||
price_function_legacy(utilization: Int64): number { | ||||||
const {exponent, weight} = this | ||||||
const max_price: number = this.max_price.value | ||||||
const min_price: number = this.min_price.value | ||||||
let price = min_price | ||||||
const new_exponent = exponent - 1.0 | ||||||
const new_exponent = Number(exponent) - 1.0 | ||||||
if (new_exponent <= 0.0) { | ||||||
return max_price | ||||||
} else { | ||||||
const util_weight = new BN(utilization) / weight | ||||||
price += (max_price - min_price) * Math.pow(util_weight, new_exponent) | ||||||
const util_weight = intToBigDecimal(utilization).divide(intToBigDecimal(weight), 18) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The precision value '18' should be extracted to a named constant to improve code readability and maintainability.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||
const difference = max_price - min_price | ||||||
price += difference * Math.pow(Number(util_weight.getValue()), new_exponent) | ||||||
} | ||||||
return price | ||||||
} | ||||||
|
||||||
price_function(utilization: Int64): number { | ||||||
const {weight} = this | ||||||
let price = this.min_price.value | ||||||
const new_exponent = Number(this.exponent) - 1.0 | ||||||
if (new_exponent <= 0.0) { | ||||||
return this.max_price.value | ||||||
} else { | ||||||
// const util_weight = utilization.dividing(weight) | ||||||
const util_weight = intToBigDecimal(utilization).divide(intToBigDecimal(weight), 18) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The precision value '18' is duplicated. Consider extracting it to a shared constant to ensure consistency and easier maintenance.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||
const difference = this.max_price.value - this.min_price.value | ||||||
price += difference * Math.pow(Number(util_weight.getValue()), new_exponent) | ||||||
} | ||||||
return price | ||||||
} | ||||||
|
||||||
// Mimic: https://github.com/EOSIO/eosio.contracts/blob/d7bc0a5cc8c0c2edd4dc61b0126517d0cb46fd94/contracts/eosio.system/src/powerup.cpp#L274-L280 | ||||||
price_integral_delta(start_utilization: number, end_utilization: number): number { | ||||||
const {exponent, weight} = this.cast() | ||||||
price_integral_delta_legacy(start_utilization: Int64, end_utilization: Int64): number { | ||||||
const {exponent, weight} = this | ||||||
const max_price: number = this.max_price.value | ||||||
const min_price: number = this.min_price.value | ||||||
const coefficient = (max_price - min_price) / exponent | ||||||
const start_u = new BN(start_utilization) / weight | ||||||
const end_u = new BN(end_utilization) / weight | ||||||
const coefficient = (max_price - min_price) / exponent.value | ||||||
const start_u = Number(start_utilization.dividing(weight)) | ||||||
const end_u = Number(end_utilization.dividing(weight)) | ||||||
const delta = | ||||||
min_price * end_u - | ||||||
min_price * start_u + | ||||||
coefficient * Math.pow(end_u, exponent) - | ||||||
coefficient * Math.pow(start_u, exponent) | ||||||
coefficient * Math.pow(end_u, exponent.value) - | ||||||
coefficient * Math.pow(start_u, exponent.value) | ||||||
return delta | ||||||
} | ||||||
|
||||||
price_integral_delta(start_utilization: Int64, end_utilization: Int64): number { | ||||||
const difference = Asset.fromUnits( | ||||||
this.max_price.units.subtracting(this.min_price.units), | ||||||
this.symbol | ||||||
) | ||||||
const coefficient = difference.value / this.exponent.value | ||||||
const start_u = Number(start_utilization.dividing(this.weight)) | ||||||
const end_u = Number(end_utilization.dividing(this.weight)) | ||||||
const delta = | ||||||
this.min_price.value * end_u - | ||||||
this.min_price.value * start_u + | ||||||
coefficient * Math.pow(end_u, this.exponent.value) - | ||||||
coefficient * Math.pow(start_u, this.exponent.value) | ||||||
return delta | ||||||
} | ||||||
|
||||||
// Mimic: https://github.com/EOSIO/eosio.contracts/blob/d7bc0a5cc8c0c2edd4dc61b0126517d0cb46fd94/contracts/eosio.system/src/powerup.cpp#L262-L315 | ||||||
fee(utilization_increase, adjusted_utilization) { | ||||||
const {utilization, weight} = this.cast() | ||||||
fee_legacy(utilization_increase, adjusted_utilization) { | ||||||
const {utilization, weight} = this | ||||||
|
||||||
let start_utilization: number = utilization | ||||||
const end_utilization: number = start_utilization + utilization_increase | ||||||
let start_utilization = Int64.from(utilization) | ||||||
const end_utilization = start_utilization.adding(utilization_increase) | ||||||
|
||||||
let fee = 0 | ||||||
if (start_utilization < adjusted_utilization) { | ||||||
const min = Math.min(utilization_increase, adjusted_utilization - start_utilization) | ||||||
if (start_utilization.lt(adjusted_utilization)) { | ||||||
const min = Math.min( | ||||||
utilization_increase, | ||||||
adjusted_utilization.subtracting(start_utilization) | ||||||
) | ||||||
fee += Number( | ||||||
new bigDecimal(this.price_function(adjusted_utilization) * min) | ||||||
.divide(new bigDecimal(weight.toString())) | ||||||
intToBigDecimal(this.price_function_legacy(adjusted_utilization) * min) | ||||||
.divide(intToBigDecimal(weight)) | ||||||
.getValue() | ||||||
) | ||||||
start_utilization = adjusted_utilization | ||||||
} | ||||||
if (start_utilization < end_utilization) { | ||||||
if (start_utilization.lt(end_utilization)) { | ||||||
fee += this.price_integral_delta(start_utilization, end_utilization) | ||||||
} | ||||||
return fee | ||||||
} | ||||||
|
||||||
fee(utilization_increase: UInt128, adjusted_utilization: Int64) { | ||||||
const {utilization, weight} = this | ||||||
|
||||||
let start_utilization = Int64.from(utilization) | ||||||
const end_utilization = start_utilization.adding(utilization_increase) | ||||||
|
||||||
let fee = 0 | ||||||
if (start_utilization.lt(adjusted_utilization)) { | ||||||
const min = Math.min( | ||||||
Number(utilization_increase), | ||||||
Number(adjusted_utilization.subtracting(start_utilization)) | ||||||
) | ||||||
fee += Number( | ||||||
intToBigDecimal(this.price_function(adjusted_utilization) * min) | ||||||
.divide(intToBigDecimal(weight)) | ||||||
.getValue() | ||||||
) | ||||||
|
||||||
start_utilization = adjusted_utilization | ||||||
} | ||||||
if (start_utilization.lt(end_utilization)) { | ||||||
fee += this.price_integral_delta(start_utilization, end_utilization) | ||||||
} | ||||||
return fee | ||||||
|
@@ -131,17 +194,19 @@ export abstract class PowerUpStateResource extends Struct { | |||||
// Mimic: https://github.com/EOSIO/eosio.contracts/blob/d7bc0a5cc8c0c2edd4dc61b0126517d0cb46fd94/contracts/eosio.system/src/powerup.cpp#L105-L117 | ||||||
determine_adjusted_utilization(options?: PowerUpStateOptions) { | ||||||
// Casting EOSIO types to usable formats for JS calculations | ||||||
const {decay_secs, utilization, utilization_timestamp} = this.cast() | ||||||
let {adjusted_utilization} = this.cast() | ||||||
const {decay_secs, utilization, utilization_timestamp} = this | ||||||
let {adjusted_utilization} = this | ||||||
// If utilization is less than adjusted, calculate real time value | ||||||
if (utilization < adjusted_utilization) { | ||||||
if (utilization.lt(adjusted_utilization)) { | ||||||
// Create now & adjust JS timestamp to match EOSIO timestamp values | ||||||
const ts = options && options.timestamp ? options.timestamp : new Date() | ||||||
const now = TimePointSec.from(ts).toMilliseconds() / 1000 | ||||||
const diff: number = adjusted_utilization - utilization | ||||||
let delta: number = diff * Math.exp(-(now - utilization_timestamp) / decay_secs) | ||||||
const diff = adjusted_utilization.subtracting(utilization).toNumber() | ||||||
let delta: number = | ||||||
diff * | ||||||
Math.exp(-(now - utilization_timestamp.toMilliseconds()) / Number(decay_secs)) | ||||||
delta = Math.min(Math.max(delta, 0), diff) // Clamp the delta | ||||||
adjusted_utilization = utilization + delta | ||||||
adjusted_utilization = utilization.adding(delta) | ||||||
} | ||||||
return adjusted_utilization | ||||||
} | ||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,9 +1,8 @@ | ||||||||||||||||||||
import {Struct, UInt128} from '@wharfkit/antelope' | ||||||||||||||||||||
import {Int64, Int64Type, Struct, UInt128, UInt128Type} from '@wharfkit/antelope' | ||||||||||||||||||||
|
||||||||||||||||||||
import {BNPrecision, SampleUsage} from '..' | ||||||||||||||||||||
import {BNPrecision, intToBigDecimal, SampleUsage} from '..' | ||||||||||||||||||||
import {PowerUpStateResource} from './abstract' | ||||||||||||||||||||
import {PowerUpStateOptions} from './options' | ||||||||||||||||||||
import BN from 'bn.js' | ||||||||||||||||||||
|
||||||||||||||||||||
@Struct.type('powerupstateresourcecpu') | ||||||||||||||||||||
export class PowerUpStateResourceCPU extends PowerUpStateResource { | ||||||||||||||||||||
|
@@ -25,52 +24,86 @@ export class PowerUpStateResourceCPU extends PowerUpStateResource { | |||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
// Convert weight to μs (microseconds) | ||||||||||||||||||||
weight_to_us(sample: UInt128, weight: number): number { | ||||||||||||||||||||
weight_to_us_legacy(sample: UInt128, weight: number): number { | ||||||||||||||||||||
return Math.ceil((weight * Number(sample)) / BNPrecision) | ||||||||||||||||||||
} | ||||||||||||||||||||
weight_to_us(sample: UInt128Type, weight: Int64Type): UInt128 { | ||||||||||||||||||||
return UInt128.from( | ||||||||||||||||||||
UInt128.from(weight).multiplying(Int64.from(sample)).dividing(BNPrecision, 'ceil') | ||||||||||||||||||||
) | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
// Convert μs (microseconds) to weight | ||||||||||||||||||||
us_to_weight(sample: UInt128, us: number): number { | ||||||||||||||||||||
return Math.floor((us / Number(sample)) * BNPrecision) | ||||||||||||||||||||
us_to_weight_legacy(sample: UInt128, us: number): number { | ||||||||||||||||||||
const sampled = intToBigDecimal(sample) | ||||||||||||||||||||
return Math.floor((us / Number(sampled.getValue())) * BNPrecision) | ||||||||||||||||||||
} | ||||||||||||||||||||
us_to_weight(sample: UInt128, us: Int64Type): Int64 { | ||||||||||||||||||||
return Int64.from(us).multiplying(BNPrecision).dividing(sample, 'floor') | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
// Default frac generation by smallest unit type | ||||||||||||||||||||
frac_legacy = (usage: SampleUsage, us: number) => this.frac_by_us_legacy(usage, us) | ||||||||||||||||||||
frac = (usage: SampleUsage, us: number) => this.frac_by_us(usage, us) | ||||||||||||||||||||
|
||||||||||||||||||||
// Frac generation by ms (milliseconds) | ||||||||||||||||||||
frac_by_ms_legacy = (usage: SampleUsage, ms: number) => this.frac_by_us_legacy(usage, ms * 1000) | ||||||||||||||||||||
frac_by_ms = (usage: SampleUsage, ms: number) => this.frac_by_us(usage, ms * 1000) | ||||||||||||||||||||
|
||||||||||||||||||||
// Frac generation by μs (microseconds) | ||||||||||||||||||||
frac_by_us(usage: SampleUsage, us: number) { | ||||||||||||||||||||
const {weight} = this.cast() | ||||||||||||||||||||
const frac = new BN(this.us_to_weight(usage.cpu, us)) / weight | ||||||||||||||||||||
return Math.floor(frac * Math.pow(10, 15)) | ||||||||||||||||||||
frac_by_us_legacy(usage: SampleUsage, us: number) { | ||||||||||||||||||||
const converted = intToBigDecimal(this.us_to_weight_legacy(usage.cpu, us)) | ||||||||||||||||||||
const current = intToBigDecimal(this.weight) | ||||||||||||||||||||
const frac = converted.divide(current, 18) | ||||||||||||||||||||
return Math.floor(Number(frac.getValue()) * Math.pow(10, 15)) | ||||||||||||||||||||
} | ||||||||||||||||||||
frac_by_us(usage: SampleUsage, us: Int64Type): Int64 { | ||||||||||||||||||||
const precision = 15 | ||||||||||||||||||||
const converted = intToBigDecimal(this.us_to_weight(usage.cpu, us)) | ||||||||||||||||||||
const current = intToBigDecimal(this.weight) | ||||||||||||||||||||
const multiplier = intToBigDecimal(Math.pow(10, precision)) | ||||||||||||||||||||
const frac = converted.divide(current, precision).multiply(multiplier) | ||||||||||||||||||||
return Int64.from(frac.getValue()) | ||||||||||||||||||||
Comment on lines
+46
to
+50
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The precision value '15' is duplicated across multiple methods. Consider extracting it to a shared constant to ensure consistency and easier maintenance.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
// Price generation by smallest units, μs (microseconds) | ||||||||||||||||||||
price_per_legacy = (usage: SampleUsage, us = 1000, options?: PowerUpStateOptions): number => | ||||||||||||||||||||
this.price_per_us_legacy(usage, us, options) | ||||||||||||||||||||
price_per = (usage: SampleUsage, us = 1000, options?: PowerUpStateOptions): number => | ||||||||||||||||||||
this.price_per_us(usage, us, options) | ||||||||||||||||||||
|
||||||||||||||||||||
// Price generation by ms (milliseconds) | ||||||||||||||||||||
price_per_ms_legacy = (usage: SampleUsage, ms = 1, options?: PowerUpStateOptions): number => | ||||||||||||||||||||
this.price_per_us_legacy(usage, ms * 1000, options) | ||||||||||||||||||||
price_per_ms = (usage: SampleUsage, ms = 1, options?: PowerUpStateOptions): number => | ||||||||||||||||||||
this.price_per_us(usage, ms * 1000, options) | ||||||||||||||||||||
|
||||||||||||||||||||
// Price generation by μs (microseconds) | ||||||||||||||||||||
price_per_us_legacy(usage: SampleUsage, us = 1000, options?: PowerUpStateOptions): number { | ||||||||||||||||||||
// Determine the utilization increase by this action | ||||||||||||||||||||
const frac = UInt128.from(this.frac_legacy(usage, us)) | ||||||||||||||||||||
const utilization_increase = this.utilization_increase_legacy(frac) | ||||||||||||||||||||
// Determine the adjusted utilization if needed | ||||||||||||||||||||
const adjusted_utilization = this.determine_adjusted_utilization(options) | ||||||||||||||||||||
// Derive the fee from the increase and utilization | ||||||||||||||||||||
const fee = this.fee_legacy(utilization_increase, adjusted_utilization) | ||||||||||||||||||||
// Force the fee up to the next highest value of precision | ||||||||||||||||||||
const precision = Math.pow(10, this.max_price.symbol.precision) | ||||||||||||||||||||
const value = Math.ceil(fee * precision) / precision | ||||||||||||||||||||
// Return the modified fee | ||||||||||||||||||||
return value | ||||||||||||||||||||
} | ||||||||||||||||||||
price_per_us(usage: SampleUsage, us = 1000, options?: PowerUpStateOptions): number { | ||||||||||||||||||||
// Determine the utilization increase by this action | ||||||||||||||||||||
const frac = UInt128.from(this.frac(usage, us)) | ||||||||||||||||||||
const utilization_increase = this.utilization_increase(usage.cpu, frac) | ||||||||||||||||||||
|
||||||||||||||||||||
const utilization_increase = this.utilization_increase(frac) | ||||||||||||||||||||
// Determine the adjusted utilization if needed | ||||||||||||||||||||
const adjusted_utilization = this.determine_adjusted_utilization(options) | ||||||||||||||||||||
|
||||||||||||||||||||
// Derive the fee from the increase and utilization | ||||||||||||||||||||
const fee = this.fee(utilization_increase, adjusted_utilization) | ||||||||||||||||||||
|
||||||||||||||||||||
// Force the fee up to the next highest value of precision | ||||||||||||||||||||
const precision = Math.pow(10, this.max_price.symbol.precision) | ||||||||||||||||||||
const value = Math.ceil(fee * precision) / precision | ||||||||||||||||||||
|
||||||||||||||||||||
// Return the modified fee | ||||||||||||||||||||
return value | ||||||||||||||||||||
} | ||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The magic number '15' should be extracted to a named constant to improve code readability and maintainability.
Copilot uses AI. Check for mistakes.