Skip to content

Commit 2e7a37b

Browse files
authored
Merge pull request #4 from web3swift-team/develop
chore: merging with development
2 parents c8827b6 + 9746b23 commit 2e7a37b

File tree

3 files changed

+117
-50
lines changed

3 files changed

+117
-50
lines changed

Sources/web3swift/Utils/EIP/EIP4361.swift

Lines changed: 65 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ private let uriPattern = "(([^:?#\\s]+):)?(([^?#\\s]*))?([^?#\\s]*)(\\?([^#\\s]*
1515

1616
/// Sign-In with Ethereum protocol and parser implementation.
1717
///
18+
/// _Regular expressions were generated using ABNF grammar from https://github.com/spruceid/siwe/blob/main/packages/siwe-parser/lib/abnf.ts#L5
19+
/// and tool https://pypi.org/project/abnf-to-regexp/ that outputs Python supported regular expressions._
20+
///
1821
/// EIP-4361:
1922
/// - https://eips.ethereum.org/EIPS/eip-4361
2023
/// - https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4361.md
@@ -60,22 +63,64 @@ public final class EIP4361 {
6063
case resources
6164
}
6265

63-
private static let domain = "(?<\(EIP4361Field.domain.rawValue)>([^?#]*)) wants you to sign in with your Ethereum account:"
64-
private static let address = "\\n(?<\(EIP4361Field.address.rawValue)>0x[a-zA-Z0-9]{40})\\n\\n"
65-
private static let statementParagraph = "((?<\(EIP4361Field.statement.rawValue)>[^\\n]+)\\n)?"
66-
private static let uri = "\\nURI: (?<\(EIP4361Field.uri.rawValue)>(\(uriPattern))?)"
67-
private static let version = "\\nVersion: (?<\(EIP4361Field.version.rawValue)>[0-9]+)"
68-
private static let chainId = "\\nChain ID: (?<\(EIP4361Field.chainId.rawValue)>[0-9a-fA-F]+)"
69-
private static let nonce = "\\nNonce: (?<\(EIP4361Field.nonce.rawValue)>[a-zA-Z0-9]{8,})"
70-
private static let issuedAt = "\\nIssued At: (?<\(EIP4361Field.issuedAt.rawValue)>(\(datetimePattern)))"
71-
private static let expirationTime = "(\\nExpiration Time: (?<\(EIP4361Field.expirationTime.rawValue)>(\(datetimePattern))))?"
72-
private static let notBefore = "(\\nNot Before: (?<\(EIP4361Field.notBefore.rawValue)>(\(datetimePattern))))?"
73-
private static let requestId = "(\\nRequest ID: (?<\(EIP4361Field.requestId.rawValue)>[-._~!$&'()*+,;=:@%a-zA-Z0-9]*))?"
74-
private static let resourcesParagraph = "(\\nResources:(?<\(EIP4361Field.resources.rawValue)>(\\n- (\(uriPattern))?)+))?"
75-
76-
private static var eip4361Pattern: String {
77-
"^\(domain)\(address)\(statementParagraph)\(uri)\(version)\(chainId)\(nonce)\(issuedAt)\(expirationTime)\(notBefore)\(requestId)\(resourcesParagraph)$"
78-
}
66+
private static let unreserved = "[a-zA-Z0-9\\-._~]"
67+
private static let pctEncoded = "%[0-9A-Fa-f][0-9A-Fa-f]"
68+
private static let subDelims = "[!$&'()*+,;=]"
69+
private static let userinfo = "(\(unreserved)|\(pctEncoded)|\(subDelims)|:)*"
70+
private static let h16 = "[0-9A-Fa-f]{1,4}"
71+
private static let decOctet = "([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])"
72+
private static let ipv4address = "\(decOctet)\\.\(decOctet)\\.\(decOctet)\\.\(decOctet)"
73+
private static let ls32 = "(\(h16):\(h16)|\(ipv4address))"
74+
private static let ipv6address = "((\(h16):){6}\(ls32)|::(\(h16):){5}\(ls32)|(\(h16))?::(\(h16):){4}\(ls32)|((\(h16):)?\(h16))?::(\(h16):){3}\(ls32)|((\(h16):){2}\(h16))?::(\(h16):){2}\(ls32)|((\(h16):){3}\(h16))?::\(h16):\(ls32)|((\(h16):){4}\(h16))?::\(ls32)|((\(h16):){5}\(h16))?::\(h16)|((\(h16):){6}\(h16))?::)"
75+
private static let ipvfuture = "[vV][0-9A-Fa-f]+\\.(\(unreserved)|\(subDelims)|:)+"
76+
private static let ipLiteral = "\\[(\(ipv6address)|\(ipvfuture))\\]"
77+
private static let regName = "(\(unreserved)|\(pctEncoded)|\(subDelims))*"
78+
private static let host = "(\(ipLiteral)|\(ipv4address)|\(regName))"
79+
private static let port = "[0-9]*"
80+
private static let authority = "(\(userinfo)@)?\(host)(:\(port))?"
81+
private static let dateFullyear = "[0-9]{4}"
82+
private static let dateMday = "[0-9]{2}"
83+
private static let dateMonth = "[0-9]{2}"
84+
private static let fullDate = "\(dateFullyear)-\(dateMonth)-\(dateMday)"
85+
private static let timeHour = "[0-9]{2}"
86+
private static let timeMinute = "[0-9]{2}"
87+
private static let timeSecond = "[0-9]{2}"
88+
private static let timeSecfrac = "\\.[0-9]+"
89+
private static let partialTime = "\(timeHour):\(timeMinute):\(timeSecond)(\(timeSecfrac))?"
90+
private static let timeNumoffset = "[+\\-]\(timeHour):\(timeMinute)"
91+
private static let timeOffset = "([zZ]|\(timeNumoffset))"
92+
private static let fullTime = "\(partialTime)\(timeOffset)"
93+
private static let dateTime = "\(fullDate)[tT]\(fullTime)"
94+
private static let pchar = "(\(unreserved)|\(pctEncoded)|\(subDelims)|[:@])"
95+
private static let fragment = "(\(pchar)|[/?])*"
96+
private static let genDelims = "[:/?#\\[\\]@]"
97+
private static let segment = "(\(pchar))*"
98+
private static let pathAbempty = "(/\(segment))*"
99+
private static let segmentNz = "(\(pchar))+"
100+
private static let pathAbsolute = "/(\(segmentNz)(/\(segment))*)?"
101+
private static let pathRootless = "\(segmentNz)(/\(segment))*"
102+
private static let pathEmpty = "(\(pchar)){0}"
103+
private static let hierPart = "(//\(authority)\(pathAbempty)|\(pathAbsolute)|\(pathRootless)|\(pathEmpty))"
104+
private static let query = "(\(pchar)|[/?])*"
105+
private static let reserved = "(\(genDelims)|\(subDelims))"
106+
private static let scheme = "[a-zA-Z][a-zA-Z0-9+\\-.]*"
107+
private static let resource = "- \(uri)"
108+
109+
// MARK: The final regular expression parts
110+
private static let domain = authority
111+
private static let address = "0x[0-9A-Fa-f]{40}"
112+
private static let statement = "(\(reserved)|\(unreserved)| )+"
113+
private static let uri = "\(scheme):\(hierPart)(\\?\(query))?(\\#\(fragment))?"
114+
private static let version = "[0-9]+"
115+
private static let chainId = "[0-9]+"
116+
private static let nonce = "[a-zA-Z0-9]{8,}"
117+
private static let issuedAt = dateTime
118+
private static let expirationTime = dateTime
119+
private static let notBefore = dateTime
120+
private static let requestId = "(\(pchar))*"
121+
private static let resources = "(\\n\(resource))*"
122+
123+
private static let eip4361Pattern = "(?<\(EIP4361Field.domain.rawValue)>\(domain)) wants you to sign in with your Ethereum account:\\n(?<\(EIP4361Field.address.rawValue)>\(address))\\n\\n((?<\(EIP4361Field.statement.rawValue)>\(statement))\\n)?\\nURI: (?<\(EIP4361Field.uri.rawValue)>\(uri))\\nVersion: (?<\(EIP4361Field.version.rawValue)>\(version))\\nChain ID: (?<\(EIP4361Field.chainId.rawValue)>\(chainId))\\nNonce: (?<\(EIP4361Field.nonce.rawValue)>\(nonce))\\nIssued At: (?<\(EIP4361Field.issuedAt.rawValue)>\(issuedAt))(\\nExpiration Time: (?<\(EIP4361Field.expirationTime.rawValue)>\(expirationTime)))?(\\nNot Before: (?<\(EIP4361Field.notBefore.rawValue)>\(notBefore)))?(\\nRequest ID: (?<\(EIP4361Field.requestId.rawValue)>\(requestId)))?(\\nResources:(?<\(EIP4361Field.resources.rawValue)>\(resources)))?"
79124

80125
private static var _eip4361OptionalPattern: String?
81126
private static var eip4361OptionalPattern: String {
@@ -95,7 +140,7 @@ public final class EIP4361 {
95140

96141
let patternParts: [String] = ["^\(domain)",
97142
"(\(address))?",
98-
"\(statementParagraph)",
143+
"((?<\(EIP4361Field.statement.rawValue)>\(statement))\\n)?",
99144
"(\(uri))?",
100145
"(\(version))?",
101146
"(\(chainId))?",
@@ -113,12 +158,7 @@ public final class EIP4361 {
113158

114159
public static func validate(_ message: String) -> EIP4361ValidationResponse {
115160
// swiftlint:disable force_try
116-
let siweConstantMessageRegex = try! NSRegularExpression(pattern: "^\(domain)\\n")
117-
guard siweConstantMessageRegex.firstMatch(in: message, range: message.fullNSRange) != nil else {
118-
return EIP4361ValidationResponse(isEIP4361: false, eip4361: nil, capturedFields: [:])
119-
}
120-
121-
let eip4361Regex = try! NSRegularExpression(pattern: eip4361OptionalPattern)
161+
let eip4361Regex = try! NSRegularExpression(pattern: EIP4361.eip4361OptionalPattern)
122162
// swiftlint:enable force_try
123163
var capturedFields: [EIP4361Field: String] = [:]
124164
for (key, value) in eip4361Regex.captureGroups(string: message) {
@@ -129,7 +169,7 @@ public final class EIP4361 {
129169
// swiftlint:enable force_unwrapping
130170
}
131171
return EIP4361ValidationResponse(isEIP4361: true,
132-
eip4361: EIP4361(message),
172+
eip4361: EIP4361(message),
133173
capturedFields: capturedFields)
134174
}
135175

@@ -167,6 +207,7 @@ public final class EIP4361 {
167207
let dateFormatter = ISO8601DateFormatter()
168208
dateFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
169209
guard let domain = groups["domain"],
210+
!domain.isEmpty,
170211
let rawAddress = groups["address"],
171212
let address = EthereumAddress(rawAddress),
172213
let rawUri = groups["uri"],

Sources/web3swift/Utils/ENS/ENSBaseRegistrar.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public extension ENS {
6464
return expirity
6565
}
6666

67-
@available(*, message: "This function should not be used to check if a name can be registered by a user. To check if a name can be registered by a user, check name availablility via the controller")
67+
@available(*, message: "This function should not be used to check if a name can be registered by a user. To check if a name can be registered by a user, check name availability via the controller")
6868
public func isNameAvailable(name: BigUInt) async throws -> Bool {
6969
guard let transaction = self.contract.createReadOperation("available", parameters: [name]) else { throw Web3Error.transactionSerializationError }
7070

0 commit comments

Comments
 (0)