Giter Site home page Giter Site logo

boilertalk / web3.swift Goto Github PK

View Code? Open in Web Editor NEW
616.0 21.0 172.0 2.12 MB

A pure swift Ethereum Web3 library

License: MIT License

Swift 100.00%
ethereum swift ethereum-dapp cocoapods carthage swift-package-manager ios macos tvos watchos swift-linux swift-library pure-swift decentralized-applications decentralized web3 smart-contracts contract-abi spm-product json-abi

web3.swift's Introduction

CI Status Code Coverage Telegram

⛓️ Web3

Web3.swift is a Swift library for signing transactions and interacting with Smart Contracts in the Ethereum Network.

It allows you to connect to a geth or erigon Ethereum node (like Chainnodes) to send transactions and read values from Smart Contracts without the need of writing your own implementations of the protocols.

Web3.swift supports iOS, macOS, tvOS, watchOS and Linux with Swift Package Manager.

Example

Check the usage below or look through the repositories tests.

Why?

There are already some Web3 library out there written in Swift. We know their strengths and weaknesses and for our use case they just didn't work.

Web3.swift was built with modularity, portability, speed and efficiency in mind.

Ok, thank you for the buzzwords. But what does this actually mean?

💾 Modularity

Web3.swift was built to be modular. If you install/use the basic Web3 SPM product, you get access to the most basic functions like transaction signing and interacting with an http rpc server.
If you want to add support for IPC rpc or something else, you can simple create a library which depends on Web3 and implements this exact functionality. More about that later.
If you want to use PromiseKit extensions for the web3 calls, you can either use the provided PromiseKit SPM product or create your own.
If you want to conveniently parse JSON ABIs for Ethereum Smart Contracts, you can use the provided ABI Parsing SPM product.

Finally, if you want to add functionality to Web3.swift which is not provided yet, you don't have to wait until it gets merged and released in a version bump. You can simple extend/update functionality within you own app as our APIs are made to be very open for changes.
For example, if you want to add a web3 method which is not provided yet by Web3.swift (we will only support Infura supported methods), you only have to add some 3 lines of code (depending on the input and output parameters of the method). Adding IPC rpc support would be only implementing a protocol and answering requests.

Like you can see, everything is possible with Web3.swift.

💻 Portability

One of the main reasons we started working on this project is because we wanted to use it with Swift Package Manager on different platforms.
Because of that, Web3.swift is available through Swift Package Manager on iOS, macOS, tvOS, watchOS and Linux.

Note: For SPM we are only testing iOS, macOS and officially supported Linux distributions (currently Ubuntu 16.04 and 20.04) but it should be compatible with all little endian systems which are able to compile the Swift Compiler, Foundation and Glibc.

⚡ Speed and Efficiency

We try to make this library as fast as possible while trying to provide an API which increases your development workflow such that you can focus on building great DAPPS instead of worrying about implementation details.

All our APIs are thread safe and designed to be used in highly concurrent applications.

Installation

Swift Package Manager

Web3 is compatible with Swift Package Manager v5 (Swift 5 and above). Simply add it to the dependencies in your Package.swift.

dependencies: [
    .package(url: "https://github.com/Boilertalk/Web3.swift.git", from: "0.6.0")
]

And then add it to your target dependencies:

targets: [
    .target(
        name: "MyProject",
        dependencies: [
            .product(name: "Web3", package: "Web3.swift"),
            .product(name: "Web3PromiseKit", package: "Web3.swift"),
            .product(name: "Web3ContractABI", package: "Web3.swift"),
        ]
    ),
    .testTarget(
        name: "MyProjectTests",
        dependencies: ["MyProject"])
]

Note: Web3PromiseKit and Web3ContractABI are optional and you only have to put them into your target dependencies (and later import them) if you want to use them.

After the installation you can import Web3 in your .swift files.

import Web3

// Optional
import Web3PromiseKit
import Web3ContractABI

CocoaPods and Carthage

Because of an internal decision, we stopped supporting any Package Managers other than SPM starting with version 0.5.0.

To elaborate a little on this decision: With XCode 11 and Swift 5.1 we reached a point with Swift Package Manager where it slowly started making other package managers irrelevant. You could already load all your dependencies in the XCode project with Swift Package Manager.
With more updates it became even more prevalent. Cocoapods and Carthage maintainers lost interest in their project and stopped maintaining it. There are many unresolved issues, many problems especially for library developers with Cocoapods.

So much hassle for no real gain. Users can already put dependencies which support SPM into their XCode project. So why bother?

The answer is simple. Some still use XCode < 11 and some library developers depend on Web3 in their own Pods/Carthages.

The decision was hard and took some time. But after seeing that the last version was very stable and used in many production apps already, we decided to start with this move now.
XCode 10 is already more than 2 years old. Most projects already upgraded, the ones that didn't have a much bigger problem than Web3.swift not making Updates for Cocoapods...
Library owners depending on Web3.swift are encouraged to drop Cocoapods and Carthage Support as well.

SPM is the Future. For all Cocoapods and Carthage Users who use it because many libraries did not switch to SPM yet: You can still add Web3.swift as a SPM product into your .xcworkspace or .xcodeproj and keep all your other dependencies inside Cocoapods/Carthage. But still. We encourage you to switch with as many dependencies as possible to SPM. Better sooner than later. See the next section on how to do this.

XCode

Using XCode 11 or later (for iOS, macOS or other Apple platforms) you can add SPM packages very easy.

In Xcode, select your project, from the Dropdown select the project, not a single Target, in the Tabs select Swift Packages.
Then you can click the + icon and put in the URL to this repository (https://github.com/Boilertalk/Web3.swift).
Now you can select all products and just click Next until the dependency was added.

That's it. If you push your changes even your CI will not make any problems. No hassles with outdated spec repositories, no problems with some weird linker errors which only occur sometimes/with some dependencies.

If you need further guidance, join our Telegram group and we will help you. https://t.me/web3_swift

Usage

Interaction with an Ethereum node

With Web3.swift you can use an Ethereum node on a server to communicate with Ethereum.
You can send signed transactions, read contract data, call contract functions and much more.

The base class for all available methods is Web3. You can, for example, instantiate it with an http provider:

let web3 = Web3(rpcURL: "https://mainnet.infura.io/<your_infura_id>")

All web3_ methods are available directly from the Web3 struct. The net_ methods are available under the net struct in the web3 struct. The eth_ methods are available under the eth struct in the web3 struct.

Please see the examples below

Note: For the examples to work you need to import Web3 and PromiseKit first

Request web3_clientVersion

Returns the current client version.

Parameters

none

Returns

String - The current client version

firstly {
    web3.clientVersion()
}.done { version in
    print(version)
}.catch { error in
    print("Error")
}

Request net_version

Returns the current network id.

Parameters

none

Returns

String - The current network id

firstly {
    web3.net.version()
}.done { version in
    print(version)
}.catch { error in
    print("Error")
}

Request net_PeerCount

Returns number of peers currently connected to the client.

Parameters

none

Returns

EthereumQuantity - BigInt of the number of connected peers.

firstly {
    web3.net.peerCount()
}.done { ethereumQuantity in
    print(ethereumQuantity.quantity)
}.catch { error in
    print("Error")
}

Send raw transaction

Creates new message call transaction or a contract creation for signed transactions.

Parameters

  1. EthereumTransaction: The signed transaction

Returns

EthereumData, 32 Bytes - The transaction hash, or the zero hash if the transaction is not yet available

To send some ETH you first need to get the current transaction count of the sender (nonce), create the transaction, sign it and then send it.

let privateKey = try! EthereumPrivateKey(hexPrivateKey: "0xa26da69ed1df3ba4bb2a231d506b711eace012f1bd2571dfbfff9650b03375af")
firstly {
    web3.eth.getTransactionCount(address: privateKey.address, block: .latest)
}.then { nonce in
    let tx = try EthereumTransaction(
        nonce: nonce,
        gasPrice: EthereumQuantity(quantity: 21.gwei),
        gas: 21000,
        to: EthereumAddress(hex: "0xC0866A1a0ed41e1aa75c932cA3c55fad847fd90D", eip55: true),
        value: EthereumQuantity(quantity: 1.eth)
    )
    return try tx.sign(with: privateKey, chainId: 1).promise
}.then { tx in
    web3.eth.sendRawTransaction(transaction: tx)
}.done { hash in
    print(hash)
}.catch { error in
    print(error)
}

Request block transaction count by block number

firstly {
    web3.eth.getBlockTransactionCountByNumber(block: .block(5397389))
}.done { count in
    print(count) // 88
}.catch { error in
    print(error)
}

More examples

For more examples either read through our test cases, the Web3 struct or the official Ethereum JSON RPC documentation.

Contract ABI interaction

We are providing an optional module for interaction with smart contracts. To use it you have to add Web3ContractABI to your target dependencies in your Podfile (for SPM). Make sure you check out the installation instructions first.

We are providing two different options to create contract abi interfaces in Swift. Either you define your functions and events manually (or use one of our provided interfaces like ERC20 or ERC721). Or you parse them from the JSON ABI representation just like in web3.js.

Static Contracts

Static contracts are classes implementing StaticContract. They provide a set of functions and events they want to use from the original smart contract. Check out our provided static contracts as a starting point (ERC20 or ERC721).

Our static ERC20 interface is called GenericERC20Contract, the ERC721 contract is called GenericERC721Contract. Both can be subclassed to add more functions for custom contracts.

With those StaticContract types you can create and use your contract like in the following example (We are using PromiseKit again in our examples).

let web3 = Web3(rpcURL: "https://mainnet.infura.io/<your_infura_id>")

let contractAddress = try EthereumAddress(hex: "0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0", eip55: true)
let contract = web3.eth.Contract(type: GenericERC20Contract.self, address: contractAddress)

// Get balance of some address
firstly {
    try contract.balanceOf(address: EthereumAddress(hex: "0x3edB3b95DDe29580FFC04b46A68a31dD46106a4a", eip55: true)).call()
}.done { outputs in
    print(outputs["_balance"] as? BigUInt)
}.catch { error in
    print(error)
}

// Send some tokens to another address (locally signing the transaction)
let myPrivateKey = try EthereumPrivateKey(hexPrivateKey: "...")
firstly {
    web3.eth.getTransactionCount(address: myPrivateKey.address, block: .latest)
}.then { nonce in
    try contract.transfer(to: EthereumAddress(hex: "0x3edB3b95DDe29580FFC04b46A68a31dD46106a4a", eip55: true), value: 100000).createTransaction(        
        nonce: nonce,
        gasPrice: EthereumQuantity(quantity: 21.gwei),
        maxFeePerGas: nil,
        maxPriorityFeePerGas: nil,
        gasLimit: 100000,
        from: myPrivateKey.address,
        value: 0,
        accessList: [:],
        transactionType: .legacy
    )!.sign(with: myPrivateKey).promise
}.then { tx in
    web3.eth.sendRawTransaction(transaction: tx)
}.done { txHash in
    print(txHash)
}.catch { error in
    print(error)
}

// Send some tokens to another address (signing will be done by the node)
let myAddress = try EthereumAddress(hex: "0x1f04ef7263804fafb839f0d04e2b5a6a1a57dc60", eip55: true)
firstly {
    web3.eth.getTransactionCount(address: myAddress, block: .latest)
}.then { nonce in
    try contract.transfer(to: EthereumAddress(hex: "0x3edB3b95DDe29580FFC04b46A68a31dD46106a4a", eip55: true), value: 100000).send(        
        nonce: nonce,
        gasPrice: EthereumQuantity(quantity: 21.gwei),
        maxFeePerGas: nil,
        maxPriorityFeePerGas: nil,
        gasLimit: 150000,
        from: myAddress,
        value: 0,
        accessList: [:],
        transactionType: .legacy
    )
}.done { txHash in
    print(txHash)
}.catch { error in
    print(error)
}

By creating your own interfaces, you can interact with any smart contract!

Dynamic Contracts

If you only have access to the JSON ABI of a smart contract or you don't want to create a static template, you can use our dynamic contract api to parse the json string into a usable contract during runtime. See the example below.

let web3 = Web3(rpcURL: "https://mainnet.infura.io/<your_infura_id>")

let contractAddress = try EthereumAddress(hex: "0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0", eip55: true)
let contractJsonABI = "<your contract ABI as a JSON string>".data(using: .utf8)!
// You can optionally pass an abiKey param if the actual abi is nested and not the top level element of the json
let contract = try web3.eth.Contract(json: contractJsonABI, abiKey: nil, address: contractAddress)

print(contract.methods.count)

// Get balance of some address
firstly {
    try contract["balanceOf"]!(EthereumAddress(hex: "0x3edB3b95DDe29580FFC04b46A68a31dD46106a4a", eip55: true)).call()
}.done { outputs in
    print(outputs["_balance"] as? BigUInt)
}.catch { error in
    print(error)
}

// Send some tokens to another address (locally signing the transaction)
let myPrivateKey = try EthereumPrivateKey(hexPrivateKey: "...")
guard let transaction = contract["transfer"]?(EthereumAddress.testAddress, BigUInt(100000)).createTransaction(
    nonce: 0,
    gasPrice: EthereumQuantity(quantity: 21.gwei),
    maxFeePerGas: nil,
    maxPriorityFeePerGas: nil,
    gasLimit: 150000,
    from: myPrivateKey.address,
    value: 0,
    accessList: [:],
    transactionType: .legacy
)) else {
    return
}
let signedTx = try transaction.sign(with: myPrivateKey)

firstly {
    web3.eth.sendRawTransaction(transaction: signedTx)
}.done { txHash in
    print(txHash)
}.catch { error in
    print(error)
}

Using this API you can interact with any smart contract in the Ethereum Network!

For more examples, including contract creation (constructor calling) check out our tests.

Common errors

Couldn't parse ABI Object

If you are getting this error when parsing your ABI from a json, it may be because your contract has a fallback function. To solve it, remove the fragment of your ABI that has the information about the fallback function. The part you should remove might look like this:

{
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "fallback"
},

Disclaimer

Until we reach version 1.0.0 our API is subject to breaking changes between minor version jumps. This is to make sure we can focus on providing the best implementation while we are in heavy development instead of trying to maintain something which is deprecated.

That being said, we will try to minimize breaking changes. Most certainly there won't be many.

Author

The awesome guys at Boilertalk ⚗️
...and even more awesome members from the community 💜

Check out the contributors list for a complete list.

License

Web3 is available under the MIT license. See the LICENSE file for more info.

web3.swift's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

web3.swift's Issues

connecting to private network

I have created a private network that can communicate between 2 computers on the same network. Executing some contracts. How do I connect this module with my own private network? I want to execute the contract at my node from iPhone. is there any guide for how to do this?

Can't call my smart contract functions

I have a simple smart contract deployed in rinkeby :

pragma solidity ^0.4.18;

contract CarChain {
    
    struct Car {
        uint id;
        string plate;
        uint userId;
        bool registered;
    }
    
    struct User{
        uint id;
        uint credit;
        uint carId;
        bool registered;
    }

    mapping(uint => User) public users;
    mapping(uint => Car) public cars;
    
    uint public minimumRentCredit;
    
    address manager;
    
    function CarChain(uint _minimumRentCredit) public {
        manager = msg.sender;
        minimumRentCredit = _minimumRentCredit;
    }
    
    function registerNewUser(uint _id, uint _credit) public{
        //Check there is no User registered with this id
        require(_id != 0);
        require(users[_id].registered == false);
        
        User storage newUser = users[_id];
        newUser.id = _id;
        newUser.credit = _credit;
        newUser.carId = 0;
        newUser.registered = true;
    }
    
    function registerNewCar(uint _id, string _plate) public restricted{
        //Check there is no Car registered with this id
        require(_id != 0);
        require(cars[_id].registered == false);
        
        Car storage newCar = cars[_id];
        newCar.id = _id;
        newCar.plate = _plate;
        newCar.registered = true;
    }
    
    function rentCar(uint _carId, uint _userId) public {
        require(cars[_carId].registered == true);
        require(users[_userId].registered == true);
        require(cars[_carId].userId == 0);
        require(users[_userId].carId == 0);
        require(users[_userId].credit >= minimumRentCredit);
        
        User storage user = users[_userId];
        user.carId = _carId;
        user.credit = user.credit - minimumRentCredit;
        Car storage car = cars[_carId];
        car.userId = _userId;
    }
    
    function returnCar(uint _carId, uint _userId) public {
        require(cars[_carId].registered == true);
        require(users[_userId].registered == true);
        require(cars[_carId].userId != 0);
        require(users[_userId].carId != 0);

        User storage user = users[_userId];
        user.carId = 0;
        Car storage car = cars[_carId];
        car.userId = 0;
    }
    
    function getUserCredit(uint _id) public view returns (uint){
        return users[_id].credit;
    }
    
    modifier restricted(){
        require(msg.sender == manager);
        _;
    }
}

But I think I am not calling the functions correctly.


 let contractJsonABI = "[{\"constant\":false,\"inputs\":[{\"name\":\"_id\",\"type\":\"uint256\"},{\"name\":\"_credit\",\"type\":\"uint256\"}],\"name\":\"registerNewUser\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"users\",\"outputs\":[{\"name\":\"id\",\"type\":\"uint256\"},{\"name\":\"credit\",\"type\":\"uint256\"},{\"name\":\"carId\",\"type\":\"uint256\"},{\"name\":\"registered\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_id\",\"type\":\"uint256\"},{\"name\":\"_plate\",\"type\":\"string\"}],\"name\":\"registerNewCar\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_id\",\"type\":\"uint256\"}],\"name\":\"getUserCredit\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_carId\",\"type\":\"uint256\"},{\"name\":\"_userId\",\"type\":\"uint256\"}],\"name\":\"rentCar\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_carId\",\"type\":\"uint256\"},{\"name\":\"_userId\",\"type\":\"uint256\"}],\"name\":\"returnCar\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"cars\",\"outputs\":[{\"name\":\"id\",\"type\":\"uint256\"},{\"name\":\"plate\",\"type\":\"string\"},{\"name\":\"userId\",\"type\":\"uint256\"},{\"name\":\"registered\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"minimumRentCredit\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}]".data(using: .utf8)!

let web3 = Web3(rpcURL: "https://rinkeby.infura.io/v3/9...")
let contractAddress = try! EthereumAddress(hex: "0x526F5F06D6f1f9e63c6f344Ab5b160DE7b1eaCEa", eip55: true)
let contract = try! web3.eth.Contract(json: contractJsonABI, abiKey: nil, address: contractAddress)
let myPrivateKey = try! EthereumPrivateKey(hexPrivateKey: "***************")

        do {
            
            let c = contract["registerNewUser"]?(contractAddress, BigUInt(1), BigUInt(100))
            let transaction: EthereumTransaction = c!
                .createTransaction(nonce: 14,
                                   from: myPrivateKey.address,
                                   value: 0,
                                   gas: 210000,
                                   gasPrice: EthereumQuantity(quantity: 1.gwei))!
            
            let signedTx: EthereumSignedTransaction = try transaction.sign(with: myPrivateKey, chainId: 4)
            
            firstly {
                web3.eth.sendRawTransaction(transaction: signedTx)
                }.done { txHash in
                    print(txHash)
                }.catch { error in
                    print(error)
            }
            
        } catch {
            print(error)
        }

It enters in .done section with a txHash but when I test at remix the deployed contract there is no proof that something got setted.

Any help to call the functions?
Am I doing it right?

Thank you in advance.

String.hexBytes() suggestion

I've encountered a few instances where string.hexBytes() threw an error because the value was not divisible by 2. It looks like the root of the issue is that hex can actually be represented with or without a leading zero, and String.init(_:, radix:) returns the most compact format.

A quick example using the Swift console:

 > String(4095, radix: 16)
$R19: String = "fff"
 > 0xfff
$R20: Int = 4095
> 0x0fff
$R21: Int = 4095

Since you're iterating over the string in 2 character increments it makes sense that the string should be divisible by 2. My suggestion would be to append a zero if it's not already divisible by two instead of throwing an error so that it is more flexible.

EthereumTransaction optional values

What do you think about making some of EthereumTransaction's values optional? Our SDK signs remotely so we'd like to be able to create an EthereumTransaction object that only includes the necessary keys for the standard eth_sendTransaction call.

Our ideal implementation would have the following as optional:

  • nonce (we populate this automatically)
  • gasPrice (we also load this automatically)
  • to (in case of uploading a contract)
  • v
  • r
  • s
  • chainId

One way to keep strict requirements here could be splitting it into EthereumTransaction and EthereumSignedTransaction with some base protocol.

Contract ABI interaction

Add a convenient optional API (subspec/target) to parse contract ABIs (either in json format or as a Swift Type) and generate correct transaction input data for function calls.

DynamicContractTests never run all tests

The DynamicContractTests always stop creating tests because of this line.

contract.deploy somehow always fails and because we just return there. We don't even notice when running the test suite.

I think the deploy fails because there is no constructor function in the ERC721.json stub.

@pixelmatrix How was this meant to be tested?

List of all transactions related to account (private key)

Is it possible to get the list of all past (historical) transactions quickly?
As I can see now - I can only get the count of transactions for account (using nonce number), but how can I get all these transactions in order to show to the user?

Loading Dynamic Contract: "Expected to decode Array<Any> but found a dictionary instead."

Hey Ybrin, I am currently working with your example for dynamic contracts (Boilertalk - Dynamic Contracts) but I get an error when I am trying to load the contract.

 // Reading json ABI
    if let path = Bundle.main.path(forResource: "BlockRunner", ofType: "json") {
        do {
            let web3 = Web3(rpcURL: RPC)
            let contractAddress = try! EthereumAddress(hex: "--------", eip55: false)
            let data = try String(contentsOf: URL(fileURLWithPath: path))
            let contractJsonABI = data.data(using: .utf8)!
            // Line where the error occurs
            // type(of: contractJsonABI) = Foundation.Data
            let contract = try! web3.eth.Contract(json: contractJsonABI, abiKey: nil, address: contractAddress)

Error:
Swift.DecodingError.typeMismatch(Swift.Array, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array but found a dictionary instead.", underlyingError: nil))

Problems with installation

Hello, I'm tying to install this library in my project. When I add pod 'Web3' to my Podfile I get this error:

[!] Unable to find a specification for Web3

Could you provide a more detail instructions to install?

Error: Ambiguous reference to member 'firstly(execute:)'

Hi, I am trying to test the "Send raw transaction" example Link Unfortunately I always get an error: "Ambiguous reference to member 'firstly(execute:)'".

    class func transaction(privateKey: String, to: String) {
        let web3 = Web3(rpcURL: "http://XXXX:XXXX")
        let privateKeyTwo = try! EthereumPrivateKey(hexPrivateKey: privateKey)
        firstly {
            web3.eth.getTransactionCount(address: getPrivateKey(privateKey: privateKey), block: .latest)
            } .then { nonce in
                let tx = createTx(nonce: nonce, to: to)
                return try tx.sign(with: privateKeyTwo, chainId: 1).promise
            } .then { tx in
                web3.eth.sendRawTransaction(transaction: tx)
            } .done { hash in
                print(hash)
            } .catch {error in
                print("error: \(error)")
            }
    }

Code Quality

Make sure our code gets a good grade on codebeat.

Using decimal precision with EthereumQuantity

'EthereumQuantity' only accepts BigUInt, however this becomes a problem if I want to do this:
EthereumQuantity(quantity: BigUInt(1.5).eth)

Is there a way to create an 'EthereumQuantity' using a Double?

How to Call Methods from a Custom ERC20 Contract?

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

Contract ABI fallback functions

The ABIObject always expect inputs, which is not correct if the contract contains a fallback function, a possible fix would be changing the parameters to be optional

Getting contract Events

Hi! I checked source code and documentation and it seems there is no way to list events of the contract or subscribe to event listener.

Am I right and it is to be implemented or I just didn't know where to look?

Make from optional for createTransaction

The from value for the createTransaction function in SolidityInvocation should be optional just like in the EthereumTransaction initializer as it is not really required if you want to send a raw transaction.

Thoughts on web3 PromiEvent

This is less of an issue, but more of a question. I'm wondering how you're thinking about handling the equivalent of web3.js' PromiEvent (promise + event emitter). More specifically, I'm curious how this Web3 library will handle callbacks related to transaction events. In Javascript, you can get called when your transaction receipt is ready, when it's confirmed, and if it fails, which is essential for the dapp experience.

Personally, I think the PromiEvent thing they're doing is a bit of a strange pattern, and there are cleaner patterns in Foundation that we can borrow from. Two options that come to mind are the delegate pattern and NotificationCenter. Have you put any thought into this area yet?

In my own prototype I've built a small TransactionWatcher class that takes a transaction hash, polls the network and fires notifications based on the status of the transaction. I'd be happy to share that code when it's ready, or adopt a different way if you already have plans around this.

Change provided static contracts to return the value instead of `[String: Any]`

I am talking about ERC20Contract in this issue but it applies to all our static contracts.

Problem

Currently all functions we provide in ERC20Contract return a SolidityInvocation. This makes our protocols very flexible but also has the disadvantage that we have to make something like that to get the return values we want from the functions:

let contract = ... // Some ERC20 contract

firstly {
    contract.balanceOf(address: address).call()
}.done { values in
    guard let balance = values["_balance"] as? BigUInt else {
        // Throw or something...
    }

    // Use balance
    print(balance)
}.catch { error in
    print(error)
}

It is very similar with other functions. My point is that we always have to extract our return value from the dictionary [String: Any] although we already know what value will be inside it.

Solution

I suggest we add another layer of abstraction to our static contracts and make our API easier to use by not returning the dictionary but calling a completion with the actual value once we have it.

This means we would automatically call the contract functions instead of giving the user the choice to call them whenever they want. If we want to still provide this option we could add functions like balanceOfInvocation to get the SolidityInvocation and manage it manually.

Implementation

Our function signatures in the static contract protocols would look a little bit like that:

func balanceOf(address: EthereumAddress, completion: (_ balance: BigUInt?, _ error: Error?))

And our PromiseKit extension would look like that:

func balanceOf(address: EthereumAddress) -> Promise<BigUInt> {
    return Promise { seal in
        self.balanceOf(address: address, completion: seal.resolve)
    }
}

So we could use them like in the following example:

let contract = ... // Some ERC20 contract

firstly {
    contract.balanceOf()
}.done { balance in
    // Use balance
    print(balance)
}.catch { error in
    print(error)
}

Bug: Insufficient funds error when there are funds available

Hi there

I'm using a dynamic contract and can make calls to it successfully. However when using 'contract.createTransaction' methods on the same contract, I keep receiving the error:

Web3.RPCResponse<Web3.EthereumData>.Error(code: -32010, message: "Insufficient funds. The account you tried to send transaction from does not have enough funds. Required 2000000000000000 and got: 0."

I'm signing the 'contract.createTransaction' with my privateKey and the correct ETH address (with sufficient balance) is being shown for privateKey.address.

I'm not sure what else to try as the transaction seems to be correctly created (as per instructions for creating dynamic contract transactions), and the ETH address definitely has a sufficient balance.

How to call methods from class DynamicContract in the example

We can get a list of events and methods when we using class DynamicContract in your example, but is there an efficient way to call these methods and interact with contracts?

We also tried Dynamic Contract way you mentioned in the Readme file. But some issues happened when parsing ContractJsonABI json file. Could you please let us know which way is better to interact with our smart contract? Thanks!

Crash in EthereumAddress. Need help!

I am using in my project web3 either as pod or with carthage and when i create a EthereumAddress object the app crashes with EXC_BAD_ACCESS (code=1, address=0x5) on line: 'let hash = SHA3(variant: .keccak256).calculate(for: Array(hex.lowercased().utf8))'
Has anybody issues with this or can help me resolve the crash? Thanks.

Increase code coverage

The aim is not to increase the code coverage value but rather to make sure our code meets the specifications in as many cases as possible.

How to get Error Code or Message?

Hi,
I have been searching for a way to extract the code and message out from the error.

For example:-
Error: Error(code: -32000, message: "insufficient funds for gas * price + value")

How can I get the Code and the Message?

Cannot extend eth send methods

I'm trying to extend the Eth module in my own app to include a couple of additional methods, but ran into an issue. Eth.properties is internal, and that's the only place where the provider and the rpcId are stored. One additional issue is that in the PromiseKit subspec, the Web3Response sealPromise method is fileprivate, so I can't easily add promise-based methods.

As a temporary workaround, I created an extension for Web3 itself which works fine, but it would be cleaner to just extend Eth.

EthereumTransactionReceiptObject cannot always be decoded

I'm doing a transaction and then calling web3.eth.getTransactionReceipt(hash) and I found a couple of issues.

  1. It seems that most of the time the EthereumTransactionReceiptObject can't be decoded because it's assuming EthereumLogObject always has the removed property. I'm not sure why, but i'm not seeing that value coming back in the logs. Changing that property to an optional value causes it to work for me.

  2. The Promise extension for this method uses seal.resolve(rpc.result, rpc.error). When the rpc response includes a null result, it ends up calling 1seal.resolve(nil, nil)`, which PromiseKit views as an error since it can't infer if it was fulfilled or rejected. As a workaround for now, i'm just allowing that to return the PMKError, and catching on that, but it's not actually an error. Not a huge issue, but just thought i'd highlight that strange behavior.

Remove Vapor Bytes dependency

The Byte array extensions from Vapor Bytes we use are not really hard to implement on our own. The problem with Vapor Bytes is that they don't regularly release updates on Cocoapods which makes it a little bit hard to use.

Error:Could not parse abi object:

let contractJsonABI = "[{\"constant\":false,\"inputs\":[{\"name\":\"spender\",\"type\":\"address\"},{\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"name\":\"\",\"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\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"who\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"to\",\"type\":\"address\"},{\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"name\":\"\",\"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\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"owner\",\"type\":\"address\"},{\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"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\"},{\"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\"}]".data(using: .utf8)! let contract = try! web3.eth.Contract(json: contractJsonABI, abiKey: nil, address: contractAddress) try! contract["balanceOf"]!(fromAddress).call(completion: { (response, error) in })

When I requested the balance through the contract, I showed the following error.
Is this a problem with the abi format? Can you provide a passable abi template, thank you

Could not parse abi object: ABIObject(constant: Optional(false), inputs: [Web3.ABIObject.Parameter(name: "spender", type: "address", components: nil, indexed: nil), Web3.ABIObject.Parameter(name: "value", type: "uint256", components: nil, indexed: nil)], outputs: Optional([Web3.ABIObject.Parameter(name: "", type: "bool", components: nil, indexed: nil)]), name: Optional("approve"), type: Web3.ABIObject.ObjectType.function, payable: Optional(false), stateMutability: nil, anonymous: nil)
Could not parse abi object: ABIObject(constant: Optional(true), inputs: [], outputs: Optional([Web3.ABIObject.Parameter(name: "", type: "uint256", components: nil, indexed: nil)]), name: Optional("totalSupply"), type: Web3.ABIObject.ObjectType.function, payable: Optional(false), stateMutability: nil, anonymous: nil)
Could not parse abi object: ABIObject(constant: Optional(false), inputs: [Web3.ABIObject.Parameter(name: "from", type: "address", components: nil, indexed: nil), Web3.ABIObject.Parameter(name: "to", type: "address", components: nil, indexed: nil), Web3.ABIObject.Parameter(name: "value", type: "uint256", components: nil, indexed: nil)], outputs: Optional([Web3.ABIObject.Parameter(name: "", type: "bool", components: nil, indexed: nil)]), name: Optional("transferFrom"), type: Web3.ABIObject.ObjectType.function, payable: Optional(false), stateMutability: nil, anonymous: nil)
Could not parse abi object: ABIObject(constant: Optional(true), inputs: [Web3.ABIObject.Parameter(name: "who", type: "address", components: nil, indexed: nil)], outputs: Optional([Web3.ABIObject.Parameter(name: "", type: "uint256", components: nil, indexed: nil)]), name: Optional("balanceOf"), type: Web3.ABIObject.ObjectType.function, payable: Optional(false), stateMutability: nil, anonymous: nil)
Could not parse abi object: ABIObject(constant: Optional(false), inputs: [Web3.ABIObject.Parameter(name: "to", type: "address", components: nil, indexed: nil), Web3.ABIObject.Parameter(name: "value", type: "uint256", components: nil, indexed: nil)], outputs: Optional([Web3.ABIObject.Parameter(name: "", type: "bool", components: nil, indexed: nil)]), name: Optional("transfer"), type: Web3.ABIObject.ObjectType.function, payable: Optional(false), stateMutability: nil, anonymous: nil)
Could not parse abi object: ABIObject(constant: Optional(false), inputs: [Web3.ABIObject.Parameter(name: "spender", type: "address", components: nil, indexed: nil), Web3.ABIObject.Parameter(name: "value", type: "uint256", components: nil, indexed: nil), Web3.ABIObject.Parameter(name: "extraData", type: "bytes", components: nil, indexed: nil)], outputs: Optional([Web3.ABIObject.Parameter(name: "", type: "bool", components: nil, indexed: nil)]), name: Optional("approveAndCall"), type: Web3.ABIObject.ObjectType.function, payable: Optional(false), stateMutability: nil, anonymous: nil)
Could not parse abi object: ABIObject(constant: Optional(true), inputs: [Web3.ABIObject.Parameter(name: "owner", type: "address", components: nil, indexed: nil), Web3.ABIObject.Parameter(name: "spender", type: "address", components: nil, indexed: nil)], outputs: Optional([Web3.ABIObject.Parameter(name: "", type: "uint256", components: nil, indexed: nil)]), name: Optional("allowance"), type: Web3.ABIObject.ObjectType.function, payable: Optional(false), stateMutability: nil, anonymous: nil)

How to send signed raw transaction with some data?

Hello,
I have a simple smart contract about read and write some value.

I can read my “owner” value successfully, but can not write value to the “owner”.
When I send transaction to contract, it will return “Error(code: -32000, message: “insufficient funds for gas * price + value”)”, but I’m sure my account have enough gas. (2996400390000000001)

I created a EthereumTransaction, signed it with my privateKey, and use “sendRawTransaction” to send it.
Please help me realize my problem, thank you very much!

My Contract:

pragma solidity ^0.4.4;

contract Car {
  bytes public vehicleName = "Tesla";
  uint public owner;

  function setOwner(uint newOwner) public
  {
    owner = newOwner;
  }
}

My Code:

////////////////////////////
/// owner
do {
    let contract = try web3.eth.Contract(json: data!, abiKey: "abi", address: contractAddress)
            firstly {
                contract["owner"]!().call()
                }.done { outputs in
                    print(outputs)
                }.catch { error in
                    print(error)
            }
} catch {
    print(error)
}

////////////////////////////
/// setOwner
do {
    let myPrivateKey = try EthereumPrivateKey(hexPrivateKey: "**********")
    
    web3.eth.getBalance(address: myPrivateKey.address, block: try .string("latest") ) { response in
        print("myAccount - result?.quantity(wei): ", response.result?.quantity)
    }
    
    let contract: DynamicContract = try web3.eth.Contract(json: data!, abiKey: "abi", address: contractAddress)
    
    let c = contract["setOwner"]?(contractAddress!,BigUInt(789))
    let transaction: EthereumTransaction = c!
        .createTransaction(nonce: 0,
                            from: myPrivateKey.address,
                           value: 0,
                             gas: 210000,
            gasPrice: EthereumQuantity(quantity: 1.gwei))!
    
    let signedTx: EthereumSignedTransaction = try transaction.sign(with: myPrivateKey)

    firstly {
        web3.eth.sendRawTransaction(transaction: signedTx)
    }.done { txHash in
        print(txHash)
    }.catch { error in
        print(error)
    }

} catch {
    print(error)
}

Can't find transaction hex in Etherscan

I have send a raw transaction via infura to Rinkeby network, the function returns me a transaction hex and I couldn't search the transaction in etherscan. Please help me on this matter. Thanks

Optional custom secp256k1_context management

Add the possibility to create and destroy (manage) an own secp256k1_context to make library calls faster for applications that require a high amount of secp256k1_context relevant calls (by sacrificing convenience).

Invoking contract methods using a [ABIEncodable] alongside ABIEncodable... parameters

Problem

Currently we have this method to invoke methods of a contract:

public subscript(_ name: String) -> ((ABIEncodable...) -> SolidityInvocation)? {
    return methods[name]?.invoke
}

However, variadic functions a bit of pain to work with, in a sense that we can't send an array of parameters, we have to type each parameter separated by commas.

Possible Solution

I suggest we drop the variadic parameter and create a subscript method that receives an array of ABIEncodable and then, create a normal method to be used with variadic parameters, something like this:

// New subscript method
public subscript(_ name: String) -> (([ABIEncodable]) -> SolidityInvocation)? {
    return methods[name]?.invoke
}

public func invokeMethod(with name: String) -> ((ABIEncodable...) -> SolidityInvocation)? {
    return methods[name]?.invoke
}

Of course we'd need to refactor the SolidityInvocation implementations.

Disclaimer

  • Mentioned this at #49
  • I'd like to hear your opinion on the topic, I'd be down to implement everything proposed here

Signing and unsigning.

I was doing some experimentation in developing an application that used the public ethereum blockchain, and accessing with web3.js. I poked around in this repository, looking for some specific web3.js functions, like "sign" and "ecrecover", but I didn't see them. I do see a "sign" method associated with transactions, however. I want to be able to sign a user generated string/data, and then later recover the signer from a signature. I was using web3.eth.personal.sign and web3.eth.personal.ecRecover. Are similar functions available in this package? Thanks.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.