0

I am trying to interact with a smart contract I set up. Basically the goal is to set from an iOS App 5 Parameters

projectTitle
projectLocation
projectStart
projectEnd
teamType

I want the user to set those parameters and write it on the ropsten testnetwork.

I also would like to get the contract information at a later point whenever the user feels for it.

my solidity code is working properly in remix and the contract is already deployed:

pragma solidity >=0.4.22 <0.7.0;

contract ProjectContent {
    
    string public projectTitle;
    string public projectLocation;
    string public projectStart;
    string public projectEnd;
    string public teamType;

    
    function projectContent(string initialProjectTitle, string initialProjectLocation, string initialProjectStart, string initialProjectEnd, string initialTeamType) public {
        projectTitle = initialProjectTitle;
        projectLocation = initialProjectLocation;
        projectStart = initialProjectStart;
        projectEnd = initialProjectEnd;
        teamType = initialTeamType;
    }
    
    function setContract(string newProjectTitle, string newProjectLocation, string newProjectStart, string newProjectEnd, string newTeamType) public {
        projectTitle = newProjectTitle;
        projectLocation = newProjectLocation;
        projectStart = newProjectStart;
        projectEnd = newProjectEnd;
        teamType = newTeamType;
        
    }
   
    function getProjectTitle() public view returns (string) {
      return projectTitle;
    } 
    
    function getProjectLocation() public view returns (string) {
      return projectLocation;
    } 
    
    function getProjectStart() public view returns (string) {
        return projectStart;
    }
    
    function getProjectEnd() public view returns (string) {
        return projectEnd;
    }
    
    function getTeamType() public view returns (string) {
        return teamType;
    }

    
   
}

My problem now is that I cannot figure out how to retrieve the data from the blockchain using the web3swift library. I am doing it like so now:

class ProjectContractViewController: UIViewController, HalfModalPresentable {

    @IBOutlet weak var contractABIView: UITextView!
    
    
    var halfModalTransitioningDelegate: HalfModalTransitioningDelegate?
    
    var contractABI = "[{\"constant\":false,\"inputs\":[{\"name\":\"initialProjectTitle\",\"type\":\"string\"},{\"name\":\"initialProjectLocation\",\"type\":\"string\"},{\"name\":\"initialProjectStart\",\"type\":\"string\"},{\"name\":\"initialProjectEnd\",\"type\":\"string\"},{\"name\":\"initialTeamType\",\"type\":\"string\"}],\"name\":\"projectContent\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"newProjectTitle\",\"type\":\"string\"},{\"name\":\"newProjectLocation\",\"type\":\"string\"},{\"name\":\"newProjectStart\",\"type\":\"string\"},{\"name\":\"newProjectEnd\",\"type\":\"string\"},{\"name\":\"newTeamType\",\"type\":\"string\"}],\"name\":\"setContract\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"getProjectEnd\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"getProjectLocation\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"getProjectStart\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"getProjectTitle\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"getTeamType\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"projectEnd\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"projectLocation\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"projectStart\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"projectTitle\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"teamType\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]"
    
    
    let str = "0x6080604052600436106100ba576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806323a35e62146100bf57806337a4fc7d1461014f5780634a5736fd146101df5780634b04811e1461026f5780634e9d1281146102ff57806363afee221461038f578063775e6d451461051057806393ee0402146105a0578063c3e20c9f14610630578063d8045412146106c0578063dad375ff14610750578063f020cd19146108d1575b600080fd5b3480156100cb57600080fd5b506100d4610961565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101145780820151818401526020810190506100f9565b50505050905090810190601f1680156101415780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015b57600080fd5b50610164610a03565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101a4578082015181840152602081019050610189565b50505050905090810190601f1680156101d15780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156101eb57600080fd5b506101f4610aa1565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610234578082015181840152602081019050610219565b50505050905090810190601f1680156102615780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561027b57600080fd5b50610284610b3f565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156102c45780820151818401526020810190506102a9565b50505050905090810190601f1680156102f15780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561030b57600080fd5b50610314610bdd565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610354578082015181840152602081019050610339565b50505050905090810190601f1680156103815780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561039b57600080fd5b5061050e600480360381019080803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290505050610c7b565b005b34801561051c57600080fd5b50610525610cf5565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561056557808201518184015260208101905061054a565b50505050905090810190601f1680156105925780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156105ac57600080fd5b506105b5610d97565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156105f55780820151818401526020810190506105da565b50505050905090810190601f1680156106225780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561063c57600080fd5b50610645610e39565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561068557808201518184015260208101905061066a565b50505050905090810190601f1680156106b25780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156106cc57600080fd5b506106d5610ed7565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156107155780820151818401526020810190506106fa565b50505050905090810190601f1680156107425780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561075c57600080fd5b506108cf600480360381019080803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290505050610f79565b005b3480156108dd57600080fd5b506108e6610ff3565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561092657808201518184015260208101905061090b565b50505050905090810190601f1680156109535780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b606060028054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156109f95780601f106109ce576101008083540402835291602001916109f9565b820191906000526020600020905b8154815290600101906020018083116109dc57829003601f168201915b5050505050905090565b60028054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a995780601f10610a6e57610100808354040283529160200191610a99565b820191906000526020600020905b815481529060010190602001808311610a7c57829003601f168201915b505050505081565b60018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b375780601f10610b0c57610100808354040283529160200191610b37565b820191906000526020600020905b815481529060010190602001808311610b1a57829003601f168201915b505050505081565b60048054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610bd55780601f10610baa57610100808354040283529160200191610bd5565b820191906000526020600020905b815481529060010190602001808311610bb857829003601f168201915b505050505081565b60038054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610c735780601f10610c4857610100808354040283529160200191610c73565b820191906000526020600020905b815481529060010190602001808311610c5657829003601f168201915b505050505081565b8460009080519060200190610c91929190611095565b508360019080519060200190610ca8929190611095565b508260029080519060200190610cbf929190611095565b508160039080519060200190610cd6929190611095565b508060049080519060200190610ced929190611095565b505050505050565b606060018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610d8d5780601f10610d6257610100808354040283529160200191610d8d565b820191906000526020600020905b815481529060010190602001808311610d7057829003601f168201915b5050505050905090565b606060048054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610e2f5780601f10610e0457610100808354040283529160200191610e2f565b820191906000526020600020905b815481529060010190602001808311610e1257829003601f168201915b5050505050905090565b60008054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610ecf5780601f10610ea457610100808354040283529160200191610ecf565b820191906000526020600020905b815481529060010190602001808311610eb257829003601f168201915b505050505081565b606060038054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610f6f5780601f10610f4457610100808354040283529160200191610f6f565b820191906000526020600020905b815481529060010190602001808311610f5257829003601f168201915b5050505050905090565b8460009080519060200190610f8f929190611095565b508360019080519060200190610fa6929190611095565b508260029080519060200190610fbd929190611095565b508160039080519060200190610fd4929190611095565b508060049080519060200190610feb929190611095565b505050505050565b606060008054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561108b5780601f106110605761010080835404028352916020019161108b565b820191906000526020600020905b81548152906001019060200180831161106e57829003601f168201915b5050505050905090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106110d657805160ff1916838001178555611104565b82800160010185558215611104579182015b828111156111035782518255916020019190600101906110e8565b5b5090506111119190611115565b5090565b61113791905b8082111561113357600081600090555060010161111b565b5090565b905600a165627a7a72305820458843a936d80ffe49dddb0955a0c1d56d0e15f994cd5ce31b386188b2724a790029"
        
    var contractAddress = EthereumAddress("0x11A0c067d7481240dCA57457eff77fc98dEAdE0F")

    
    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }

    
    func callContract(Password: String) {
        
        // Get from address from private key
        
        let formattedKey = Password.trimmingCharacters(in: .whitespacesAndNewlines)
        let dataKey = Data.fromHex(formattedKey )!
                    
                    // @@@ use [passKey]
        let keystore = try! EthereumKeystoreV3(privateKey: dataKey, password: "")!

  
        let keyData = try! JSONEncoder().encode(keystore.keystoreParams)
//            let address = keystore.addresses!.first!.address
        let address =  keystore.addresses!.first!.address
        let ethAddress = EthereumAddress(address)
        
        
        let infura = Web3.InfuraMainnetWeb3()
        // 1
        let contract = infura.contract(contractABI, at: contractAddress, abiVersion: 2)
        // 2
        var options = TransactionOptions.defaultOptions
            options.from = keystore.addresses!.first!
        // 3
        
        let data = Data.init(hex: str)

        let transactionIntermediate = contract?.method("getProjectTitle", parameters: [address] as [AnyObject], extraData: data, transactionOptions: options)
            
        // 4
        let result = transactionIntermediate!.call(transactionOptions: 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))
            }
        }
    }
}

I get an error for the resultsaying: "Call can throw, but it is not marked with 'try' and the error is not handled"

and in general I find it really hard to set up the interaction with a smart contract abi.

I am already using the web3swift functionality for sending transactions at it works like a charm.

Maybe someone knows how I can record information on the blockchain and get it using web3swift.

TylerH
  • 20,799
  • 66
  • 75
  • 101
Schaedel420
  • 175
  • 11

1 Answers1

1

You are close. Starting with the error "Call can throw, but it is not marked with 'try' and the error is not handled" this is caused by trying to call a contract function without using the Try Catch pattern. Do to the way web3 library is designed this pattern is necessary for all write and call methods.

// Incorrect
let result = transactionIntermediate!.call(transactionOptions: options)

// Correct
do {
    let result = try transactionIntermediate!.call(transactionOptions: options)
}catch{
    print("Error trying to call method \(error)")
}

Additionally, I recommend using the DispatchQueue.main.async along with Promise Kit library when making your contract calls.

ABIs are hard to read and messy, don't recommend using it to help find callable methods and parameters within the contract. Instead I would have the contract open along side Xcode and through the use of either an enum or struct containing all the contract methods that are going to be used.

// Methods available within the contract
enum ContractMethods:String {

    case projectContract = "projectContent"
    case setContract = "setContract"
    case getProjectTitle = "getProjectTitle"
    case getProjectLocation = "getProjectLocation"
    case getProjectStart = "getProjectStart"
    case getProjectEnd = "getProjectEnd"
    case getTeamType = "getTeamType"
}

// Usage
ContractMethods.setContract.rawValue

I moved the ABI to a separate file within xcode to keep it clean. Here is link to the file.

Here is a good example to help get you started. Check out my GitHub repo for the improved version.

import UIKit
import web3swift
import PromiseKit

struct Wallet {
    let address: String
    let data: Data
    let name:String
    let isHD:Bool
}

struct HDKey {
    let name:String?
    let address:String
}

var password = "" // leave empty for ganache or use your wallet password
let privateKey = "<PrivateKey>" // Private key of wallet
let walletName = "MyWallet"

let contractAddress = "<ContractAddress>" // 0x11A0c067d7481240dCA57457eff77fc98dEAdE0F

let endpoint = URL(string:"http://127.0.0.1:7545")! // Im using Ganache but it might look like endpoint = URL(string:"https://rinkeby.infura.io/v3/<APIKEY>")!
let abiVersion = 2

class ViewController: UIViewController {
    // Mock data used within contract
     let projectTitle = "HouseSiding"
     let projectLocation = "299 Race Ave. Dacula, GA 30019"
     let projectStart = "May 14, 2021"
     let projectEnd = "June 15, 2021"
     let teamType = "Collaboration"
    
    var web3:web3?
    var contract:web3.web3contract?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 1. Create wallet using a private key
        let formattedKey = privateKey.trimmingCharacters(in: .whitespacesAndNewlines)
        let dataKey = Data.fromHex(formattedKey)!
        let keyStore = try! EthereumKeystoreV3(privateKey:dataKey, password: password)!
        let keyData = try! JSONEncoder().encode(keyStore.keystoreParams)
        let address = keyStore.addresses!.first!.address
        let wallet = Wallet(address: address, data: keyData, name: walletName, isHD: false)
        
        // 2. Construct web3 and keystoreManager
        do {
            web3 = try Web3.new(endpoint)

            let data = wallet.data
            var keystoreManager: KeystoreManager
            if wallet.isHD {
                let keystore = BIP32Keystore(data)!
                keystoreManager = KeystoreManager([keystore])
            }else{
                let keystore = EthereumKeystoreV3(data)!
                keystoreManager = KeystoreManager([keystore])
            }
            print(keystoreManager.addresses)
            web3!.addKeystoreManager(keystoreManager)
            let ethContractAddress = EthereumAddress(contractAddress, ignoreChecksum: true)!
            contract = web3!.contract(contractABI, at: ethContractAddress, abiVersion: abiVersion)!
            
        }catch{
            print ("Failed to construct contract and/or keystoreManager \(error)")
        }
        
        // 3. Create and callout a contract method
        //let parameters = [projectTitle,projectLocation,projectStart,projectEnd,teamType] as [AnyObject] // parameters used to created a new project
        let parameters = [] as [AnyObject] // no parameters
     
            let response = Promise<Any> { seal in
                DispatchQueue.global().async {
                   
                    // Catch errors within async call
                    do {
                        // No extra data for method call
                        let extraData: Data = Data()
                        // Options for method call
                        var options = TransactionOptions.defaultOptions
                        
                        options.from = EthereumAddress(wallet.address)! // current wallet address
                        // Leave automatic for gas
                        options.gasPrice = .automatic
                        options.gasLimit = .automatic

                        // Calling get Project title from contract
                        // NOTE: First call setContract with parameters
                        let tx = self.contract!.method("getProjectTitle",
                                                 parameters: parameters,
                                                 extraData: extraData,
                                                 transactionOptions: options)
                        // Depending on the type of call a password might be needed
                        //if password != nil {
                            //let result = try tx!.send(password: password)
                        //    seal.resolve(.fulfilled(true))
                        //}else{
                            let result = try tx!.call()
                            // fulfill are result from contract
                            let anyResult = result["0"] as Any
                            seal.resolve(.fulfilled(anyResult))
                        //}
                    }catch {
                        // error
                        seal.reject(error)
                    }
                }
            }

        response.done({result in
            print(result) // Optional(HouseSiding)
        })
        
    }
}
TylerH
  • 20,799
  • 66
  • 75
  • 101
Mitch
  • 576
  • 5
  • 11