UNPKG

4.15 kBJavaScriptView Raw
1const lodash = require('lodash');
2const { FunctionCoder, errorCoder } = require('../abi');
3const callable = require('../lib/callable');
4
5/**
6 * @memberOf ContractMethod
7 */
8class Called {
9 constructor(cfx, method, { to, data }) {
10 this.cfx = cfx;
11 this.method = method;
12 this.to = to;
13 this.data = data;
14 }
15
16 /**
17 * Will send a transaction to the smart contract and execute its method.
18 * set contract.address as `to`,
19 * set contract method encode as `data`.
20 *
21 * > Note: This can alter the smart contract state.
22 *
23 * @param options {object} - See `format.sendTx`
24 * @return {Promise<PendingTransaction>} The PendingTransaction object.
25 */
26 sendTransaction(options) {
27 return this.cfx.sendTransaction({
28 to: this.to,
29 data: this.data,
30 ...options,
31 });
32 }
33
34 /**
35 * Executes a message call or transaction and returns the amount of the gas used.
36 * set contract.address as `to`,
37 * set contract method encode as `data`.
38 *
39 * @param options {object} - See `format.estimateTx`
40 * @return {Promise<object>} The gas used and storage occupied for the simulated call/transaction.
41 */
42 async estimateGasAndCollateral(options) {
43 try {
44 return await this.cfx.estimateGasAndCollateral({ to: this.to, data: this.data, ...options });
45 } catch (e) {
46 throw errorCoder.decodeError(e);
47 }
48 }
49
50 /**
51 * Executes a message call transaction,
52 * set contract.address as `to`,
53 * set contract method encode as `data`.
54 *
55 * > Note: Can not alter the smart contract state.
56 *
57 * @param options {object} - See `format.callTx`.
58 * @param epochNumber {string|number} - See `Conflux.call`.
59 * @return {Promise<*>} Decoded contact call return.
60 */
61 async call(options, epochNumber) {
62 try {
63 const hex = await this.cfx.call({ to: this.to, data: this.data, ...options }, epochNumber);
64 return this.method.decodeOutputs(hex);
65 } catch (e) {
66 throw errorCoder.decodeError(e);
67 }
68 }
69
70 async then(resolve, reject) {
71 try {
72 const result = await this.call();
73 resolve(result);
74 } catch (e) {
75 reject(e);
76 }
77 }
78}
79
80class ContractMethod extends FunctionCoder {
81 constructor(cfx, contract, fragment) {
82 super(fragment);
83 this.cfx = cfx;
84 this.contract = contract;
85
86 return callable(this, this.call.bind(this));
87 }
88
89 call(...args) {
90 const to = this.contract.address;
91 const data = this.encodeData(args);
92 return new Called(this.cfx, this, { to, data });
93 }
94
95 decodeData(hex) {
96 const namedTuple = super.decodeData(hex);
97 return {
98 name: this.name,
99 fullName: this.fullName,
100 type: this.type,
101 signature: this.signature,
102 array: [...namedTuple],
103 object: namedTuple.toObject(),
104 };
105 }
106}
107
108/**
109 * @memberOf ContractMethod
110 */
111class ContractMethodOverride {
112 constructor(cfx, contract, methods) {
113 this.cfx = cfx;
114 this.contract = contract;
115 this.signatureToMethod = lodash.keyBy(methods, 'signature');
116
117 return callable(this, this.call.bind(this));
118 }
119
120 call(...args) {
121 const acceptArray = [];
122 const rejectArray = [];
123
124 let called;
125 for (const method of Object.values(this.signatureToMethod)) {
126 try {
127 called = method(...args);
128 acceptArray.push(method.type);
129 } catch (e) {
130 rejectArray.push(method.type);
131 }
132 }
133
134 if (!acceptArray.length) {
135 throw new Error(`can not match override "${rejectArray.join('|')}" with args (${args.join(',')})`);
136 }
137 if (acceptArray.length > 1) {
138 throw new Error(`can not determine override "${acceptArray.join('|')}" with args (${args.join(',')})`);
139 }
140
141 return called;
142 }
143
144 decodeData(hex) {
145 const signature = hex.slice(0, 10); // '0x' + 8 hex
146 const method = this.signatureToMethod[signature];
147 return method.decodeData(hex);
148 }
149}
150
151module.exports = ContractMethod;
152module.exports.ContractMethodOverride = ContractMethodOverride;
153module.exports.Called = Called;