1 | require = require('esm')(module);
|
2 | import { decorate } from './util';
|
3 | import format from './util/format';
|
4 |
|
5 | import providerFactory from './provider';
|
6 | import Contract from './contract';
|
7 | import Account from './Account';
|
8 | import { PendingTransaction, LogIterator } from './subscribe';
|
9 |
|
10 | /**
|
11 | * A sdk of conflux.
|
12 | */
|
13 | export 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 | }
|