Web3Swift 与自定义 ABI 的交互

时间:2021-03-15 20:04:28

标签: swift xcode ethereum web3 web3swift

我正在尝试与我设置的智能合约进行交互。 基本上目标是从 iOS App 5 参数中设置 projectTitle projectLocation projectStart projectEnd teamType

我希望用户设置这些参数并将其写入 ropsten 测试网络。

我也想在以后用户有需要的时候获取合同信息。

我的 Solidity 代码在 remix 中工作正常,并且合约已经部署:

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;
    }

    
   
}

我现在的问题是我无法弄清楚如何使用 web3swift 库从区块链中检索数据。我现在就是这样做的:

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))
            }
        }
    }
}

我收到了一个错误,因为 result 说: "调用可以抛出,但是没有标记'try',错误没有处理"

总的来说,我发现很难建立与智能合约 abi 的交互。

我已经在使用 web3swift 功能发送交易了,它的功能非常棒。

也许有人知道我如何在区块链上记录信息并使用 web3swift 获取它。

可悲的是,互联网上的内容在这方面确实没有帮助。

感谢您的帮助:)

1 个答案:

答案 0 :(得分:1)

你很近。从错误 "Call can throw, but it is not marked with 'try' and the error is not handled" 开始,这是由于在不使用 Try Catch 模式的情况下尝试 call 合同函数引起的。按照 web3 库的设计方式,这个模式对于所有的 write 和 call 方法都是必要的。

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

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

此外,我建议您在进行合同调用时将 DispatchQueue.main.asyncPromise Kit library 一起使用。

ABI 难以阅读且混乱,不建议使用它来帮助查找合同中的可调用方法和参数。相反,我会将合约与 Xcode 一起打开,并通过使用包含将要使用的所有合约方法的枚举或结构。

// 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

我将 ABI 移动到 xcode 中的一个单独文件中以保持其干净。这是文件的 link

这是一个很好的例子,可以帮助您入门。查看我的 Github repo 以获得改进版本。希望这会有所帮助,如果您有更多问题,请告诉我。 :)

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)
        })
        
    }
}