3

It is my first time working with smart contracts and my goal is to create a mobile app which can interact with it by calling methods to save or retrieve data. For this reason I have created a very simple contract using Remix and I have also deployed on Rinkeby testnet.

contract Storage {

uint256 number;

function store(uint256 num) public {
    number = num;
}

function retrieve() public view returns (uint256){
    return number;
}
}

Then I built a SwiftUI app, which has one button. When I press this button, I want to call the store method and save some int number. For example number 9. Therefore I have created a function called write which looks as following:

 let myInt = 9

  func write() {

    let web3 = Web3.InfuraRinkebyWeb3(accessToken: "https://rinkeby.infura.io/v3/a146daf63d93490995823f0910f50118")

    let walletAddress = EthereumAddress("0xc65943Fae5a554e7DCF916F8828a73E5f7b1bDCd")! // Your wallet address
    let contractMethod = "store" // Contract method you want to write
    let contractABI = contractABIString // Contract ABI
    let contractAddress = EthereumAddress("0x2826C42354FE5B816c7E21AD9e3B395Ced512C0C")!
    let abiVersion = 2 // Contract ABI version
    let parameters = [myInt] as [AnyObject]
    let extraData: Data = Data() // Extra data for contract method
    let contract = web3.contract(contractABI, at: contractAddress, abiVersion: abiVersion)!
    var options = TransactionOptions.defaultOptions
    options.from = walletAddress
    options.gasPrice = .automatic
    options.gasLimit = .automatic

    do {
        contract.write(
            contractMethod,
            parameters: parameters,
            extraData: extraData,
            transactionOptions: options)

    } catch {
        print("error:", error)
    }

Unfortunately, when I run this code, nothing is happening. I don't get any error but when I refresh the contract I see that number 9 is not passed.

I am using web3swift library, https://github.com/skywinder/web3swift/tree/master#send-erc-20-token. According to the documentation, it should be fine, but it is not working, therefore I would really appreciate some assistant to make it work or some example projects where I can take a look, because I also couldn't find anything.

I found some other project using JS and I see the people there use their private keys, maybe I also need it, but since it is not shown in the documentation, I was not sure how to use it.

Dakata
  • 1,227
  • 2
  • 14
  • 33
  • found anything ? – Cublax Jun 06 '22 at 01:01
  • Still looking :( If you know something please let me know – Dakata Jun 06 '22 at 19:03
  • did the answer below work ? – Cublax Jun 13 '22 at 00:56
  • Hi, I will check it but have another problem now, do you know how to connect automatically to Metamask in stead of manually providing the private key? – Dakata Jun 13 '22 at 08:22
  • No I do not know how to do that yet. I am not sure it's such an easy thing to do since Metamask is javascript. If you wanna avoid risk store your privateKey in a gitignore file so it isn't sent on github(because you will be hacked in 1h if you do so). Otherwise create a dummy account just for testing purpose. But I am afraid that if you wanna login to an account you need it's key. Anyway try to give it a try so we can declare the answer as the right or if not I will update it :) – Cublax Jun 14 '22 at 03:18
  • Yes it works, but can you give me advice how to interact with metamask. Really most of the users have it and it is very convenient if you can connect your iOS app with the Metamask app, so that the users can reuse their already created accounts. If you download Openseapp, it directly makes this kind of connection with the Metamaskapp – Dakata Jun 16 '22 at 11:06
  • I haven't been further than the answer I gave you here. But I didn't know it's such a common feature. I have some stuff to do first but I will give it a try and let you know how this works :) – Cublax Jun 17 '22 at 02:25

1 Answers1

2

The smart contract interaction requires a few initialisations steps:

  1. Your wallet
  2. KeyStoreManager
  3. web3

Let's assume you already got a Metamask wallet. Separately you need to have a separate file containing the ABI of the smartContract you want to interact with. It is an array and having it in a Swift.String is easier.(I won't show you this step)

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

    class SmartContractInteraction {
        var wallet: Wallet!
        var keystoreManager: KeystoreManager!
        var web3: web3!
        
        init() {
            wallet = initializeWallet()
            keystoreManager = getKeyStoreManager()
            web3 = initializeweb3()
        }
        
        private func initializeWallet() -> Wallet? {
            let password = "PasswordMetamask"
            let key = "AccountPrivateKey"
            let formattedKey = key.trimmingCharacters(in: .whitespacesAndNewlines)
            let dataKey = Data.fromHex(formattedKey)!
            let name = "Account 1"
            do {
                let keystore = try EthereumKeystoreV3(privateKey: dataKey, password: password)!
                let keyData = try JSONEncoder().encode(keystore.keystoreParams)
                let address = keystore.addresses!.first!.address
                return Wallet(address: address, data: keyData, name: name, isHD: false)
            } catch {
                print("wallet init failed: \(error)")
                return nil
            }
        }
        
        private func getKeyStoreManager() -> KeystoreManager {
            let data = wallet.data
            let keystoreManager: KeystoreManager
            if wallet.isHD {
                let keystore = BIP32Keystore(data)!
                keystoreManager = KeystoreManager([keystore])
            } else {
                let keystore = EthereumKeystoreV3(data)!
                keystoreManager = KeystoreManager([keystore])
            }
            return keystoreManager
        }
        
        private func initializeweb3() -> web3 {
            let endpoint = "https://ropsten.infura.io/v3/....."
            let web3 = web3swift.web3(provider: Web3HttpProvider(URL(string: endpoint)!)!)
            web3.addKeystoreManager(keystoreManager)
            return web3
        }
        
        func callSmartContract() {
            let value: String = "1"
            let walletAddress = EthereumAddress(wallet.address)!
            let contractAddress = EthereumAddress("SmartContractAddress")!
            let contractMethod = "store"
            let contractABI = MyContractABI
            let abiVersion = 2
            let parameters = [9] as [AnyObject]
            let extraData: Data = Data()
            let contract = web3.contract(contractABI, at: contractAddress, abiVersion: abiVersion)!
            let amount = Web3.Utils.parseToBigUInt(value, units: .wei)
            var options = TransactionOptions.defaultOptions
            options.value = amount
            options.from = walletAddress
            options.gasPrice = .automatic
            options.gasLimit = .automatic
            let tx = contract.write(
                contractMethod,
                parameters: parameters,
                extraData: extraData,
                transactionOptions: options)!
            do {
                let password = "MetamaskPassword"
                let result = try tx.send(password: password)
                print(result)
            } catch {
                print("Token Balance failed: \(error)")
            }
        }
    }

Like this should work, I think passing the value/option.value in the smartContract call method isn't necessary, but since I don't have your ABI I prefer to not remove anything from how it worked for me. Also I am not sure about the type of the number you pass. Feel free to edit if this works without :)

Cublax
  • 1,232
  • 1
  • 11
  • 20