Skip to content

Commit

Permalink
Merge b94b323 into fa21ebe
Browse files Browse the repository at this point in the history
  • Loading branch information
allenan committed Jul 2, 2020
2 parents fa21ebe + b94b323 commit 0513ec2
Show file tree
Hide file tree
Showing 13 changed files with 219 additions and 30 deletions.
8 changes: 8 additions & 0 deletions packages/http/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,11 @@ await client.elections.get('hash')
const list = await client.elections.list()
const elections = await list.take(10)
```

#### Price Oracle

##### Get the Current Oracle Price

```js
await client.oracle.getCurrentPrice()
```
5 changes: 5 additions & 0 deletions packages/http/src/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Hotspots from './resources/Hotspots'
import Challenges from './resources/Challenges'
import Stats from './resources/Stats'
import Vars from './resources/Vars'
import Oracle from './resources/Oracle'
import PendingTransactions from './resources/PendingTransactions'
import type Account from './models/Account'
import type Block from './models/Block'
Expand Down Expand Up @@ -92,6 +93,10 @@ export default class Client {
return new Vars(this)
}

public get oracle(): Oracle {
return new Oracle(this)
}

async get(path: string, params: Object = {}) {
const query = qs.stringify(params)
const url = query.length > 0 ? [path, query].join('?') : path
Expand Down
2 changes: 1 addition & 1 deletion packages/http/src/models/Account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export default class Account {
this.secBalance = toBalance(account.sec_balance, CurrencyType.security)
this.nonce = account.nonce
this.dcNonce = account.dc_nonce
this.dcBalance = toBalance(account.dc_balance, CurrencyType.data_credit)
this.dcBalance = toBalance(account.dc_balance, CurrencyType.dataCredit)
this.block = account.block
this.balance = toBalance(account.balance, CurrencyType.default)
this.address = account.address
Expand Down
69 changes: 59 additions & 10 deletions packages/http/src/models/Balance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,33 +15,82 @@ BigNumber.config({
FORMAT,
})

const DC_TO_USD_MULTIPLIER = 0.00001

export default class Balance {
public integerBalance: number
public floatBalance: number
public type: CurrencyType
private bigBalance: BigNumber
public bigBalance: BigNumber

constructor(integerBalance: number | undefined, type: CurrencyType) {
this.integerBalance = integerBalance || 0
this.bigBalance = new BigNumber(this.integerBalance)
this.floatBalance = this.bigBalance.times(type.coefficient).toNumber()
this.bigBalance = new BigNumber(this.integerBalance).times(type.coefficient)
this.floatBalance = this.bigBalance.toNumber()
this.type = type
}

toString(maxDecimalPlaces?: number): string {
const number = new BigNumber(this.floatBalance)
let numberString = number.toFormat(maxDecimalPlaces)
let numberString = this.bigBalance.toFormat(maxDecimalPlaces)
// if it's an integer, just show the integer
if (parseInt(numberString.split('.')[1]) === 0) {
numberString = numberString.split('.')[0]
}
// if the rounded amount is 0, then show the full amount
if (numberString === '0') {
numberString = number.toFormat()
numberString = this.bigBalance.toFormat()
}
return [numberString, this.type.ticker].join(' ')
}

toDefault(oraclePrice?: Balance): Balance {
if (this.type.ticker === CurrencyType.default.ticker) return this
if (!oraclePrice) throw 'oracle price required'
return new Balance(
this.toUsd()
.bigBalance.dividedBy(oraclePrice.bigBalance)
.dividedBy(CurrencyType.default.coefficient)
.toNumber(),
CurrencyType.default,
)
}

toUsd(oraclePrice?: Balance): Balance {
switch (this.type.ticker) {
case CurrencyType.dataCredit.ticker:
return new Balance(
this.bigBalance
.times(DC_TO_USD_MULTIPLIER)
.dividedBy(CurrencyType.usd.coefficient)
.toNumber(),
CurrencyType.usd,
)
case CurrencyType.default.ticker:
if (!oraclePrice) throw 'oracle price required'
return new Balance(
this.bigBalance
.times(oraclePrice.bigBalance)
.dividedBy(CurrencyType.usd.coefficient)
.toNumber(),
CurrencyType.usd,
)
default:
return this
}
}

toDataCredit(oraclePrice?: Balance): Balance {
switch (this.type.ticker) {
case CurrencyType.usd.ticker:
return new Balance(
this.bigBalance.dividedBy(DC_TO_USD_MULTIPLIER).toNumber(),
CurrencyType.dataCredit,
)
case CurrencyType.default.ticker:
if (!oraclePrice) throw 'oracle price required'
return this.toUsd(oraclePrice).toDataCredit()
default:
return this
}
return [
numberString,
this.type.ticker,
].join(' ')
}
}
7 changes: 6 additions & 1 deletion packages/http/src/models/CurrencyType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import BigNumber from 'bignumber.js'
const TICKER = 'HNT'
const DC_TICKER = 'DC'
const SEC_TICKER = 'STO'
const USD = 'USD'

export default class CurrencyType {
public ticker: string
Expand All @@ -16,11 +17,15 @@ export default class CurrencyType {
return new CurrencyType(TICKER, 0.00000001)
}

static get data_credit(): CurrencyType {
static get dataCredit(): CurrencyType {
return new CurrencyType(DC_TICKER, 1)
}

static get security(): CurrencyType {
return new CurrencyType(SEC_TICKER, 0.00000001)
}

static get usd(): CurrencyType {
return new CurrencyType(USD, 0.00000001)
}
}
4 changes: 2 additions & 2 deletions packages/http/src/models/Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,11 @@ export type AnyTransaction = PaymentV1 | RewardsV1 | AddGatewayV1 | AssertLocati

function prepareTxn(txn: any) {
if (txn.fee) {
txn.fee = new Balance(txn.fee, CurrencyType.data_credit)
txn.fee = new Balance(txn.fee, CurrencyType.dataCredit)
}

if (txn.stakingFee) {
txn.stakingFee = new Balance(txn.stakingFee, CurrencyType.data_credit)
txn.stakingFee = new Balance(txn.stakingFee, CurrencyType.dataCredit)
}

return camelcaseKeys(txn)
Expand Down
85 changes: 73 additions & 12 deletions packages/http/src/models/__tests__/Balance.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
import Balance from '../Balance'
import CurrencyType from '../CurrencyType'

describe('floatBalance', () => {
it('returns a float based on the currency type', () => {
const balance = new Balance(123456789012, CurrencyType.default)
expect(balance.floatBalance).toBe(1234.56789012)
})
})

describe('integerBalance', () => {
it('returns the integer balance', () => {
const balance = new Balance(123456789012, CurrencyType.default)
expect(balance.integerBalance).toBe(123456789012)
})
})

describe('toString', () => {
it('handles numbers with a large amount of decimal places', () => {
const balance = new Balance(1, CurrencyType.default)
Expand All @@ -10,39 +24,86 @@ describe('toString', () => {
it('returns a string representation of the balance', () => {
const balance = new Balance(10000, CurrencyType.default)
expect(balance.toString()).toBe('0.0001 HNT')
});
})

it('does not round the string by default', () => {
const balance = new Balance(1000000001, CurrencyType.default)
expect(balance.toString()).toBe('10.00000001 HNT')
});
})

it('optionally allows the string to be rounded', () => {
const balance = new Balance(1001000001, CurrencyType.default)
expect(balance.toString(2)).toBe('10.01 HNT')
});
})

it('returns an integer if the rounded decimal places are 0', () => {
const balance = new Balance(1000000001, CurrencyType.default)
expect(balance.toString(2)).toBe('10 HNT')
});
})

it('does not round if the result would be 0 HNT', () => {
const balance = new Balance(10000, CurrencyType.default)
expect(balance.toString(2)).toBe('0.0001 HNT')
})
})

describe('floatBalance', () => {
it('returns a float based on the currency type', () => {
const balance = new Balance(123456789012, CurrencyType.default)
expect(balance.floatBalance).toBe(1234.56789012)
describe('toUsd', () => {
it('converts a dc balance to a usd balance', () => {
const dcBalance = new Balance(10 * 100000, CurrencyType.dataCredit)
const usdBalance = dcBalance.toUsd()
expect(usdBalance.toString(2)).toBe('10 USD')
})

it('converts an hnt balance to a usd balance', () => {
const hntBalance = new Balance(10 * 100000000, CurrencyType.default)
const oraclePrice = new Balance(0.45 * 100000000, CurrencyType.usd)
const usdBalance = hntBalance.toUsd(oraclePrice)
expect(usdBalance.toString(2)).toBe('4.50 USD')
})

it('returns itself if called on a usd balance', () => {
const usdBalance = new Balance(10 * 100000000, CurrencyType.usd)
expect(usdBalance.toUsd().toString()).toBe('10 USD')
})
})

describe('integerBalance', () => {
it('returns the integer balance', () => {
const balance = new Balance(123456789012, CurrencyType.default)
expect(balance.integerBalance).toBe(123456789012)
describe('toDefault', () => {
it('converts a dc balance to an hnt balance', () => {
const dcBalance = new Balance(10 * 100000, CurrencyType.dataCredit)
const oraclePrice = new Balance(0.45 * 100000000, CurrencyType.usd)
const hntBalance = dcBalance.toDefault(oraclePrice)
expect(hntBalance.toString(2)).toBe('22.22 HNT')
})

it('converts a usd balance to an hnt balance', () => {
const usdBalance = new Balance(10 * 100000000, CurrencyType.usd)
const oraclePrice = new Balance(0.45 * 100000000, CurrencyType.usd)
const hntBalance = usdBalance.toDefault(oraclePrice)
expect(hntBalance.toString(2)).toBe('22.22 HNT')
})

it('returns itself if called on an hnt balance', () => {
const hntBalance = new Balance(10 * 100000000, CurrencyType.default)
expect(hntBalance.toDefault().toString()).toBe('10 HNT')
})
})

describe('toDataCredit', () => {
it('converts a usd balance to a dc balance', () => {
const usdBalance = new Balance(10 * 100000000, CurrencyType.usd)
const dcBalance = usdBalance.toDataCredit()
expect(dcBalance.toString()).toBe('1,000,000 DC')
})

it('converts an hnt balance to a dc balance', () => {
const hntBalance = new Balance(10 * 100000000, CurrencyType.default)
const oraclePrice = new Balance(0.45 * 100000000, CurrencyType.usd)
const dcBalance = hntBalance.toDataCredit(oraclePrice)
expect(dcBalance.toString()).toBe('450,000 DC')
})

it('returns itself if called on a dc balance', () => {
const dcBalance = new Balance(10 * 100000, CurrencyType.dataCredit)
expect(dcBalance.toDataCredit().toString()).toBe('1,000,000 DC')
})
})
18 changes: 18 additions & 0 deletions packages/http/src/resources/Oracle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type Client from '../Client'
import Balance from '../models/Balance'
import CurrencyType from '../models/CurrencyType'

export default class Oracle {
private client!: Client

constructor(client: Client) {
this.client = client
}

async getCurrentPrice(): Promise<any> {
const url = `/oracle/prices/current`
const { data: { data: { price } } } = await this.client.get(url)

return new Balance(price, CurrencyType.usd)
}
}
19 changes: 19 additions & 0 deletions packages/http/src/resources/__tests__/Oracle.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import nock from 'nock'
import Client from '../../Client'

describe('current oracle price', () => {
nock('https://api.helium.io')
.get('/v1/oracle/prices/current')
.reply(200, {
data: {
price: 42040700,
block: 385010,
},
})

it('gets the current price', async () => {
const client = new Client()
const price = await client.oracle.getCurrentPrice()
expect(price.integerBalance).toBe(42040700)
})
})
3 changes: 2 additions & 1 deletion packages/transactions/src/AddGatewayV1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ export default class AddGatewayV1 extends Transaction {
this.payerSignature && !forSigning
? toUint8Array(this.payerSignature)
: null,
stakingFee: this.stakingFee,
stakingFee:
this.stakingFee && this.stakingFee > 0 ? this.stakingFee : null,
fee: this.fee && this.fee > 0 ? this.fee : null,
})
}
Expand Down
3 changes: 2 additions & 1 deletion packages/transactions/src/AssertLocationV1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ export default class AssertLocationV1 extends Transaction {
this.payerSignature && !forSigning
? toUint8Array(this.payerSignature)
: null,
stakingFee: this.stakingFee,
stakingFee:
this.stakingFee && this.stakingFee > 0 ? this.stakingFee : null,
fee: this.fee && this.fee > 0 ? this.fee : null,
location: this.location,
nonce: this.nonce,
Expand Down
12 changes: 11 additions & 1 deletion packages/transactions/src/__tests__/AddGatewayV1.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ Transaction.config({
stakingFeeTxnAssertLocationV1: 10 * 100000,
})

const addGatewayFixture = async () => {
const addGatewayFixture = async (payer = false) => {
const { bob, alice } = await usersFixture()

return new AddGatewayV1({
owner: bob.address,
gateway: alice.address,
payer: payer ? bob.address : undefined,
})
}

Expand All @@ -30,6 +31,15 @@ test('create an add gateway txn', async () => {
expect(addGw.stakingFee).toBe(4000000)
})

test('create an add gateway txn with payer', async () => {
const addGw = await addGatewayFixture(true)
expect(addGw.owner?.b58).toBe(bobB58)
expect(addGw.gateway?.b58).toBe(aliceB58)
expect(addGw.payer?.b58).toBe(bobB58)
expect(addGw.fee).toBe(65000)
expect(addGw.stakingFee).toBe(4000000)
})

describe('serialize', () => {
it('serializes an add gw txn', async () => {
const txn = await addGatewayFixture()
Expand Down
Loading

0 comments on commit 0513ec2

Please sign in to comment.