Skip to content

Commit f0c066d

Browse files
author
Alex Vlasov
committed
add convenience methods for ERC20 token transfers
1 parent 668e5f2 commit f0c066d

File tree

8 files changed

+79
-14
lines changed

8 files changed

+79
-14
lines changed

web3swift/Contract/Classes/ContractProtocol.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//
88

99
import Foundation
10+
import BigInt
1011

1112
public protocol ContractProtocol {
1213
var address: EthereumAddress? {get set}
@@ -35,6 +36,18 @@ public protocol EventFilterable: EventFilterComparable, EventFilterEncodable {
3536

3637
}
3738

39+
extension BigUInt: EventFilterable {
40+
}
41+
extension BigInt: EventFilterable {
42+
}
43+
extension Data: EventFilterable {
44+
}
45+
extension String: EventFilterable {
46+
}
47+
extension EthereumAddress: EventFilterable {
48+
}
49+
50+
3851
public struct EventFilter {
3952
public enum Block {
4053
case latest

web3swift/Convenience/Classes/SynchronizedQueue.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ public extension SynchronizedArray {
101101
/// - Returns: An array of the non-nil results of calling transform with each element of the sequence.
102102
func flatMap<ElementOfResult>(_ transform: (Element) -> ElementOfResult?) -> [ElementOfResult] {
103103
var result = [ElementOfResult]()
104-
queue.sync { result = self.array.flatMap(transform) }
104+
queue.sync { result = self.array.compactMap(transform) }
105105
return result
106106
}
107107

web3swift/Web3/Classes/Web3+Contract.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ extension web3 {
5454
}
5555

5656
public func method(_ method:String = "fallback", parameters: [AnyObject] = [AnyObject](), extraData: Data = Data(), options: Web3Options?) -> TransactionIntermediate? {
57-
5857
let mergedOptions = Web3Options.merge(self.options, with: options)
5958
guard var tx = self.contract.method(method, parameters: parameters, extraData: extraData, options: mergedOptions) else {return nil}
6059
tx.chainID = self.web3.provider.network?.chainID

web3swift/Web3/Classes/Web3+Eth.swift

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -439,12 +439,36 @@ extension web3.Eth {
439439
}
440440

441441
public func sendETH(to: EthereumAddress, amount: String, units: Web3.Utils.Units = .eth, extraData: Data = Data(), options: Web3Options? = nil) -> TransactionIntermediate? {
442-
let contract = self.web3.contract(Web3.Utils.coldWalletABI, at: to, abiVersion: 2)
443-
guard var mergedOptions = Web3Options.merge(self.options, with: options) else {return nil}
444442
guard let value = Web3.Utils.parseToBigUInt(amount, units: .eth) else {return nil}
445-
mergedOptions.value = value
446-
let intermediate = contract?.method("fallback", extraData: extraData, options: mergedOptions)
443+
return sendETH(to: to, amount: value, extraData: extraData, options: options)
444+
}
445+
446+
public func sendERC20tokensWithKnownDecimals(tokenAddress: EthereumAddress, from: EthereumAddress, to: EthereumAddress, amount: BigUInt, options: Web3Options? = nil) -> TransactionIntermediate? {
447+
let contract = self.web3.contract(Web3.Utils.erc20ABI, at: tokenAddress, abiVersion: 2)
448+
guard var mergedOptions = Web3Options.merge(self.options, with: options) else {return nil}
449+
mergedOptions.from = from
450+
guard let intermediate = contract?.method("transfer", parameters: [to, amount] as [AnyObject], options: mergedOptions) else {return nil}
447451
return intermediate
448452
}
449453

454+
public func sendERC20tokensWithNaturalUnits(tokenAddress: EthereumAddress, from: EthereumAddress, to: EthereumAddress, amount: String, options: Web3Options? = nil) -> TransactionIntermediate? {
455+
let contract = self.web3.contract(Web3.Utils.erc20ABI, at: tokenAddress, abiVersion: 2)
456+
guard var mergedOptions = Web3Options.merge(self.options, with: options) else {return nil}
457+
mergedOptions.from = from
458+
guard let intermediate = contract?.method("decimals", options: mergedOptions) else {return nil}
459+
let callResult = intermediate.call(options: mergedOptions, onBlock: "latest")
460+
var decimals = BigUInt(0)
461+
switch callResult {
462+
case .success(let response):
463+
guard let dec = response["0"], let decTyped = dec as? BigUInt else {return nil}
464+
decimals = decTyped
465+
break
466+
case .failure(let error):
467+
break
468+
}
469+
let intDecimals = Int(decimals)
470+
guard let value = Web3.Utils.parseToBigUInt(amount, decimals: intDecimals) else {return nil}
471+
return sendERC20tokensWithKnownDecimals(tokenAddress: tokenAddress, from: from, to: to, amount: value, options: options)
472+
}
473+
450474
}

web3swift/Web3/Classes/Web3+EventParser.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,6 @@ extension web3.web3contract {
113113
var allResults = [EventParserResultProtocol]()
114114
if (self.filter != nil) {
115115
let eventFilter = self.filter!
116-
// TODO NYI
117116
let filteredLogs = decodedLogs.filter { (result) -> Bool in
118117
if eventFilter.addresses == nil {
119118
return true
@@ -134,7 +133,7 @@ extension web3.web3contract {
134133
}
135134
return false
136135
})
137-
if keys.count != eventFilter.parameterFilters!.count {
136+
if keys.count < eventFilter.parameterFilters!.count {
138137
return false
139138
}
140139
for i in 0 ..< keys.count {
@@ -150,6 +149,7 @@ extension web3.web3contract {
150149
for value in allowedValues! {
151150
if value.isEqualTo(actualValue! as AnyObject) {
152151
inAllowed = true
152+
break
153153
}
154154
}
155155
if !inAllowed {

web3swift/Web3/Classes/Web3+Utils.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,15 @@ extension Web3.Utils {
121121
}
122122

123123
public static func parseToBigUInt(_ amount: String, units: Web3.Utils.Units = .eth) -> BigUInt? {
124+
let unitDecimals = units.decimals
125+
return parseToBigUInt(amount, decimals: unitDecimals)
126+
}
127+
128+
public static func parseToBigUInt(_ amount: String, decimals: Int = 18) -> BigUInt? {
124129
let separators = CharacterSet(charactersIn: ".,")
125130
let components = amount.trimmingCharacters(in: .whitespacesAndNewlines).components(separatedBy: separators)
126131
guard components.count == 1 || components.count == 2 else {return nil}
127-
let unitDecimals = units.decimals
132+
let unitDecimals = decimals
128133
guard let beforeDecPoint = BigUInt(components[0], radix: 10) else {return nil}
129134
var mainPart = beforeDecPoint*BigUInt(10).power(unitDecimals)
130135
if (components.count == 2) {

web3swiftTests/web3swiftTests.swift

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2105,13 +2105,13 @@ class web3swiftTests: XCTestCase {
21052105
// BKX TOKEN
21062106
let web3 = Web3.InfuraMainnetWeb3()
21072107
let coldWalletAddress = EthereumAddress("0x6394b37Cf80A7358b38068f0CA4760ad49983a1B")
2108-
let constractAddress = EthereumAddress("0x45245bc59219eeaaf6cd3f382e078a461ff9de7b")
2108+
let contractAddress = EthereumAddress("0x45245bc59219eeaaf6cd3f382e078a461ff9de7b")
21092109
var options = Web3Options()
21102110
options.from = coldWalletAddress
21112111
let tempKeystore = try! EthereumKeystoreV3(password: "")
21122112
let keystoreManager = KeystoreManager([tempKeystore!])
21132113
web3.addKeystoreManager(keystoreManager)
2114-
let contract = web3.contract(Web3.Utils.erc20ABI, at: constractAddress, abiVersion: 2)!
2114+
let contract = web3.contract(Web3.Utils.erc20ABI, at: contractAddress, abiVersion: 2)!
21152115
let bkxBalanceSend = contract.method("transfer", parameters: [coldWalletAddress, BigUInt(1)] as [AnyObject], options: options)!.call(options: nil)
21162116
switch bkxBalanceSend {
21172117
case .success(let result):
@@ -2122,6 +2122,25 @@ class web3swiftTests: XCTestCase {
21222122
}
21232123
}
21242124

2125+
func testTokenBalanceTransferOnMainNetUsingConvenience() {
2126+
// BKX TOKEN
2127+
let web3 = Web3.InfuraMainnetWeb3()
2128+
let coldWalletAddress = EthereumAddress("0x6394b37Cf80A7358b38068f0CA4760ad49983a1B")
2129+
let contractAddress = EthereumAddress("0x45245bc59219eeaaf6cd3f382e078a461ff9de7b")
2130+
let tempKeystore = try! EthereumKeystoreV3(password: "")
2131+
let keystoreManager = KeystoreManager([tempKeystore!])
2132+
web3.addKeystoreManager(keystoreManager)
2133+
let intermediate = web3.eth.sendERC20tokensWithNaturalUnits(tokenAddress:contractAddress, from: coldWalletAddress, to: coldWalletAddress, amount: "1.0")
2134+
let bkxBalanceSend = intermediate!.call(options: nil)
2135+
switch bkxBalanceSend {
2136+
case .success(let result):
2137+
print(result)
2138+
case .failure(let error):
2139+
print(error)
2140+
XCTFail()
2141+
}
2142+
}
2143+
21252144
func testPerformanceExample() {
21262145
// This is an example of a performance test case.
21272146
self.measure {

web3swiftTests/web3swift_remote_Tests.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,15 +145,20 @@ class web3swift_remote_Tests: XCTestCase {
145145
}
146146
}
147147

148-
func testEventParsing2usingABIv4() {
148+
func testEventParsing4usingABIv2() {
149149
let jsonString = "[{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_spender\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_from\",\"type\":\"address\"},{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"name\":\"\",\"type\":\"uint8\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"version\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"name\":\"balance\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_spender\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"},{\"name\":\"_extraData\",\"type\":\"bytes\"}],\"name\":\"approveAndCall\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"},{\"name\":\"_spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"name\":\"remaining\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"_initialAmount\",\"type\":\"uint256\"},{\"name\":\"_tokenName\",\"type\":\"string\"},{\"name\":\"_decimalUnits\",\"type\":\"uint8\"},{\"name\":\"_tokenSymbol\",\"type\":\"string\"}],\"type\":\"constructor\"},{\"payable\":false,\"type\":\"fallback\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_from\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_owner\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_spender\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},]"
150150
let web3 = Web3.InfuraMainnetWeb3()
151151
let contract = web3.contract(jsonString, at: nil, abiVersion: 2)
152-
guard let eventParser = contract?.createEventParser("Transfer", filter: nil) else {return XCTFail()}
152+
var filter = EventFilter()
153+
filter.addresses = [EthereumAddress("0x53066cddbc0099eb6c96785d9b3df2aaeede5da3")]
154+
filter.parameterFilters = [([EthereumAddress("0xefdcf2c36f3756ce7247628afdb632fa4ee12ec5")] as [EventFilterable]), ([EthereumAddress("0xd5395c132c791a7f46fa8fc27f0ab6bacd824484")] as [EventFilterable])]
155+
guard let eventParser = contract?.createEventParser("Transfer", filter: filter) else {return XCTFail()}
153156
let present = eventParser.parseBlockByNumber(UInt64(5200120))
154157
guard case .success(let pres) = present else {return XCTFail()}
155158
print(pres)
156-
XCTAssert(pres.count == 81)
159+
XCTAssert(pres.count == 1)
160+
// with filter would be
161+
// [web3swift_iOS.EventLog(address: web3swift_iOS.EthereumAddress(_address: "0x53066cddbc0099eb6c96785d9b3df2aaeede5da3", type: web3swift_iOS.EthereumAddress.AddressType.normal), data: 32 bytes, logIndex: 132, removed: false, topics: [32 bytes, 32 bytes, 32 bytes])], status: web3swift_iOS.TransactionReceipt.TXStatus.ok, logsBloom: Optional(web3swift_iOS.EthereumBloomFilter(bytes: 256 bytes))), contractAddress: web3swift_iOS.EthereumAddress(_address: "0x53066cddbc0099eb6c96785d9b3df2aaeede5da3", type: web3swift_iOS.EthereumAddress.AddressType.normal), decodedResult: ["name": "Transfer", "1": web3swift_iOS.EthereumAddress(_address: "0xd5395c132c791a7f46fa8fc27f0ab6bacd824484", type: web3swift_iOS.EthereumAddress.AddressType.normal), "_from": web3swift_iOS.EthereumAddress(_address: "0xefdcf2c36f3756ce7247628afdb632fa4ee12ec5", type: web3swift_iOS.EthereumAddress.AddressType.normal), "_to": web3swift_iOS.EthereumAddress(_address: "0xd5395c132c791a7f46fa8fc27f0ab6bacd824484", type: web3swift_iOS.EthereumAddress.AddressType.normal), "2": 5000000000000000000, "0": web3swift_iOS.EthereumAddress(_address: "0xefdcf2c36f3756ce7247628afdb632fa4ee12ec5", type: web3swift_iOS.EthereumAddress.AddressType.normal), "_value": 5000000000000000000])]
157162
}
158163

159164

0 commit comments

Comments
 (0)