Skip to content

Commit

Permalink
Fix abi decoder (#20)
Browse files Browse the repository at this point in the history
* Fix event decoding

1. ethereumjs-abi is deprecated, it needs to be replaced for a new library 2. the indexed address[] events were wrongly decoded. This change introduced 1. Leading 0s on not-even hex representations 2. try/catch on decodeLog

* Remove ethereumjs-abi from Contract

* Add address[] event arg to addresses

* Update package.json
  • Loading branch information
ilanolkies authored Sep 8, 2022
1 parent db52b61 commit 8ae26fc
Show file tree
Hide file tree
Showing 26 changed files with 10,526 additions and 701 deletions.
15 changes: 15 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: ci

on: push

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm ci
- run: npm run test:local
- run: mv dist/ dist2/
- run: npm run build
- run: diff -arq dist/ dist2/
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ src/tmp
tmp
coverage
.nyc_output
dist2
36 changes: 6 additions & 30 deletions dist/lib/Contract.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.default = Contract;var _rskUtils = require("@rsksmart/rsk-utils");
var _ethereumjsAbi = _interopRequireDefault(require("ethereumjs-abi"));function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}
"use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.default = Contract;var _abi = require("@ethersproject/abi");

function Contract(abi, { address, nod3 } = {}) {
if (!abi || typeof abi !== 'object') throw new Error('Invalid abi');
const contractInterface = new _abi.Interface(abi);

const at = newAddress => {
address = newAddress;
Expand All @@ -12,38 +12,14 @@ function Contract(abi, { address, nod3 } = {}) {
nod3 = nod3Instance;
};

const abiFind = (type, name) => abi.find(i => i.type === type && i.name === name);

const isMethod = name => abiFind('function', name);

// const isEvent = name => abiFind('event', name)

const getMethod = methodName => {
const abiDef = isMethod(methodName);
if (!abiDef) throw new Error(`Unknown method: "${methodName}"`);
const { name, inputs, outputs } = abiDef;
const types = inputs.filter(i => i.type).map(i => i.type);
const returns = outputs.filter(o => o.type).map(o => o.type);
const id = _ethereumjsAbi.default.methodID(name, types).toString('hex');
const method = `${name}(${types.join(' ')})`;
return { types, id, name, method, returns };
};

const encodeCall = (methodName, params = []) => {
try {
const { id, types } = getMethod(methodName);
let data = _ethereumjsAbi.default.rawEncode(types, params).toString('hex');
data = (0, _rskUtils.add0x)(`${id}${data}`);
return data;
} catch (err) {
throw err;
}
};
const encodeCall = (methodName, params = []) => contractInterface.encodeFunctionData(methodName, params);

const decodeCall = (methodName, data) => {
const { returns } = getMethod(methodName);
const decoded = _ethereumjsAbi.default.rawDecode(returns, (0, _rskUtils.toBuffer)(data));
return Array.isArray(decoded) && returns.length < 2 ? decoded[0] : decoded;
const { outputs } = contractInterface.getFunction(methodName);
const decoded = contractInterface.decodeFunctionResult(methodName, data);
return Array.isArray(decoded) && outputs && outputs.length < 2 ? decoded[0] : decoded;
};

const call = async (methodName, params = [], txData = {}) => {
Expand Down
8 changes: 7 additions & 1 deletion dist/lib/ContractParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ class ContractParser {
let value = args[i] || [];
if (Array.isArray(value)) {// temp fix to undecoded events
value.forEach(v => _addresses.push(v));
} else {
let i = 0;
while (2 + (i + 1) * 40 <= value.length) {
_addresses.push('0x' + value.slice(2 + i * 40, 2 + (i + 1) * 40));
i++;
}
}
}
});
Expand All @@ -97,7 +103,7 @@ class ContractParser {

decodeLogs(logs, abi) {
abi = abi || this.abi;
const eventDecoder = (0, _EventDecoder.default)(abi);
const eventDecoder = (0, _EventDecoder.default)(abi, this.log);
if (!this.nativeContracts || !this.nativeContractsEvents) {
throw new Error(`Native contracts decoder is missing, check the value of netId:${this.netId}`);
}
Expand Down
71 changes: 40 additions & 31 deletions dist/lib/EventDecoder.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
"use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.default = void 0;var _ethereumjsAbi = _interopRequireDefault(require("ethereumjs-abi"));
var _utils = require("./utils");
var _rskUtils = require("@rsksmart/rsk-utils");function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}
"use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.default = void 0;var _utils = require("./utils");
var _rskUtils = require("@rsksmart/rsk-utils");
var _abi = require("@ethersproject/abi");

function EventDecoder(abi) {
abi = (0, _utils.addSignatureDataToAbi)(abi);

const formatDecoded = decoded => {
return (0, _rskUtils.add0x)(Buffer.isBuffer(decoded) ? (0, _rskUtils.bufferToHex)(decoded) : decoded.toString(16));
};
function EventDecoder(abi, logger) {
const contractInterface = new _abi.Interface((0, _utils.addSignatureDataToAbi)(abi));

const getEventAbi = topics => {
topics = [...topics];
Expand All @@ -21,38 +17,51 @@ function EventDecoder(abi) {
return { eventABI, topics };
};

const decodeElement = (data, types) => {
let decoded = _ethereumjsAbi.default.rawDecode(types, (0, _rskUtils.toBuffer)(data));
const formatElement = (type, decoded) => {
if (decoded._isIndexed) return { _isIndexed: true, hash: decoded.hash };
if (decoded._isBigNumber) {
return decoded.toHexString();
}
const res = (0, _rskUtils.add0x)(Buffer.isBuffer(decoded) ? (0, _rskUtils.bufferToHex)(decoded) : decoded.toString(16));
if (type === 'address' || type === 'address[]') return res.toLowerCase();
return res;
};

const encodeElement = (type, decoded) => {
if (Array.isArray(decoded)) {
decoded = decoded.map(d => formatDecoded(d));
decoded = decoded.map(d => formatElement(type, d));
if (decoded.length === 1) decoded = decoded.join();
} else {
decoded = formatDecoded(decoded);
decoded = formatElement(type, decoded);
}
return decoded;
};

const decodeData = (data, types) => {
let decoded = _ethereumjsAbi.default.rawDecode(types, (0, _rskUtils.toBuffer)(data));
return decoded.map(d => formatDecoded(d));
};
const decodeLog = log => {
log = Object.assign({}, log);
const { eventABI, topics } = getEventAbi(log.topics);
const { address } = log;
if (!eventABI) return log;
const { name } = eventABI;
const { signature } = (0, _utils.getSignatureDataFromAbi)(eventABI);
const { inputs } = eventABI;
const indexedInputs = inputs.filter(i => i.indexed === true);
let decodedTopics = topics.map((topic, index) => decodeElement(topic, [indexedInputs[index].type]));
const decodedData = decodeData(log.data, inputs.filter(i => i.indexed === false).map(i => i.type));
const args = [];
for (let input of inputs) {
args.push(input.indexed ? decodedTopics.shift() : decodedData.shift());
try {
const { eventFragment, name, args, topic } = contractInterface.parseLog(log);

const { address } = log;

const parsedArgs = [];

for (const i in eventFragment.inputs) parsedArgs.push(
encodeElement(eventFragment.inputs[i].type, args[i]));


return Object.assign({}, log, {
signature: (0, _rskUtils.remove0x)(topic),
event: name,
address,
args: parsedArgs,
abi: JSON.parse(eventFragment.format('json')) });

} catch (e) {
logger.error(e);
return log;
}
return Object.assign(log, { event: name, address, args, abi: eventABI, signature });
};

return Object.freeze({ decodeLog, getEventAbi });
}var _default =

Expand Down
39 changes: 0 additions & 39 deletions dist/lib/NativeContracts.js

This file was deleted.

Loading

0 comments on commit 8ae26fc

Please sign in to comment.