Skip to content

Commit b15e063

Browse files
Merge pull request #802 from 6od9i/develop
- ABIDecoding getting data slice in followTheData changed to using start data index
2 parents 203c41a + 3b28de6 commit b15e063

File tree

4 files changed

+115
-12
lines changed

4 files changed

+115
-12
lines changed

Sources/Web3Core/Contract/ContractProtocol.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,8 @@ extension ContractProtocol {
210210

211211
func decodeInputData(_ data: Data) -> [String: Any]? {
212212
guard data.count >= 4 else { return nil }
213-
let methodId = data[0..<4].toHexString()
214-
let data = data[4...]
213+
let methodId = data[data.indices.startIndex ..< data.indices.startIndex + 4].toHexString()
214+
let data = data[(data.indices.startIndex + 4)...]
215215
return decodeInputData(methodId, data: data)
216216
}
217217
}
@@ -333,14 +333,14 @@ extension DefaultContractProtocol {
333333

334334
public func decodeInputData(_ data: Data) -> [String: Any]? {
335335
guard data.count % 32 == 4 else { return nil }
336-
let methodSignature = data[0..<4].toHexString().addHexPrefix().lowercased()
336+
let methodSignature = data[data.indices.startIndex ..< data.indices.startIndex + 4].toHexString().addHexPrefix().lowercased()
337337

338338
guard let function = methods[methodSignature]?.first else { return nil }
339-
return function.decodeInputData(Data(data[4 ..< data.count]))
339+
return function.decodeInputData(Data(data[data.indices.startIndex + 4 ..< data.indices.startIndex + data.count]))
340340
}
341341

342342
public func getFunctionCalled(_ data: Data) -> ABI.Element.Function? {
343343
guard data.count >= 4 else { return nil }
344-
return methods[data[0..<4].toHexString().addHexPrefix()]?.first
344+
return methods[data[data.indices.startIndex ..< data.indices.startIndex + 4].toHexString().addHexPrefix()]?.first
345345
}
346346
}

Sources/Web3Core/EthereumABI/ABIDecoding.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,12 +188,12 @@ extension ABIDecoder {
188188
fileprivate static func followTheData(type: ABI.Element.ParameterType, data: Data, pointer: UInt64 = 0) -> (elementEncoding: Data?, nextElementPointer: UInt64?) {
189189
if type.isStatic {
190190
guard data.count >= pointer + type.memoryUsage else {return (nil, nil)}
191-
let elementItself = data[pointer ..< pointer + type.memoryUsage]
191+
let elementItself = data[data.indices.startIndex + Int(pointer) ..< data.indices.startIndex + Int(pointer + type.memoryUsage)]
192192
let nextElement = pointer + type.memoryUsage
193193
return (Data(elementItself), nextElement)
194194
} else {
195195
guard data.count >= pointer + type.memoryUsage else {return (nil, nil)}
196-
let dataSlice = data[pointer ..< pointer + type.memoryUsage]
196+
let dataSlice = data[data.indices.startIndex + Int(pointer) ..< data.indices.startIndex + Int(pointer + type.memoryUsage)]
197197
let bn = BigUInt(dataSlice)
198198
if bn > UInt64.max || bn >= data.count {
199199
// there are ERC20 contracts that use bytes32 instead of string. Let's be optimistic and return some data

Sources/Web3Core/EthereumABI/ABIElements.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -397,9 +397,9 @@ extension ABI.Element.Function {
397397
/// 4) `messageLength` is used to determine where message bytes end to decode string correctly.
398398
/// 5) The rest of the `data` must be 0 bytes or empty.
399399
if data.bytes.count >= 100,
400-
Data(data[0..<4]) == Data.fromHex("08C379A0"),
401-
BigInt(data[4..<36]) == 32,
402-
let messageLength = Int(Data(data[36..<68]).toHexString(), radix: 16),
400+
Data(data[data.indices.startIndex ..< data.indices.startIndex + 4]) == Data.fromHex("08C379A0"),
401+
BigInt(data[data.indices.startIndex + 4 ..< data.indices.startIndex + 36]) == 32,
402+
let messageLength = Int(Data(data[data.indices.startIndex + 36 ..< data.indices.startIndex + 68]).toHexString(), radix: 16),
403403
let message = String(bytes: data.bytes[68..<(68+messageLength)], encoding: .utf8),
404404
(68+messageLength == data.count || data.bytes[68+messageLength..<data.count].reduce(0) { $0 + $1 } == 0) {
405405
return ["_success": false,
@@ -410,11 +410,11 @@ extension ABI.Element.Function {
410410

411411
if data.count >= 4,
412412
let errors = errors,
413-
let customError = errors[data[0..<4].toHexString().stripHexPrefix()] {
413+
let customError = errors[data[data.indices.startIndex ..< data.indices.startIndex + 4].toHexString().stripHexPrefix()] {
414414
var errorResponse: [String: Any] = ["_success": false, "_abortedByRevertOrRequire": true, "_error": customError.errorDeclaration]
415415

416416
if (data.count > 32 && !customError.inputs.isEmpty),
417-
let decodedInputs = ABIDecoder.decode(types: customError.inputs, data: Data(data[4..<data.count])) {
417+
let decodedInputs = ABIDecoder.decode(types: customError.inputs, data: Data(data[data.indices.startIndex + 4 ..< data.indices.startIndex + data.count])) {
418418
for idx in decodedInputs.indices {
419419
errorResponse["\(idx)"] = decodedInputs[idx]
420420
if !customError.inputs[idx].name.isEmpty {
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
//
2+
// ABIDecoderSliceTests.swift
3+
// localTests
4+
//
5+
// Created by 6od9i on 24.03.2023.
6+
//
7+
8+
import Foundation
9+
import Web3Core
10+
import XCTest
11+
import BigInt
12+
@testable import web3swift
13+
14+
final class ABIDecoderSliceTests: XCTestCase {
15+
func testBallancesDataSlice() throws {
16+
/// Arrange
17+
let balanceofMethod = try EthereumContract(Web3.Utils.erc20ABI).methods["balanceOf"]!.first!
18+
let correctValues = ["13667129429770787859", "3298264", "47475", "19959", "607690442193821", "999170411478050086"]
19+
let hex6Responses =
20+
"000000000000000000000000000000000000000000000000bdab65ce08c65c1300000000000000000000000000000000000000000000000000000000003253d8000000000000000000000000000000000000000000000000000000000000b9730000000000000000000000000000000000000000000000000000000000004df7000000000000000000000000000000000000000000000000000228b0f4f0bb9d0000000000000000000000000000000000000000000000000dddc432063ae526"
21+
let data = Data(hex: hex6Responses)
22+
let answerSize = 32
23+
var startIndex = 0
24+
var results = [String]()
25+
26+
/// Act
27+
while startIndex < data.count {
28+
let slice = data[startIndex ..< startIndex + answerSize]
29+
startIndex += answerSize
30+
guard let bigInt = balanceofMethod.decodeReturnData(slice)["0"] as? BigUInt else {
31+
throw Web3Error.processingError(desc: "Can not decode returned parameters")
32+
}
33+
let value = Utilities.formatToPrecision(bigInt, units: .wei)
34+
results.append(value)
35+
}
36+
37+
/// Assert
38+
XCTAssertEqual(correctValues, results)
39+
}
40+
41+
func testDecodeMulticallDifferentValues() async throws {
42+
/// Arrange
43+
let multiCall2Contract = try EthereumContract(Self.multiCall2, at: nil)
44+
let differentRequestsContract = try EthereumContract(Self.differentRequestsContract, at: nil)
45+
46+
let data = Data(hex: "0000000000000000000000000000000000000000000000000000000001980dd40000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000001af3f329e8be154074d8769d1ffa4ee058b1dbc3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000038d7ea4c68000000000000000000000000000000000000000000000000000000000006358d8a5000000000000000000000000000000000000000000000000000000007628d02500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000212295b818158b400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000")
47+
48+
let methods = [differentRequestsContract.methods["arrayValue"]?.first,
49+
differentRequestsContract.methods["firstValue"]?.first,
50+
differentRequestsContract.methods["secondValue"]?.first].compactMap({$0})
51+
52+
XCTAssertEqual(methods.count, 3)
53+
54+
/// Act
55+
guard let decodedData = multiCall2Contract.decodeReturnData("aggregate", data: data) else {
56+
throw Web3Error.processingError(desc: "Can not decode returned parameters")
57+
}
58+
59+
guard let returnData = decodedData["returnData"] as? [Data] else {
60+
throw Web3Error.dataError
61+
}
62+
63+
XCTAssertEqual(returnData.count, 3)
64+
65+
for item in methods.enumerated() {
66+
XCTAssertNotNil(item.element.decodeReturnData(returnData[item.offset])["0"])
67+
}
68+
}
69+
70+
func testDecodeMulticallCopy() throws {
71+
/// Arrange
72+
let data = Data(hex: "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000004fe6dab4abca350650")
73+
let contract = try EthereumContract(Self.multiCall2, at: nil)
74+
let erc20_balanceof = try EthereumContract(Web3.Utils.erc20ABI).methods["balanceOf"]!.first!
75+
76+
/// Act
77+
guard let decodedData = contract.decodeReturnData("tryAggregate", data: data) else {
78+
throw Web3Error.processingError(desc: "Can not decode returned parameters")
79+
}
80+
81+
guard let returnData = decodedData["returnData"] as? [[Any]] else {
82+
throw Web3Error.dataError
83+
}
84+
var resultArray = [BigUInt]()
85+
for i in 0..<2 {
86+
guard let data = returnData[i][1] as? Data,
87+
let balance = erc20_balanceof.decodeReturnData(data)["0"] as? BigUInt else {
88+
resultArray.append(0)
89+
continue
90+
}
91+
resultArray.append(balance)
92+
}
93+
94+
/// Assert
95+
XCTAssert(resultArray.count == 2)
96+
}
97+
}
98+
99+
extension ABIDecoderSliceTests {
100+
public static let differentRequestsContract = "[{\"inputs\":[],\"name\":\"firstValue\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"arrayValue\",\"outputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"enum IInstanceV1.Period\",\"name\":\"period\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"startTime\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"endTime\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"secondValue\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]"
101+
102+
public static let multiCall2 = "[{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"internalType\":\"struct Multicall2.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\"}],\"name\":\"aggregate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"returnData\",\"type\":\"bytes[]\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"internalType\":\"struct Multicall2.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\"}],\"name\":\"blockAndAggregate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"internalType\":\"struct Multicall2.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"name\":\"getBlockHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getBlockNumber\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentBlockCoinbase\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"coinbase\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentBlockDifficulty\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"difficulty\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentBlockGasLimit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"gaslimit\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentBlockTimestamp\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"getEthBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLastBlockHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"requireSuccess\",\"type\":\"bool\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"internalType\":\"struct Multicall2.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\"}],\"name\":\"tryAggregate\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"internalType\":\"struct Multicall2.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"requireSuccess\",\"type\":\"bool\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"internalType\":\"struct Multicall2.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\"}],\"name\":\"tryBlockAndAggregate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"internalType\":\"struct Multicall2.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]"
103+
}

0 commit comments

Comments
 (0)