Skip to content

Commit

Permalink
parse event logs
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex Vlasov committed Dec 26, 2017
1 parent 0eb09df commit 0929284
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,13 @@ class ViewController: UIViewController {
let result = try await((intermediate?.call(options: options))!)
print("BKX token name = " + (result!["0"] as! String))


let erc20receipt = try await(web3Main.eth.getTransactionReceipt("0x76bb19c0b7e2590f724871960599d28db99cd587506fdfea94062f9c8d61eb30"))
for l in (erc20receipt?.logs)! {
guard let result = contract?.parseEvent(l), let name = result.eventName, let data = result.eventData else {continue}
print("Parsed event " + name)
print("Parsed content")
print(data)
}
// Block number on Main

let blockNumber = try await(web3Main.eth.getBlockNumber())
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
- Serialize and deserialize transactions and results to native Swift types
- Convenience functions for chain state: block number, gas price
- Check transaction results and get receipt
- WIP: Parse event logs for transaction
- Parse event logs for transaction

## Example

Expand Down Expand Up @@ -40,4 +40,4 @@ Petr Korolev, @skywinder, [email protected]

## License

web3swift is available under the MIT license. See the LICENSE file for more info.
web3swift is available under the MIT license. See the LICENSE file for more info.
2 changes: 1 addition & 1 deletion web3swift.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "web3swift"
s.version = "0.1.0"
s.version = "0.1.1"
s.summary = "Web3 implementation in vanilla Swift"

s.description = <<-DESC
Expand Down
75 changes: 75 additions & 0 deletions web3swift/ABI/Classes/ABIDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,78 @@ extension ABIElement {
}
}
}

extension ABIElement {
func decodeReturnedLogs(_ eventLog: EventLog) -> [String:Any]? {
switch self {
case .constructor(_):
return nil
case .event(let event):
if event.anonymous {return nil}
if eventLog.topics[0] != event.topic {
return nil
}
var eventContent = [String: Any]()
eventContent["name"]=event.name
let logs = eventLog.topics
var j = 1
for i in 0 ..< event.inputs.count {
let el = event.inputs[i]
if el.indexed {
let elementData = logs[j]
j = j + 1
let expectedType = el.type
switch expectedType {
case .staticType(let type):
let decoded = type.decode(expectedType: type, data: elementData, tailPointer: BigUInt(0))
guard let value = decoded.value, let _ = decoded.bytesConsumed else {break}
let name = "\(i)"
eventContent[name] = value
if el.name != "" {
eventContent[el.name] = value
}
case .dynamicType(let type):
let decoded = type.decode(expectedType: type, data: elementData, tailPointer: BigUInt(0))
guard let value = decoded.value, let _ = decoded.bytesConsumed else {break}
let name = "\(i)"
eventContent[name] = value
if el.name != "" {
eventContent[el.name] = value
}
}
} else {
var dataForProcessing = eventLog.data
var tailPointer = BigUInt(0)
let expectedType = el.type
switch expectedType {
case .staticType(let type):
let decoded = type.decode(expectedType: type, data: dataForProcessing, tailPointer: BigUInt(0))
guard let value = decoded.value, let consumed = decoded.bytesConsumed else {break}
let name = "\(i)"
eventContent[name] = value
if el.name != "" {
eventContent[el.name] = value
}
dataForProcessing = Data(dataForProcessing[consumed...])
tailPointer = tailPointer + BigUInt(consumed)
case .dynamicType(let type):
let decoded = type.decode(expectedType: type, data: dataForProcessing, tailPointer: tailPointer)
guard let value = decoded.value, let consumed = decoded.bytesConsumed else {break}
let name = "\(i)"
eventContent[name] = value
if el.name != "" {
eventContent[el.name] = value
}
dataForProcessing = Data(dataForProcessing[consumed...])
tailPointer = tailPointer + BigUInt(consumed)
}
}
}
return eventContent
case .fallback(_):
return nil
case .function(_):
return nil
}
}
}
15 changes: 15 additions & 0 deletions web3swift/ABI/Classes/ABITypes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ public struct ABIRecord: Decodable {
var anonymous: Bool?
}

public struct EventLogJSON {

}

// Native parsing

Expand Down Expand Up @@ -233,6 +236,17 @@ extension ABIElement.Function {
}
}

// MARK: - Event topic
extension ABIElement.Event {
public var signature: String {
return "\(name)(\(inputs.map { $0.type.abiRepresentation }.joined(separator: ",")))"
}

public var topic: Data {
return signature.data(using: .ascii)!.sha3(.keccak256)
}
}

protocol AbiEncoding {
var abiRepresentation: String { get }
}
Expand Down Expand Up @@ -284,3 +298,4 @@ extension ABIElement.ParameterType.DynamicType: AbiEncoding {
}
}
}

24 changes: 24 additions & 0 deletions web3swift/Contract/Classes/Contract.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,20 @@ public struct Contract {
}
return toReturn
}
var events: [String: ABIElement] {
var toReturn = [String: ABIElement]()
for m in self._abi {
switch m {
case .event(let event):
let name = event.name
toReturn[name] = m
default:
continue
}
}
return toReturn
}

var options: Web3Options? = Web3Options.defaultOptions()
var chainID: BigUInt = BigUInt(1)

Expand Down Expand Up @@ -83,4 +97,14 @@ public struct Contract {
let transaction = EthereumTransaction(nonce: nonce, gasPrice: gasPrice, gasLimit: gas, to: to, value: value, data: encodedData, v: chainID, r: BigUInt(0), s: BigUInt(0))
return transaction
}

public func parseEvent(_ eventLog: EventLog) -> (eventName:String?, eventData:[String:Any]?) {
for (eName, ev) in self.events {
let parsed = ev.decodeReturnedLogs(eventLog)
if parsed != nil {
return (eName, parsed!)
}
}
return (nil, nil)
}
}
4 changes: 4 additions & 0 deletions web3swift/Contract/Classes/Web3+Contract.swift
Original file line number Diff line number Diff line change
Expand Up @@ -192,5 +192,9 @@ extension web3 {
let intermediate = transactionIntermediate(transaction: tx, web3: self.web3, contract: self.contract, method: method, options: mergedOptions)
return intermediate
}

public func parseEvent(_ eventLog: EventLog) -> (eventName:String?, eventData:[String:Any]?) {
return self.contract.parseEvent(eventLog)
}
}
}
42 changes: 35 additions & 7 deletions web3swift/Contract/Classes/Web3+Structures.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public struct TransactionReceipt {
public var contractAddress: EthereumAddress?
public var cumulativeGasUsed: BigUInt
public var gasUsed: BigUInt
public var logs: [Data] = [Data]()
public var logs: [EventLog]
public var status: TXStatus

public enum TXStatus {
Expand All @@ -81,7 +81,7 @@ public struct TransactionReceipt {
let ca = json["contractAddress"] as? String
guard let cgu = json["cumulativeGasUsed"] as? String else {return nil}
guard let gu = json["gasUsed"] as? String else {return nil}
// guard let ls = json["logs"] as? [String] else {return nil}
guard let ls = json["logs"] as? Array<[String:Any]> else {return nil}
guard let st = json["status"] as? String else {return nil}

transactionHash = h
Expand All @@ -97,11 +97,11 @@ public struct TransactionReceipt {
cumulativeGasUsed = cguUnwrapped
guard let guUnwrapped = BigUInt(gu.stripHexPrefix(), radix: 16) else {return nil}
gasUsed = guUnwrapped
var allLogs = [Data]()
// for l in ls {
// let logData = Data(Array<UInt8>(hex: l.lowercased().stripHexPrefix()))
// allLogs.append(logData)
// }
var allLogs = [EventLog]()
for l in ls {
guard let log = EventLog(l) else {return nil}
allLogs.append(log)
}
logs = allLogs
if st == "0x1" {
status = TXStatus.ok
Expand All @@ -111,3 +111,31 @@ public struct TransactionReceipt {
}
}

public struct EventLog {
public var address: EthereumAddress
public var data: Data
public var logIndex: BigUInt
public var removed: Bool
public var topics: [Data]

public init? (_ json: [String: Any]) {
guard let ad = json["address"] as? String else {return nil}
guard let d = json["data"] as? String else {return nil}
guard let li = json["logIndex"] as? String else {return nil}
guard let rm = json["removed"] as? Int else {return nil}
guard let tpc = json["topics"] as? [String] else {return nil}
address = EthereumAddress(ad)
data = Data(Array<UInt8>(hex: d.lowercased().stripHexPrefix()))

guard let liUnwrapped = BigUInt(li.stripHexPrefix(), radix: 16) else {return nil}
logIndex = liUnwrapped
removed = rm == 1 ? true : false
var tops = [Data]()
for t in tpc {
let topic = Data(Array<UInt8>(hex: t.lowercased().stripHexPrefix()))
tops.append(topic)
}
topics = tops
}
}

0 comments on commit 0929284

Please sign in to comment.