/
utils.ts
189 lines (169 loc) · 5.09 KB
/
utils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
import * as _ from 'lodash'
import BigNumber from 'bignumber.js'
import {deriveKeypair} from 'ripple-keypairs'
import {Amount, RippledAmount} from './types/objects'
import {ValidationError} from './errors'
function isValidSecret(secret: string): boolean {
try {
deriveKeypair(secret)
return true
} catch (err) {
return false
}
}
function dropsToXrp(drops: BigNumber.Value): string {
if (typeof drops === 'string') {
if (!drops.match(/^-?[0-9]*\.?[0-9]*$/)) {
throw new ValidationError(
`dropsToXrp: invalid value '${drops}',` +
` should be a number matching (^-?[0-9]*\\.?[0-9]*$).`
)
} else if (drops === '.') {
throw new ValidationError(
`dropsToXrp: invalid value '${drops}',` +
` should be a BigNumber or string-encoded number.`
)
}
}
// Converting to BigNumber and then back to string should remove any
// decimal point followed by zeros, e.g. '1.00'.
// Important: specify base 10 to avoid exponential notation, e.g. '1e-7'.
drops = new BigNumber(drops).toString(10)
// drops are only whole units
if (drops.includes('.')) {
throw new ValidationError(
`dropsToXrp: value '${drops}' has` + ` too many decimal places.`
)
}
// This should never happen; the value has already been
// validated above. This just ensures BigNumber did not do
// something unexpected.
if (!drops.match(/^-?[0-9]+$/)) {
throw new ValidationError(
`dropsToXrp: failed sanity check -` +
` value '${drops}',` +
` does not match (^-?[0-9]+$).`
)
}
return new BigNumber(drops).dividedBy(1000000.0).toString(10)
}
function xrpToDrops(xrp: BigNumber.Value): string {
if (typeof xrp === 'string') {
if (!xrp.match(/^-?[0-9]*\.?[0-9]*$/)) {
throw new ValidationError(
`xrpToDrops: invalid value '${xrp}',` +
` should be a number matching (^-?[0-9]*\\.?[0-9]*$).`
)
} else if (xrp === '.') {
throw new ValidationError(
`xrpToDrops: invalid value '${xrp}',` +
` should be a BigNumber or string-encoded number.`
)
}
}
// Important: specify base 10 to avoid exponential notation, e.g. '1e-7'.
xrp = new BigNumber(xrp).toString(10)
// This should never happen; the value has already been
// validated above. This just ensures BigNumber did not do
// something unexpected.
if (!xrp.match(/^-?[0-9.]+$/)) {
throw new ValidationError(
`xrpToDrops: failed sanity check -` +
` value '${xrp}',` +
` does not match (^-?[0-9.]+$).`
)
}
const components = xrp.split('.')
if (components.length > 2) {
throw new ValidationError(
`xrpToDrops: failed sanity check -` +
` value '${xrp}' has` +
` too many decimal points.`
)
}
const fraction = components[1] || '0'
if (fraction.length > 6) {
throw new ValidationError(
`xrpToDrops: value '${xrp}' has` + ` too many decimal places.`
)
}
return new BigNumber(xrp)
.times(1000000.0)
.integerValue(BigNumber.ROUND_FLOOR)
.toString(10)
}
function toRippledAmount(amount: Amount): RippledAmount {
if (amount.currency === 'XRP') {
return xrpToDrops(amount.value)
}
if (amount.currency === 'drops') {
return amount.value
}
return {
currency: amount.currency,
issuer: amount.counterparty
? amount.counterparty
: amount.issuer
? amount.issuer
: undefined,
value: amount.value
}
}
function convertKeysFromSnakeCaseToCamelCase(obj: any): any {
if (typeof obj === 'object') {
const accumulator = Array.isArray(obj) ? [] : {}
let newKey
return _.reduce(
obj,
(result, value, key) => {
newKey = key
// taking this out of function leads to error in PhantomJS
const FINDSNAKE = /([a-zA-Z]_[a-zA-Z])/g
if (FINDSNAKE.test(key)) {
newKey = key.replace(FINDSNAKE, r => r[0] + r[2].toUpperCase())
}
result[newKey] = convertKeysFromSnakeCaseToCamelCase(value)
return result
},
accumulator
)
}
return obj
}
function removeUndefined<T extends object>(obj: T): T {
return _.omitBy(obj, _.isUndefined) as T
}
/**
* @param {Number} rpepoch (seconds since 1/1/2000 GMT)
* @return {Number} ms since unix epoch
*/
function rippleToUnixTimestamp(rpepoch: number): number {
return (rpepoch + 0x386d4380) * 1000
}
/**
* @param {Number|Date} timestamp (ms since unix epoch)
* @return {Number} seconds since ripple epoch (1/1/2000 GMT)
*/
function unixToRippleTimestamp(timestamp: number): number {
return Math.round(timestamp / 1000) - 0x386d4380
}
function rippleTimeToISO8601(rippleTime: number): string {
return new Date(rippleToUnixTimestamp(rippleTime)).toISOString()
}
/**
* @param {string} iso8601 international standard date format
* @return {number} seconds since ripple epoch (1/1/2000 GMT)
*/
function iso8601ToRippleTime(iso8601: string): number {
return unixToRippleTimestamp(Date.parse(iso8601))
}
export {
dropsToXrp,
xrpToDrops,
toRippledAmount,
convertKeysFromSnakeCaseToCamelCase,
removeUndefined,
rippleTimeToISO8601,
iso8601ToRippleTime,
isValidSecret
}