initial commit

This commit is contained in:
Aaron Cox
2021-03-01 13:54:38 -05:00
commit 6f23bf55bb
25 changed files with 3678 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
+29
View File
@@ -0,0 +1,29 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"ignorePatterns": ["lib/*", "node_modules/**"],
"extends": [
"eslint:recommended",
"plugin:prettier/recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"rules": {
"prettier/prettier": "warn",
"no-console": "warn",
"sort-imports": [
"warn",
{
"ignoreCase": true,
"ignoreDeclarationSort": true
}
],
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-namespace": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-empty-function": "warn",
"no-inner-declarations": "off",
}
}
+1
View File
@@ -0,0 +1 @@
custom: "https://greymass.com/support-us"
+19
View File
@@ -0,0 +1,19 @@
name: Tests
on: push
jobs:
test:
name: Node 14
runs-on: ubuntu-latest
steps:
- name: Setup Node.js
uses: actions/setup-node@v1
with:
node-version: '14'
- name: Checkout code
uses: actions/checkout@v2
- name: Install dependencies
run: make node_modules
- name: Run tests
run: make ci-test
- name: Run linter
run: make ci-lint
+5
View File
@@ -0,0 +1,5 @@
node_modules/
lib/
coverage/
.nyc_output/
docs/
+8
View File
@@ -0,0 +1,8 @@
arrowParens: "always"
bracketSpacing: false
endOfLine: "lf"
printWidth: 100
semi: false
singleQuote: true
tabWidth: 4
trailingComma: "es5"
+4
View File
@@ -0,0 +1,4 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
}
+29
View File
@@ -0,0 +1,29 @@
Copyright (c) 2021 Greymass Inc. All Rights Reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistribution of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistribution in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
YOU ACKNOWLEDGE THAT THIS SOFTWARE IS NOT DESIGNED, LICENSED OR INTENDED FOR USE
IN THE DESIGN, CONSTRUCTION, OPERATION OR MAINTENANCE OF ANY MILITARY FACILITY.
+50
View File
@@ -0,0 +1,50 @@
SRC_FILES := $(shell find src -name '*.ts')
BIN := ./node_modules/.bin
lib: ${SRC_FILES} package.json tsconfig.json node_modules rollup.config.js
@${BIN}/rollup -c && touch lib
.PHONY: test
test: node_modules
@TS_NODE_PROJECT='./test/tsconfig.json' ${BIN}/mocha \
-u tdd -r ts-node/register --extension ts test/*.ts --grep '$(grep)'
.PHONY: coverage
coverage: node_modules
@TS_NODE_PROJECT='./test/tsconfig.json' ${BIN}/nyc --reporter=html \
${BIN}/mocha -u tdd -r ts-node/register --extension ts test/*.ts \
-R nyan && open coverage/index.html
.PHONY: lint
lint: node_modules
@${BIN}/eslint src --ext .ts --fix
.PHONY: ci-test
ci-test: node_modules
@TS_NODE_PROJECT='./test/tsconfig.json' ${BIN}/nyc --reporter=text \
${BIN}/mocha -u tdd -r ts-node/register --extension ts test/*.ts -R list
.PHONY: ci-lint
ci-lint: node_modules
@${BIN}/eslint src --ext .ts --max-warnings 0 --format unix && echo "Ok"
docs: $(SRC_FILES) node_modules
@${BIN}/typedoc --out docs \
--excludeInternal --excludePrivate --excludeProtected \
--includeVersion --readme none \
src/index.ts
.PHONY: deploy-pages
deploy-pages: docs
@${BIN}/gh-pages -d docs
node_modules:
yarn install --non-interactive --frozen-lockfile --ignore-scripts
.PHONY: clean
clean:
rm -rf lib/ coverage/ docs/
.PHONY: distclean
distclean: clean
rm -rf node_modules/
+25
View File
@@ -0,0 +1,25 @@
# eosio-resources
## Installation
The `eosio-resources` package is distributed as a module on [npm](https://www.npmjs.com/package/eosio-resources).
```
yarn add eosio-resources
# or
npm install --save eosio-resources
```
## Usage
TODO
## Developing
You need [Make](https://www.gnu.org/software/make/), [node.js](https://nodejs.org/en/) and [yarn](https://classic.yarnpkg.com/en/docs/install) installed.
Clone the repository and run `make` to checkout all dependencies and build the project. See the [Makefile](./Makefile) for other useful targets. Before submitting a pull request make sure to run `make lint`.
---
Made with ☕️ & ❤️ by [Greymass](https://greymass.com), if you find this useful please consider [supporting us](https://greymass.com/support-us).
+47
View File
@@ -0,0 +1,47 @@
{
"name": "@greymass/eosio-resources",
"description": "Library to assist in EOSIO Resource calculations.",
"version": "0.0.1",
"homepage": "https://github.com/greymass/eosio-resources",
"license": "BSD-3-Clause",
"main": "lib/eosio-resources.js",
"module": "lib/eosio-resources.m.js",
"types": "lib/eosio-resources.d.ts",
"browser": {
"buffer": false,
"crypto": false
},
"sideEffects": false,
"files": [
"lib/*",
"src/*"
],
"scripts": {
"prepare": "make"
},
"dependencies": {
"@greymass/eosio": "^0.3.0",
"tslib": "^2.1.0"
},
"devDependencies": {
"@rollup/plugin-typescript": "^8.1.1",
"@types/mocha": "^8.0.3",
"@types/node": "^14.14.28",
"@typescript-eslint/eslint-plugin": "^4.15.1",
"@typescript-eslint/parser": "^4.15.1",
"assert": "^2.0.0",
"eslint": "^7.19.0",
"eslint-config-prettier": "^7.0.0",
"eslint-plugin-prettier": "^3.2.0",
"gh-pages": "^3.1.0",
"node-fetch": "^2.6.1",
"mocha": "^8.2.1",
"nyc": "^15.1.0",
"prettier": "^2.2.1",
"rollup": "^2.38.2",
"rollup-plugin-dts": "^2.0.0",
"ts-node": "^9.1.0",
"typedoc": "^0.20.25",
"typescript": "^4.1.2"
}
}
+69
View File
@@ -0,0 +1,69 @@
import fs from 'fs'
import dts from 'rollup-plugin-dts'
import typescript from '@rollup/plugin-typescript'
import pkg from './package.json'
const name = pkg.name
const license = fs.readFileSync('LICENSE').toString('utf-8').trim()
const banner = `
/**
* ${name} v${pkg.version}
* ${pkg.homepage}
*
* @license
* ${license.replace(/\n/g, '\n * ')}
*/
`.trim()
const external = Object.keys(pkg.dependencies)
export default [
{
input: 'src/index.ts',
output: {
banner,
file: pkg.main,
format: 'cjs',
sourcemap: true,
},
plugins: [typescript({target: 'es6'})],
external,
onwarn,
},
{
input: 'src/index.ts',
output: {
banner,
file: pkg.module,
format: 'esm',
sourcemap: true,
},
plugins: [typescript({target: 'esnext'})],
external,
onwarn,
},
{
input: 'src/index.ts',
output: {banner, file: pkg.types, format: 'esm'},
onwarn,
plugins: [dts()],
},
]
function onwarn(warning, rollupWarn) {
if (warning.code === 'CIRCULAR_DEPENDENCY') {
// unnecessary warning
return
}
if (
warning.code === 'UNUSED_EXTERNAL_IMPORT' &&
warning.source === 'tslib' &&
warning.names[0] === '__read'
) {
// when using ts with importHelpers: true rollup complains about this
// seems safe to ignore since __read is not actually imported or used anywhere in the resulting bundles
return
}
rollupWarn(warning)
}
+41
View File
@@ -0,0 +1,41 @@
import {Asset, Float64, Int64, Struct, TimePointSec, UInt32, UInt64, UInt8} from '@greymass/eosio'
@Struct.type('powerupstateresource')
export class PowerUpStateResource extends Struct {
@Struct.field('uint8') version!: UInt8
@Struct.field('int64') weight!: Int64
@Struct.field('int64') weight_ratio!: Int64
@Struct.field('int64') assumed_stake_weight!: Int64
@Struct.field('int64') initial_weight_ratio!: Int64
@Struct.field('int64') target_weight_ratio!: Int64
@Struct.field('time_point_sec') initial_timestamp!: TimePointSec
@Struct.field('time_point_sec') target_timestamp!: TimePointSec
@Struct.field('float64') exponent!: Float64
@Struct.field('uint32') decay_secs!: UInt32
@Struct.field('asset') min_price!: Asset
@Struct.field('asset') max_price!: Asset
@Struct.field('int64') utilization!: Int64
@Struct.field('int64') adjusted_utilization!: Int64
@Struct.field('time_point_sec') utilization_timestamp!: TimePointSec
}
@Struct.type('powerupstate')
export class PowerUpState extends Struct {
@Struct.field('uint8') version!: UInt8
@Struct.field(PowerUpStateResource) net!: PowerUpStateResource
@Struct.field(PowerUpStateResource) cpu!: PowerUpStateResource
@Struct.field('uint32') powerup_days!: UInt32
@Struct.field('asset') min_powerup_fee!: Asset
}
@Struct.type('rexstate')
export class REXState extends Struct {
@Struct.field('uint8') version!: UInt8
@Struct.field('asset') total_lent!: Asset
@Struct.field('asset') total_unlent!: Asset
@Struct.field('asset') total_rent!: Asset
@Struct.field('asset') total_lendable!: Asset
@Struct.field('asset') total_rex!: Asset
@Struct.field('asset') namebid_proceeds!: Asset
@Struct.field('uint64') loan_num!: UInt64
}
+53
View File
@@ -0,0 +1,53 @@
import {APIClient} from '@greymass/eosio'
import {PowerUpAPI} from './powerup'
import {REXAPI} from './rex'
interface ResourcesOptions {
api?: APIClient
symbol?: string
url?: string
}
interface SampleUsage {
cpu: number
net: number
}
export class Resources {
static __className = 'Resources'
readonly api: APIClient
// target rex weight at end of any existing transition
rex_target_weight = Math.pow(10, 13)
// ms per day
mspd = 200 * 2 * 60 * 60 * 24
// token precision/symbol
symbol = '4,EOS'
constructor(options: ResourcesOptions) {
if (options.api) {
this.api = options.api
} else if (options.url) {
this.api = new APIClient({url: options.url})
} else {
throw new Error('Missing url or api client')
}
}
v1 = {
powerup: new PowerUpAPI(this),
rex: new REXAPI(this),
}
async getSampledUsage(): Promise<SampleUsage> {
const account = await this.api.v1.chain.get_account('teamgreymass')
return {
cpu: account.cpu_limit.max.value / account.total_resources.cpu_weight.value / 1000,
net: account.net_limit.max.value / account.total_resources.net_weight.value / 1000,
}
}
}
+108
View File
@@ -0,0 +1,108 @@
import {Asset} from '@greymass/eosio'
import {Resources} from '.'
import {PowerUpState} from './abi-types'
export class PowerUpAPI {
constructor(private parent: Resources) {}
async get_state() {
const response = await this.parent.api.v1.chain.get_table_rows({
code: 'eosio',
scope: '',
table: 'powup.state',
type: PowerUpState,
})
return response.rows[0]
}
async get_reserved(resource: string) {
const state = await this.get_state()
const {utilization, weight} = state[resource]
return Number(utilization) / Number(weight)
}
async get_allocated() {
const state = await this.get_state()
const {weight_ratio} = state.cpu
return 1 - Number(weight_ratio) / this.parent.rex_target_weight / 100
}
async get_price_per_ms(ms = 1): Promise<Asset> {
// Retrieve state
const state = await this.get_state()
const allocated = await this.get_allocated()
// Casting EOSIO types to usable formats for JS calculations
let adjusted_utilization = Number(state.cpu.adjusted_utilization)
const decay_secs = Number(state.cpu.decay_secs.value)
const exponent = Number(state.cpu.exponent)
const max_price: number = state.cpu.max_price.value
const min_price: number = state.cpu.min_price.value
const utilization = Number(state.cpu.utilization)
const utilization_timestamp = Number(state.cpu.utilization_timestamp.value)
const weight = Number(state.cpu.weight)
// Milliseconds available per day available in PowerUp (factoring in shift)
const mspdAvailable = this.parent.mspd * allocated
// Percentage to rent
const percentToRent = ms / mspdAvailable
const utilization_increase = weight * percentToRent
// If utilization is less than adjusted, calculate real time value
if (utilization < adjusted_utilization) {
// Create now & adjust JS timestamp to match EOSIO timestamp values
const now: number = Math.floor(Date.now() / 1000)
const diff: number = adjusted_utilization - utilization
let delta: number = Math.floor(
diff * Math.exp(-(now - utilization_timestamp) / decay_secs)
)
delta = Math.min(Math.max(delta, 0), diff) // Clamp the delta
adjusted_utilization = utilization + delta
}
const price_integral_delta = (
start_utilization: number,
end_utilization: number
): number => {
const coefficient = (max_price - min_price) / exponent
const start_u = start_utilization / weight
const end_u = end_utilization / weight
return (
min_price * end_u -
min_price * start_u +
coefficient * Math.pow(end_u, exponent) -
coefficient * Math.pow(start_u, exponent)
)
}
const price_function = (utilization: number): number => {
let price = min_price
const new_exponent = exponent - 1.0
if (new_exponent <= 0.0) {
return max_price
} else {
price += (max_price - min_price) * Math.pow(utilization / weight, new_exponent)
}
return price
}
let fee = 0
let start_utilization: number = utilization
const end_utilization: number = start_utilization + utilization_increase
if (start_utilization < adjusted_utilization) {
fee +=
(price_function(adjusted_utilization) *
Math.min(utilization_increase, adjusted_utilization - start_utilization)) /
weight
start_utilization = adjusted_utilization
}
if (start_utilization < end_utilization) {
fee += price_integral_delta(start_utilization, end_utilization)
}
return Asset.fromUnits(Math.ceil(fee * 10000), this.parent.symbol)
}
}
+42
View File
@@ -0,0 +1,42 @@
import {Asset} from '@greymass/eosio'
import {Resources} from './'
import {REXState} from './abi-types'
export class REXAPI {
constructor(private parent: Resources) {}
async get_state() {
const response = await this.parent.api.v1.chain.get_table_rows({
code: 'eosio',
scope: 'eosio',
table: 'rexpool',
type: REXState,
})
return response.rows[0]
}
async get_reserved() {
const state = await this.get_state()
const {total_lent, total_lendable} = state
return Number(total_lent.units) / Number(total_lendable.units)
}
async get_allocated() {
const state = await this.parent.v1.powerup.get_state()
const {weight_ratio} = state.cpu
return Number(weight_ratio) / this.parent.rex_target_weight / 100
}
async get_price_per_ms(ms = 1): Promise<Asset> {
const allocated = await this.get_allocated()
const state = await this.get_state()
const {cpu} = await this.parent.getSampledUsage()
const totalRent = state.total_rent
const totalUnlent = state.total_unlent
const tokens = 1
const msPerToken = (tokens / (totalRent.value / totalUnlent.value)) * cpu * allocated
return Asset.from((tokens / msPerToken) * ms, this.parent.symbol)
}
}
@@ -0,0 +1,7 @@
{
"rows": [
"00008c06f04621000000fac780e9141803004e79bb7de00000000080c6a47e8d030000a0724e1809000093c83560e8b96e6000000000000000408051010040787d010000000004454f53000000008017b42c0000000004454f5300000000614a250000000000bb0fb1000000000079353d6000311ac01b85000000fac780e91418030038e5edf6810300000080c6a47e8d030000a0724e1809000093c83560e8b96e6000000000000000408051010040787d010000000004454f53000000008017b42c0000000004454f53000000009985cf10000000000a07f0100000000079353d6001000000010000000000000004454f5300000000"
],
"more": false,
"next_key": ""
}
@@ -0,0 +1,7 @@
{
"rows": [
"00ad8f4a884f00000004454f53000000001eafab5b3f00000004454f5300000000053341050000000004454f5300000000cb3ef6e38e00000004454f53000000004855ced3ea9d15000452455800000000000000000000000004454f53000000000419070000000000"
],
"more": false,
"next_key": ""
}
@@ -0,0 +1,185 @@
{
"account_name": "teamgreymass",
"head_block_num": 170942680,
"head_block_time": "2021-03-01T18:47:26.000",
"privileged": false,
"last_code_update": "1970-01-01T00:00:00.000",
"created": "2018-06-10T13:04:15.000",
"core_liquid_balance": "2956.3573 EOS",
"ram_quota": 67988,
"net_weight": 29224,
"cpu_weight": 129514594,
"net_limit": {
"used": 266415,
"available": 3070460,
"max": 3336875
},
"cpu_limit": {
"used": 1047582,
"available": 92233,
"max": 1139815
},
"ram_usage": 16346,
"permissions": [
{
"perm_name": "active",
"parent": "owner",
"required_auth": {
"threshold": 1,
"keys": [
{
"key": "EOS6gqJ7sdPgjHLFLtks9cRPs5qYHa9U3CwK4P2JasTLWKQ9kXZK1",
"weight": 1
}
],
"accounts": [],
"waits": []
}
},
{
"perm_name": "claim",
"parent": "active",
"required_auth": {
"threshold": 1,
"keys": [
{
"key": "EOS6DLD9HxMcwn73U41jjdGsNe9vDFRKB26um6qTAqrtYcJFtED4C",
"weight": 1
}
],
"accounts": [],
"waits": []
}
},
{
"perm_name": "decentium",
"parent": "active",
"required_auth": {
"threshold": 1,
"keys": [
{
"key": "EOS7knG7M5TUEdRv1bkVjTPddVoDQnwS7oEZXAgFk3A4hhocA3eJf",
"weight": 1
}
],
"accounts": [],
"waits": []
}
},
{
"perm_name": "oracle",
"parent": "active",
"required_auth": {
"threshold": 1,
"keys": [
{
"key": "EOS88VqmDmJJ9S23eNqdeWYf2zySxv3ckQrWBKy7EvVRCUuhSU4f3",
"weight": 1
}
],
"accounts": [],
"waits": []
}
},
{
"perm_name": "owner",
"parent": "",
"required_auth": {
"threshold": 1,
"keys": [
{
"key": "EOS8QzGtCea2thiqcTVeXGdyRZpdKYptQznbcWSMj73FD5RgwKN82",
"weight": 1
}
],
"accounts": [],
"waits": []
}
},
{
"perm_name": "producerjson",
"parent": "active",
"required_auth": {
"threshold": 1,
"keys": [
{
"key": "EOS5JCEciUdfXnQmTyj85T98bXTAZZ1g7Nmajseu7ZWB8DrDa6Etp",
"weight": 1
}
],
"accounts": [],
"waits": []
}
},
{
"perm_name": "transfer",
"parent": "active",
"required_auth": {
"threshold": 1,
"keys": [
{
"key": "EOS6AkZZ5YZ6G5eCQGJBAPbkmouEaiSKFkdM289wEMKcf2rnx7mrb",
"weight": 1
},
{
"key": "EOS6RWZ1CmDL4B6LdixuertnzxcRuUDac3NQspJEvMnebGcUwhvfX",
"weight": 1
}
],
"accounts": [],
"waits": []
}
},
{
"perm_name": "vote",
"parent": "active",
"required_auth": {
"threshold": 1,
"keys": [
{
"key": "EOS65NrHPVXaV4voxepQREmYCmnMJm4tAWdxPaK46CbUN1rrVmRzg",
"weight": 1
}
],
"accounts": [],
"waits": []
}
},
{
"perm_name": "voting",
"parent": "active",
"required_auth": {
"threshold": 1,
"keys": [
{
"key": "EOS7pn6P5FftyNAKRfx9VcUzBFMvC4UitNbnoKbfxNe8SShELo2it",
"weight": 1
}
],
"accounts": [],
"waits": []
}
}
],
"total_resources": {
"owner": "teamgreymass",
"net_weight": "2.9224 EOS",
"cpu_weight": "12951.4594 EOS",
"ram_bytes": 66588
},
"self_delegated_bandwidth": null,
"refund_request": null,
"voter_info": {
"owner": "teamgreymass",
"proxy": "greymassvote",
"producers": [],
"staked": 100200,
"last_vote_weight": "243319576912.68286132812500000",
"proxied_vote_weight": "0.00000000000000000",
"is_proxy": 0,
"flags1": 0,
"reserved2": 0,
"reserved3": "0.0000 EOS"
},
"rex_info": null
}
+50
View File
@@ -0,0 +1,50 @@
import 'mocha'
import {strict as assert} from 'assert'
import {join as joinPath} from 'path'
import {APIClient, Name} from '@greymass/eosio'
import {MockProvider} from './utils/mock-provider'
import {Resources} from '../src'
import * as ABIs from '../src/abi-types'
const eos = new APIClient({
provider: new MockProvider(joinPath(__dirname, 'data'), 'https://eos.greymass.com'),
})
const resources = new Resources({api: eos})
suite('powerup', function () {
this.slow(200)
test('v1.powerup.get_state', async function () {
const state = await resources.v1.powerup.get_state()
assert.equal(state instanceof ABIs.PowerUpState, true)
})
test('v1.powerup.get_allocated', async function () {
const cpu = await resources.v1.powerup.get_allocated()
// 12.7957297784418% represented as float
assert.equal(cpu, 0.12909697392435804)
})
test('v1.powerup.get_reserved(cpu)', async function () {
const cpu = await resources.v1.powerup.get_reserved('cpu')
// 0.04985526440273404% represented as float
assert.equal(cpu, 0.0004933312426372583)
})
test('v1.powerup.get_reserved(net)', async function () {
const net = await resources.v1.powerup.get_reserved('net')
// 0.0017273973739949453% represented as float
assert.equal(net, 0.000017099101893048595)
})
test('v1.powerup.get_price_per_ms(1)', async function () {
const price = await resources.v1.powerup.get_price_per_ms()
assert.equal(String(price), '0.0006 EOS')
assert.equal(price.value, 0.0006)
assert.equal(Number(price.units), 6)
})
test('v1.powerup.get_price_per_ms(1000)', async function () {
const price = await resources.v1.powerup.get_price_per_ms(1000)
assert.equal(String(price), '0.5702 EOS')
assert.equal(price.value, 0.5702)
assert.equal(Number(price.units), 5702)
})
})
+45
View File
@@ -0,0 +1,45 @@
import 'mocha'
import {strict as assert} from 'assert'
import {join as joinPath} from 'path'
import {APIClient, Name} from '@greymass/eosio'
import {MockProvider} from './utils/mock-provider'
import {Resources} from '../src'
import * as ABIs from '../src/abi-types'
const eos = new APIClient({
provider: new MockProvider(joinPath(__dirname, 'data'), 'https://eos.greymass.com'),
})
const resources = new Resources({api: eos})
suite('rex', function () {
this.slow(200)
test('v1.rex.get_state', async function () {
const state = await resources.v1.rex.get_state()
assert.equal(state instanceof ABIs.REXState, true)
})
test('v1.rex.get_allocated', async function () {
const cpu = await resources.v1.rex.get_allocated()
// 0.872042702215582% represented as float
assert.equal(cpu, 0.870903026075642)
})
test('v1.rex.get_reserved', async function () {
const reserved = await resources.v1.rex.get_reserved()
// 55.66151698897704% represented as float
assert.equal(reserved, 0.5565968415413414)
})
test('v1.rex.get_price_per_ms(1)', async function () {
const price = await resources.v1.rex.get_price_per_ms()
assert.equal(String(price), '0.0042 EOS')
assert.equal(price.value, 0.0042)
assert.equal(Number(price.units), 42)
})
test('v1.rex.get_price_per_ms(1000)', async function () {
const price = await resources.v1.rex.get_price_per_ms(1000)
assert.equal(String(price), '4.2269 EOS')
assert.equal(price.value, 4.2269)
assert.equal(Number(price.units), 42269)
})
})
+9
View File
@@ -0,0 +1,9 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"isolatedModules": false,
"module": "commonjs",
"target": "es6",
},
"include": ["./**/*.ts"]
}
+51
View File
@@ -0,0 +1,51 @@
import fetch from 'node-fetch'
import {join as joinPath} from 'path'
import {promisify} from 'util'
import {readFile as _readFile, writeFile as _writeFile} from 'fs'
const readFile = promisify(_readFile)
const writeFile = promisify(_writeFile)
import {APIProvider, Bytes, Checksum160, FetchProvider} from '@greymass/eosio'
export class MockProvider implements APIProvider {
recordProvider = new FetchProvider(this.api, {fetch})
constructor(private dir: string, private api: string = 'https://jungle3.greymass.com') {}
getFilename(path: string, params?: unknown) {
const digest = Checksum160.hash(
Bytes.from(this.api + path + (params ? JSON.stringify(params) : ''), 'utf8')
).hexString
return joinPath(this.dir, digest + '.json')
}
async getExisting(filename: string) {
try {
const data = await readFile(filename)
return JSON.parse(data.toString('utf8'))
} catch (error) {
if (error.code !== 'ENOENT') {
throw error
}
}
}
async call(path: string, params?: unknown) {
const filename = this.getFilename(path, params)
if (process.env['MOCK_RECORD'] !== 'overwrite') {
const existing = await this.getExisting(filename)
if (existing) {
return existing
}
}
if (process.env['MOCK_RECORD']) {
const response = await this.recordProvider.call(path, params)
const json = JSON.stringify(response, undefined, 4)
await writeFile(filename, json)
return response
} else {
throw new Error(`No data for ${path}`)
}
}
}
+17
View File
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"downlevelIteration": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"importHelpers": true,
"isolatedModules": true,
"lib": ["esnext"],
"module": "esnext",
"moduleResolution": "node",
"noImplicitAny": false,
"sourceMap": true,
"strict": true,
"target": "esnext"
},
"include": ["src/**/*"]
}
+2765
View File
File diff suppressed because it is too large Load Diff