1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
import { DataItem, MethodAbi } from 'ethereum-types';
import * as ethUtil from 'ethereumjs-util';
import * as _ from 'lodash';
import { RawCalldata } from '../calldata';
import * as Constants from '../constants';
import { DataType, DataTypeFactory, MemberDataType } from '../data_type';
import { DecodingRules, EncodingRules } from '../utils/rules';
import { StaticBytes } from './static_bytes';
import { Tuple } from './tuple';
export class Method extends MemberDataType {
// TMP
public selector: string;
private readonly _methodSignature: string;
private readonly _methodSelector: string;
private readonly _returnDataTypes: DataType[];
private readonly _returnDataItem: DataItem;
public constructor(abi: MethodAbi, dataTypeFactory: DataTypeFactory) {
super({ type: 'method', name: abi.name, components: abi.inputs }, dataTypeFactory);
this._methodSignature = this._computeSignature();
this.selector = this._methodSelector = this._computeSelector();
this._returnDataTypes = [];
this._returnDataItem = { type: 'tuple', name: abi.name, components: abi.outputs };
const dummy = new StaticBytes({ type: 'byte', name: 'DUMMY' }, dataTypeFactory); // @TODO TMP
_.each(abi.outputs, (dataItem: DataItem) => {
this._returnDataTypes.push(this.getFactory().create(dataItem, dummy));
});
}
public encode(value: any, rules?: EncodingRules): string {
const calldata = super.encode(value, rules, this.selector);
return calldata;
}
public decode(calldata: string, rules?: DecodingRules): any[] | object {
if (!calldata.startsWith(this.selector)) {
throw new Error(
`Tried to decode calldata, but it was missing the function selector. Expected '${this.selector}'.`,
);
}
const hasSelector = true;
const value = super.decode(calldata, rules, hasSelector);
return value;
}
public encodeReturnValues(value: any, rules?: EncodingRules): string {
const returnDataType = new Tuple(this._returnDataItem, this.getFactory());
const returndata = returnDataType.encode(value, rules);
return returndata;
}
public decodeReturnValues(returndata: string, rules?: DecodingRules): any {
const returnValues: any[] = [];
const rules_: DecodingRules = rules ? rules : { structsAsObjects: false };
const rawReturnData = new RawCalldata(returndata, false);
_.each(this._returnDataTypes, (dataType: DataType) => {
returnValues.push(dataType.generateValue(rawReturnData, rules_));
});
return returnValues;
}
public getSignature(): string {
return this._methodSignature;
}
public getSelector(): string {
return this._methodSelector;
}
private _computeSignature(): string {
const memberSignature = this._computeSignatureOfMembers();
const methodSignature = `${this.getDataItem().name}${memberSignature}`;
return methodSignature;
}
private _computeSelector(): string {
const signature = this._computeSignature();
const selector = ethUtil.bufferToHex(
ethUtil.toBuffer(
ethUtil
.sha3(signature)
.slice(Constants.HEX_SELECTOR_BYTE_OFFSET_IN_CALLDATA, Constants.HEX_SELECTOR_LENGTH_IN_BYTES),
),
);
return selector;
}
}
|