1 | const lodash = require('lodash');
|
2 | const ContractABICoder = require('./ContractABICoder');
|
3 | const ContractConstructor = require('./ContractConstructor');
|
4 | const ContractMethod = require('./ContractMethod');
|
5 | const ContractEvent = require('./ContractEvent');
|
6 |
|
7 | /**
|
8 | * Contract with all its methods and events defined in its abi.
|
9 | */
|
10 | class Contract {
|
11 | /**
|
12 | *
|
13 | * @param cfx {Conflux} - Conflux instance.
|
14 | * @param options {object}
|
15 | * @param options.abi {array} - The json interface for the contract to instantiate
|
16 | * @param [options.address] {string} - The address of the smart contract to call, can be added later using `contract.address = '0x1234...'`
|
17 | * @param [options.bytecode] {string} - The byte code of the contract, can be added later using `contract.constructor.code = '0x1234...'`
|
18 | * @return {object}
|
19 | *
|
20 | * @example
|
21 | * > const contract = cfx.Contract({ abi, bytecode });
|
22 |
|
23 | * > contract.constructor.bytecode; // input code
|
24 | "0x6080604052600080..."
|
25 |
|
26 | * @example
|
27 | * > const contract = cfx.Contract({ abi, address });
|
28 | * > contract.address
|
29 | "0xc3ed1a06471be1d3bcd014051fbe078387ec0ad8"
|
30 |
|
31 | * > await contract.count(); // call a method without parameter, get decoded return value.
|
32 | 100n
|
33 | * > await contract.inc(1); // call a method with parameters, get decoded return value.
|
34 | 101n
|
35 | * > await contract.count().call({ from: account }); // call a method from a account.
|
36 | 100n
|
37 | * > await contract.count().estimateGas();
|
38 | 21655n
|
39 | * > await contract.count().estimateGas({ from: ADDRESS, nonce: 68 }); // if from is a address string, nonce is required
|
40 | 21655n
|
41 |
|
42 | * // send transaction from account instance, then wait till confirmed, and get receipt.
|
43 | * > await contract.inc(1)
|
44 | .sendTransaction({ from: account1 })
|
45 | .confirmed({ threshold: 0.01, timeout: 30 * 1000 });
|
46 | {
|
47 | "blockHash": "0xba948c8925f6d7f14faf540c3b9e6d24d33c78168b2dd81a6021a50949d9f0d7",
|
48 | "index": 0,
|
49 | "transactionHash": "0x8a5f48c2de0f1bdacfe90443810ad650e4b327a0d19ce49a53faffb224883e42",
|
50 | "outcomeStatus": 0,
|
51 | ...
|
52 | }
|
53 |
|
54 | * > tx = await cfx.getTransactionByHash('0x8a5f48c2de0f1bdacfe90443810ad650e4b327a0d19ce49a53faffb224883e42');
|
55 | * > await contract.abi.decodeData(tx.data)
|
56 | {
|
57 | name: 'inc',
|
58 | fullName: 'inc(uint256 num)',
|
59 | type: 'inc(uint256)',
|
60 | signature: '0x7f98a45e',
|
61 | array: [ JSBI.BigInt(101) ],
|
62 | object: { num: JSBI.BigInt(101) }
|
63 | }
|
64 |
|
65 | * > await contract.count(); // data in block chain changed by transaction.
|
66 | JSBI.BigInt(101)
|
67 |
|
68 | * > logs = await contract.SelfEvent(account1.address).getLogs()
|
69 | [
|
70 | {
|
71 | address: '0xc3ed1a06471be1d3bcd014051fbe078387ec0ad8',
|
72 | blockHash: '0xc8cb678891d4914aa66670e3ebd7a977bb3e38d2cdb1e2df4c0556cb2c4715a4',
|
73 | data: '0x000000000000000000000000000000000000000000000000000000000000000a',
|
74 | epochNumber: 545896,
|
75 | logIndex: 0,
|
76 | removed: false,
|
77 | topics: [
|
78 | '0xc4c01f6de493c58245fb681341f3a76bba9551ce81b11cbbb5d6d297844594df',
|
79 | '0x000000000000000000000000bbd9e9be525ab967e633bcdaeac8bd5723ed4d6b'
|
80 | ],
|
81 | transactionHash: '0x9100f4f84f711aa358e140197e9d2e5aab1f99751bc26a660d324a8282fc54d0',
|
82 | transactionIndex: 0,
|
83 | transactionLogIndex: 0,
|
84 | type: 'mined',
|
85 | params: [ '0xbbd9e9be525ab967e633bcdaeac8bd5723ed4d6b', '10' ]
|
86 | }
|
87 | ]
|
88 |
|
89 | * > contract.abi.decodeLog(logs[0]);
|
90 | {
|
91 | name: 'SelfEvent',
|
92 | fullName: 'SelfEvent(address indexed sender, uint256 current)',
|
93 | type: 'SelfEvent(address,uint256))',
|
94 | signature: '0xc4c01f6de493c58245fb681341f3a76bba9551ce81b11cbbb5d6d297844594df',
|
95 | array: [ '0xbbd9e9be525ab967e633bcdaeac8bd5723ed4d6b', JSBI.BigInt(100) ],
|
96 | object: {
|
97 | sender: '0xbbd9e9be525ab967e633bcdaeac8bd5723ed4d6b',
|
98 | current: JSBI.BigInt(100),
|
99 | },
|
100 | }
|
101 | */
|
102 | constructor(cfx, { abi, address, bytecode }) {
|
103 | const abiTable = lodash.groupBy(abi, 'type');
|
104 |
|
105 | this.constructor = new ContractConstructor(cfx, this, lodash.first(abiTable.constructor), bytecode);
|
106 | this.abi = new ContractABICoder(this); // XXX: Create a method named `abi` in solidity is a `Warning`.
|
107 | this.address = address; // XXX: Create a method named `address` in solidity is a `ParserError`
|
108 |
|
109 | const methodArray = lodash.map(abiTable.function, fragment => new ContractMethod(cfx, this, fragment));
|
110 | const eventArray = lodash.map(abiTable.event, fragment => new ContractEvent(cfx, this, fragment));
|
111 |
|
112 | // name to instance
|
113 | lodash.forEach(lodash.groupBy(methodArray, 'name'), (array, name) => {
|
114 | this[name] = array.length === 1
|
115 | ? lodash.first(array) // no override
|
116 | : new ContractMethod.ContractMethodOverride(cfx, this, array);
|
117 | });
|
118 | lodash.forEach(lodash.groupBy(eventArray, 'name'), (array, name) => {
|
119 | this[name] = array.length === 1
|
120 | ? lodash.first(array) // no override
|
121 | : new ContractEvent.ContractEventOverride(cfx, this, array);
|
122 | });
|
123 |
|
124 | // type to instance
|
125 | // signature for contract abi decoder to decode
|
126 | methodArray.forEach(method => {
|
127 | this[method.type] = method;
|
128 | this[method.signature] = method; // signature for contract abi decoder to decode
|
129 | });
|
130 | eventArray.forEach(event => {
|
131 | this[event.type] = event;
|
132 | this[event.signature] = event; // signature for contract abi decoder to decode
|
133 | });
|
134 | }
|
135 | }
|
136 |
|
137 | module.exports = Contract;
|