Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/resources",
"description": "Library to assist in Antelope-blockchain resource calculations.",
"version": "1.3.1",
"version": "1.5.0-rc3",
"homepage": "https://github.com/wharfkit/resources",
"license": "BSD-3-Clause",
"main": "lib/wharfkit-resources.js",
Expand All @@ -20,7 +20,7 @@
"prepare": "make"
},
"dependencies": {
"@wharfkit/antelope": "^1.0.0",
"@wharfkit/antelope": "^1.1.0",
"bn.js": "^4.11.9",
"js-big-decimal": "^2.0.7",
"tslib": "^2.1.0"
Expand All @@ -31,7 +31,7 @@
"@types/node": "^14.14.28",
"@typescript-eslint/eslint-plugin": "^4.15.1",
"@typescript-eslint/parser": "^4.15.1",
"@wharfkit/mock-data": "^1.2.0",
"@wharfkit/mock-data": "^1.3.0",
"assert": "^2.0.0",
"eslint": "^7.19.0",
"eslint-config-prettier": "^7.0.0",
Expand Down
7 changes: 6 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import {API, APIClient, APIClientOptions, FetchProvider, UInt128} from '@wharfkit/antelope'
import {API, APIClient, APIClientOptions, FetchProvider, Int, UInt128} from '@wharfkit/antelope'
import BN from 'bn.js'

import {PowerUpAPI} from './powerup'
import {RAMAPI} from './ram'
import {REXAPI} from './rex'
import bigDecimal from 'js-big-decimal'

export * from './powerup'
export * from './ram'
Expand Down Expand Up @@ -85,3 +86,7 @@ function divCeil(num: BN, den: BN): UInt128 {
}
return UInt128.from(v)
}

export function intToBigDecimal(value: Int | number) {
return new bigDecimal(String(value))
}
155 changes: 110 additions & 45 deletions src/powerup/abstract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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())
Comment on lines +57 to +58
Copy link
Preview

Copilot AI Jul 15, 2025

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.

Suggested change
const multiplier = intToBigDecimal(Math.pow(10, 15))
return UInt128.from(base.multiply(weight).divide(multiplier, 15).ceil().getValue())
const multiplier = intToBigDecimal(Math.pow(10, DIVISION_PRECISION))
return UInt128.from(base.multiply(weight).divide(multiplier, DIVISION_PRECISION).ceil().getValue())

Copilot uses AI. Check for mistakes.

}

// 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)
Copy link
Preview

Copilot AI Jul 15, 2025

Choose a reason for hiding this comment

The 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
const util_weight = intToBigDecimal(utilization).divide(intToBigDecimal(weight), 18)
const util_weight = intToBigDecimal(utilization).divide(intToBigDecimal(weight), DECIMAL_PRECISION)

Copilot uses AI. Check for mistakes.

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)
Copy link
Preview

Copilot AI Jul 15, 2025

Choose a reason for hiding this comment

The 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
const util_weight = intToBigDecimal(utilization).divide(intToBigDecimal(weight), 18)
const util_weight = intToBigDecimal(utilization).divide(intToBigDecimal(weight), DECIMAL_PRECISION)

Copilot uses AI. Check for mistakes.

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
Expand All @@ -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
}
Expand Down
63 changes: 48 additions & 15 deletions src/powerup/cpu.ts
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 {
Expand All @@ -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
Copy link
Preview

Copilot AI Jul 15, 2025

Choose a reason for hiding this comment

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

Copilot uses AI. Check for mistakes.

}

// 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
}
Expand Down
Loading
Loading