1 | const { decorate } = require('./util');
|
2 | const format = require('./util/format');
|
3 |
|
4 | const providerFactory = require('./provider');
|
5 | const Contract = require('./contract');
|
6 | const Wallet = require('./wallet');
|
7 | const { PendingTransaction, LogIterator } = require('./subscribe');
|
8 |
|
9 | /**
|
10 | * A sdk of conflux.
|
11 | */
|
12 | class Conflux {
|
13 | /**
|
14 | * @param [options] {object} - Conflux and Provider constructor options.
|
15 | * @param [options.url=''] {string} - Url of provider to create.
|
16 | * @param [options.defaultEpoch="latest_state"] {string|number} - Default epochNumber. (deprecated)
|
17 | * @param [options.defaultGasPrice] {string|number|BigNumber} - The default gas price in drip to use for transactions. (deprecated)
|
18 | * @param [options.defaultGas] {string|number|BigNumber} - The default maximum gas provided for a transaction. (deprecated)
|
19 | *
|
20 | * @example
|
21 | * > const Conflux = require('conflux-web');
|
22 | * > const cfx = new Conflux({url:'http://testnet-jsonrpc.conflux-chain.org:12537'});
|
23 | *
|
24 | * @example
|
25 | * > const cfx = new Conflux({
|
26 | * url: 'http://localhost:8000',
|
27 | * defaultGasPrice: 100,
|
28 | * defaultGas: 100000,
|
29 | * logger: console,
|
30 | * });
|
31 | */
|
32 | constructor({
|
33 | url = '',
|
34 | defaultEpoch = 'latest_state',
|
35 | defaultGasPrice,
|
36 | defaultGas,
|
37 | ...rest
|
38 | } = {}) {
|
39 | this.provider = this.setProvider(url, rest);
|
40 | this.wallet = new Wallet(this);
|
41 |
|
42 | /**
|
43 | * Default epoch number for following methods:
|
44 | * - `Conflux.getBalance`
|
45 | * - `Conflux.getTransactionCount`
|
46 | * - `Conflux.getCode`
|
47 | * - `Conflux.call`
|
48 | *
|
49 | * @deprecated
|
50 | * @type {number|string}
|
51 | */
|
52 | this.defaultEpoch = defaultEpoch;
|
53 |
|
54 | /**
|
55 | * Default gas price for following methods:
|
56 | * - `Conflux.sendTransaction`
|
57 | * - `Conflux.call`
|
58 | * - `Conflux.estimateGas`
|
59 | *
|
60 | * @deprecated
|
61 | * @type {number|BigNumber|string}
|
62 | */
|
63 | this.defaultGasPrice = defaultGasPrice;
|
64 |
|
65 | /**
|
66 | * Default gas limit for following methods:
|
67 | * - `Conflux.sendTransaction`
|
68 | * - `Conflux.call`
|
69 | * - `Conflux.estimateGas`
|
70 | *
|
71 | * @deprecated
|
72 | * @type {number|BigNumber|string}
|
73 | */
|
74 | this.defaultGas = defaultGas;
|
75 |
|
76 | decorate(this, 'sendTransaction', (func, params) => {
|
77 | return new PendingTransaction(this, func, params);
|
78 | });
|
79 |
|
80 | decorate(this, 'sendRawTransaction', (func, params) => {
|
81 | return new PendingTransaction(this, func, params);
|
82 | });
|
83 |
|
84 | decorate(this, 'getLogs', (func, params) => {
|
85 | return new LogIterator(this, func, params);
|
86 | });
|
87 | }
|
88 |
|
89 | /**
|
90 | * Create and set `provider`.
|
91 | *
|
92 | * @param url {string} - Url of provider to create.
|
93 | * @param [options] {object} - Provider constructor options.
|
94 | * @return {Object}
|
95 | *
|
96 | * @example
|
97 | * > cfx.provider;
|
98 | HttpProvider {
|
99 | url: 'http://testnet-jsonrpc.conflux-chain.org:12537',
|
100 | timeout: 60000,
|
101 | ...
|
102 | }
|
103 |
|
104 | * > cfx.setProvider('http://localhost:8000');
|
105 | * > cfx.provider;
|
106 | HttpProvider {
|
107 | url: 'http://localhost:8000',
|
108 | timeout: 60000,
|
109 | ...
|
110 | }
|
111 | */
|
112 | setProvider(url, options = {}) {
|
113 | if (!this.provider) {
|
114 | this.provider = providerFactory(url, options);
|
115 | } else if (url !== this.provider.url) {
|
116 | const provider = providerFactory(url, { ...this.provider, ...options });
|
117 | this.provider.close(); // close after factory create success
|
118 | this.provider = provider;
|
119 | } else {
|
120 | Object.assign(this.provider, options);
|
121 | }
|
122 |
|
123 | return this.provider;
|
124 | }
|
125 |
|
126 | /**
|
127 | * A shout cut for `new Contract(cfx, options);`
|
128 | *
|
129 | * @param options {object} - See `Contract.constructor`
|
130 | * @return {Contract}
|
131 | */
|
132 | Contract(options) {
|
133 | return new Contract(this, options);
|
134 | }
|
135 |
|
136 | /**
|
137 | * close connection.
|
138 | *
|
139 | * @example
|
140 | * > cfx.close();
|
141 | */
|
142 | close() {
|
143 | if (this.provider) {
|
144 | this.provider.close();
|
145 | }
|
146 | }
|
147 |
|
148 | // --------------------------------------------------------------------------
|
149 | /**
|
150 | * Returns the current gas price oracle. The gas price is determined by the last few blocks median gas price.
|
151 | *
|
152 | * @return {Promise<BigNumber>} Gas price in drip.
|
153 | *
|
154 | * @example
|
155 | * > await cfx.getGasPrice();
|
156 | "0"
|
157 | */
|
158 | async getGasPrice() {
|
159 | const result = await this.provider.call('cfx_gasPrice');
|
160 | return format.bigNumber(result);
|
161 | }
|
162 |
|
163 | /**
|
164 | * Returns the current epochNumber the client is on.
|
165 | *
|
166 | * @param [epochNumber] {string|number} - The end epochNumber to count balance of.
|
167 | * @return {Promise<number>} EpochNumber
|
168 | *
|
169 | * @example
|
170 | * > await cfx.getEpochNumber();
|
171 | 200109
|
172 | */
|
173 | async getEpochNumber(epochNumber = 'latest_mined') {
|
174 | const result = await this.provider.call('cfx_epochNumber', format.epochNumber(epochNumber));
|
175 | return format.number(result);
|
176 | }
|
177 |
|
178 | /**
|
179 | * Gets past logs, matching the given options.
|
180 | *
|
181 | * @param [options] {object}
|
182 | * @param [options.fromEpoch] {string|number} - The number of the start block. (>=)
|
183 | * @param [options.toEpoch] {string|number} - The number of the stop block.(<=)
|
184 | * @param [options.blockHashes] {string[]} - The block hash list
|
185 | * @param [options.address] {string|string[]} - An address or a list of addresses to only get logs from particular account(s).
|
186 | * @param [options.topics] {array} - An array of values which must each appear in the log entries. The order is important, if you want to leave topics out use null, e.g. [null, '0x12...']. You can also pass an array for each topic with options for that topic e.g. [null, ['option1', 'option2']]
|
187 | * @param [options.limit] {number} - Limit log number.
|
188 | * @return {Promise<LogIterator>} Array of log objects.
|
189 | * - `string` address: Address this event originated from.
|
190 | * - `string[]` topics: An array with max 4 32 Byte topics, topic 1-3 contains indexed parameters of the event.
|
191 | * - `string` data: The data containing non-indexed log parameter.
|
192 | * - `string` type: TODO
|
193 | * - `boolean` removed: TODO
|
194 | * - `number` epochNumber: The epochNumber this log was created in. null when still pending.
|
195 | * - `string` blockHash: Hash of the block this event was created in. null when it’s still pending.
|
196 | * - `string` transactionHash: Hash of the transaction this event was created in.
|
197 | * - `string` transactionIndex: Integer of the transaction’s index position the event was created in.
|
198 | * - `number` logIndex: Integer of the event index position in the block.
|
199 | * - `number` transactionLogIndex: Integer of the event index position in the transaction.
|
200 | *
|
201 | * @example
|
202 | * > await cfx.getLogs({
|
203 | address: '0xbd72de06cd4a94ad31ed9303cf32a2bccb82c404',
|
204 | fromEpoch: 0,
|
205 | toEpoch: 'latest_mined',
|
206 | limit: 1,
|
207 | topics: [
|
208 | '0xb818399ffd68e821c34de8d5fbc5aeda8456fdb9296fc1b02bf6245ade7ebbd4',
|
209 | '0x0000000000000000000000001ead8630345121d19ee3604128e5dc54b36e8ea6'
|
210 | ]
|
211 | });
|
212 |
|
213 | [
|
214 | {
|
215 | address: '0xbd72de06cd4a94ad31ed9303cf32a2bccb82c404',
|
216 | blockHash: '0x701afee0ffc49aaebadf0e6618b6ec1715d31e7aa639e2e00dc8df10994e0283',
|
217 | data: '0x',
|
218 | epochNumber: 542556,
|
219 | logIndex: 0,
|
220 | removed: false,
|
221 | topics: [
|
222 | '0xb818399ffd68e821c34de8d5fbc5aeda8456fdb9296fc1b02bf6245ade7ebbd4',
|
223 | '0x0000000000000000000000001ead8630345121d19ee3604128e5dc54b36e8ea6'
|
224 | ],
|
225 | transactionHash: '0x5a301d2c342709d7de9da24bd096ab3754ea328b016d85ab3410d375616f5d0d',
|
226 | transactionIndex: 0,
|
227 | transactionLogIndex: 0,
|
228 | type: 'mined'
|
229 | },
|
230 | ]
|
231 |
|
232 | * @example
|
233 | * > logIter = cfx.getLogs({
|
234 | address: '0xbd72de06cd4a94ad31ed9303cf32a2bccb82c404',
|
235 | fromEpoch: 'latest_mined',
|
236 | limit: 2,
|
237 | })
|
238 | * > await logIter.next({threshold: 0.01, delta: 1000});
|
239 | {
|
240 | address: '0xbd72de06cd4a94ad31ed9303cf32a2bccb82c404',
|
241 | ...
|
242 | }
|
243 | * > await logIter.next();
|
244 | {
|
245 | address: '0xbd72de06cd4a94ad31ed9303cf32a2bccb82c404',
|
246 | ...
|
247 | }
|
248 | * > await logIter.next();
|
249 | undefined
|
250 |
|
251 | * @example
|
252 | * > logIter = cfx.getLogs({
|
253 | address: '0xbd72de06cd4a94ad31ed9303cf32a2bccb82c404',
|
254 | fromEpoch: 'latest_mined',
|
255 | limit: 2,
|
256 | })
|
257 | * > for await (const log of iter) {
|
258 | console.log(log);
|
259 | }
|
260 | {
|
261 | address: '0xbd72de06cd4a94ad31ed9303cf32a2bccb82c404',
|
262 | ...
|
263 | }
|
264 | ...
|
265 | */
|
266 | async getLogs(options) {
|
267 | if (options.blockHashes !== undefined && (options.fromEpoch !== undefined || options.toEpoch !== undefined)) {
|
268 | throw new Error('Override waring, do not use `blockHashes` with `fromEpoch` or `toEpoch`, cause only `blockHashes` will take effect');
|
269 | }
|
270 |
|
271 | const result = await this.provider.call('cfx_getLogs', format.getLogs(options));
|
272 |
|
273 | return format.logs(result);
|
274 | }
|
275 |
|
276 | // ------------------------------- address ----------------------------------
|
277 | /**
|
278 | * Get the balance of an address at a given epochNumber.
|
279 | *
|
280 | * @param address {string} - The address to get the balance of.
|
281 | * @param [epochNumber=this.defaultEpoch] {string|number} - The end epochNumber to count balance of.
|
282 | * @return {Promise<BigNumber>} Address balance number in drip.
|
283 | *
|
284 | * @example
|
285 | * > let balance = await cfx.getBalance("0xbbd9e9be525ab967e633bcdaeac8bd5723ed4d6b");
|
286 | * > balance;
|
287 | BigNumber { s: 1, e: 18, c: [ 17936, 36034970586632 ] }
|
288 |
|
289 | * > Drip.toCFX(balance).toString(10);
|
290 | 1.793636034970586632
|
291 |
|
292 | * > balance = await cfx.getBalance("0xbbd9e9be525ab967e633bcdaeac8bd5723ed4d6b", 0);
|
293 | * > balance.toString(10);
|
294 | 0
|
295 | */
|
296 | async getBalance(address, epochNumber = this.defaultEpoch) {
|
297 | const result = await this.provider.call('cfx_getBalance',
|
298 | format.address(address), format.epochNumber(epochNumber),
|
299 | );
|
300 | return format.bigNumber(result);
|
301 | }
|
302 |
|
303 | /**
|
304 | * Get the numbers of transactions sent from this address.
|
305 | *
|
306 | * @param address {string} - The address to get the numbers of transactions from.
|
307 | * @param [epochNumber=this.defaultEpoch] {string|number} - The end epochNumber to count transaction of.
|
308 | * @return {Promise<number>}
|
309 | *
|
310 | * @example
|
311 | * > await cfx.getTransactionCount("0xbbd9e9be525ab967e633bcdaeac8bd5723ed4d6b");
|
312 | 61
|
313 |
|
314 | * > await cfx.getTransactionCount("0xbbd9e9be525ab967e633bcdaeac8bd5723ed4d6b", 0);
|
315 | 0
|
316 | */
|
317 | async getTransactionCount(address, epochNumber = this.defaultEpoch) {
|
318 | const result = await this.provider.call('cfx_getTransactionCount',
|
319 | format.address(address), format.epochNumber(epochNumber),
|
320 | );
|
321 | return format.number(result);
|
322 | }
|
323 |
|
324 | // -------------------------------- epoch -----------------------------------
|
325 |
|
326 | // eslint-disable-next-line no-unused-vars
|
327 | async getRiskCoefficient(epochNumber) {
|
328 | // FIXME rpc not implement yet.
|
329 | // const result = await this.provider.call('cfx_getRiskCoefficient', format.epochNumber(epochNumber));
|
330 | return 0;
|
331 | }
|
332 |
|
333 | /**
|
334 | * Get the epochNumber pivot block info.
|
335 | *
|
336 | * @param epochNumber {string|number} - EpochNumber or string in ["latest_state", "latest_mined"]
|
337 | * @param [detail=false] {boolean} - `true` return transaction object, `false` return TxHash array
|
338 | * @return {Promise<object|null>} The block info (same as `getBlockByHash`).
|
339 | *
|
340 | * @example
|
341 | * > await cfx.getBlockByEpochNumber(449);
|
342 | {
|
343 | hash: '0x59339ff28bc235cceac9fa588ebafcbf61316e6a8c86c7a1d7239b9445d98e40',
|
344 | ...
|
345 | }
|
346 | */
|
347 | async getBlockByEpochNumber(epochNumber, detail = false) {
|
348 | const result = await this.provider.call('cfx_getBlockByEpochNumber',
|
349 | format.epochNumber(epochNumber), format.boolean(detail),
|
350 | );
|
351 | return format.block.or(null)(result);
|
352 | }
|
353 |
|
354 | /**
|
355 | * Get block hash array of a epochNumber.
|
356 | *
|
357 | * @param epochNumber {string|number} - EpochNumber or string in ["latest_state", "latest_mined"]
|
358 | * @return {Promise<string[]>} Block hash array, last one is the pivot block hash of this epochNumber.
|
359 | *
|
360 | * @example
|
361 | * > await cfx.getBlocksByEpochNumber(0);
|
362 | ['0x2da120ad267319c181b12136f9e36be9fba59e0d818f6cc789f04ee937b4f593']
|
363 |
|
364 | * > await cfx.getBlocksByEpochNumber(449);
|
365 | [
|
366 | '0x3d8b71208f81fb823f4eec5eaf2b0ec6b1457d381615eff2fbe24605ea333c39',
|
367 | '0x59339ff28bc235cceac9fa588ebafcbf61316e6a8c86c7a1d7239b9445d98e40'
|
368 | ]
|
369 | */
|
370 | async getBlocksByEpochNumber(epochNumber) {
|
371 | return this.provider.call('cfx_getBlocksByEpoch', format.epochNumber(epochNumber));
|
372 | }
|
373 |
|
374 | // -------------------------------- block -----------------------------------
|
375 | /**
|
376 | * > TODO
|
377 | *
|
378 | * @return {Promise<string>}
|
379 | *
|
380 | * @example
|
381 | * > await cfx.getBestBlockHash();
|
382 | "0x43ddda130fff8539b9f3c431aa1b48e021b3744aacd224cbd4bcdb64373f3dd5"
|
383 | */
|
384 | async getBestBlockHash() {
|
385 | return this.provider.call('cfx_getBestBlockHash');
|
386 | }
|
387 |
|
388 | /**
|
389 | * Returns a block matching the block hash.
|
390 | *
|
391 | * @param blockHash {string} - The hash of block to be get.
|
392 | * @param [detail=false] {boolean} - `true` return transaction object, `false` return TxHash array
|
393 | * @return {Promise<object|null>} Block info object.
|
394 | * - `string` miner: The address of the beneficiary to whom the mining rewards were given.
|
395 | * - `string|null` hash: Hash of the block. `null` when its pending block.
|
396 | * - `string` parentHash: Hash of the parent block.
|
397 | * - `string[]` refereeHashes: Array of referee hashes.
|
398 | * - `number|null` epochNumber: The current block epochNumber in the client's view. `null` when it's not in best block's past set.
|
399 | * - `boolean|null` stable: If the block stable or not. `null` for pending stable.
|
400 | * - `string` nonce: Hash of the generated proof-of-work. `null` when its pending block.
|
401 | * - `number` gas: The maximum gas allowed in this block.
|
402 | * - `string` difficulty: Integer string of the difficulty for this block.
|
403 | * - `number` height: The block heights. `null` when its pending block.
|
404 | * - `number` size: Integer the size of this block in bytes.
|
405 | * - `number` blame: 0 if there's nothing to blame; k if the block is blaming on the state info of its k-th ancestor.
|
406 | * - `boolean` adaptive: If the block's weight adaptive or not.
|
407 | * - `number` timestamp: The unix timestamp for when the block was collated.
|
408 | * - `string` transactionsRoot: The hash of the transactions of the block.
|
409 | * - `string[]` transactions: Array of transaction objects, or 32 Bytes transaction hashes depending on the last given parameter.
|
410 | * - `string` deferredLogsBloomHash: The hash of the deferred block's log bloom filter
|
411 | * - `string` deferredReceiptsRoot: The hash of the receipts of the block after deferred execution.
|
412 | * - `string` deferredStateRoot: The root of the final state trie of the block after deferred execution.
|
413 | * - `object` deferredStateRootWithAux: Information of deferred state root
|
414 | *
|
415 | * @example
|
416 | * > await cfx.getBlockByHash('0x59339ff28bc235cceac9fa588ebafcbf61316e6a8c86c7a1d7239b9445d98e40');
|
417 | {
|
418 | "miner": "0x0000000000000000000000000000000000000015",
|
419 | "hash": "0x59339ff28bc235cceac9fa588ebafcbf61316e6a8c86c7a1d7239b9445d98e40",
|
420 | "parentHash": "0xe75f82d86f51cdab5a2ed7b4e225c714d1fda7e0aa568c6b4618015ee6666506",
|
421 | "refereeHashes": [
|
422 | "0x3d8b71208f81fb823f4eec5eaf2b0ec6b1457d381615eff2fbe24605ea333c39"
|
423 | ],
|
424 | "epochNumber": 449,
|
425 | "stable": null,
|
426 | "nonce": 17364797680136698000,
|
427 | "gas": 3000000000,
|
428 | "difficulty": "20000000",
|
429 | "height": 449,
|
430 | "size": 384,
|
431 | "blame": 0,
|
432 | "adaptive": false,
|
433 | "timestamp": 1571150247,
|
434 | "transactionsRoot": "0x2b8f5e08ca12eb66ae89f40a6b52938222ce835f0b786cae0befdbbecd8b55e1"
|
435 | "transactions": [
|
436 | "0xbe007c3eca92d01f3917f33ae983f40681182cf618defe75f490a65aac016914"
|
437 | ],
|
438 | "deferredLogsBloomHash": "0xd397b3b043d87fcd6fad1291ff0bfd16401c274896d8c63a923727f077b8e0b5",
|
439 | "deferredReceiptsRoot": "0x522717233b96e0a03d85f02f8127aa0e23ef2e0865c95bb7ac577ee3754875e4",
|
440 | "deferredStateRoot": "0x39975f9bf46884e7c3c269577177af9a041c5e36a69ef2a4cf581f8a061fa911",
|
441 | "deferredStateRootWithAux": {
|
442 | "auxInfo": {
|
443 | "intermediateDeltaEpochId": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
|
444 | "previousSnapshotRoot": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
|
445 | },
|
446 | "stateRoot": {
|
447 | "deltaRoot": "0x752a3f391da1a584812a9f50ec92542abda59c3cc0ad49741461471680cf1528",
|
448 | "intermediateDeltaRoot": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
|
449 | "snapshotRoot": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
|
450 | }
|
451 | },
|
452 | }
|
453 |
|
454 | * @example
|
455 | * > await cfx.getBlockByHash('0x59339ff28bc235cceac9fa588ebafcbf61316e6a8c86c7a1d7239b9445d98e40', true);
|
456 | {
|
457 | "hash": "0x59339ff28bc235cceac9fa588ebafcbf61316e6a8c86c7a1d7239b9445d98e40",
|
458 | "transactions": [
|
459 | {
|
460 | "blockHash": "0x59339ff28bc235cceac9fa588ebafcbf61316e6a8c86c7a1d7239b9445d98e40",
|
461 | "transactionIndex": 0,
|
462 | "hash": "0xbe007c3eca92d01f3917f33ae983f40681182cf618defe75f490a65aac016914",
|
463 | "nonce": 0,
|
464 | "from": "0xa70ddf9b9750c575db453eea6a041f4c8536785a",
|
465 | "to": "0x63f0a574987f6893e068a08a3fb0e63aec3785e6",
|
466 | "value": "1000000000000000000"
|
467 | "data": "0x",
|
468 | "gas": 21000,
|
469 | "gasPrice": "819",
|
470 | "status": 0,
|
471 | "contractCreated": null,
|
472 | "r": "0x88e43a02a653d5895ffa5495718a5bd772cb157776108c5c22cee9beff890650",
|
473 | "s": "0x24e3ba1bb0d11c8b1da8d969ecd0c5e2372326a3de71ba1231c876c0efb2c0a8",
|
474 | "v": 0,
|
475 | }
|
476 | ],
|
477 | ...
|
478 | }
|
479 | */
|
480 | async getBlockByHash(blockHash, detail = false) {
|
481 | const result = await this.provider.call('cfx_getBlockByHash',
|
482 | format.blockHash(blockHash), format.boolean(detail),
|
483 | );
|
484 | return format.block.or(null)(result);
|
485 | }
|
486 |
|
487 | /**
|
488 | * Get block by `blockHash` if pivot block of `epochNumber` is `pivotBlockHash`.
|
489 | *
|
490 | * @param blockHash {string} - Block hash which epochNumber expect to be `epochNumber`.
|
491 | * @param pivotBlockHash {string} - Block hash which expect to be the pivot block of `epochNumber`.
|
492 | * @param epochNumber {number} - EpochNumber or string in ["latest_state", "latest_mined"]
|
493 | * @return {Promise<object>} The block info (same as `getBlockByHash`).
|
494 | *
|
495 | * @example
|
496 | * > await cfx.getBlockByHashWithPivotAssumption(
|
497 | * '0x3d8b71208f81fb823f4eec5eaf2b0ec6b1457d381615eff2fbe24605ea333c39',
|
498 | * '0x59339ff28bc235cceac9fa588ebafcbf61316e6a8c86c7a1d7239b9445d98e40'
|
499 | * 449,
|
500 | * );
|
501 | {
|
502 | hash: '0x3d8b71208f81fb823f4eec5eaf2b0ec6b1457d381615eff2fbe24605ea333c39',
|
503 | ...
|
504 | }
|
505 | */
|
506 | async getBlockByHashWithPivotAssumption(blockHash, pivotBlockHash, epochNumber) {
|
507 | const result = await this.provider.call('cfx_getBlockByHashWithPivotAssumption',
|
508 | format.blockHash(blockHash), format.blockHash(pivotBlockHash), format.epochNumber(epochNumber),
|
509 | );
|
510 | return format.block(result);
|
511 | }
|
512 |
|
513 | // ----------------------------- transaction --------------------------------
|
514 | /**
|
515 | * Returns a transaction matching the given transaction hash.
|
516 | *
|
517 | * @param txHash {string} - The transaction hash.
|
518 | * @return {Promise<object|null>} Transaction info object
|
519 | * - `string` blockHash: Hash of the block where this transaction was in and got executed. `null` when its pending.
|
520 | * - `number` transactionIndex: Integer of the transactions index position in the block.
|
521 | * - `string` hash: Hash of the transaction.
|
522 | * - `number` nonce: The number of transactions made by the sender prior to this one.
|
523 | * - `string` from: Address of the sender.
|
524 | * - `string` to: Address of the receiver. null when its a contract creation transaction.
|
525 | * - `string` value: Value transferred in Drip.
|
526 | * - `string` data: The data send along with the transaction.
|
527 | * - `number` gas: Gas provided by the sender.
|
528 | * - `number` gasPrice: Gas price provided by the sender in Drip.
|
529 | * - `string` status: '0x0' successful execution; '0x1' exception happened but nonce still increased; '0x2' exception happened and nonce didn't increase.
|
530 | * - `string|null` contractCreated: The contract address created, if the transaction was a contract creation, otherwise null.
|
531 | * - `string` r: ECDSA signature r
|
532 | * - `string` s: ECDSA signature s
|
533 | * - `string` v: ECDSA recovery id
|
534 | *
|
535 | * @example
|
536 | * > await cfx.getTransactionByHash('0xbe007c3eca92d01f3917f33ae983f40681182cf618defe75f490a65aac016914');
|
537 | {
|
538 | "blockHash": "0x59339ff28bc235cceac9fa588ebafcbf61316e6a8c86c7a1d7239b9445d98e40",
|
539 | "transactionIndex": 0,
|
540 | "hash": "0xbe007c3eca92d01f3917f33ae983f40681182cf618defe75f490a65aac016914",
|
541 | "nonce": 0,
|
542 | "from": "0xa70ddf9b9750c575db453eea6a041f4c8536785a",
|
543 | "to": "0x63f0a574987f6893e068a08a3fb0e63aec3785e6",
|
544 | "value": "1000000000000000000"
|
545 | "data": "0x",
|
546 | "gas": 21000,
|
547 | "gasPrice": "819",
|
548 | "status": 0,
|
549 | "contractCreated": null,
|
550 | "r": "0x88e43a02a653d5895ffa5495718a5bd772cb157776108c5c22cee9beff890650",
|
551 | "s": "0x24e3ba1bb0d11c8b1da8d969ecd0c5e2372326a3de71ba1231c876c0efb2c0a8",
|
552 | "v": 0,
|
553 | }
|
554 | */
|
555 | async getTransactionByHash(txHash) {
|
556 | const result = await this.provider.call('cfx_getTransactionByHash', format.txHash(txHash));
|
557 | return format.transaction.or(null)(result);
|
558 | }
|
559 |
|
560 | /**
|
561 | * Returns the receipt of a transaction by transaction hash.
|
562 | *
|
563 | * > Note: The receipt is not available for pending transactions and returns null.
|
564 | *
|
565 | * @param txHash {string} - The transaction hash.
|
566 | * @return {Promise<object|null>}
|
567 | * - `number` outcomeStatus: `0`: the transaction was successful, `1`: EVM reverted the transaction.
|
568 | * - `string` stateRoot: The state root of transaction execution.
|
569 | * - `number` epochNumber: EpochNumber where this transaction was in.
|
570 | * - `string` blockHash: Hash of the block where this transaction was in.
|
571 | * - `string` transactionHash: Hash of the transaction.
|
572 | * - `number` index: Integer of the transactions index position in the block.
|
573 | * - `string` from: Address of the sender.
|
574 | * - `string` to: Address of the receiver. null when its a contract creation transaction.
|
575 | * - `string|null` contractCreated: The contract address created, if the transaction was a contract creation, otherwise null.
|
576 | * - `number` gasUsed: The amount of gas used by this specific transaction alone.
|
577 | * - `[object]` logs: Array of log objects, which this transaction generated.
|
578 | * - `[string]` logs[].address: The address of the contract executing at the point of the `LOG` operation.
|
579 | * - `[string]` logs[].topics: The topics associated with the `LOG` operation.
|
580 | * - `[string]` logs[].data: The data associated with the `LOG` operation.
|
581 | * - `string` logsBloom: Log bloom.
|
582 | *
|
583 | * @example
|
584 | * > await cfx.getTransactionReceipt('0xbe007c3eca92d01f3917f33ae983f40681182cf618defe75f490a65aac016914');
|
585 | {
|
586 | "outcomeStatus": 0,
|
587 | "stateRoot": "0x3854f64be6c124dffd0ddca57270846f0f43a119ea681b4e5d022ade537d9f07",
|
588 | "epochNumber": 449,
|
589 | "blockHash": "0x59339ff28bc235cceac9fa588ebafcbf61316e6a8c86c7a1d7239b9445d98e40",
|
590 | "transactionHash": "0xbe007c3eca92d01f3917f33ae983f40681182cf618defe75f490a65aac016914"
|
591 | "index": 0,
|
592 | "from": "0xa70ddf9b9750c575db453eea6a041f4c8536785a",
|
593 | "to": "0x63f0a574987f6893e068a08a3fb0e63aec3785e6",
|
594 | "contractCreated": null,
|
595 | "gasUsed": 21000,
|
596 | "logs": [],
|
597 | "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
598 | }
|
599 | */
|
600 | async getTransactionReceipt(txHash) {
|
601 | const result = await this.provider.call('cfx_getTransactionReceipt', format.txHash(txHash));
|
602 | return format.receipt.or(null)(result);
|
603 | }
|
604 |
|
605 | /**
|
606 | * Creates new message call transaction or a contract creation, if the data field contains code.
|
607 | *
|
608 | * > FIXME: rpc `cfx_sendTransaction` not implement yet.
|
609 | *
|
610 | * > NOTE: if `from` options is a instance of `Account`, this methods will sign by account local and send by `cfx_sendRawTransaction`, else send by `cfx_sendTransaction`
|
611 | *
|
612 | * @param options {object} - See `format.sendTx`
|
613 | * @return {Promise<PendingTransaction>} The PendingTransaction object.
|
614 | *
|
615 | * @example
|
616 | * > // TODO call with address, need `cfx_sendTransaction`
|
617 | *
|
618 | * @example
|
619 | * > const account = cfx.wallet.add(KEY);
|
620 | * > await cfx.sendTransaction({
|
621 | from: account, // from account instance will sign by local.
|
622 | to: ADDRESS,
|
623 | value: Drip.fromCFX(0.023),
|
624 | });
|
625 | "0x459473cb019bb59b935abf5d6e76d66564aafa313efd3e337b4e1fa6bd022cc9"
|
626 |
|
627 | * @example
|
628 | * > await cfx.sendTransaction({
|
629 | from: account,
|
630 | to: account, // to account instance
|
631 | value: Drip.fromCFX(0.03),
|
632 | }).get(); // send then get transaction by hash.
|
633 | {
|
634 | "blockHash": null,
|
635 | "transactionIndex": null,
|
636 | "hash": "0xf2b258b49d33dd22419526e168ebb79b822889cf8317ce1796e816cce79e49a2",
|
637 | "contractCreated": null,
|
638 | "data": "0x",
|
639 | "from": "0xbbd9e9be525ab967e633bcdaeac8bd5723ed4d6b",
|
640 | "nonce": 111,
|
641 | "status": null,
|
642 | "to": "0xbbd9e9be525ab967e633bcdaeac8bd5723ed4d6b",
|
643 | "value": "30000000000000000",
|
644 | ...
|
645 | }
|
646 |
|
647 | * @example
|
648 | * > const promise = cfx.sendTransaction({ // Not await here, just get promise
|
649 | from: account1,
|
650 | to: ADDRESS1,
|
651 | value: Drip.fromCFX(0.007),
|
652 | });
|
653 |
|
654 | * > await promise; // transaction
|
655 | "0x91fbdfb33f3a585f932c627abbe268c7e3aedffc1633f9338f9779c64702c688"
|
656 |
|
657 | * > await promise.get(); // get transaction
|
658 | {
|
659 | "blockHash": null,
|
660 | "transactionIndex": null,
|
661 | "hash": "0x91fbdfb33f3a585f932c627abbe268c7e3aedffc1633f9338f9779c64702c688",
|
662 | ...
|
663 | }
|
664 |
|
665 | * > await promise.mined(); // wait till transaction mined
|
666 | {
|
667 | "blockHash": "0xe9b22ce311003e26c7330ac54eea9f8afea0ffcd4905828f27c9e2c02f3a00f7",
|
668 | "transactionIndex": 0,
|
669 | "hash": "0x91fbdfb33f3a585f932c627abbe268c7e3aedffc1633f9338f9779c64702c688",
|
670 | ...
|
671 | }
|
672 |
|
673 | * > await promise.executed(); // wait till transaction executed in right status. and return it's receipt.
|
674 | {
|
675 | "blockHash": "0xe9b22ce311003e26c7330ac54eea9f8afea0ffcd4905828f27c9e2c02f3a00f7",
|
676 | "index": 0,
|
677 | "transactionHash": "0x91fbdfb33f3a585f932c627abbe268c7e3aedffc1633f9338f9779c64702c688",
|
678 | "outcomeStatus": 0,
|
679 | ...
|
680 | }
|
681 |
|
682 | * > await promise.confirmed(); // wait till transaction risk coefficient '<' threshold.
|
683 | {
|
684 | "blockHash": "0xe9b22ce311003e26c7330ac54eea9f8afea0ffcd4905828f27c9e2c02f3a00f7",
|
685 | "index": 0,
|
686 | "transactionHash": "0x91fbdfb33f3a585f932c627abbe268c7e3aedffc1633f9338f9779c64702c688",
|
687 | "outcomeStatus": 0,
|
688 | ...
|
689 | }
|
690 | */
|
691 | async sendTransaction(options) {
|
692 | if (options.gasPrice === undefined) {
|
693 | options.gasPrice = this.defaultGasPrice;
|
694 | }
|
695 |
|
696 | if (options.gas === undefined) {
|
697 | options.gas = this.defaultGas;
|
698 | }
|
699 |
|
700 | if (options.nonce === undefined) {
|
701 | options.nonce = await this.getTransactionCount(options.from);
|
702 | }
|
703 |
|
704 | if (options.from instanceof Wallet.Account) {
|
705 | // sign by local
|
706 | const tx = options.from.signTransaction(options);
|
707 | return this.sendRawTransaction(tx.serialize());
|
708 | }
|
709 |
|
710 | // sign by remote
|
711 | return this.provider.call('cfx_sendTransaction', format.sendTx(options));
|
712 | }
|
713 |
|
714 | /**
|
715 | * Signs a transaction. This account needs to be unlocked.
|
716 | *
|
717 | * @param hex {string|Buffer} - Raw transaction string.
|
718 | * @return {Promise<PendingTransaction>} The PendingTransaction object. See `sendTransaction`
|
719 | *
|
720 | * @example
|
721 | * > await cfx.sendRawTransaction('0xf85f800382520894bbd9e9b...');
|
722 | "0xbe007c3eca92d01f3917f33ae983f40681182cf618defe75f490a65aac016914"
|
723 | */
|
724 | async sendRawTransaction(hex) {
|
725 | return this.provider.call('cfx_sendRawTransaction', format.hex(hex));
|
726 | }
|
727 |
|
728 | // ------------------------------ contract ----------------------------------
|
729 | /**
|
730 | * Get the code at a specific address.
|
731 | *
|
732 | * @param address {string} - The contract address to get the code from.
|
733 | * @param [epochNumber=this.defaultEpoch] {string|number} - EpochNumber or string in ["latest_state", "latest_mined"]
|
734 | * @return {Promise<string>} Code hex string
|
735 | *
|
736 | * @example
|
737 | * > await cfx.getCode('0xb385b84f08161f92a195953b980c8939679e906a');
|
738 | "0x6080604052348015600f57600080fd5b506004361060325760003560e01c806306661abd1460375780638..."
|
739 | */
|
740 | async getCode(address, epochNumber = this.defaultEpoch) {
|
741 | return this.provider.call('cfx_getCode', format.address(address), format.epochNumber(epochNumber));
|
742 | }
|
743 |
|
744 | /**
|
745 | * Executes a message call transaction, which is directly executed in the VM of the node,
|
746 | * but never mined into the block chain.
|
747 | *
|
748 | * @param options {object} - See `format.sendTx`
|
749 | * @param [epochNumber=this.defaultEpoch] {string|number} - The end epochNumber to execute call of.
|
750 | * @return {Promise<string>} Hex bytes the contract method return.
|
751 | */
|
752 | async call(options, epochNumber = this.defaultEpoch) {
|
753 | if (options.gasPrice === undefined) {
|
754 | options.gasPrice = this.defaultGasPrice;
|
755 | }
|
756 |
|
757 | if (options.gas === undefined) {
|
758 | options.gas = this.defaultGas;
|
759 | }
|
760 |
|
761 | if (options.from && options.nonce === undefined) {
|
762 | options.nonce = await this.getTransactionCount(options.from);
|
763 | }
|
764 |
|
765 | return this.provider.call('cfx_call', format.callTx(options), format.epochNumber(epochNumber));
|
766 | }
|
767 |
|
768 | /**
|
769 | * Executes a message call or transaction and returns the amount of the gas used.
|
770 | *
|
771 | * @param options {object} - See `format.estimateTx`
|
772 | * @return {Promise<BigNumber>} The used gas for the simulated call/transaction.
|
773 | */
|
774 | async estimateGas(options) {
|
775 | if (options.gasPrice === undefined) {
|
776 | options.gasPrice = this.defaultGasPrice;
|
777 | }
|
778 |
|
779 | if (options.gas === undefined) {
|
780 | options.gas = this.defaultGas;
|
781 | }
|
782 |
|
783 | if (options.from && options.nonce === undefined) {
|
784 | options.nonce = await this.getTransactionCount(options.from);
|
785 | }
|
786 |
|
787 | const result = await this.provider.call('cfx_estimateGas', format.estimateTx(options));
|
788 | return format.bigNumber(result);
|
789 | }
|
790 | }
|
791 |
|
792 | module.exports = Conflux;
|