@com-chain/jsc3l 2.0.1-rc.2 → 2.0.1-rc.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -2
- package/build/bcRead.d.ts +8 -10
- package/build/bcRead.js +162 -49
- package/build/bcRead.js.map +1 -1
- package/build/bcTransaction.d.ts +8 -2
- package/build/bcTransaction.js +141 -108
- package/build/bcTransaction.js.map +1 -1
- package/build/blockies.js.map +1 -1
- package/build/config/transactions.d.ts +44 -0
- package/build/config/transactions.js +29 -0
- package/build/config/transactions.js.map +1 -0
- package/build/connection.d.ts +1 -1
- package/build/connection.js +44 -13
- package/build/connection.js.map +1 -1
- package/build/customization.js +13 -6
- package/build/customization.js.map +1 -1
- package/build/ethereum/cipher.js +3 -3
- package/build/ethereum/cipher.js.map +1 -1
- package/build/ethereum/ethFuncs.js +37 -1
- package/build/ethereum/ethFuncs.js.map +1 -1
- package/build/ethereum/etherUnits.js +5 -5
- package/build/ethereum/etherUnits.js.map +1 -1
- package/build/ethereum/myetherwallet.d.ts +1 -2
- package/build/ethereum/myetherwallet.js.map +1 -1
- package/build/ethereum/uiFuncs.d.ts +1 -3
- package/build/ethereum/uiFuncs.js +13 -23
- package/build/ethereum/uiFuncs.js.map +1 -1
- package/build/exception.d.ts +8 -0
- package/build/exception.js +14 -0
- package/build/exception.js.map +1 -0
- package/build/index.d.ts +8 -5
- package/build/index.js +38 -19
- package/build/index.js.map +1 -1
- package/build/memo.js +6 -3
- package/build/memo.js.map +1 -1
- package/build/qr.d.ts +31 -6
- package/build/qr.js +32 -12
- package/build/qr.js.map +1 -1
- package/build/rest/ajaxReq.d.ts +3 -10
- package/build/rest/ajaxReq.js +16 -33
- package/build/rest/ajaxReq.js.map +1 -1
- package/build/rest/http.js +6 -1
- package/build/rest/http.js.map +1 -1
- package/build/rest/serializer.js.map +1 -1
- package/build/type.d.ts +4 -4
- package/build/utils.d.ts +15 -0
- package/build/utils.js +270 -0
- package/build/utils.js.map +1 -0
- package/build/wallet.d.ts +6 -8
- package/build/wallet.js +35 -39
- package/build/wallet.js.map +1 -1
- package/package.json +10 -5
- package/skip-prod-transpilation.ts +48 -0
- package/src/bcRead.ts +184 -63
- package/src/bcTransaction.ts +143 -114
- package/src/config/transactions.ts +30 -0
- package/src/connection.ts +42 -9
- package/src/customization.ts +17 -2
- package/src/ethereum/ethFuncs.ts +42 -1
- package/src/ethereum/etherUnits.ts +5 -5
- package/src/ethereum/uiFuncs.ts +16 -25
- package/src/exception.ts +16 -0
- package/src/index.ts +53 -27
- package/src/qr.ts +86 -12
- package/src/rest/ajaxReq.ts +18 -40
- package/src/utils.ts +300 -0
- package/src/wallet.ts +37 -48
- package/tests/environment.ts +27 -0
- package/tests/setup.ts +12 -0
- package/tsconfig.json +8 -2
- package/vitest.config.ts +14 -0
package/src/bcTransaction.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { getNakedAddress, padLeft, encodeNumber } from './ethereum/ethFuncs'
|
|
3
3
|
import { generateTx } from './ethereum/uiFuncs'
|
|
4
4
|
import AjaxReq from './rest/ajaxReq'
|
|
5
|
-
|
|
5
|
+
import * as utils from './utils'
|
|
6
6
|
|
|
7
7
|
function roundCent (strAmount:string) {
|
|
8
8
|
return Math.round(100 * parseFloat(strAmount))
|
|
@@ -13,20 +13,26 @@ function typeConv (label: string): (x:any) => (string | number) {
|
|
|
13
13
|
if (label.endsWith('Address')) {
|
|
14
14
|
return (a) => padLeft(getNakedAddress(a), 64)
|
|
15
15
|
}
|
|
16
|
+
if (label === 'int') {
|
|
17
|
+
return (nb) => encodeNumber(parseInt(nb, 10))
|
|
18
|
+
}
|
|
16
19
|
if (label.startsWith('limit') || label === 'amount') {
|
|
17
20
|
return (nb) => encodeNumber(roundCent(nb))
|
|
18
21
|
}
|
|
19
|
-
if (label.endsWith('Status') || label.endsWith('Type')) {
|
|
22
|
+
if (label.endsWith('Status') || label.endsWith('Type') || label.endsWith('Int')) {
|
|
20
23
|
return (nb) => encodeNumber(nb)
|
|
21
24
|
}
|
|
25
|
+
if (label.endsWith('Hex')) {
|
|
26
|
+
return (s) => s.padEnd(128, '0')
|
|
27
|
+
}
|
|
22
28
|
if (label === 'status') {
|
|
23
|
-
return (nb) => (parseInt(nb, 10) === 0 ? 0 : 1)
|
|
29
|
+
return (nb) => encodeNumber(parseInt(nb, 10) === 0 ? 0 : 1)
|
|
24
30
|
}
|
|
25
31
|
throw new Error(`Unexpected label '${label}' in FnDefs.`)
|
|
26
32
|
}
|
|
27
33
|
|
|
28
34
|
|
|
29
|
-
export
|
|
35
|
+
export abstract class BcTransactionAbstract {
|
|
30
36
|
|
|
31
37
|
abstract ajaxReq: AjaxReq
|
|
32
38
|
abstract contracts: string[]
|
|
@@ -34,124 +40,147 @@ export default abstract class BcTransactionAbstract {
|
|
|
34
40
|
// //////////////////////////////////////////////////////////////////////////
|
|
35
41
|
// CM VS Nant Handling
|
|
36
42
|
|
|
37
|
-
getSplitting (
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
res = res - cmVal
|
|
51
|
-
cmVal = 0
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (nantVal > 0) {
|
|
56
|
-
if (nantVal >= res) {
|
|
57
|
-
nant = res
|
|
58
|
-
res = 0
|
|
59
|
-
} else {
|
|
60
|
-
nant = nantVal
|
|
61
|
-
res = res - nantVal
|
|
62
|
-
// nantVal=0;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (res > 0 && cmVal - parseFloat(cmMinusLim) >= res) {
|
|
67
|
-
cm = cm + res
|
|
68
|
-
res = 0
|
|
43
|
+
static getSplitting (nantBal, cmBal, cmSrcMin, amount) {
|
|
44
|
+
console.warn("Obsolete usage of `BcTransactionAbstract.getSplitting()'," +
|
|
45
|
+
" prefer `jsc3l.utils.getSplitting()'")
|
|
46
|
+
cmBal = parseFloat(cmBal)
|
|
47
|
+
nantBal = parseFloat(nantBal)
|
|
48
|
+
amount = parseFloat(amount)
|
|
49
|
+
cmSrcMin = parseFloat(cmSrcMin)
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
let {nant, cm} = utils.getSplitting(amount, { cm: cmBal, nant: nantBal}, cmSrcMin)
|
|
53
|
+
return { possible: true, nant, cm}
|
|
54
|
+
} catch(e: any) {
|
|
55
|
+
return { possible: false}
|
|
69
56
|
}
|
|
70
|
-
|
|
71
|
-
const possible = res === 0
|
|
72
|
-
return { possible: possible, nant: nant, cm: cm }
|
|
73
57
|
}
|
|
58
|
+
getSplitting = BcTransactionAbstract.getSplitting
|
|
74
59
|
|
|
75
|
-
|
|
60
|
+
}
|
|
76
61
|
|
|
62
|
+
/* @skip-prod-transpilation */
|
|
63
|
+
if (import.meta.vitest) {
|
|
64
|
+
const { it, expect, describe } = import.meta.vitest
|
|
65
|
+
describe('split', () => {
|
|
66
|
+
it('should split 0 to cm: 0, nant: 0', () => {
|
|
67
|
+
expect(BcTransactionAbstract.getSplitting(0, 0, 0, 0))
|
|
68
|
+
.toStrictEqual({ possible: true, nant: 0, cm: 0})
|
|
69
|
+
});
|
|
70
|
+
it('should NOT split 1 with no funds', () => {
|
|
71
|
+
expect(BcTransactionAbstract.getSplitting(0, 0, 0, 1).possible)
|
|
72
|
+
.toBe(false)
|
|
73
|
+
});
|
|
74
|
+
// Only Cm
|
|
75
|
+
it('should split 1 to cm: 1, nant: 0 with bal cm: 2, nant: 0', () => {
|
|
76
|
+
expect(BcTransactionAbstract.getSplitting(0, 2, 0, 1))
|
|
77
|
+
.toStrictEqual({ possible: true, nant: 0, cm: 1})
|
|
78
|
+
});
|
|
79
|
+
it('should split 2 to cm: 2, nant: 0 with bal cm: 2, nant: 0', () => {
|
|
80
|
+
expect(BcTransactionAbstract.getSplitting(0, 2, 0, 2))
|
|
81
|
+
.toStrictEqual({ possible: true, nant: 0, cm: 2})
|
|
82
|
+
});
|
|
83
|
+
it('should NOT split 3 with bal cm: 2, nant: 0', () => {
|
|
84
|
+
expect(BcTransactionAbstract.getSplitting(0, 2, 0, 3).possible)
|
|
85
|
+
.toBe(false)
|
|
86
|
+
});
|
|
87
|
+
// Only Nant
|
|
88
|
+
it('should split 1 to cm: 0, nant: 1 with bal cm: 0, nant: 2', () => {
|
|
89
|
+
expect(BcTransactionAbstract.getSplitting(2, 0, 0, 1))
|
|
90
|
+
.toStrictEqual({ possible: true, nant: 1, cm: 0})
|
|
91
|
+
});
|
|
92
|
+
it('should split 2 to cm: 0, nant: 2 with bal cm: 0, nant: 2', () => {
|
|
93
|
+
expect(BcTransactionAbstract.getSplitting(2, 0, 0, 2))
|
|
94
|
+
.toStrictEqual({ possible: true, nant: 2, cm: 0})
|
|
95
|
+
});
|
|
96
|
+
it('should NOT split 3 with bal cm: 0, nant: 2', () => {
|
|
97
|
+
expect(BcTransactionAbstract.getSplitting(2, 0, 0, 3).possible)
|
|
98
|
+
.toBe(false)
|
|
99
|
+
});
|
|
100
|
+
// Both Nant and Cm
|
|
101
|
+
it('should split 2 to cm: 1, nant: 1 with bal cm: 1, nant: 1', () => {
|
|
102
|
+
expect(BcTransactionAbstract.getSplitting(1, 1, 0, 2))
|
|
103
|
+
.toStrictEqual({ possible: true, nant: 1, cm: 1})
|
|
104
|
+
});
|
|
105
|
+
it('should split 2 to cm: 0, nant: 2 with bal cm: 0, nant: 2 (limSrcCm: -2)', () => {
|
|
106
|
+
expect(BcTransactionAbstract.getSplitting(2, 0, -2, 2))
|
|
107
|
+
.toStrictEqual({ possible: true, nant: 2, cm: 0})
|
|
108
|
+
});
|
|
109
|
+
it('should split 2 to cm: 1, nant: 1 with bal cm: 0, nant: 1 (limSrcCm: -1)', () => {
|
|
110
|
+
expect(BcTransactionAbstract.getSplitting(1, 0, -1, 2))
|
|
111
|
+
.toStrictEqual({ possible: true, nant: 1, cm: 1})
|
|
112
|
+
});
|
|
113
|
+
it('should NOT split 3 with bal cm: 0, nant: 1 (limSrcCm: -1)', () => {
|
|
114
|
+
expect(BcTransactionAbstract.getSplitting(1, 0, -1, 3).possible)
|
|
115
|
+
.toBe(false)
|
|
116
|
+
});
|
|
117
|
+
it('should split 3 to cm: 2, nant: 1 with bal cm: 1, nant: 1 (limSrcCm: -1)', () => {
|
|
118
|
+
expect(BcTransactionAbstract.getSplitting(1, 1, -1, 3))
|
|
119
|
+
.toStrictEqual({ possible: true, nant: 1, cm: 2})
|
|
120
|
+
});
|
|
121
|
+
// Negative cm bal and limSrcCm
|
|
122
|
+
it('should split 1 to cm: -1, nant: 0 with bal cm: -1, nant: 0 (limSrcCm: -2)', () => {
|
|
123
|
+
expect(BcTransactionAbstract.getSplitting(0, -1, -2, 1))
|
|
124
|
+
.toStrictEqual({ possible: true, nant: 0, cm: 1})
|
|
125
|
+
});
|
|
126
|
+
it('should NOT split 2 with bal cm: -1, nant: 0 (limSrcCm: -2)', () => {
|
|
127
|
+
expect(BcTransactionAbstract.getSplitting(0, -1, -2, 2).possible)
|
|
128
|
+
.toBe(false)
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
});
|
|
77
132
|
}
|
|
78
133
|
|
|
134
|
+
export function transactionFactory(transactionDefs: any[], bcTransactionClass: any) {
|
|
79
135
|
|
|
80
|
-
|
|
81
|
-
// First Contract
|
|
82
|
-
{
|
|
83
|
-
setAccountParam: '848b2592:accAddress accStatus accType limitPlus limitMinus',
|
|
84
|
-
pledgeAccount: '6c343eef:accAddress amount *',
|
|
85
|
-
setAllowance: 'd4e12f2e:spenderAddress amount',
|
|
86
|
-
setDelegation: '75741c79:spenderAddress limit',
|
|
87
|
-
setTaxAmount: 'f6f1897d:amount',
|
|
88
|
-
setTaxLegAmount: 'fafaf4c0:amount',
|
|
89
|
-
setTaxAccount: 'd0385b5e:accAddress',
|
|
90
|
-
setOwnerAccount: 'f2fde38b:accAddress',
|
|
91
|
-
setContractStatus: '0x88b8084f:status',
|
|
92
|
-
},
|
|
93
|
-
// Second Contract
|
|
94
|
-
{
|
|
95
|
-
transferNant: 'a5f7c148:toAddress amount *',
|
|
96
|
-
transferCM: '60ca9c4c:toAddress amount *',
|
|
97
|
-
transferOnBehalfNant: '1b6b1ee5:fromAddress toAddress amount D',
|
|
98
|
-
transferOnBehalfCM: '74c421fe:fromAddress toAddress amount D',
|
|
99
|
-
askTransferFrom: '58258353:fromAddress amount',
|
|
100
|
-
askTransferCMFrom: '2ef9ade2:fromAddress amount',
|
|
101
|
-
payRequestNant: '132019f4:toAddress amount *',
|
|
102
|
-
payRequestCM: '1415707c:toAddress amount *',
|
|
103
|
-
rejectRequest: 'af98f757:toAddress',
|
|
104
|
-
dismissAcceptedInfo: 'ccf93c7a:accAddress',
|
|
105
|
-
dismissRejectedInfo: '88759215:accAddress',
|
|
106
|
-
}
|
|
107
|
-
].forEach((contractFnDefs, contractNb) => {
|
|
108
|
-
|
|
109
|
-
for (const fnName in contractFnDefs) {
|
|
110
|
-
|
|
111
|
-
const [fnHash, argStringList] = contractFnDefs[fnName].split(':')
|
|
112
|
-
const argList = argStringList.split(' ')
|
|
113
|
-
let hasAdditionalPostData = false
|
|
114
|
-
let hasDelegate = false
|
|
115
|
-
if (argList.slice(-1)[0] === '*') {
|
|
116
|
-
hasAdditionalPostData = true
|
|
117
|
-
argList.pop()
|
|
118
|
-
} else if (argList.slice(-1)[0] === 'D') {
|
|
119
|
-
hasAdditionalPostData = true
|
|
120
|
-
hasDelegate = true
|
|
121
|
-
argList.pop()
|
|
122
|
-
}
|
|
136
|
+
transactionDefs.forEach((contractFnDefs, contractNb) => {
|
|
123
137
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if (
|
|
134
|
-
|
|
135
|
-
|
|
138
|
+
for (const fnName in contractFnDefs) {
|
|
139
|
+
|
|
140
|
+
const [fnHash, argStringList] = contractFnDefs[fnName].split(':')
|
|
141
|
+
const argList = argStringList.split(' ')
|
|
142
|
+
let hasAdditionalPostData = false
|
|
143
|
+
let hasDelegate = false
|
|
144
|
+
if (argList.slice(-1)[0] === '*') {
|
|
145
|
+
hasAdditionalPostData = true
|
|
146
|
+
argList.pop()
|
|
147
|
+
} else if (argList.slice(-1)[0] === 'D') {
|
|
148
|
+
hasAdditionalPostData = true
|
|
149
|
+
hasDelegate = true
|
|
150
|
+
argList.pop()
|
|
136
151
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
152
|
+
|
|
153
|
+
// Build argument array function
|
|
154
|
+
const argFnList = argList.map((arg) => typeConv(arg))
|
|
155
|
+
const concatArgs = (args) =>
|
|
156
|
+
args.map((arg, idx) => argFnList[idx](arg)).join('')
|
|
157
|
+
|
|
158
|
+
bcTransactionClass.prototype[fnName] = async function (wallet, ...args) {
|
|
159
|
+
const addr = wallet.getAddressString()
|
|
160
|
+
const data = await this.ajaxReq.getTransactionData(addr)
|
|
161
|
+
// TODO: must test this
|
|
162
|
+
if (data.error) {
|
|
163
|
+
console.log(`Failed getTransactionData(${addr})`)
|
|
164
|
+
throw new Error(data.msg)
|
|
165
|
+
}
|
|
166
|
+
const additionalPostData = hasAdditionalPostData ? args.pop() : {}
|
|
167
|
+
if (hasDelegate) {
|
|
168
|
+
additionalPostData.delegate = wallet.getAddressString()
|
|
169
|
+
}
|
|
170
|
+
const rawSignedTx = generateTx({
|
|
171
|
+
gasLimit: 500000,
|
|
172
|
+
data: fnHash + concatArgs(args),
|
|
173
|
+
to: this.contracts[contractNb],
|
|
174
|
+
unit: 'ether',
|
|
175
|
+
value: 0,
|
|
176
|
+
nonce: 1,
|
|
177
|
+
gasPrice: null,
|
|
178
|
+
donate: false,
|
|
179
|
+
from: addr,
|
|
180
|
+
key: wallet.getPrivateKeyString()
|
|
181
|
+
}, data)
|
|
182
|
+
return this.ajaxReq.sendTx(rawSignedTx, additionalPostData)
|
|
140
183
|
}
|
|
141
|
-
const rawTx = generateTx({
|
|
142
|
-
gasLimit: 500000,
|
|
143
|
-
data: fnHash + concatArgs(args),
|
|
144
|
-
to: this.contracts[contractNb],
|
|
145
|
-
unit: 'ether',
|
|
146
|
-
value: 0,
|
|
147
|
-
nonce: 1,
|
|
148
|
-
gasPrice: null,
|
|
149
|
-
donate: false,
|
|
150
|
-
from: addr,
|
|
151
|
-
key: wallet.getPrivateKeyString()
|
|
152
|
-
}, data)
|
|
153
|
-
if (rawTx.isError) return rawTx
|
|
154
|
-
return this.ajaxReq.sendTx(rawTx.signedTx, additionalPostData)
|
|
155
184
|
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
185
|
+
})
|
|
186
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
export default [
|
|
4
|
+
// First Contract
|
|
5
|
+
{
|
|
6
|
+
setAccountParam: '848b2592:accAddress accStatus accType limitPlus limitMinus',
|
|
7
|
+
pledgeAccount: '6c343eef:accAddress amount *',
|
|
8
|
+
setAllowance: 'd4e12f2e:spenderAddress amount',
|
|
9
|
+
setDelegation: '75741c79:spenderAddress limit',
|
|
10
|
+
setTaxAmount: 'f6f1897d:int',
|
|
11
|
+
setTaxLegAmount: 'fafaf4c0:int',
|
|
12
|
+
setTaxAccount: 'd0385b5e:accAddress',
|
|
13
|
+
setOwnerAccount: 'f2fde38b:accAddress',
|
|
14
|
+
setContractStatus: '88b8084f:status',
|
|
15
|
+
},
|
|
16
|
+
// Second Contract
|
|
17
|
+
{
|
|
18
|
+
transferNant: 'a5f7c148:toAddress amount *',
|
|
19
|
+
transferCM: '60ca9c4c:toAddress amount *',
|
|
20
|
+
transferOnBehalfNant: '1b6b1ee5:fromAddress toAddress amount D',
|
|
21
|
+
transferOnBehalfCM: '74c421fe:fromAddress toAddress amount D',
|
|
22
|
+
askTransferFrom: '58258353:fromAddress amount',
|
|
23
|
+
askTransferCMFrom: '2ef9ade2:fromAddress amount',
|
|
24
|
+
payRequestNant: '132019f4:toAddress amount *',
|
|
25
|
+
payRequestCM: '1415707c:toAddress amount *',
|
|
26
|
+
rejectRequest: 'af98f757:toAddress',
|
|
27
|
+
dismissAcceptedInfo: 'ccf93c7a:accAddress',
|
|
28
|
+
dismissRejectedInfo: '88759215:accAddress',
|
|
29
|
+
},
|
|
30
|
+
]
|
package/src/connection.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import * as config from './config'
|
|
2
2
|
import HttpAbstract from './rest/http'
|
|
3
3
|
import * as t from './type'
|
|
4
|
+
import * as e from './exception'
|
|
4
5
|
|
|
6
|
+
const wait = async (ms: number): Promise<void> => {
|
|
7
|
+
return new Promise(resolve => setTimeout(resolve, ms))
|
|
8
|
+
}
|
|
5
9
|
|
|
6
10
|
abstract class ConnectionAbstract {
|
|
7
11
|
|
|
@@ -36,15 +40,42 @@ abstract class ConnectionAbstract {
|
|
|
36
40
|
public async acquireEndPoint (repo: string) {
|
|
37
41
|
const apiNodes = await this.getCCEndPointList(repo)
|
|
38
42
|
if (!apiNodes) return false
|
|
39
|
-
|
|
43
|
+
let endpoint: boolean | string = false
|
|
44
|
+
let retry = 30
|
|
45
|
+
let count = 0
|
|
46
|
+
console.log(`Pick first endpoint that pass checkdb test`)
|
|
47
|
+
while (!endpoint) {
|
|
48
|
+
if (count >= 1) {
|
|
49
|
+
await wait(200)
|
|
50
|
+
console.log(`No endpoints passed the checkdb test... retry (${count}/${retry})`)
|
|
51
|
+
}
|
|
52
|
+
endpoint = await this.selectEndPoint([...apiNodes])
|
|
53
|
+
count++
|
|
54
|
+
if (count >= retry) {
|
|
55
|
+
break
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (typeof endpoint === 'string') {
|
|
59
|
+
console.log(` endpoint ${endpoint} %cpassed%c the checkdb test`,
|
|
60
|
+
'color: #0b0;', 'color: none')
|
|
61
|
+
} else {
|
|
62
|
+
console.log(`Failed to find a functional endpoint passing checkdb.`)
|
|
63
|
+
}
|
|
40
64
|
return { apiNodes, endpoint }
|
|
41
65
|
}
|
|
42
66
|
|
|
43
67
|
///
|
|
44
68
|
// [Lower level] Get the list of ComChain end-points from given repo
|
|
45
69
|
///
|
|
46
|
-
getCCEndPointList (repo: string) {
|
|
47
|
-
|
|
70
|
+
async getCCEndPointList (repo: string) {
|
|
71
|
+
try {
|
|
72
|
+
return await this.http.get(
|
|
73
|
+
repo + config.nodesRepo,
|
|
74
|
+
{ _: new Date().getTime() })
|
|
75
|
+
} catch (e) {
|
|
76
|
+
console.error(`Failed to get endpoint list on ${repo}`, e)
|
|
77
|
+
return false
|
|
78
|
+
}
|
|
48
79
|
}
|
|
49
80
|
|
|
50
81
|
///
|
|
@@ -59,6 +90,7 @@ abstract class ConnectionAbstract {
|
|
|
59
90
|
// check the node is up and running
|
|
60
91
|
const success = await this.testNode(node)
|
|
61
92
|
if (success) return node
|
|
93
|
+
console.log(` endpoint ${node} %cfailed%c the checkdb test`, 'color: #b00;', 'color:none')
|
|
62
94
|
nodes.splice(id, 1)
|
|
63
95
|
}
|
|
64
96
|
return false
|
|
@@ -187,7 +219,7 @@ export default abstract class ConnectionMgrAbstract extends ConnectionAbstract {
|
|
|
187
219
|
}
|
|
188
220
|
const { apiNodes, endpoint } = apiNodesEndpoint
|
|
189
221
|
if (typeof endpoint !== 'string') {
|
|
190
|
-
throw new
|
|
222
|
+
throw new e.NoEndpointAvailable('No endpoint in list seems available.')
|
|
191
223
|
}
|
|
192
224
|
|
|
193
225
|
this.persistentStore.set('ApiNodes', JSON.stringify(apiNodes))
|
|
@@ -232,11 +264,12 @@ export default abstract class ConnectionMgrAbstract extends ConnectionAbstract {
|
|
|
232
264
|
public getLocalConfJSON () {
|
|
233
265
|
if (this.conf) return this.conf
|
|
234
266
|
const cfgJson = this.persistentStore.get('ServerConf')
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
267
|
+
const cfg = cfgJson ? JSON.parse(cfgJson) : {}
|
|
268
|
+
// Completing with other informations if available
|
|
269
|
+
if (this.repo) {
|
|
270
|
+
cfg.repo = this.repo
|
|
271
|
+
cfg.custoRepo = this.repo + config.custoRepo
|
|
272
|
+
}
|
|
240
273
|
return cfg
|
|
241
274
|
}
|
|
242
275
|
|
package/src/customization.ts
CHANGED
|
@@ -131,14 +131,25 @@ export default abstract class CustomizationAbstract {
|
|
|
131
131
|
if (!currencyName) {
|
|
132
132
|
currencyName = this.cfg.server.name
|
|
133
133
|
}
|
|
134
|
+
if (!this.cfg.custoRepo) {
|
|
135
|
+
throw Error(
|
|
136
|
+
'Requested getCurrencyAssetBaseUrl while configuration is not available (yet?)'
|
|
137
|
+
)
|
|
138
|
+
}
|
|
134
139
|
return `${this.cfg.custoRepo}${currencyName}`
|
|
135
140
|
}
|
|
136
141
|
|
|
137
142
|
public getCssUrl (currencyName?: string) {
|
|
138
143
|
try {
|
|
139
|
-
// XXXvlab: I guess that we don't need to keep 'etherwallet' css
|
|
140
|
-
|
|
144
|
+
// XXXvlab: I guess that we don't need to keep 'etherwallet' css
|
|
145
|
+
// names
|
|
146
|
+
return this.getCurrencyAssetBaseUrl(currencyName) +
|
|
147
|
+
'/css/etherwallet-master.min.css'
|
|
141
148
|
} catch (e) {
|
|
149
|
+
console.log(
|
|
150
|
+
'`customization.getCssUrl(..)` called before configuration was ' +
|
|
151
|
+
"ready. Returning `localDefaultConf`'s value."
|
|
152
|
+
)
|
|
142
153
|
return this.localDefaultConf.server.url_Css
|
|
143
154
|
}
|
|
144
155
|
}
|
|
@@ -149,6 +160,10 @@ export default abstract class CustomizationAbstract {
|
|
|
149
160
|
// be agnostic ?
|
|
150
161
|
return `${this.getCurrencyAssetBaseUrl(currencyName)}/images/lem.png`
|
|
151
162
|
} catch (e) {
|
|
163
|
+
console.log(
|
|
164
|
+
'`customization.getCurrencyLogoUrl(..)` called before configuration ' +
|
|
165
|
+
'was ready. Returning empty string.'
|
|
166
|
+
)
|
|
152
167
|
return ''
|
|
153
168
|
}
|
|
154
169
|
}
|
package/src/ethereum/ethFuncs.ts
CHANGED
|
@@ -36,7 +36,7 @@ function padLeftEven (hex) {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
export function addTinyMoreToGas (hex) {
|
|
39
|
-
hex =
|
|
39
|
+
hex = sanitizeHex(hex)
|
|
40
40
|
return new BigNumber(hex).plus(etherUnits.getValueOfUnit('gwei'))
|
|
41
41
|
.toDigits(2).toString(16)
|
|
42
42
|
}
|
|
@@ -71,3 +71,44 @@ export function encodeNumber (number) {
|
|
|
71
71
|
|
|
72
72
|
return valueHex
|
|
73
73
|
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
/* @skip-prod-transpilation */
|
|
77
|
+
if (import.meta.vitest) {
|
|
78
|
+
const { it, expect, describe } = import.meta.vitest
|
|
79
|
+
describe('encodeNumber', () => {
|
|
80
|
+
it('should encode 0 to "00...0000"', () => {
|
|
81
|
+
expect(encodeNumber(0)).toBe('0000000000000000000000000000000000000000000000000000000000000000')
|
|
82
|
+
})
|
|
83
|
+
it('should encode 1 to "00...0001"', () => {
|
|
84
|
+
expect(encodeNumber(1)).toBe('0000000000000000000000000000000000000000000000000000000000000001')
|
|
85
|
+
})
|
|
86
|
+
it('should encode 16 to "00...0010"', () => {
|
|
87
|
+
expect(encodeNumber(16)).toBe('0000000000000000000000000000000000000000000000000000000000000010')
|
|
88
|
+
})
|
|
89
|
+
it('should encode 0x1f_ffff_ffff_ffff to "00...001f_ffff_ffff_ffff"', () => {
|
|
90
|
+
expect(encodeNumber(0x1f_ffff_ffff_ffff)).toBe('000000000000000000000000000000000000000000000000001fffffffffffff')
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('should encode -1 to "ff...ffff"', () => {
|
|
94
|
+
expect(encodeNumber(-1)).toBe('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')
|
|
95
|
+
})
|
|
96
|
+
it('should encode -0xf to "ff...fff1"', () => {
|
|
97
|
+
expect(encodeNumber(-0xf)).toBe('fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1')
|
|
98
|
+
})
|
|
99
|
+
it('should encode -0x10 to "ff...fff0"', () => {
|
|
100
|
+
expect(encodeNumber(-0x10)).toBe('fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0')
|
|
101
|
+
})
|
|
102
|
+
it('should encode -0x1f_ffff_ffff_fffff to "ff...ffe0_0000_0000_0001"', () => {
|
|
103
|
+
expect(encodeNumber(-0x1f_ffff_ffff_ffff)).toBe('ffffffffffffffffffffffffffffffffffffffffffffffffffe0000000000001')
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('should refuse to encode 0x20_0000_0000_0000', () => {
|
|
107
|
+
expect(() => encodeNumber(0x20_0000_0000_0000)).toThrowError('number type')
|
|
108
|
+
})
|
|
109
|
+
it('should refuse to encode -0x20_0000_0000_0000', () => {
|
|
110
|
+
expect(() => encodeNumber(-0x20_0000_0000_0000)).toThrowError('number type')
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
})
|
|
114
|
+
}
|
|
@@ -41,26 +41,26 @@ export function getValueOfUnit (unit) {
|
|
|
41
41
|
export function fiatToWei (number, pricePerEther) {
|
|
42
42
|
return new BigNumber(String(number))
|
|
43
43
|
.div(pricePerEther)
|
|
44
|
-
.times(
|
|
44
|
+
.times(getValueOfUnit('ether'))
|
|
45
45
|
.round(0)
|
|
46
46
|
.toString(10)
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
export function toFiat (number, unit, multi) {
|
|
50
|
-
return new BigNumber(
|
|
50
|
+
return new BigNumber(toEther(number, unit))
|
|
51
51
|
.times(multi)
|
|
52
52
|
.round(5)
|
|
53
53
|
.toString(10)
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
export function toEther (number, unit) {
|
|
57
|
-
return new BigNumber(
|
|
58
|
-
.div(
|
|
57
|
+
return new BigNumber(toWei(number, unit))
|
|
58
|
+
.div(getValueOfUnit('ether'))
|
|
59
59
|
.toString(10)
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
export function toWei (number, unit) {
|
|
63
63
|
return new BigNumber(String(number))
|
|
64
|
-
.times(
|
|
64
|
+
.times(getValueOfUnit(unit))
|
|
65
65
|
.toString(10)
|
|
66
66
|
}
|
package/src/ethereum/uiFuncs.ts
CHANGED
|
@@ -24,32 +24,23 @@ function isTxDataValid (txData) {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export function generateTx (txData, data) {
|
|
27
|
-
|
|
28
|
-
isTxDataValid(txData)
|
|
27
|
+
isTxDataValid(txData)
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
const eTx = new Tx(rawTx)
|
|
42
|
-
|
|
43
|
-
eTx.sign(Buffer.from(txData.key, 'hex'))
|
|
44
|
-
rawTx.rawTx = JSON.stringify(rawTx)
|
|
45
|
-
rawTx.signedTx = '0x' + eTx.serialize().toString('hex')
|
|
46
|
-
rawTx.isError = false
|
|
47
|
-
return rawTx
|
|
48
|
-
} catch (e) {
|
|
49
|
-
return {
|
|
50
|
-
isError: true,
|
|
51
|
-
error: e
|
|
52
|
-
}
|
|
29
|
+
const rawTx: {[k: string]: any} = {
|
|
30
|
+
nonce: ethFuncs.sanitizeHex(data.nonce),
|
|
31
|
+
gasPrice: ethFuncs.sanitizeHex(
|
|
32
|
+
ethFuncs.addTinyMoreToGas(data.gasprice)),
|
|
33
|
+
gasLimit: ethFuncs.sanitizeHex(
|
|
34
|
+
ethFuncs.decimalToHex(txData.gasLimit)),
|
|
35
|
+
to: ethFuncs.sanitizeHex(txData.to),
|
|
36
|
+
value: ethFuncs.sanitizeHex(
|
|
37
|
+
ethFuncs.decimalToHex(etherUnits.toWei(txData.value, txData.unit))),
|
|
38
|
+
data: ethFuncs.sanitizeHex(txData.data)
|
|
53
39
|
}
|
|
40
|
+
const eTx = new Tx(rawTx)
|
|
41
|
+
|
|
42
|
+
eTx.sign(Buffer.from(txData.key, 'hex'))
|
|
43
|
+
rawTx.rawTx = JSON.stringify(rawTx)
|
|
44
|
+
return '0x' + eTx.serialize().toString('hex')
|
|
54
45
|
}
|
|
55
46
|
|
package/src/exception.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export class APIError extends Error {
|
|
2
|
+
data: string
|
|
3
|
+
constructor (message, data) {
|
|
4
|
+
super(message)
|
|
5
|
+
this.name = 'APIError'
|
|
6
|
+
this.data = data
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class NoEndpointAvailable extends Error {
|
|
11
|
+
data: string
|
|
12
|
+
constructor (message) {
|
|
13
|
+
super(message)
|
|
14
|
+
this.name = 'NoEndpointAvailable'
|
|
15
|
+
}
|
|
16
|
+
}
|