Skip to content
Alekseeva Ekaterina edited this page Nov 6, 2018 · 44 revisions

Question #1: Why does “Use of unresolved identifier” error occurs only for Device Archive Builds?

I've been working with CocoaPods using several dependencies, and I have tested my project and it works perfectly using the simulator, but I'm unable to build my project for a real device with the error: "Use of unresolved identifier". The Pod that's causing the problem is web3swift and there are a number of Classes that the compiler can't find when I try to build for a device, ie.

let contract = web3.contract(contractABI, at EthereumAdress(Rinkeby.address))  

Error: Use of unresolved identifier " EthereumAdress" I have no idea why this problem occurs exclusively when I try to build/archive for a real device. I'm hopeless about this issue as it goes beyond my knowledge, I'd really appreciate your help (the solutions from StackOverflow that I've tried did not work for me)

ANSWER:

This issue has been resolved for the latest versions of web3_swift, since this issue is quite old now

[TODO - Answer @ José Estrella Campaña] source:https://stackoverflow.com/questions/48939394/why-does-use-of-unresolved-identifier-error-occurs-only-for-device-archive-bui

Question #2: Sign any message with the user’s private key and verify the signature on Ethereum

I'm trying to explore Ethereum and creating a app which let user sign message and validate the message. I'm using web3swift framework for this and what I have tried so far is as follows -

let web3Rinkeby = Web3.InfuraRinkebyWeb3()
    let msgStr = "This is my first Ethereum App";
    let data = msgStr.data(using: .utf8)
    let signMsg = web3Rinkeby.wallet.signPersonalMessage(data!, account: address);

    print(signMsg);

but I'm not sure if this is right and how to validate any message as well. Please help.

Answer :

let web3Rinkeby = Web3.InfuraRinkebyWeb3()
/// 
    let keystore = try! EthereumKeystoreV3(password: "")
    let keystoreManager = KeystoreManager([keystore!])
    web3Rinkeby.addKeystoreManager(keystoreManager)
    let address = keystoreManager.addresses![0]
///
let msgStr = "This is my first Ethereum App";
let data = msgStr.data(using: .utf8)
let signMsg = web3Rinkeby.wallet.signPersonalMessage(data!, account: address);
print(signMsg);

Here is an example, how to sign transactions in the tests of the project: [TODO - Answer @skywinder] source: https://stackoverflow.com/questions/52455188/sign-any-message-with-the-user-s-private-key-and-verify-the-signature-on-ethereu/52600931#52600931

Question #3 Interacting with custom smart contract using web3swift

I am using the web3swift library and I managed to do some transactions, mostly gets (balanceOf, owner etc). I read the whole readme(documentation), but I am not quite sure, can we use this library to call functions from our custom smart contracts? For example I have store smart contract and I want to call the buy function from it? I saw that we can transfer eth and ERC20 tokens but that is not enough for me. Any help on this?

ANSWER :

Yes, you can call any function on your custom smart contract. Here is an example.

let infura = Web3.InfuraMainnetWeb3()
        // 1
        let contract = infura.contract(someABI, at: ethContractAddress, abiVersion: 2)
        // 2
        var options = Web3Options.defaultOptions()
        options.from = address
        // 3 
        let transactionIntermediate = contract?.method("accountExists", parameters:[address] as [AnyObject], options: options)
        // 4
        let result = transactionIntermediate!.call(options: options)
        switch result {
        // 5    
        case .success(let res):
            let ans = res["0"] as! Bool
            DispatchQueue.main.async {
                completion(Result.Success(ans))
            }
        case .failure(let error):
            DispatchQueue.main.async {
                completion(Result.Error(error))
            }
        }
    }  
  1. Setting up the contract and ABI. You need contract address for this in Data or String format. let ethContractAddress = EthereumAddress("0xfa28eC7198028438514b49a3CF353BcA5541ce1d")! You can get the ABI of your contract directly from Remix IDE.
  2. Set up all the options you want.
  3. Probably one of the main parts of the answer - here you create the transaction with contract method name and put into it all the parameters this method needs. 4.Here you can either call or send the transaction. call method is for methods with view identifier in solidity, so you won't pay for it and method send() is for the methods of smart contract that should be paid with gas for execution.
  4. Here you just parse the result, returned by the method. You should know data types of the variables you want to obtain from the concrete method in order to parse them correctly.

[TODO - Answer @ Georgy Fesenko]

source: https://stackoverflow.com/questions/51307215/interacting-with-custom-smart-contract-using-web3swift/51965276#51965276

Question #4 How to sign personal message with the personal extension of web3?

I'm making a Dapp with web3swift by matter inc. One method I come across is one of the web3.Personal extension:

public func signPersonalMessage(message: Data, from: 
    web3swift.EthereumAddress, password: String = default) -> 
    Result.Result<Data, web3swift.Web3Error>

I was trying like this:

let web3 = Web3.InfuraMainnetWeb3()
let res = web3.personal.signPersonalMessage(message: msgHash!, 
    from: self.keystore.getAddress()!, password: password)

but what I got was always a Web3ConnectionError. There must not be in the right way I guess. So, any tip to get a usable instance of web3.Personal, and to call the signPersonalMessage method? Thanks :)

By the way, Web3.Utils.signPersonalMessage method is not what I am looking for.

Answer:

Please do check that you have a keystore attached to the web3 object. If there is no local keystore than the message is sent to the remote node for signing, but Infura nodes do not contain any private keys.

If a problem persists please open an issue on gitHub repo.

[TODO - Answer @skywinder] source:https://stackoverflow.com/questions/50794445/how-to-sign-personal-message-with-the-personal-extension-of-web3/51115496#51115496

Question #5 How to create raw transaction using web3swift?

I'm working on a project related to blockchain and need to figure out how raw transaction can be possible. I got no reference for this as of now.

I have tried matterinc/web3swift but unable to get exact thing.

Answer:

    var options = Web3Options.defaultOptions()

    options.gasLimit = BigUInt(21000)

    options.from = self.bip32keystore?.addresses?.first!

    let amountDouble = Int((Double(amount) ?? 0.0)*pow(10, 18))

    let am = BigUInt.init(amountDouble)

    options.value = am

    let estimatedGasResult =   self.web3Rinkeby?.contract(Web3.Utils.coldWalletABI, at: toaddress)!.method(options: options)!.estimateGas(options: nil)

    guard case .success(let estimatedGas)? = estimatedGasResult else {return}

    options.gasLimit = estimatedGas

    var intermediateSend = self.web3Rinkeby?.contract(Web3.Utils.coldWalletABI, at: toaddress, abiVersion: 2)!.method(options: options)!

    intermediateSend = self.web3Rinkeby?.contract(Web3.Utils.coldWalletABI, at: toaddress, abiVersion: 2)!.method(options: options)!

    let sendResult = intermediateSend?.send(password: pass)

    switch sendResult {

        case .success(let r)?:

        print("Sucess",r)

      case .failure(let err)?:

      print("Eroor",err)

     case .none:

     print("sendResultBip32",sendResult)

     }     

[TODO - Answer @anton] source: https://stackoverflow.com/questions/48877073/how-to-create-raw-transaction-using-web3swift

Question #6 Compile error when trying to import web3swift

I am trying to import web3swift into one of my Swift files, but get the compiler !Error - No such module 'web3swift'".

The import statements look like this:

import Geth
import web3swift

In my pod file, I have:

pod 'web3swift', :git => 'https://github.com/MercuryProtocol/web3.swift.git'

I have also tried the following fix which hasn't worked:

  • Go to swift Build Settings
  • Search swift Framework Search Paths (case sensitive)
  • Double click on swift <Multiple values>
  • Click the swift +
  • swift Add $(SRCROOT) and set it to recursive

Answer:

According to your question - probably you are using another repo. Please, kindly check actual version 0.7.0

Installation web3swift is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'web3swift', git: 'https://github.com/matterinc/web3swift'

Run swift pod install from the command line

It should work fine after that. If you still have problems, feel free to open issue

[TODO - Answer @skywinder] source: https://stackoverflow.com/questions/49859007/compile-error-when-trying-to-import-web3swift/50613486#50613486

Question #7 Sign any message with the user’s private key and verify the signature on Ethereum

I'm trying to explore Ethereum and creating a app which let user sign message and validate the message. I'm using web3swift framework for this and what I have tried so far is as follows

    let web3Rinkeby = Web3.InfuraRinkebyWeb3()
    let msgStr = "This is my first Ethereum App";
    let data = msgStr.data(using: .utf8)
    let signMsg = web3Rinkeby.wallet.signPersonalMessage(data!, account: address);

    print(signMsg);

but I'm not sure if this is right and how to validate any message as well. Please help.

Answer:

Seems, that you didn't specify exact address. Here is an example:

let web3Rinkeby = Web3.InfuraRinkebyWeb3()

/// 
    let keystore = try! EthereumKeystoreV3(password: "")
    let keystoreManager = KeystoreManager([keystore!])
    web3Rinkeby.addKeystoreManager(keystoreManager)
    let address = keystoreManager.addresses![0]
///

let msgStr = "This is my first Ethereum App";
let data = msgStr.data(using: .utf8)
let signMsg = web3Rinkeby.wallet.signPersonalMessage(data!, account: address);

print(signMsg);

Here is an example, how to sign transactions in the tests of the project: https://github.com/matterinc/web3swift/blob/develop/web3swiftTests/web3swift_rinkeby_personalSignature_Tests.swift#L20

Question #8 How to use custom json contract with web3swift

How can I use a JSON contract local, for example something like this:

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\"},]"

ANSWER:

You can use swift it in let contract = Web3.InfuraMainnetWeb3().contract(<abiString: String>, at: <EthereumAddress?>, abiVersion: <Int>)

Also you should call its needed method: swift let transaction = contract.method(<method: String>, parameters: <[AnyObject]>, extraData: <Data>, options: <Web3Options?>)

Example of preparing transaction function:

func prepareTransaction(parameters: Data, gasLimit: BigUInt = 27500, completion: @escaping (Result<TransactionIntermediate>) -> Void) {
    DispatchQueue.global().async {
        guard let addressFrom: String = <YOURS WALLET ADDRESS> else {return}
        guard let ethAddressFrom = EthereumAddress(addressFrom) else {return}

        let yourContractAddress = "<YOUR CONTRACT ETH ADDRESS>"
        guard let ethContractAddress = EthereumAddress(contractAddress) else {return}

        let web3 = Web3.InfuraMainnetWeb3() //or any test network
        web3.addKeystoreManager(KeystoreManager.defaultManager)

        var options = Web3Options.defaultOptions()
        options.from = ethAddressFrom
        options.value = 0 // or any other value you want to send

        guard let contract = web3.contract(yourContractJsonABI, at: ethContractAddress, abiVersion: 2) else {return}

        guard let gasPrice = web3.eth.getGasPrice().value else {return}
        options.gasPrice = gasPrice
        options.gasLimit = gasLimit

        guard let transaction = contract.method("<METHOD OF CONTRACT YOU WANT TO CALL>", parameters: [parameters] as [AnyObject], options: options) else {return}
        guard case .success(let estimate) = transaction.estimateGas(options: options) else {return} //here is estimated gas - something like confirming that you made a transaction correctly
        print("estimated cost: \(estimate)")

        DispatchQueue.main.async {
            completion(Result.Success(transaction))
        }
    }
}

[TODO - Answer @Anton Grigorev] source: https://stackoverflow.com/questions/50761595/how-to-use-custom-json-contract-with-web3swift

Question #9 Export mnemonics function in web3swift library

I have a problem interacting with web3swift. I need to show mnemonics to my users when they want it. Is there a way to do it? I did a little research and found out that in trust wallet this could be done just with code:

String(data: decryptedPK, encoding: .utf8)

However, in web3swift even the length in bytes of decryptedPK is different(82 in web3swift, 73 in Trust).

ANSWER:

In web3swift there is no backward conversion from PK to the mnemonic. Also it is theoretically impossible to recover a phrase from a PK.

When the user is creating his keystore you should ask him if he wants to save a passphrase. After seed phrase is converted to some initial entropy the “master key is derived” and the initial entropy is discarded.

The simplest solution is to encrypt the phrase using users pincode and save it in some keystore.

Hope I've answered your question!

[TODO - Answer @ Anton Grigorev] source:https://stackoverflow.com/questions/51963782/export-mnemonics-function-in-web3swift-library

Question #10 Generating 12-word mnemonic from private key

Hey all, I would like to get a mnemonic representation of a private key. Right now, I am getting 24 words by doing the following:

    private func generateKey() -> String {
        let userDir = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
        var ks: EthereumKeystoreV3?
        ks = try! EthereumKeystoreV3(password: "PASSWORD")
        let keydata = try! JSONEncoder().encode(ks!.keystoreParams)
        FileManager.default.createFile(atPath: userDir + "/keystore"+"/key.json", contents: keydata, attributes: nil)
        var mnemonic: String = ""
        do {
            if let pk = try ks?.UNSAFE_getPrivateKeyData(password: "PASSWORD", account: (ks?.addresses?.first)!) {
                print("pk to hex: \(pk.toHexString())")
                let entropy = Data(hex: pk.toHexString())
                mnemonic = Mnemonic.create(entropy: entropy)
                print("pk hex to menmonic: \(mnemonic)")
            }
        } catch {print("Error with getting private key: \(error)")}
        return mnemonic
    }

I am using the Mnemonic helper class found here: https://github.com/essentiaone/HDWallet/blob/develop/HDWalletKit/Mnemonic/Mnemonic.swift

This is the Mnemonic.create function:

    public static func create(entropy: Data, language: WordList = .english) -> String {
        let entropybits = String(entropy.flatMap { ("00000000" + String($0, radix: 2)).suffix(8) })
        let hashBits = String(entropy.sha256().flatMap { ("00000000" + String($0, radix: 2)).suffix(8) })
        let checkSum = String(hashBits.prefix((entropy.count * 8) / 32))
        
        let words = language.words
        let concatenatedBits = entropybits + checkSum
        
        var mnemonic: [String] = []
        for index in 0..<(concatenatedBits.count / 11) {
            let startIndex = concatenatedBits.index(concatenatedBits.startIndex, offsetBy: index * 11)
            let endIndex = concatenatedBits.index(startIndex, offsetBy: 11)
            let wordIndex = Int(strtoul(String(concatenatedBits[startIndex..<endIndex]), nil, 2))
            mnemonic.append(String(words[wordIndex]))
        }
        
        return mnemonic.joined(separator: " ")
    }

A couple questions:

Would the user still be able to derive their private key from this mnemonic? Also, instead of generating a 24-word mnemonic, is it possible to generate a 12-word mnemonic?

ANSWER:

I was able to generate a 12-word mnemonic by changing the following:

        for index in 0..<(concatenatedBits.count / 11) {
            ...

Changed to:

        for index in 0..<(concatenatedBits.count / 22) {
            ...
concatenatedBits.count
``` is equal to 264. By dividing by 22, the for loop index now runs from 0 to 11.
[TODO - Answer @barrasso ]
source: https://github.com/matterinc/web3swift/issues/79

### Question #10  please support Carthage
I know it wont be easy because its dependencies do not support Cartharge :(
but any change?

### ANSWER: 
I'll have a look what is the simplest way to make it work. Worst case I'll have to provide a custom repository for libsodium.

[TODO - Answer @shamatar]
source: https://github.com/matterinc/web3swift/issues/44
Clone this wiki locally