UNPKG

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