@@ -25,14 +25,11 @@ extension Web3 {
2525 public struct EIP681Parameter {
2626 public var type : ABI . Element . ParameterType
2727 public var value : AnyObject
28- < <<<<<< HEAD
29- =======
3028
3129 public init ( type: ABI . Element . ParameterType , value: AnyObject ) {
3230 self . type = type
3331 self . value = value
3432 }
35- > >>>>>> c159 f067 ... Make value as any object in eip681 parameters
3633 }
3734 public var isPayRequest : Bool
3835 public var targetAddress : TargetAddress
@@ -108,7 +105,7 @@ extension Web3 {
108105 if tail == nil {
109106 return code
110107 }
111- guard let components = URLComponents ( string: tail!) else { return code}
108+ guard let components = URLComponents ( string: tail!. addingPercentEncoding ( withAllowedCharacters : . urlQueryAllowed ) ?? tail! ) else { return code}
112109 if components. path == " " {
113110 code. isPayRequest = true
114111 } else {
@@ -119,72 +116,16 @@ extension Web3 {
119116 var inputs = [ ABI . Element. InOut] ( )
120117 for comp in queryItems {
121118 if let inputType = try ? ABITypeParser . parseTypeString ( comp. name) {
122- guard let value = comp. value else { continue }
123- var nativeValue : AnyObject ? = nil
124- switch inputType {
125- case . address:
126- let val = EIP681Code . TargetAddress ( value)
127- switch val {
128- case . ethereumAddress( let ethereumAddress) :
129- nativeValue = ethereumAddress as AnyObject
130- // default:
131- // return nil
132- case . ensAddress( let ens) :
133- do {
134- let web = await web3 ( provider: InfuraProvider ( Networks . fromInt ( UInt ( code. chainID ?? 1 ) ) ?? Networks . Mainnet) !)
135- let ensModel = ENS ( web3: web)
136- try await ensModel? . setENSResolver ( withDomain: ens)
137- let address = try await ensModel? . getAddress ( forNode: ens)
138- nativeValue = address as AnyObject
139- } catch {
140- return nil
141- }
142- }
143- case . uint( bits: _) :
144- if let val = BigUInt ( value, radix: 10 ) {
145- nativeValue = val as AnyObject
146- } else if let val = BigUInt ( value. stripHexPrefix ( ) , radix: 16 ) {
147- nativeValue = val as AnyObject
148- }
149- case . int( bits: _) :
150- if let val = BigInt ( value, radix: 10 ) {
151- nativeValue = val as AnyObject
152- } else if let val = BigInt ( value. stripHexPrefix ( ) , radix: 16 ) {
153- nativeValue = val as AnyObject
154- }
155- case . string:
156- nativeValue = value as AnyObject
157- case . dynamicBytes:
158- if let val = Data . fromHex ( value) {
159- nativeValue = val as AnyObject
160- } else if let val = value. data ( using: . utf8) {
161- nativeValue = val as AnyObject
162- }
163- case . bytes( length: _) :
164- if let val = Data . fromHex ( value) {
165- nativeValue = val as AnyObject
166- } else if let val = value. data ( using: . utf8) {
167- nativeValue = val as AnyObject
168- }
169- case . bool:
170- switch value {
171- case " true " , " True " , " TRUE " , " 1 " :
172- nativeValue = true as AnyObject
173- case " false " , " False " , " FALSE " , " 0 " :
174- nativeValue = false as AnyObject
175- default :
176- nativeValue = true as AnyObject
177- }
178- default :
179- continue
180- }
181- if nativeValue != nil {
182- inputs. append ( ABI . Element. InOut ( name: String ( inputNumber) , type: inputType) )
183- code. parameters. append ( EIP681Code . EIP681Parameter ( type: inputType, value: nativeValue!) )
184- inputNumber = inputNumber + 1
185- } else {
186- return nil
187- }
119+ guard let rawValue = comp. value,
120+ let functionArgument = parseFunctionArgument ( inputType,
121+ rawValue. trimmingCharacters ( in: . whitespacesAndNewlines) ,
122+ chainID: code. chainID ?? 0 ,
123+ inputNumber: inputNumber)
124+ else { continue }
125+
126+ inputs. append ( functionArgument. argType)
127+ code. parameters. append ( functionArgument. parameter)
128+ inputNumber = inputNumber + 1
188129 } else {
189130 switch comp. name {
190131 case " value " :
@@ -235,5 +176,196 @@ extension Web3 {
235176 print ( code)
236177 return code
237178 }
179+
180+ private static func parseFunctionArgument( _ inputType: ABI . Element . ParameterType ,
181+ _ rawValue: String ,
182+ chainID: BigUInt ,
183+ inputNumber: Int ) -> FunctionArgument ? {
184+ var parsedValue : AnyObject ? = nil
185+ switch inputType {
186+ case . address:
187+ let val = EIP681Code . TargetAddress ( rawValue)
188+ switch val {
189+ case . ethereumAddress( let ethereumAddress) :
190+ parsedValue = ethereumAddress as AnyObject
191+ case . ensAddress( let ens) :
192+ do {
193+ let web = web3 ( provider: InfuraProvider ( Networks . fromInt ( Int ( chainID) ) ?? Networks . Mainnet) !)
194+ let ensModel = ENS ( web3: web)
195+ try ensModel? . setENSResolver ( withDomain: ens)
196+ let address = try ensModel? . getAddress ( forNode: ens)
197+ parsedValue = address as AnyObject
198+ } catch {
199+ return nil
200+ }
201+ }
202+ case . uint( bits: _) :
203+ if let val = BigUInt ( rawValue, radix: 10 ) {
204+ parsedValue = val as AnyObject
205+ } else if let val = BigUInt ( rawValue. stripHexPrefix ( ) , radix: 16 ) {
206+ parsedValue = val as AnyObject
207+ }
208+ case . int( bits: _) :
209+ if let val = BigInt ( rawValue, radix: 10 ) {
210+ parsedValue = val as AnyObject
211+ } else if let val = BigInt ( rawValue. stripHexPrefix ( ) , radix: 16 ) {
212+ parsedValue = val as AnyObject
213+ }
214+ case . string:
215+ parsedValue = rawValue as AnyObject
216+ case . dynamicBytes:
217+ if let val = Data . fromHex ( rawValue) {
218+ parsedValue = val as AnyObject
219+ } else if let val = rawValue. data ( using: . utf8) {
220+ parsedValue = val as AnyObject
221+ }
222+ case . bytes( length: _) :
223+ if let val = Data . fromHex ( rawValue) {
224+ parsedValue = val as AnyObject
225+ } else if let val = rawValue. data ( using: . utf8) {
226+ parsedValue = val as AnyObject
227+ }
228+ case . bool:
229+ switch rawValue {
230+ case " true " , " True " , " TRUE " , " 1 " :
231+ parsedValue = true as AnyObject
232+ case " false " , " False " , " FALSE " , " 0 " :
233+ parsedValue = false as AnyObject
234+ default :
235+ parsedValue = true as AnyObject
236+ }
237+ case let . array( type, length) :
238+ var rawValues : [ String ] = [ ]
239+ if case . array = type {
240+ guard let internalArrays = splitArrayOfArrays ( rawValue) ,
241+ ( length == 0 || UInt64 ( internalArrays. count) == length) else { return nil }
242+ rawValues = internalArrays
243+ } else if case let . tuple( internalTypes) = type {
244+ // TODO: implement!
245+ } else if case . string = type {
246+ guard let strings = splitArrayOfStrings ( rawValue) ,
247+ ( length == 0 || UInt64 ( strings. count) == length) else { return nil }
248+ rawValues = strings
249+ } else {
250+ let rawValue = String ( rawValue. dropFirst ( ) . dropLast ( ) )
251+ rawValues = rawValue. split ( separator: " , " ) . map { String ( $0) }
252+ }
253+
254+ parsedValue = rawValues. compactMap {
255+ parseFunctionArgument ( type,
256+ String ( $0) ,
257+ chainID: chainID,
258+ inputNumber: inputNumber) ?
259+ . parameter
260+ . value
261+ } as AnyObject
262+
263+ guard ( parsedValue as? [ AnyObject ] ) ? . count == rawValues. count &&
264+ ( length == 0 || UInt64 ( rawValues. count) == length) else { return nil }
265+ case let . tuple( types) :
266+ // TODO: implement!
267+ return nil
268+ default : return nil
269+ }
270+
271+ guard let parsedValue = parsedValue else { return nil }
272+ return FunctionArgument ( ABI . Element. InOut ( name: String ( inputNumber) , type: inputType) ,
273+ EIP681Code . EIP681Parameter ( type: inputType, value: parsedValue) )
274+ }
275+
276+ // MARK: - Parsing functions for complex data structures
277+
278+ /// Attempts to split given ``rawValue`` into `[String]` where each element of that array
279+ /// represent a valid, stringified, array in of itself.
280+ /// With an input like `"[[],[],[]]"` the output is expected to be `["[]","[]","[]"]`.
281+ /// - Parameter rawValue: supposedly an array of arrays in a form `[[...],[...],...]`
282+ /// - Returns: separated stringified arrays, or `nil` if separation failed.
283+ private static func splitArrayOfArrays( _ rawValue: String ) -> [ String ] ? {
284+ /// Dropping first and last square brackets.
285+ /// That modifies the upper bound value of the first match of `squareBracketRegex`.
286+ let rawValue = String ( rawValue. dropFirst ( ) . dropLast ( ) )
287+
288+ let squareBracketRegex = try ! NSRegularExpression ( pattern: " ( \\ [*) " )
289+ let match = squareBracketRegex. firstMatch ( in: rawValue, range: rawValue. fullNSRange)
290+
291+ guard let bracketsCount = match? . range. upperBound,
292+ bracketsCount > 0 else {
293+ return nil
294+ }
295+
296+ let splitRegex = try ! NSRegularExpression ( pattern: " ( \\ ]){ \( bracketsCount) },( \\ [){ \( bracketsCount) } " )
297+ var indices : [ Int ] = splitRegex. matches ( in: rawValue, range: rawValue. fullNSRange)
298+ . map { $0. range. lowerBound + bracketsCount }
299+ if !indices. isEmpty {
300+ indices. append ( rawValue. count)
301+ var prevIndex = 0
302+ var result = [ String] ( )
303+ for index in indices {
304+ result. append ( rawValue [ prevIndex..< index] )
305+ prevIndex = index + 1
306+ }
307+ return result
308+ }
309+ return [ rawValue]
310+ }
311+
312+ /// Attempts to split a string that represents an array of strings.
313+ /// Example:
314+ ///
315+ /// // input
316+ /// "[\"1\",\"abcd,efgh\",\"-=-=-\"]"
317+ /// // output
318+ /// ["1","abcd,efgh","-=-=-"]
319+ ///
320+ /// // input
321+ /// "[1,abcd,efgh,-=-=-]"
322+ /// // output
323+ /// ["1","abcd","efgh","-=-=-"]
324+ ///
325+ /// - Parameter rawValue: stringified array of strings.
326+ /// - Returns: an array of separated individual elements, or `nil` if separation failed.
327+ private static func splitArrayOfStrings( _ rawValue: String ) -> [ String ] ? {
328+ /// Dropping first and last square brackets to exclude them from the first and the last separated element.
329+ let rawValue = String ( rawValue. dropFirst ( ) . dropLast ( ) )
330+
331+ let elementsBoundary = try ! NSRegularExpression ( pattern: " \" , \" " )
332+ var indices = Array ( elementsBoundary
333+ . matches ( in: rawValue, range: rawValue. fullNSRange)
334+ . map { result in
335+ result. range. lowerBound + 1
336+ } )
337+
338+ if !indices. isEmpty {
339+ indices. append ( rawValue. count)
340+ var prevIndex = 0
341+ var rawValues = [ String] ( )
342+ for index in indices {
343+ var argument = rawValue [ prevIndex..< index]
344+ if let index = argument. firstIndex ( of: " \" " ) ,
345+ argument. distance ( from: argument. startIndex, to: index) == 0 {
346+ argument = String ( argument. dropFirst ( ) )
347+ }
348+ if let index = argument. lastIndex ( of: " \" " ) ,
349+ argument. distance ( from: argument. endIndex, to: index) == - 1 {
350+ argument = String ( argument. dropLast ( ) )
351+ }
352+ rawValues. append ( argument)
353+ prevIndex = index + 1
354+ }
355+ return rawValues
356+ }
357+ return rawValue. split ( separator: " , " ) . map { String ( $0) }
358+ }
359+ }
360+ }
361+
362+ fileprivate class FunctionArgument {
363+ let argType : ABI . Element . InOut
364+ let parameter : Web3 . EIP681Code . EIP681Parameter
365+
366+ init ( _ argType: ABI . Element . InOut ,
367+ _ parameter: Web3 . EIP681Code . EIP681Parameter ) {
368+ self . argType = argType
369+ self . parameter = parameter
238370 }
239371}
0 commit comments