Boilertalk / Web3.swift

A pure swift Ethereum Web3 library
MIT License
645 stars 189 forks source link

How to Call Methods from a Custom ERC20 Contract? #15

Open jlstr opened 6 years ago

jlstr commented 6 years ago

Hello!

I have my own ERC20 contract, with transfer, transferFrom methods, etc. I would like to know how to get a reference to such a contract (via ABI + address combination?) and then make calls to its methods. Is this possible? If so, would you mind pointing out where can I find an example.

Thanks in advance, -Jose

jaramburu commented 6 years ago

I am also interested in an answer to this.

koraykoska commented 6 years ago

@jlstr @jaramburu

First of all: Calling ABI functions is something I want to make MUCH easier than it is right now. I am currently designing something for it and you can track the development of it in #9

This does not mean you can't interact with smart contracts with the current version.

If you want to read from a smart contract you can just construct an instance of EthereumCall and then call eth.call(call:block:).

The return data will include the actual return value from the smart contract call.

Read the official contract abi specification to see how to construct the data field of the EthereumCall in order to read the smart contract values you want.

For calling actual state changing methods there is a similar procedure. You have to construct a normal EthereumTransaction just like you would if you wanted to send some ETH. Just set the value to 0 (you don't want to send ETH, just call a method), set the to to the smart contract address you want to call and add the input data into the data parameter.
Then you have to sign this transaction with your private key as always (transaction.sign(with privateKey:)) and call eth.sendRawTransaction(transaction:) just like you would for a normal transaction.

You will once again have to format the input data according to the contract abi specification I linked to above.

Like I said before, it's not quite easy yet but still possible, and in fact I have some working solutions.


If there is a high demand for ERC20 contract ABI calls I will post a gist with an example so you can copy & paste the hard part until the easy Contract ABI parser is finalized.

jlstr commented 6 years ago

Hello @Ybrin!, this ethereum library has tremendous potential as it is tremendously easy to use and understand, and it's also semantically better than the rest of the libraries out there. I do hope however, that Contract interaction is finalized soon because that's a Core feature, or more like THE CORE feature any web3 implementation should enable. If possible I would really like to see the Example you're referring to.

Thank you for your detailed response.

koraykoska commented 6 years ago

@jlstr

Ok I made something which you can use for now specifically for ERC20 contracts.

Copy the following file into your project:

https://gist.github.com/Ybrin/c2db36fce19b821221b3f8fdb7bae7da

This adds an ERC20 struct into your project which has helper functions to create transactions for writing to and calls for reading from an ERC20 smart contract.

You can use it like that (Note: The examples below expect you to have installed the PromiseKit extensions and imported PromiseKit and Web3):

First of all initialize a new instance of ERC20 with the contract address

let erc20 = try ERC20(contractAddress: .init(hex: "0xb63b606ac810a52cca15e44bb630fd42d8d1d83d", eip55: false))

Also create your instance of Web3 with your JSON RPC URL


let infuraUrl = "https://mainnet.infura.io/<your_infura_id>"

let web3 = Web3(rpcURL: infuraUrl)


> Then you can start reading from the smart contract like that
```Swift
// *** Read ERC20 name
firstly {
    web3.eth.call(call: erc20.reading.name(), block: .latest)
}.done { name in
    // print the ERC20 name
    print(String(bytes: Array(name.bytes.dropFirst(64))))
}.catch { error in
    // Something went wrong
    print(error)
}

// *** Read balance of an address
firstly {
    try web3.eth.call(call: erc20.reading.balanceOf(owner: .init(hex: "0x00c09c3f03517ceb0a73eda1ff16e6c99f298175", eip55: false)), block: .latest)
}.done { balance in
    // Print the balance of the above address
    print(balance.hex())
}.catch { error in
    // Something went wrong
    print(error)
}

// All other reading functions work very similar...

If you want to call state changing functions like transfer or approve, you have to create the transaction and sign it. Below are some examples...

let privateKey = try EthereumPrivateKey(hexPrivateKey: "0xc144c24c9d75a46aad6517e4a864c13a5f302f1534b4bba39687e6d6af29d4fb")

firstly {
    // Get the nonce
    web3.eth.getTransactionCount(address: privateKey.address, block: .latest)
}.then { nonce in
    // Create and sign transaction
    return Promise { seal in
        var transferTx = try erc20.writing.transfer(
            nonce: nonce,
            // Replace with whatever you want
            gasPrice: EthereumQuantity(quantity: 21.gwei),
            gasLimit: 150000,
            // Replace with the address you are sending the tokens to
            to: EthereumAddress(hex: "0xF92AB39A37CF96316132B50c8a85ED467A0deBE6", eip55: false),
            // Replace with the number of tokens (in atomic units)
            value: 100000,
            // ChainId. 1 for mainnet. 3 for ropsten. 4 for rinkeby. 42 for kovan.
            chainId: 1
        )
        // Sign with your private key
        try transferTx.sign(with: privateKey)
        seal.fulfill(transferTx)
    }
}.then { tx in
    // Send transaction to the Ethereum network
    web3.eth.sendRawTransaction(transaction: tx)
}.done { hash in
    // Print transactionHash. You can look this hash up and track the tx on https://etherscan.io once it was posted.
    print(hash)
}.catch { error in
    // Something went wrong
    print(error)
}

The other writing functions should work similar. Just look through the struct ERC20.Writing to see all possible options.

I hope I have covered everything for ERC20 tokens now.

If you have any questions or something doesn't work as expected just drop a comment.


You are right. General Contract ABI is actually our main priority right now as we also need it in our projects. Currently we are just tricking around like in the example above. This will definitely be done soon.

jlstr commented 6 years ago

Hello, This is absolutely gorgeous. I haven't tested it yet, but it does look Magnificent! I will test it once I get a chance and let you know how it goes.

Always a pleasure to be able to be in contact with such talented individuals, can't thank you enough for this, Kindly, -Jose

keithpiong commented 6 years ago

Hi, Is it possible to sign transaction with keystore (generated by Geth) instead of private keys ?

koraykoska commented 6 years ago

@keithpiong You would have to extract the private key from the keystore. So you will need the passphrase and the keystore.
With both of them you can follow the instructions on how to extract the private key (with AES-128-CTR and scrypt) and then use this private key to create an instance of EthereumPrivateKey to sign transactions like in the examples above.

If you want this private key extraction as a feature of Web3 please open a new issue. I think this is certainly something we will need at some point in time.

keithpiong commented 6 years ago

Thanks for the guide, I shall attempt to do so. Will also open a new issue on extracting private key from keystore.

blockcowboy commented 6 years ago

I have contract deployed to Ropsten. The contract has functions with arguments. Not clear how to use Web3 to select the function in the contract. Any examples or tutorial available?

koraykoska commented 6 years ago

@blockcowboy Did you check out the examples for contract function calling in the README?

blockcowboy commented 6 years ago

Yes. I had to change the eip55 to false to get around checksum error.

I see the try contract but can not identify the function selector.

Sorry

IJN

On Jul 6, 2018, at 10:34 AM, Koray Koska notifications@github.com wrote:

@blockcowboy Did you check out the examples for contract function calling in the README?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.

blockcowboy commented 6 years ago

I guess I need guide how to "create a static template"