Skip to content

Commit

Permalink
Cleaning up ABI encoding to correctly encode function calls as tuples,
Browse files Browse the repository at this point in the history
also adding other Int/UInt ABIValueTypes
  • Loading branch information
pr0zac committed Sep 14, 2018
1 parent cb718d4 commit fc8cc58
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 35 deletions.
108 changes: 101 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,42 @@ 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()

if tupleElem.isDynamic {
// placeholder to be filled in later
let placeholder = BigUInt(1).abiType.encode()
headDatas.append(placeholder)
valueDatas.append(elemData)
prefixLength = prefixLength + placeholder.count
} else {
headDatas.append(elemData)
valueDatas.append(Data())
prefixLength = prefixLength + elemData.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 +269,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 +358,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 fc8cc58

Please sign in to comment.