Skip to content

Commit

Permalink
Merge pull request #36 from Vaultio/fixing_abi_encoding
Browse files Browse the repository at this point in the history
Cleaning up ABI encoding to correctly encode function calls as tuples
  • Loading branch information
pr0zac authored Sep 14, 2018
2 parents cb718d4 + 0e9963b commit 9478b54
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 35 deletions.
109 changes: 102 additions & 7 deletions EtherKit/ABI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ public indirect enum ABIType: CustomStringConvertible {
case address(value: Address)
case bytes(count: UnformattedDataMode, value: Data)
case array(count: UnformattedDataMode, type: ABIType, value: [ABIType])
case functionSelector(name: String, parameterTypes: [ABIType], contract: Address?)
case tuple(value: [ABIType])
case function(name: String, parameters: [ABIType], contract: Address?)

public var description: String {
switch self {
Expand Down Expand Up @@ -43,11 +44,13 @@ public indirect enum ABIType: CustomStringConvertible {
case let .constrained(count):
return String(describing: type) + "[\(count)]"
}
case let .functionSelector(name, parameterTypes, _):
let parameterString = parameterTypes
case let .function(name, parameters, _):
let parameterString = parameters
.compactMap { String(describing: $0) }
.joined(separator: ",")
return "\(name)(\(parameterString))"
case .tuple:
return "tuple[]"
}
}

Expand Down Expand Up @@ -77,7 +80,14 @@ public indirect enum ABIType: CustomStringConvertible {
case .constrained:
return type.isDynamic
}
case .functionSelector:
case .function:
return false
case let .tuple(value):
for subValue in value {
if subValue.isDynamic {
return true
}
}
return false
}
}
Expand Down Expand Up @@ -174,7 +184,7 @@ public indirect enum ABIType: CustomStringConvertible {
data.append(elemData)
}
}
case let .functionSelector(_, _, contract):
case let .function(_, _, contract):
let funcSig = String(describing: self)

if let contract = contract {
Expand All @@ -185,6 +195,43 @@ public indirect enum ABIType: CustomStringConvertible {
}
let fullHash = asciiBytes.sha3(.keccak256)
data.append(fullHash[0 ..< 4])
case let .tuple(value):
var headDatas: [Data] = []
var valueDatas: [Data] = []
var prefixLength = 0

for tupleElem in value {
let elemData = tupleElem.encode()

guard tupleElem.isDynamic else {
headDatas.append(elemData)
valueDatas.append(Data())
prefixLength = prefixLength + elemData.count
continue
}

// placeholder to be filled in later
let placeholder = BigUInt(1).abiType.encode()
headDatas.append(placeholder)
valueDatas.append(elemData)
prefixLength = prefixLength + placeholder.count
}

for i in 0 ... (value.count - 1) {
let tupleElem = value[i]

if tupleElem.isDynamic {
headDatas[i] = BigUInt(prefixLength).abiType.encode()
prefixLength = prefixLength + valueDatas[i].count
}
}

for headData in headDatas {
data.append(headData)
}
for valueData in valueDatas {
data.append(valueData)
}
}

return data
Expand Down Expand Up @@ -223,12 +270,60 @@ extension Int: ABIValueType {
}
}

extension Int8: ABIValueType {
public var abiType: ABIType {
return .int(size: 8, value: BigInt(self))
}
}

extension Int16: ABIValueType {
public var abiType: ABIType {
return .int(size: 16, value: BigInt(self))
}
}

extension Int32: ABIValueType {
public var abiType: ABIType {
return .int(size: 32, value: BigInt(self))
}
}

extension Int64: ABIValueType {
public var abiType: ABIType {
return .int(size: 64, value: BigInt(self))
}
}

extension UInt: ABIValueType {
public var abiType: ABIType {
return .uint(size: 64, value: BigUInt(self))
}
}

extension UInt8: ABIValueType {
public var abiType: ABIType {
return .uint(size: 8, value: BigUInt(self))
}
}

extension UInt16: ABIValueType {
public var abiType: ABIType {
return .uint(size: 16, value: BigUInt(self))
}
}

extension UInt32: ABIValueType {
public var abiType: ABIType {
return .uint(size: 32, value: BigUInt(self))
}
}

extension UInt64: ABIValueType {
public var abiType: ABIType {
return .uint(size: 64, value: BigUInt(self))
}
}

extension Address: ABIValueType {
public var abiType: ABIType {
return .address(value: self)
Expand Down Expand Up @@ -264,12 +359,12 @@ extension Array: ABIValueType where Element: ABIValueType {
}
}

extension FunctionSelector: ABIValueType {
extension Function: ABIValueType {
public var isDynamic: Bool {
return false
}

public var abiType: ABIType {
return .functionSelector(name: name, parameterTypes: parameterTypes, contract: contract)
return .function(name: name, parameters: parameters, contract: contract)
}
}
34 changes: 12 additions & 22 deletions EtherKit/Models/Function.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,29 @@
import BigInt
import Foundation

public struct FunctionSelector {
public struct Function {
public var name: String
public var parameterTypes: [ABIType]
public var parameters: [ABIType]
public var contract: Address?

public init(name: String, parameterTypes: [ABIType], contract: Address? = nil) {
public init(name: String, parameters: [ABIType], contract: Address? = nil) {
self.name = name
self.parameterTypes = parameterTypes
self.contract = contract
}
}

public struct Function {
public var functionSelector: FunctionSelector
public var parameters: [ABIValueType]

public init(functionSelector: FunctionSelector, parameters: [ABIValueType]) {
self.functionSelector = functionSelector
self.parameters = parameters
self.contract = contract
}

public init(name: String, parameters: [ABIValueType]) {
let parameterTypes: [ABIType] = parameters.compactMap { $0.abiType }
functionSelector = FunctionSelector(name: name, parameterTypes: parameterTypes)
self.parameters = parameters
public init(name: String, parameters: [ABIValueType], contract: Address? = nil) {
self.name = name
self.parameters = parameters.compactMap { $0.abiType }
self.contract = contract
}

public func encodeToCall() -> Data {
var data = Data()
data.append(functionSelector.abiType.encode())
for parameter in parameters {
data.append(parameter.abiType.encode())
}
data.append(abiType.encode())

var paramTuple = ABIType.tuple(value: parameters)
data.append(paramTuple.encode())
return data
}
}
2 changes: 2 additions & 0 deletions Example/EtherKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@
A16A63C6B34BCC2C66991033 /* Pods */,
374062D57F115FAEB32E6109 /* Frameworks */,
);
indentWidth = 2;
sourceTree = "<group>";
tabWidth = 2;
};
607FACD11AFB9204008FA782 /* Products */ = {
isa = PBXGroup;
Expand Down
12 changes: 6 additions & 6 deletions Example/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ PODS:
- BigInt (3.1.0):
- SipHash (~> 1.2)
- CryptoSwift (0.10.0)
- EtherKit (0.1.5):
- EtherKit/Core (= 0.1.5)
- EtherKit/PromiseKit (= 0.1.5)
- EtherKit/Core (0.1.5):
- EtherKit (0.2.0-beta1):
- EtherKit/Core (= 0.2.0-beta1)
- EtherKit/PromiseKit (= 0.2.0-beta1)
- EtherKit/Core (0.2.0-beta1):
- BigInt
- CryptoSwift
- Marshal
- Result (~> 4.0.0)
- secp256k1.swift
- Starscream
- EtherKit/PromiseKit (0.1.5):
- EtherKit/PromiseKit (0.2.0-beta1):
- EtherKit/Core
- PromiseKit/CorePromise
- Marshal (1.2.4)
Expand Down Expand Up @@ -46,7 +46,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
BigInt: 76b5dfdfa3e2e478d4ffdf161aeede5502e2742f
CryptoSwift: 6c778d69282bed3b4e975ff97a79d074f20bb011
EtherKit: f0ea0a27a5996b898261892c421a8284ebf15c29
EtherKit: 2e07c1d3f30f12cb31e721d92c747437ee2ea1aa
Marshal: 8e04e6624e506921db7143b0bfd83caee03f32d6
PromiseKit: cf84bbb1235a61473b326c5cf0b41f6828f87ba5
Result: 7645bb3f50c2ce726dd0ff2fa7b6f42bbe6c3713
Expand Down

0 comments on commit 9478b54

Please sign in to comment.