UNPKG

31 kBJavaScriptView Raw
1const { decorate } = require('./util');
2const format = require('./util/format');
3
4const providerFactory = require('./provider');
5const Contract = require('./contract');
6const Wallet = require('./wallet');
7const { PendingTransaction, LogIterator } = require('./subscribe');
8
9/**
10 * A sdk of conflux.
11 */
12class 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
792module.exports = Conflux;