|
| 1 | +// |
| 2 | +// Web3+ERC721.swift |
| 3 | +// web3swift-iOS |
| 4 | +// |
| 5 | +// Created by Anton Grigorev on 17.10.2018. |
| 6 | +// Copyright © 2018 The Matter Inc. All rights reserved. |
| 7 | +// |
| 8 | + |
| 9 | +import Foundation |
| 10 | +import BigInt |
| 11 | +import Result |
| 12 | + |
| 13 | +// This namespace contains functions to work with ERC721 tokens. |
| 14 | +// variables are lazyly evaluated or global token information (name, ticker, total supply) |
| 15 | +// can be imperatively read and saved |
| 16 | +class ERC721 { |
| 17 | + private var _name: String? = nil |
| 18 | + private var _symbol: String? = nil |
| 19 | + private var _tokenId: BigUInt? = nil |
| 20 | + private var _tokenURI: String? = nil |
| 21 | + private var _owner: EthereumAddress? = nil |
| 22 | + private var _hasReadProperties: Bool = false |
| 23 | + |
| 24 | + public var options: Web3Options |
| 25 | + public var web3: web3 |
| 26 | + public var provider: Web3Provider |
| 27 | + public var address: EthereumAddress |
| 28 | + |
| 29 | + lazy var contract: web3.web3contract = { |
| 30 | + let contract = self.web3.contract(Web3.Utils.erc721ABI, at: self.address, abiVersion: 2) |
| 31 | + precondition(contract != nil) |
| 32 | + return contract! |
| 33 | + }() |
| 34 | + |
| 35 | + public init(web3: web3, provider: Web3Provider, address: EthereumAddress) { |
| 36 | + self.web3 = web3 |
| 37 | + self.provider = provider |
| 38 | + self.address = address |
| 39 | + var mergedOptions = web3.options |
| 40 | + mergedOptions.to = address |
| 41 | + self.options = mergedOptions |
| 42 | + } |
| 43 | + |
| 44 | + public var name: String { |
| 45 | + self.readProperties() |
| 46 | + if self._name != nil { |
| 47 | + return self._name! |
| 48 | + } |
| 49 | + return "" |
| 50 | + } |
| 51 | + |
| 52 | + public var symbol: String { |
| 53 | + self.readProperties() |
| 54 | + if self._symbol != nil { |
| 55 | + return self._symbol! |
| 56 | + } |
| 57 | + return "" |
| 58 | + } |
| 59 | + |
| 60 | + public var tokenId: BigUInt { |
| 61 | + self.readProperties() |
| 62 | + if self._tokenId != nil { |
| 63 | + return self._tokenId! |
| 64 | + } |
| 65 | + return 0 |
| 66 | + } |
| 67 | + |
| 68 | + public var tokenURI: String { |
| 69 | + self.readProperties() |
| 70 | + if self._tokenURI != nil { |
| 71 | + return self._tokenURI! |
| 72 | + } |
| 73 | + return "" |
| 74 | + } |
| 75 | + |
| 76 | + public func readProperties() { |
| 77 | + if self._hasReadProperties { |
| 78 | + return |
| 79 | + } |
| 80 | + defer { self._hasReadProperties = true } |
| 81 | + let contract = self.contract |
| 82 | + guard contract.contract.address != nil else {return} |
| 83 | + guard let nameResult = contract.method("name", parameters: [] as [AnyObject], extraData: Data(), options: self.options)?.call(options: nil, onBlock: "latest") else {return} |
| 84 | + switch nameResult { |
| 85 | + case .success(let returned): |
| 86 | + guard let res = returned["0"] as? String else {break} |
| 87 | + self._name = res |
| 88 | + default: |
| 89 | + self._name = "" |
| 90 | + } |
| 91 | + |
| 92 | + guard let symbol = contract.method("symbol", parameters: [] as [AnyObject], extraData: Data(), options: self.options)?.call(options: nil, onBlock: "latest") else {return} |
| 93 | + switch symbol { |
| 94 | + case .success(let returned): |
| 95 | + guard let res = returned["0"] as? String else {break} |
| 96 | + self._symbol = res |
| 97 | + default: |
| 98 | + self._symbol = "" |
| 99 | + } |
| 100 | + |
| 101 | + guard let tokenId = contract.method("tokenId", parameters: [] as [AnyObject], extraData: Data(), options: self.options)?.call(options: nil, onBlock: "latest") else {return} |
| 102 | + switch tokenId { |
| 103 | + case .success(let returned): |
| 104 | + guard let res = returned["0"] as? BigUInt else {break} |
| 105 | + self._tokenId = res |
| 106 | + default: |
| 107 | + self._tokenId = 0 |
| 108 | + } |
| 109 | + |
| 110 | + guard let tokenURI = contract.method("tokenURI", parameters: [_tokenId] as [AnyObject], extraData: Data(), options: self.options)?.call(options: nil, onBlock: "latest") else {return} |
| 111 | + switch tokenURI { |
| 112 | + case .success(let returned): |
| 113 | + guard let res = returned["0"] as? String else {return} |
| 114 | + self._tokenURI = res |
| 115 | + default: |
| 116 | + self._tokenURI = "" |
| 117 | + } |
| 118 | + } |
| 119 | + |
| 120 | + func getBalance(account: EthereumAddress) -> Result<BigUInt, Web3Error> { |
| 121 | + let contract = self.contract |
| 122 | + let result = contract.method("balanceOf", parameters: [account] as [AnyObject], extraData: Data(), options: self.options)!.call(options: nil, onBlock: "latest") |
| 123 | + switch result { |
| 124 | + case .success(let returned): |
| 125 | + guard let res = returned["0"] as? BigUInt else {return Result.failure(Web3Error.processingError(desc: "Failed to get result of expected type from the Ethereum node"))} |
| 126 | + return Result(res) |
| 127 | + case .failure(let error): |
| 128 | + return Result.failure(error) |
| 129 | + } |
| 130 | + } |
| 131 | + |
| 132 | + func getOwner(tokenId: BigUInt) -> Result<EthereumAddress, Web3Error> { |
| 133 | + let contract = self.contract |
| 134 | + let result = contract.method("ownerOf", parameters: [account] as [tokenId], extraData: Data(), options: self.options)!.call(options: nil, onBlock: "latest") |
| 135 | + switch result { |
| 136 | + case .success(let returned): |
| 137 | + guard let res = returned["0"] as? EthereumAddress else {return Result.failure(Web3Error.processingError(desc: "Failed to get result of expected type from the Ethereum node"))} |
| 138 | + return Result(res) |
| 139 | + case .failure(let error): |
| 140 | + return Result.failure(error) |
| 141 | + } |
| 142 | + } |
| 143 | + |
| 144 | +// func getAllowance(originalOwner: EthereumAddress, delegate: EthereumAddress) -> Result<BigUInt, Web3Error> { |
| 145 | +// let contract = self.contract |
| 146 | +// let result = contract.method("allowance", parameters: [originalOwner, delegate] as [AnyObject], extraData: Data(), options: self.options)!.call(options: nil, onBlock: "latest") |
| 147 | +// switch result { |
| 148 | +// case .success(let returned): |
| 149 | +// guard let res = returned["0"] as? BigUInt else {return Result.failure(Web3Error.processingError(desc: "Failed to get result of expected type from the Ethereum node"))} |
| 150 | +// return Result(res) |
| 151 | +// case .failure(let error): |
| 152 | +// return Result.failure(error) |
| 153 | +// } |
| 154 | +// } |
| 155 | + |
| 156 | + func transfer(from: EthereumAddress, to: EthereumAddress, tokenId: BigUInt) -> Result<TransactionIntermediate, Web3Error> { |
| 157 | + let contract = self.contract |
| 158 | + var basicOptions = Web3Options() |
| 159 | + basicOptions.from = from |
| 160 | + basicOptions.to = self.address |
| 161 | + |
| 162 | + let intermediateToSend = contract.method("transfer", parameters: [to, tokenId] as [AnyObject], options: basicOptions)! |
| 163 | + return Result(intermediateToSend) |
| 164 | + } |
| 165 | + |
| 166 | + func transferFrom(from: EthereumAddress, to: EthereumAddress, originalOwner: EthereumAddress, tokenId: BigUInt) -> Result<TransactionIntermediate, Web3Error> { |
| 167 | + let contract = self.contract |
| 168 | + var basicOptions = Web3Options() |
| 169 | + basicOptions.from = from |
| 170 | + basicOptions.to = self.address |
| 171 | + |
| 172 | + let intermediateToSend = contract.method("transferFrom", parameters: [originalOwner, to, tokenId] as [AnyObject], options: basicOptions)! |
| 173 | + return Result(intermediateToSend) |
| 174 | + } |
| 175 | + |
| 176 | +// func setAllowance(from: EthereumAddress, to: EthereumAddress, newAmount: String) -> Result<TransactionIntermediate, Web3Error> { |
| 177 | +// let contract = self.contract |
| 178 | +// var basicOptions = Web3Options() |
| 179 | +// basicOptions.from = from |
| 180 | +// basicOptions.to = self.address |
| 181 | +// |
| 182 | +// // get the decimals manually |
| 183 | +// let intermediate = contract.method("setAllowance", options: basicOptions)! |
| 184 | +// let callResult = intermediate.call(options: basicOptions, onBlock: "latest") |
| 185 | +// var decimals = BigUInt(0) |
| 186 | +// switch callResult { |
| 187 | +// case .success(let response): |
| 188 | +// guard let dec = response["0"], let decTyped = dec as? BigUInt else { |
| 189 | +// return Result.failure(Web3Error.inputError(desc: "Contract may be not ERC20 compatible, can not get decimals"))} |
| 190 | +// decimals = decTyped |
| 191 | +// break |
| 192 | +// case .failure(let error): |
| 193 | +// return Result.failure(error) |
| 194 | +// } |
| 195 | +// let intDecimals = Int(decimals) |
| 196 | +// guard let value = Web3.Utils.parseToBigUInt(newAmount, decimals: intDecimals) else { |
| 197 | +// return Result.failure(Web3Error.inputError(desc: "Can not parse inputted amount")) |
| 198 | +// } |
| 199 | +// let intermediateToSend = contract.method("setAllowance", parameters: [to, value] as [AnyObject], options: basicOptions)! |
| 200 | +// return Result(intermediateToSend) |
| 201 | +// } |
| 202 | + |
| 203 | + |
| 204 | +} |
0 commit comments