Skip to content

Commit d58f502

Browse files
dominiciampogo
authored andcommitted
sdk/node: use BigNumber for serializing amounts
Any numbers longer than 15 digits will be deserialized into `BigNumber` objects (from BigNumber.js). This represents a breaking change for anyone passing numbers that large, but also numbers that larger were losing precision under the current system. Closes #3541 Author: Dominic Dagradi <ddagradi@gmail.com> Date: Thu May 3 10:25:49 2018 -0700 upstream:22b59ddd385ab26623d55a1f2d4884cb92fd5390
1 parent bd2406b commit d58f502

7 files changed

Lines changed: 71 additions & 14 deletions

File tree

package-lock.json

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"ajv": "^6.4.0",
2525
"btoa": "^1.1.2",
2626
"fetch-ponyfill": "^3.0.2",
27+
"json-bigint": "^0.2.3",
2728
"uuid": "~3.0.0"
2829
},
2930
"devDependencies": {

src/@types/json-bigint/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
declare module 'json-bigint'

src/api/transactions.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { BigNumber } from 'bignumber.js'
12
import { Client } from '../client'
23
import { Page } from '../page'
34
import { Query } from '../query'
@@ -49,15 +50,15 @@ export class TransactionBuilder {
4950
*
5051
* @param {Object} params - Action parameters.
5152
* @param {String} params.flavorId - ID of flavor to be issued.
52-
* @param {String} params.amount - Amount of the flavor to be issued.
53+
* @param {Number|BigNumber} params.amount - Amount of the flavor to be issued.
5354
* @param {String} params.destinationAccountId - Account ID specifying the
5455
* account controlling the flavor.
5556
* @param {Object} params.tokenTags - Tags to add to the receiving tokens.
5657
* @param {Object} params.actionTags - Tags to add to the action.
5758
*/
5859
public issue(params: {
5960
flavorId: string
60-
amount: number
61+
amount: number | BigNumber
6162
destinationAccountId: string
6263
tokenTags?: object
6364
actionTags?: object
@@ -73,15 +74,15 @@ export class TransactionBuilder {
7374
* @param {String} params.sourceAccountId - Account ID specifying the account
7475
* controlling the flavor.
7576
* @param {String} params.flavorId - ID of flavor to be retired.
76-
* @param {Number} params.amount - Amount of the flavor to be retired.
77+
* @param {Number|BigNumber} params.amount - Amount of the flavor to be retired.
7778
* @param {String} params.filter - Token filter string, see {@link https://dashboard.seq.com/docs/filters}.
7879
* @param {Array<String|Number>} params.filterParams - Parameter values for filter string (if needed).
7980
* @param {Object} params.actionTags - Tags to add to the action.
8081
*/
8182
public retire(params: {
8283
sourceAccountId: string
8384
flavorId: string
84-
amount: number
85+
amount: number | BigNumber
8586
filter?: string
8687
filterParams?: object
8788
actionTags?: object
@@ -98,7 +99,7 @@ export class TransactionBuilder {
9899
* @param {String} params.sourceAccountId - Account ID specifying the account
99100
* controlling the flavor.
100101
* @param {String} params.flavorId - ID of flavor to be transferred.
101-
* @param {Integer} params.amount - Amount of the flavor to be transferred.
102+
* @param {Number|BigNumber} params.amount - Amount of the flavor to be transferred.
102103
* @param {String} params.destinationAccountId - Account ID specifying the
103104
* account controlling the flavor.
104105
* @param {String} params.filter - Token filter string, see {@link https://dashboard.seq.com/docs/filters}.
@@ -109,7 +110,7 @@ export class TransactionBuilder {
109110
public transfer(params: {
110111
sourceAccountId: string
111112
flavorId: string
112-
amount: number
113+
amount: number | BigNumber
113114
destinationAccountId: string
114115
filter?: string
115116
filterParams?: object

src/connection.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
// use the ponyfill for unsupported browsers.
44
import * as uuid from 'uuid'
55

6-
// some ugly business to get the right types for fetch while still polyfilling
7-
const fetch: typeof window.fetch = require('fetch-ponyfill')().fetch
8-
const version: string = require('../package.json').version
96
import { readFileSync } from 'fs'
107
import { Agent } from 'https'
118
import { errors } from './errors'
129

1310
const crypto = require('crypto')
11+
// some ugly business to get the right types for fetch while still polyfilling
12+
const fetch: typeof window.fetch = require('fetch-ponyfill')().fetch
13+
const JSONbig = require('json-bigint')
14+
const version: string = require('../package.json').version
1415

1516
const userJsonAttributes = [
1617
'account_tags',
@@ -207,7 +208,7 @@ export class Connection {
207208
const req: any = {
208209
method: 'POST',
209210
headers,
210-
body: JSON.stringify(snakeBody),
211+
body: JSONbig.stringify(snakeBody),
211212
}
212213

213214
if (this.agent) {
@@ -246,7 +247,8 @@ export class Connection {
246247

247248
let body
248249
try {
249-
body = await resp.json()
250+
const text = await resp.text()
251+
body = JSONbig.parse(text)
250252
body = filterLegacyData(body)
251253
} catch {
252254
throw new errors.JsonError(resp)

src/validate.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,9 @@ ajv.addSchema(
7272
{
7373
properties: {
7474
actionTags: { type: 'object' },
75-
amount: { type: 'number' },
75+
amount: {
76+
anyOf: [{ type: 'number' }, { type: 'object' }],
77+
},
7678
destinationAccountId: { type: 'string' },
7779
filter: { type: 'string' },
7880
filterParams: { type: 'array' },
@@ -89,7 +91,9 @@ ajv.addSchema(
8991
{
9092
properties: {
9193
actionTags: { type: 'object' },
92-
amount: { type: 'number' },
94+
amount: {
95+
anyOf: [{ type: 'number' }, { type: 'object' }],
96+
},
9397
destinationAccountId: { type: 'string' },
9498
flavorId: { type: 'string' },
9599
tokenTags: { type: 'object' },
@@ -103,7 +107,9 @@ ajv.addSchema(
103107
{
104108
properties: {
105109
actionTags: { type: 'object' },
106-
amount: { type: 'number' },
110+
amount: {
111+
anyOf: [{ type: 'number' }, { type: 'object' }],
112+
},
107113
filter: { type: 'string' },
108114
filterParams: { type: 'array' },
109115
flavorId: { type: 'string' },

test/transactions.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import * as uuid from 'uuid'
99
chai.use(chaiAsPromised)
1010
const expect = chai.expect
1111

12+
import { BigNumber } from 'bignumber.js'
1213
import { testHelpers } from './testHelpers'
1314

1415
const {
@@ -73,6 +74,38 @@ describe('Transaction', () => {
7374
assert.deepEqual(items[0].tags, actionTags)
7475
assert.deepEqual(items[0].type, 'issue')
7576
})
77+
78+
it('handles large numbers', async () => {
79+
const gold = await createFlavor('gold')
80+
const alice = await createAccount('alice')
81+
const amount = '9223372036854775807'
82+
83+
const tx = await client.transactions.transact(builder => {
84+
builder.issue({
85+
flavorId: gold.id,
86+
amount: new BigNumber(amount),
87+
destinationAccountId: alice.id,
88+
})
89+
})
90+
91+
assert.equal(amount, tx.actions[0].amount.toString())
92+
})
93+
94+
it('throws an error at the amount boundary', async () => {
95+
const gold = await createFlavor('gold')
96+
const alice = await createAccount('alice')
97+
const amount = '9223372036854775808'
98+
99+
expect(
100+
client.transactions.transact(builder => {
101+
builder.issue({
102+
flavorId: gold.id,
103+
amount: new BigNumber(amount),
104+
destinationAccountId: alice.id,
105+
})
106+
})
107+
).to.be.rejectedWith('SEQ706')
108+
})
76109
})
77110

78111
describe('Transfer', () => {

0 commit comments

Comments
 (0)