Giter Club home page Giter Club logo

ethcontract-rs's Introduction

Build Status Crates.io Docs.rs Rustc Version

ethcontract-rs

Crate used for generating code for Ethereum smart contracts. It provides a function procedural macro that generates safe bindings for contract interaction based on the contract ABI.

Getting Started

Add a dependency to the ethcontract crate in your Cargo.toml:

[dependencies]
ethcontract = "..."

Then generate a struct for interacting with the smart contract with a type-safe API:

ethcontract::contract!("path/to/truffle/build/contract/Contract.json");

This will generate a new struct ContractName with contract generated methods for interacting with contract functions in a type-safe way.

Minimum Supported Rust Version

The minimum supported Rust version is 1.64.0

Generator API

As an alternative to the procedural macro, a generator API is provided for generating contract bindings from build.rs scripts. More information can be found in the ethcontract-generate README.

Running the Examples

In order to run local examples you will additionally need:

  • NodeJS LTS
  • Yarn

For all examples, the smart contracts must first be built:

cd examples/truffle
yarn && yarn build

Truffle Examples

Truffle examples rely on the local truffle development server. In a separate terminal run:

cd examples/truffle
yarn start

ABI:

The abi example deploys a simple contract and performs various eth_calls to illustrate how Solidity types are mapped to Rust types by ethcontract.

cargo run --example abi

Async/Await:

The async example deploys an ERC20 token and interacts with the contract with various accounts.

cargo run --example async

Manual Deployments:

The deployments example illustrates how the deployments parameter can be specified when generating a contract with the ethcontract::contract! macro. This can be useful for specifying addresses in testing environments that are deterministic but either not included, or inaccurate in the artifact's networks property (when for example the contract is developed upstream, but a separate testnet deployment wants to be used).

cargo run --example deployments

Events:

The events example illustrates how to listen to logs emitted by smart contract events.

cargo run --example events

Generator API (with build.rs script):

The generator example (actually a separate crate to be able to have a build script) demonstrates how the generator API can be used for creating type-safe bindings to a smart contract with a build.rs build script.

cargo run --package examples-generate

Contract Linking:

The linked example deploys a library and a contract that links to it then makes a method call.

cargo run --example linked

Deployed Bytecode

The bytecode example deploys a contract and verifies its bytecode matches the expected value from the contract artifact.

cargo run --example bytecode

Rinkeby Example

There is a provided example that runs with Rinkeby and Infura. Running this example is a little more involved to run because it requires a private key with funds on Rinkeby (for gas) as well as an Infura project ID in order to connect to a node. Parameters are provided to the Rinkeby example by environment variables:

export PK="private key"
export INFURA_PROJECT_ID="Infura project ID"
cargo run --example rinkeby

Mainnet Examples

Sources:

This example generates contract bindings from online sources:

  • A verified contract on Etherscan
  • An npmjs contract

It also queries some contract state with Infura. Running this example requires an Infura project ID in order to connect to a node. Parameters are provided to the example by environment variables:

export INFURA_PROJECT_ID="Infura project ID"
cargo run --example sources

Past Events:

This example retrieves the entire event history of token OWL contract and prints the total number of events since deployment.

Note the special handling of the tokenOWLProxy contract and how it is cast into a tokenOWL instance using Contract's with_deployment_info feature.

export INFURA_PROJECT_ID="Infura project ID"
cargo run --example past_events

Sample Contracts Documentation

You can view example generated contract documentation by fist building the contracts and then generating documentation for the crate:

(cd examples/truffle; yarn && yarn build)
cargo doc --package examples-documentation --no-deps --open

This will open a browser at the documentation root.

ethcontract-rs's People

Contributors

bh2smith avatar dependabot-preview[bot] avatar dependabot[bot] avatar dtolnay avatar e00e avatar fleupold avatar hyche avatar jaxkr avatar josojo avatar martinquaxd avatar nlordell avatar pgold avatar pzixel avatar squadgazzz avatar sunce86 avatar vkgnosis avatar

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

ethcontract-rs's Issues

Refine crates.io Experience

We should:

  • Improve keywords, maybe include truffle, smart contract, etc.
  • Specify a readme field in the Cargo.toml with some better introductory documentation
  • Add crates.io badges
  • Maybe more? ...

Upstream Confirmation Fixes.

There are currently some issues with transaction confirmation in web3 crate. Current issues that are addressed are:

  • Waiting for confirmations with 0 confirmations (i.e. just wait for the transaction to mine with no extra blocks) panics.
  • Does not work with development nodes like Ganache that auto-mine transaction as soon as they are sent since the block filter is set up after the transaction is already mined.
  • Might have some issues with ommer blocks where the actual block where the transaction was mined can change during reorgs (to be confirmed).
  • Does not work with nodes that do not support filters (like Infura over HTTP/HTTPS). This can be addressed by falling back to polling the node.

Hopefully some of these fixes can be upstreamed.

Rename Contract::deploy to something more buildery

https://github.com/gnosis/ethcontract-rs/blob/1490a1fdf7aa3b7344bdfd231c8b6bd9a9592b6b/derive/src/lib.rs#L228

Currently in order to deploy an example contract, we need to

let instance = RustCoin::deploy(&web3)
    .gas(4_712_388.into())
    .confirmations(0)
    .deploy()
    .await;

I find the double deploy() a bit confusing and am wondering if it would be a good idea to make it explicit that the first deploy returns a DeployBuilder (which doesn't yet deploy anything).

So maybe this could be renamed to something like newDeployBuilder()?

[ErrorHandling] Include method that causes tx/call/deploy failure

At the moment upon tx failure we print the plain Web3 error message, which might be very generic (e.g. out of gas).

It would be very helpful to print information about the method name (or if it was a deploy of a certain contract) that failed to help find the call-site in the code.

Use `Self` Where Possible

We have a lot of builders, using Self instead of the actual builder type on the return types is not only more concise but less prone to copy-pasta bugs.

Add distinction between payable and non-payable contract functions.

This allows us to generate functions for methods that include (or not) the SendBuilder::value(self, U256) so the compiler can yell at us if we try to send a transaction with a value to a non-payable function.

This requires modifications to the ethabi crate so that information on whether or not a function is payable gets added to the contract ABI (it currently is already included in the solc generated contract ABI).

Setup CI

CI should:

  • Check all unit tests pass
  • Check code is properly formatted
  • Check example compiles
  • Other stuff?

Turn Builder into a Future which builds underlying default future on first poll

Right now, we need to call a method on the builder in order to turn it into a future. E.g. for a transaction, we need to invoke .send() in order to turn its MethodBuilder into a SendFuture.

A less verbose approach could be to have the builder itself be a future and upon first poll build and drive the underlying (default) SendFuture. If someone wanted to call a non-view method (e.g. to check if execution is likely to succeed), they could still explicitly turn it into a CallFuture by invoking .call() on the builder.

This would save one line of boilerplate code in the majority of the cases (e.g. deploy & call can only ever be invoked with one type of Future).

Parameter names that are reserved key words cause errors.

An example of this is the SimpleLibrary contract where one of the input parameters is named self (but does not refer to the library itself but an instance of a type for which the library is being used for). This is a general problem with libraries.

Generate Code For ethPM Contracts.

This will be in two parts:

  • Don't depend on the contract being in Truffle artifact representation (needs to support plain JSON ABI + extra data)
  • Support ethPM manifests and packages

Property generate contract bindings for {i,u}{8,16,32,64,128} types.

Currently all solidity (u)integer types that are 64 bits or smaller map to u64 since Tokenizable trait is not implemented for these other types (i8, i16, u32, etc.). Submitted a PR #277 to rust-web3 which, if it lands, means that we can properly generate bindings with the correct types instead of always coercing them to a u64.

[Refactor] Rename `example` feature to something more meaningful

We currently feature gate generating bindings to sample contracts on a feature called example. The idea was to build documentation on docs.rs with this feature enabled but have it disabled by default. This would basically allow us to have rustdoc documentation for a sample contract as a "here is what a generated contract binding looks like".

While this works the feature name is unclear and can be confused with cargo examples which it has nothing to do with.

Generate method for fallback function if it is defined.

Right now there is no generated method for calling the fallback function on the generated contract types, we should add them if it is defined in the contract ABI.

  1. First this issue would require adding a ethcontract::contract::Instance::fallback() method that would create a builder for creating a Tx.
  2. Define a FallbackMethodBuilder. It should be almost identical to a MethodBuilder with the exception that it can also set the transaction data.
  3. Generate a fallback() method on the generated contract types, note that this should only be generated if a fallback method is defined in the contract ABI.

Improve deployment of contracts with linked libraries

At the moment the interface to deploy a contract that depends on linked libraries is the following:

let instance = BatchExchange::builder(
  &web3,
  library1.address(),
  library2.address(),
  ..parameters)

This is not very typesafe, as the order of addresses passed is important, but there is no way to type ceck it (all arguments are of type H160, therefore it's easy to e.g. switch dep1 and dep2).
One improvement could be to code-generate a struct which has a named field for each dependent library.

Moreover, it might be possible to avoid a bunch of boilerplate code for library creation, by allowing to pass in an Either<address, artefact> for each dependent libraries. As libraries cannot have constructors, the way to instantiate the is always the same and could be part of the code generation as well". If the argument is an address the code will directly link this address (e.g. when we reuse a previously deployed version of that library). If the argument is an artefact, we can internally invoke the following line to create an instance.

let lib = Library::builder(&web3).deploy().await.expect()

Add strategy for name collision.

Since we 'rustify' the contract identifiers (i.e. balanceOf -> balance_of) we can run into name collisions. This can also happen if some contract functions names are the same as default function names (for example address() to retrieve contract instance address, then the contract currently can't define a function address(...) as the identifiers will collide). Also solidity supports (? confirm) function overloading which rust does not.

We need to come up with a strategy for name collisions.

Unify "confirmations" behavior for contract.deploy and tx.send

At the moment contract deployments wait by default for 1 confirmation, whereas transactions by default do not wait for confirmations.

Moreover, deploy uses an optional .confirmations() method on the builder object:

Library::builder(&web3)
  .confirmations(0)
  .deploy()

whereas transactions require to be issued with a send_and_confirm call:

instance.method().send_and_confirm(poll_interval, 0)

Since under the hood a deployment is just a transaction, the interface and default behaviour should probably be the same. Arguably, the default should be 0 confirmations (but wait for the transaction to be mined). Just doing a fire and forget (without waiting for the tx to be mined), should probably also be an option.

I personally prefer the deployment interface over exposing another flavour of send method.

Extend manual deployments syntax to detect collisions with artifact deployments.

Currently there is no indication if a manually specified deployment overrides deployments in the Truffle artifact. Add support in code generation and the proc macro to specify if it should be an error or allowed. The current working idea is:

5777! => "0x..." // override network ID 5777 even if it is in the contract.
5777? => "0x..." // Use as a backup if it is not in the contract.
5777 => "0x..." // error if already exists in contract and is a different address.

Add unit tests for contract deployment.

We currently have no test coverage for contract deployment. A new test should be added (there is an unimplemented!() placeholder in src/contract/deploy.rs) that goes through a deployment. Since deployment uses the TransactionBuilder::execute_and_confirm API we will get test coverage on that method as well. There are a couple deployment paths that need to be tested, the paths that require testing being:

  • Deploy contract without libraries and without constructor
  • Deploy contract with constructor without parameters
  • Deploy contract with constructor that requires parameters
  • Deploy contract with library(ies)
  • Deploy contract with signed transaction (either with Account::Locked or Account::Offline)
  • Deploy contract with transaction request (with Account::Local)

Note that some of these paths, but not all are mutually exclusive so we probably don't need a test for each of these to achieve full coverage.

For some context on how to set up the test transport for these take a look at the tests in src/transaction.rs.

Support `revert(string)` and `revert()` on Geth Nodes for `eth_call`

Geth and Ganache behave differently when an eth_call reverts. In Geth this is not treated as an RPC error while in Ganache (and presumably Parity) it is.

We want to extend the ExecutionError enum to have Revert(Option<String>) and InvalidOpcode error variants and make the errors consistent so that:

  • revert(string) produces an ExecutionError::Revert(Some(message)) on Geth and Ganache nodes for eth_call requests what revert with a message.
  • revert() produces an ExecutionError::Revert(None) on Geth and Ganache nodes for eth_call requests that revert without a message.
  • invalid() produces an ExecutionError::InvalidOpcode on Ganache nodes for eth_call requests that execute an INVALID opcode. Note that this is not supported for Geth as there is no good way to distinguish between revert() and invalid().

Here is a table of eth_call responses from both Geth and Ganache:

Client  | revert(string)                        | revert()               | invalid()
--------+---------------------------------------+------------------------+-------------------------------
Geth    | Ok("0x08c379a0...")                   | Ok("0x")               | Ok("0x")
Ganache | Err({error: "revert", reason: "..."}) | Err({error: "revert"}) | Err({error: "invalid opcode"})

Add some extra `contract!` macro options for code generation

Here are a couple options:

  • For now we don't generate a generic type, instead we wrap the web3 Transport in a transport that uses dynamic dispatch. We should add the ability to generate the contract type generic over T: Transport.
  • We always change the identifier case to 'rusty' cases (PascalCase for contract name and snake_case for functions), we should not impose this and make an option to disable this.
  • Ability to specify library types so the contructors for linked contracts are even more typesafe
  • Ability to have returned named-tuples generate struct types, this requires some manual input since the ABI does not provide a name for returned structs.

The Main Example Should Be Doc Tested

It is currently ignored since we would have to include a MyContract.json for it to compile but it would be nice to have a compiled example so it doesn't show the warning icon on the left.

Build Docs on Docs.rs With Example Contracts

Docs.rs documentation currently does not include smart contracts. I am unsure whether or not the Docs.rs will be able to build the truffle project (might not even have node installed) so we might have to include the built truffle project in the repo ๐Ÿ˜ข

@fleupold @bh2smith any ideas?

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.