Skip to content

Commit 6c98428

Browse files
authored
Merge pull request #115 from BANKEX/develop
Implement IBAN under Web3.Utils.Iban
2 parents 2695368 + d29f3c7 commit 6c98428

File tree

7 files changed

+194
-2
lines changed

7 files changed

+194
-2
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ You can try it by yourself by running the example project:
7272

7373
### Requirements
7474

75-
Web3swift requires Swift 4.1 and iOS 9.0 or macOS 10.13 although we recommend to use the latest iOS and MacOS versions for your own safety. Don't forget to set iOS version i na Podfile, otherwise you get an error if deployment target is less than the latest SDK.
75+
Web3swift requires Swift 4.1 and iOS 9.0 or macOS 10.13 although we recommend to use the latest iOS and MacOS versions for your own safety. Don't forget to set iOS version in a Podfile, otherwise you get an error if deployment target is less than the latest SDK.
7676

7777
### Installation
7878

@@ -113,7 +113,7 @@ Changes from this branch will be merged into the [master](https://github.com/BAN
113113

114114
## Appreciation
115115

116-
When using this pod references to this repo, [Bankex](http://bankex.com) and [Bankex Foundation](http://bankexfoundation.org) are appreciated.
116+
When using this pod references to this repo, [BANKEX](http://bankex.com) and [BANKEX Foundation](http://bankexfoundation.org) are appreciated.
117117

118118
## Authors
119119

web3swift.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@
9494
815630002007B48800A0EC2F /* BIP32KeystoreJSONStructure.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81562FFF2007B48800A0EC2F /* BIP32KeystoreJSONStructure.swift */; };
9595
815630022007B53C00A0EC2F /* BIP32Keystore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 815630012007B53C00A0EC2F /* BIP32Keystore.swift */; };
9696
815630042007BC8F00A0EC2F /* BIP39+WordLists.swift in Sources */ = {isa = PBXBuildFile; fileRef = 815630032007BC8F00A0EC2F /* BIP39+WordLists.swift */; };
97+
8160E5CE20B8245A0070070B /* IBAN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8160E5CD20B8245A0070070B /* IBAN.swift */; };
98+
8160E5CF20B8245A0070070B /* IBAN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8160E5CD20B8245A0070070B /* IBAN.swift */; };
9799
817EBB102004FE2800E02EAA /* EthereumAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 817EBB0F2004FE2800E02EAA /* EthereumAddress.swift */; };
98100
817EBB122004FE2F00E02EAA /* BIP32HDNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 817EBB112004FE2F00E02EAA /* BIP32HDNode.swift */; };
99101
817EBB162004FE4200E02EAA /* Web3+HttpProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 817EBB142004FE4200E02EAA /* Web3+HttpProvider.swift */; };
@@ -247,6 +249,7 @@
247249
815630012007B53C00A0EC2F /* BIP32Keystore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BIP32Keystore.swift; sourceTree = "<group>"; };
248250
815630032007BC8F00A0EC2F /* BIP39+WordLists.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BIP39+WordLists.swift"; sourceTree = "<group>"; };
249251
815630052008A64C00A0EC2F /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
252+
8160E5CD20B8245A0070070B /* IBAN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBAN.swift; sourceTree = "<group>"; };
250253
817EBB0F2004FE2800E02EAA /* EthereumAddress.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EthereumAddress.swift; sourceTree = "<group>"; };
251254
817EBB112004FE2F00E02EAA /* BIP32HDNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BIP32HDNode.swift; sourceTree = "<group>"; };
252255
817EBB142004FE4200E02EAA /* Web3+HttpProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Web3+HttpProvider.swift"; sourceTree = "<group>"; };
@@ -491,6 +494,7 @@
491494
81531A9F1FE7C07A002192CC /* EthereumKeystoreV3.swift */,
492495
817EBB26200673D100E02EAA /* KeystoreV3JSONStructure.swift */,
493496
8103BBCB2077B84400499769 /* PlainKeystore.swift */,
497+
8160E5CD20B8245A0070070B /* IBAN.swift */,
494498
);
495499
path = Classes;
496500
sourceTree = "<group>";
@@ -1057,6 +1061,7 @@
10571061
81C5DA282072E18200424CD6 /* NativeTypesEncoding+Extensions.swift in Sources */,
10581062
8123E1C7200CBAC200B6D3AB /* Dictionary+Extension.swift in Sources */,
10591063
81D7D97820A61E3800A193EC /* EventFiltering.swift in Sources */,
1064+
8160E5CE20B8245A0070070B /* IBAN.swift in Sources */,
10601065
8103BBC920779E2200499769 /* Web3+TransactionAndBlockDetailsOperations.swift in Sources */,
10611066
81FB21F12078142D007F9A83 /* Web3+Deprecated.swift in Sources */,
10621067
810B0F9C1FEC520500CF0DA2 /* Web3+Methods.swift in Sources */,
@@ -1147,6 +1152,7 @@
11471152
41948130203630530065A83B /* BIP32Keystore.swift in Sources */,
11481153
8103BBCA20779E2200499769 /* Web3+TransactionAndBlockDetailsOperations.swift in Sources */,
11491154
81D7D97920A61E3800A193EC /* EventFiltering.swift in Sources */,
1155+
8160E5CF20B8245A0070070B /* IBAN.swift in Sources */,
11501156
81FB21F22078142D007F9A83 /* Web3+Deprecated.swift in Sources */,
11511157
41948131203630530065A83B /* BIP32KeystoreJSONStructure.swift in Sources */,
11521158
41948132203630530065A83B /* BIP32HDNode.swift in Sources */,

web3swift/Convenience/Classes/String+Extension.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ extension String {
4646
return String(self[start..<end])
4747
}
4848

49+
subscript (bounds: CountablePartialRangeFrom<Int>) -> String {
50+
let start = index(self.startIndex, offsetBy: bounds.lowerBound)
51+
let end = self.endIndex
52+
return String(self[start..<end])
53+
}
54+
4955
func leftPadding(toLength: Int, withPad character: Character) -> String {
5056
let stringLength = self.count
5157
if stringLength < toLength {
@@ -113,4 +119,20 @@ extension String {
113119
else { return nil }
114120
return from ..< to
115121
}
122+
123+
var asciiValue: Int {
124+
get {
125+
let s = self.unicodeScalars
126+
return Int(s[s.startIndex].value)
127+
}
128+
}
129+
}
130+
131+
extension Character {
132+
var asciiValue: Int {
133+
get {
134+
let s = String(self).unicodeScalars
135+
return Int(s[s.startIndex].value)
136+
}
137+
}
116138
}

web3swift/KeystoreManager/Classes/EthereumAddress.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,13 @@ public struct EthereumAddress: Equatable {
100100
public static func contractDeploymentAddress() -> EthereumAddress {
101101
return EthereumAddress("0x", type: .contractDeployment)!
102102
}
103+
104+
// public static func fromIBAN(_ iban: String) -> EthereumAddress {
105+
//
106+
// }
107+
103108
}
109+
110+
111+
112+
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
//
2+
// IBAN.swift
3+
// web3swift
4+
//
5+
// Created by Alexander Vlasov on 25.05.2018.
6+
// Copyright © 2018 Bankex Foundation. All rights reserved.
7+
//
8+
9+
import Foundation
10+
import BigInt
11+
12+
public struct ICAP {
13+
public var asset: String
14+
public var institution: String
15+
public var client: String
16+
}
17+
18+
public struct IBAN {
19+
public var iban: String
20+
21+
public var isDirect: Bool {
22+
return self.iban.count == 34 || self.iban.count == 35
23+
}
24+
25+
public var isIndirect: Bool {
26+
return self.iban.count == 20
27+
}
28+
29+
public var checksum: String {
30+
return self.iban[2..<4];
31+
}
32+
33+
public var asset: String {
34+
if (self.isIndirect) {
35+
return self.iban[4..<7]
36+
} else {
37+
return ""
38+
}
39+
}
40+
41+
public var institution : String {
42+
if (self.isIndirect) {
43+
return self.iban[7..<11]
44+
} else {
45+
return ""
46+
}
47+
}
48+
49+
public var client : String {
50+
if self.isIndirect {
51+
return self.iban[11...]
52+
} else {
53+
return ""
54+
}
55+
}
56+
57+
58+
public func toEthereumAddress() -> EthereumAddress? {
59+
if self.isDirect {
60+
let base36 = self.iban[4...];
61+
guard let asBigNumber = BigUInt(base36, radix: 36) else {return nil}
62+
let addressString = String(asBigNumber, radix: 16).leftPadding(toLength: 40, withPad: "0")
63+
return EthereumAddress(addressString.addHexPrefix())
64+
} else {
65+
return nil
66+
}
67+
}
68+
69+
internal static func decodeToInts(_ iban: String) -> String {
70+
// let codePointForA = "A".asciiValue
71+
// let codePointForZ = "Z".asciiValue
72+
73+
let uppercasedIBAN = iban.replacingOccurrences(of: " ", with: "").uppercased()
74+
let begining = String(uppercasedIBAN[0..<4])
75+
let end = String(uppercasedIBAN[4...])
76+
let IBAN = end + begining
77+
var arrayOfInts = [Int]()
78+
for ch in IBAN {
79+
guard let dataPoint = String(ch).data(using: .ascii) else {return ""}
80+
guard dataPoint.count == 1 else {return ""}
81+
let code = Int(dataPoint[0])
82+
if code >= 65 && code <= 90 {
83+
arrayOfInts.append(code - 65 + 10)
84+
} else {
85+
arrayOfInts.append(code - 48)
86+
}
87+
}
88+
let joinedString = arrayOfInts.map({ (intCh) -> String in
89+
return String(intCh)
90+
}).joined()
91+
return joinedString
92+
}
93+
94+
internal static func calculateChecksumMod97(_ preparedString: String) -> Int {
95+
var m = 0
96+
for digit in preparedString.split(intoChunksOf: 1) {
97+
m = m * 10
98+
m = m + Int(digit)!
99+
m = m % 97
100+
}
101+
return m
102+
}
103+
104+
public static func isValidIBANaddress(_ iban: String, noValidityCheck: Bool = false) -> Bool {
105+
let regex = "^XE[0-9]{2}(ETH[0-9A-Z]{13}|[0-9A-Z]{30,31})$"
106+
let matcher = try! NSRegularExpression(pattern: regex, options: NSRegularExpression.Options.dotMatchesLineSeparators)
107+
let match = matcher.matches(in: iban, options: NSRegularExpression.MatchingOptions.anchored, range: iban.fullNSRange)
108+
guard match.count == 1 else {
109+
return false
110+
}
111+
if (iban.hasPrefix("XE") && !noValidityCheck) {
112+
let remainder = calculateChecksumMod97(decodeToInts(iban))
113+
return remainder == 1
114+
} else {
115+
return true
116+
}
117+
}
118+
119+
public init?(_ ibanString: String) {
120+
let matched = ibanString.replacingOccurrences(of: " ", with: "").uppercased()
121+
guard IBAN.isValidIBANaddress(matched) else {return nil}
122+
self.iban = matched
123+
}
124+
125+
public init?(_ address: EthereumAddress) {
126+
let addressString = address.address.lowercased().stripHexPrefix()
127+
guard let bigNumber = BigUInt(addressString, radix: 16) else {return nil}
128+
let base36EncodedString = String(bigNumber, radix: 36);
129+
guard base36EncodedString.count <= 30 else {return nil}
130+
let padded = base36EncodedString.leftPadding(toLength: 30, withPad: "0")
131+
let prefix = "XE"
132+
let remainder = IBAN.calculateChecksumMod97(IBAN.decodeToInts(prefix + "00" + padded));
133+
let checkDigits = "0" + String(98 - remainder)
134+
let twoDigits = checkDigits[checkDigits.count-2..<checkDigits.count]
135+
let fullIban = prefix + twoDigits + padded
136+
self.iban = fullIban.uppercased()
137+
}
138+
139+
}

web3swift/Web3/Classes/Web3+Utils.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ public typealias Web3Utils = Web3.Utils
1414

1515
extension Web3 {
1616
public struct Utils {
17+
18+
typealias Iban = IBAN
1719
}
1820
}
1921

web3swiftTests/web3swiftTests.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2339,6 +2339,20 @@ class web3swiftTests: XCTestCase {
23392339
XCTAssert(formatted == "-1,1000")
23402340
}
23412341

2342+
func testIBANcreation() {
2343+
let iban = "XE7338O073KYGTWWZN0F2WZ0R8PX5ZPPZS"
2344+
let native = Web3.Utils.Iban(iban)
2345+
XCTAssert(native != nil)
2346+
let expectedAddress = "0x00c5496aEe77C1bA1f0854206A26DdA82a81D6D8"
2347+
let createdAddress = native?.toEthereumAddress()?.address
2348+
XCTAssert(createdAddress == expectedAddress)
2349+
2350+
let address = EthereumAddress("0x03c5496aee77c1ba1f0854206a26dda82a81d6d8")!
2351+
let fromAddress = Web3.Utils.Iban(address)
2352+
let ibn = fromAddress?.iban
2353+
XCTAssert(ibn == "XE83FUTTUNPK7WZJSGGCWVEBARQWQ8YML4")
2354+
}
2355+
23422356
func testPerformanceExample() {
23432357
// This is an example of a performance test case.
23442358
self.measure {

0 commit comments

Comments
 (0)