Skip to content

Commit 4c855e9

Browse files
author
Alex Vlasov
committed
main work for synchronous search through indexed events
1 parent f0c066d commit 4c855e9

File tree

9 files changed

+222
-56
lines changed

9 files changed

+222
-56
lines changed

web3swift/Contract/Classes/ContractProtocol.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,26 @@ public struct EventFilter {
6969
public var toBlock: Block?
7070
public var addresses: [EthereumAddress]?
7171
public var parameterFilters: [[EventFilterable]?]?
72+
73+
public func rpcPreEncode() -> EventFilterParameters {
74+
var encoding = EventFilterParameters()
75+
if self.fromBlock != nil {
76+
encoding.fromBlock = self.fromBlock!.encoded
77+
}
78+
if self.toBlock != nil {
79+
encoding.toBlock = self.toBlock!.encoded
80+
}
81+
if self.addresses != nil {
82+
if self.addresses!.count == 1 {
83+
encoding.address = [self.addresses![0].address]
84+
} else {
85+
var encodedAddresses = [String?]()
86+
for addr in self.addresses! {
87+
encodedAddresses.append(addr.address)
88+
}
89+
encoding.address = encodedAddresses
90+
}
91+
}
92+
return encoding
93+
}
7294
}

web3swift/Contract/Classes/EthereumStringEncodingExtensions.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@ extension BigInt: EventFilterEncodable {
2323

2424
extension Data: EventFilterEncodable {
2525
public func eventFilterEncoded() -> String? {
26-
return self.toHexString().addHexPrefix()
26+
guard let padded = self.setLengthLeft(32) else {return nil}
27+
return padded.toHexString().addHexPrefix()
2728
}
2829
}
2930

3031
extension EthereumAddress: EventFilterEncodable {
3132
public func eventFilterEncoded() -> String? {
32-
return self.address
33+
guard let padded = self.addressData.setLengthLeft(32) else {return nil}
34+
return padded.toHexString().addHexPrefix()
3335
}
3436
}
3537

web3swift/Web3/Classes/Web3+EventParser.swift

Lines changed: 143 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -113,52 +113,7 @@ extension web3.web3contract {
113113
var allResults = [EventParserResultProtocol]()
114114
if (self.filter != nil) {
115115
let eventFilter = self.filter!
116-
let filteredLogs = decodedLogs.filter { (result) -> Bool in
117-
if eventFilter.addresses == nil {
118-
return true
119-
} else {
120-
if eventFilter.addresses!.contains(result.contractAddress) {
121-
return true
122-
} else {
123-
return false
124-
}
125-
}
126-
}.filter { (result) -> Bool in
127-
if eventFilter.parameterFilters == nil {
128-
return true
129-
} else {
130-
let keys = result.decodedResult.keys.filter({ (key) -> Bool in
131-
if let _ = UInt64(key) {
132-
return true
133-
}
134-
return false
135-
})
136-
if keys.count < eventFilter.parameterFilters!.count {
137-
return false
138-
}
139-
for i in 0 ..< keys.count {
140-
let allowedValues = eventFilter.parameterFilters![i]
141-
let actualValue = result.decodedResult["\(i)"]
142-
if actualValue == nil {
143-
return false
144-
}
145-
if allowedValues == nil {
146-
continue
147-
}
148-
var inAllowed = false
149-
for value in allowedValues! {
150-
if value.isEqualTo(actualValue! as AnyObject) {
151-
inAllowed = true
152-
break
153-
}
154-
}
155-
if !inAllowed {
156-
return false
157-
}
158-
}
159-
return true
160-
}
161-
}
116+
let filteredLogs = filterLogs(decodedLogs: decodedLogs, eventFilter: eventFilter)
162117
allResults = filteredLogs
163118
} else {
164119
allResults = decodedLogs
@@ -171,10 +126,149 @@ extension web3.web3contract {
171126
guard let hash = transaction.hash else {return Result.failure(Web3Error.dataError)}
172127
return self.parseTransactionByHash(hash)
173128
}
174-
175-
public func getEventsByTopics(_ filter: EventFilter) -> Result<[EventParserResultProtocol], Web3Error> {
176-
return Result.failure(Web3Error.nodeError("NYI"))
129+
}
130+
}
131+
132+
extension web3.web3contract {
133+
public func getIndexedEvents(eventName: String?, filter: EventFilter) -> Result<[EventParserResultProtocol], Web3Error> {
134+
guard let rawContract = self.contract as? ContractV2 else {return Result.failure(Web3Error.nodeError("ABIv1 is not supported for this method"))}
135+
var eventTopic: Data? = nil
136+
var event: ABIv2.Element.Event? = nil
137+
if eventName != nil {
138+
guard let ev = rawContract.events[eventName!] else {return Result.failure(Web3Error.dataError)}
139+
event = ev
140+
eventTopic = ev.topic
141+
}
142+
var topics = [[String?]?]()
143+
if eventTopic != nil {
144+
topics.append([eventTopic!.toHexString().addHexPrefix()])
145+
} else {
146+
topics.append(nil as [String?]?)
147+
}
148+
if filter.parameterFilters != nil {
149+
if event == nil {return Result.failure(Web3Error.dataError)}
150+
// let indexedInputs = event!.inputs.filter { (inp) -> Bool in
151+
// return inp.indexed
152+
// }
153+
var lastNonemptyFilter = 0
154+
for i in 0 ..< filter.parameterFilters!.count {
155+
let filterValue = filter.parameterFilters![i]
156+
if filterValue != nil {
157+
lastNonemptyFilter = i
158+
}
159+
}
160+
if lastNonemptyFilter != 0 {
161+
guard lastNonemptyFilter <= event!.inputs.count else {return Result.failure(Web3Error.dataError)}
162+
for i in 0 ... lastNonemptyFilter {
163+
let filterValues = filter.parameterFilters![i]
164+
let input = event!.inputs[i]
165+
if filterValues != nil && !input.indexed {
166+
return Result.failure(Web3Error.dataError)
167+
}
168+
if filterValues == nil {
169+
topics.append(nil as [String?]?)
170+
continue
171+
}
172+
var encodings = [String]()
173+
for val in filterValues! {
174+
guard let enc = val.eventFilterEncoded() else {return Result.failure(Web3Error.dataError)}
175+
encodings.append(enc)
176+
}
177+
// if encodings.count == 1 {
178+
// topics.append(encodings[0] as AnyObject)
179+
// } else {
180+
topics.append(encodings)
181+
// }
182+
}
183+
}
184+
}
185+
var preEncoding = filter.rpcPreEncode()
186+
preEncoding.topics = topics
187+
let request = JSONRPCRequestFabric.prepareRequest(.getLogs, parameters: [preEncoding])
188+
let response = self.web3.provider.send(request: request)
189+
let result = ResultUnwrapper.getResponse(response)
190+
switch result {
191+
case .failure(let error):
192+
return Result.failure(error)
193+
case .success(let payload):
194+
if payload is NSNull {
195+
return Result.failure(Web3Error.nodeError("Empty response"))
196+
}
197+
guard let resultArray = payload as? [[String: AnyObject]] else {
198+
return Result.failure(Web3Error.dataError)
199+
}
200+
var allLogs = [EventLog]()
201+
for log in resultArray {
202+
guard let parsedLog = EventLog.init(log) else {return Result.failure(Web3Error.dataError)}
203+
allLogs.append(parsedLog)
204+
}
205+
if event != nil {
206+
let decodedLogs = allLogs.compactMap({ (log) -> EventParserResultProtocol? in
207+
let (n, d) = contract.parseEvent(log)
208+
guard let evName = n, let evData = d else {return nil}
209+
return EventParserResult(eventName: evName, transactionReceipt: nil, contractAddress: log.address, decodedResult: evData)
210+
}).filter { (res:EventParserResultProtocol?) -> Bool in
211+
if eventName != nil {
212+
return res != nil && res?.eventName == eventName
213+
} else {
214+
return res != nil
215+
}
216+
}
217+
var allResults = [EventParserResultProtocol]()
218+
allResults = decodedLogs
219+
return Result(allResults)
220+
}
221+
return Result([EventParserResultProtocol]())
177222
}
178223
}
179224
}
180225

226+
fileprivate func filterLogs(decodedLogs: [EventParserResultProtocol], eventFilter: EventFilter) -> [EventParserResultProtocol] {
227+
let filteredLogs = decodedLogs.filter { (result) -> Bool in
228+
if eventFilter.addresses == nil {
229+
return true
230+
} else {
231+
if eventFilter.addresses!.contains(result.contractAddress) {
232+
return true
233+
} else {
234+
return false
235+
}
236+
}
237+
}.filter { (result) -> Bool in
238+
if eventFilter.parameterFilters == nil {
239+
return true
240+
} else {
241+
let keys = result.decodedResult.keys.filter({ (key) -> Bool in
242+
if let _ = UInt64(key) {
243+
return true
244+
}
245+
return false
246+
})
247+
if keys.count < eventFilter.parameterFilters!.count {
248+
return false
249+
}
250+
for i in 0 ..< eventFilter.parameterFilters!.count {
251+
let allowedValues = eventFilter.parameterFilters![i]
252+
let actualValue = result.decodedResult["\(i)"]
253+
if actualValue == nil {
254+
return false
255+
}
256+
if allowedValues == nil {
257+
continue
258+
}
259+
var inAllowed = false
260+
for value in allowedValues! {
261+
if value.isEqualTo(actualValue! as AnyObject) {
262+
inAllowed = true
263+
break
264+
}
265+
}
266+
if !inAllowed {
267+
return false
268+
}
269+
}
270+
return true
271+
}
272+
}
273+
return filteredLogs
274+
}

web3swift/Web3/Classes/Web3+HttpProvider.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ public class Web3HttpProvider: Web3Provider {
122122

123123
static func syncPost(_ request: JSONRPCrequestBatch, providerURL: URL) -> Any? {
124124
guard let _ = try? JSONEncoder().encode(request) else {return nil}
125-
// print(String(data: try! JSONEncoder().encode(request), encoding: .utf8))
125+
// print(String(data: try! JSONEncoder().encode(request), encoding: .utf8))
126126
let headers: HTTPHeaders = [
127127
"Content-Type": "application/json",
128128
"Accept": "application/json"

web3swift/Web3/Classes/Web3+JSONRPC.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,13 @@ public struct TransactionParameters: Codable {
8888
}
8989
}
9090

91+
public struct EventFilterParameters: Codable {
92+
public var fromBlock: String?
93+
public var toBlock: String?
94+
public var topics: [[String?]?]?
95+
public var address: [String?]?
96+
}
97+
9198
public struct JSONRPCparams: Encodable{
9299
public var params = [Any]()
93100

@@ -100,6 +107,8 @@ public struct JSONRPCparams: Encodable{
100107
try container.encode(p)
101108
} else if let p = par as? Bool {
102109
try container.encode(p)
110+
} else if let p = par as? EventFilterParameters {
111+
try container.encode(p)
103112
}
104113
}
105114
}

web3swift/Web3/Classes/Web3+Methods.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public enum JSONRPCmethod: String, Encodable {
2828
case getBlockByNumber = "eth_getBlockByNumber"
2929
case personalSign = "eth_sign"
3030
case unlockAccount = "personal_unlockAccount"
31+
case getLogs = "eth_getLogs"
3132

3233
public var requiredNumOfParameters: Int {
3334
get {

web3swift/Web3/Classes/Web3+Protocols.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public protocol EventParserResultProtocol {
2323
var eventName: String {get}
2424
var decodedResult: [String:Any] {get}
2525
var contractAddress: EthereumAddress {get}
26-
var transactionReceipt: TransactionReceipt {get}
26+
var transactionReceipt: TransactionReceipt? {get}
2727
}
2828

2929
public protocol EventParserProtocol {

web3swift/Web3/Classes/Web3+Structures.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ public struct Block:Decodable {
331331

332332
public struct EventParserResult:EventParserResultProtocol {
333333
public var eventName: String
334-
public var transactionReceipt: TransactionReceipt
334+
public var transactionReceipt: TransactionReceipt?
335335
public var contractAddress: EthereumAddress
336336
public var decodedResult: [String:Any]
337337
}

0 commit comments

Comments
 (0)