Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add hello world example #12

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,3 @@ FROM mcr.microsoft.com/devcontainers/base

COPY --from=avalanchego /avalanchego/build /go/src/github.com/ava-labs/avalanchego/build
COPY --from=avalanche-cli /avalanche /usr/local/bin/avalanche

COPY --from=foundry /usr/local/bin/forge /usr/local/bin/forge
COPY --from=foundry /usr/local/bin/cast /usr/local/bin/cast
COPY --from=foundry /usr/local/bin/anvil /usr/local/bin/anvil
COPY --from=foundry /usr/local/bin/chisel /usr/local/bin/chisel
4 changes: 2 additions & 2 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"build": {
"dockerfile": "Dockerfile",
"args": {
"AVALANCHEGO_VERSION": "v1.11.7"
"AVALANCHEGO_VERSION": "v1.11.11"
}
},
"runArgs": ["--network=host"],
Expand All @@ -26,7 +26,7 @@
"version": "latest"
},
"ghcr.io/devcontainers/features/node:1": {},
"ghcr.io/devcontainers/features/go:1": {}
"ghcr.io/devcontainers/features/go:1": {"version": 1.22}
},

// Use 'forwardPorts' to make a list of ports inside the container available locally.
Expand Down
24 changes: 24 additions & 0 deletions .github/workflows/sync.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Sync
on:
push:
branches:
- main

jobs:
sync-branches:
runs-on: ubuntu-latest
name: Syncing branches
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Node
uses: actions/setup-node@v1
with:
node-version: 12
- name: Opening pull request
id: pull
uses: tretuna/[email protected]
with:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
FROM_BRANCH: "main"
TO_BRANCH: "develop"
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
pull_request:

env:
min_go_version: "~1.21.11"
min_go_version: "~1.21.12"

jobs:
lint_test:
Expand Down
159 changes: 137 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,38 +28,153 @@ In order to run the Dev Container locally:

## Learn about Precompile-EVM

To get a comprehensive introduction to Precompile-EVM, take the Avalanche Academy course on [Customizing the EVM](https://academy.avax.com/course/customize-evm).
To get a comprehensive introduction to Precompile-EVM, take the Avalanche Academy course on [Customizing the EVM](https://academy.avax.com/course/customizing-evm).

## How to use
## Hello World Example

There is an example branch [hello-world-example](https://github.com/ava-labs/precompile-evm/tree/hello-world-example) in this repository. You can check the example branch to see how to register precompiles and test them.
### 1. Clone the Repo

### Generate Precompile Files
```bash
git clone https://github.com/ava-labs/precompile-evm.git
cd precompile-evm/
```

### 2. Checkout the `hello-world-example` Branch

```bash
git checkout hello-world-example
```

### 3. Install NodeJS Dependencies

First you have to `cd contracts/` and run `npm install` to get the dependencies.

```bash
cd contracts/
npm install
```

### 4. Create a New Contract

`hello-world-example` branch has already a precompile contract called `HelloWorld.sol`. All necessary files were already created for you. You can check existing files and see how a fully implemented precompile should look like. If you'd like to redo steps to create a new precompile contract, you can follow the steps below.

Copy the existing `IHelloWorld.sol` interface to a new file called `IHolaMundo.sol`.

```bash
cd .. # change directory back to the root of the repo
```
```bash
cp contracts/contracts/interfaces/IHelloWorld.sol contracts/contracts/interfaces/IHolaMundo.sol
```

### 5. Generate an ABI

Now generate a `.abi` from a `.sol` using `solc`.

Passing in the following flags

- `--abi`
- ABI specification of the contracts.
- `--base-path path`
- Use the given path as the root of the source tree instead of the root of the filesystem.
- `--include-path path`
- Make an additional source directory available to the default import callback. Use this option if you want to import contracts whose location is not fixed in relation to your main source tree, e.g. third-party libraries installed using a package manager. Can be used multiple times. Can only be used if base path has a non-empty value.
- `--output-dir path`
- If given, creates one file per output component and contract/file at the specified directory.
- `--overwrite`
- Overwrite existing files (used together with `--output-dir`).

```bash
cd contracts/ # change directory to the contracts/ directory
```
```bash
npx solc --abi contracts/interfaces/IHolaMundo.sol --output-dir abis --base-path . --include-path ./node_modules
```

Rename the files for easier readability
```bash
mv abis/@avalabs_subnet-evm-contracts_contracts_interfaces_IAllowList_sol_IAllowList.abi abis/IAllowList.abi
```
```bash
mv abis/contracts_interfaces_IHolaMundo_sol_IHelloWorld.abi abis/IHolaMundo.abi
```

### 6. Generate Precompile Files

First, you need to create your precompile contract interface in the `contracts` directory and build the ABI. Then you can generate your precompile files with `./scripts/generate_precompile.sh --abi {abiPath} --out {outPath}`. This script installs the `precompilegen` tool from Subnet-EVM and runs it to generate your precompile.

### Register Precompile
```bash
cd .. # change directory back to the root directory of the repo
```
```bash
./scripts/generate_precompile.sh --abi contracts/abis/IHolaMundo.abi --out holamundo/
```
```bash
Using branch: hello-world-example
installing precompilegen from Subnet-EVM v0.5.2
generating precompile with Subnet-EVM v0.5.2
Precompile files generated successfully at: holamundo/
```

Confirm that the new `holamundo/` directory has the appropriate files.

In `plugin/main.go` Subnet-EVM is already imported and ready to be Run from the main package. All you need to do is explicitly register your precompiles to Subnet-EVM in `plugin/main.go` and build it together with Subnet-EVM. Precompiles generated by `precompilegen` tool have a self-registering mechanism in their `module.go/init()` function. All you need to do is to force-import your precompile packprecompile package in `plugin/main.go`.
```bash
ls -lh holamundo
```
```bash
-rw-r--r-- 1 user group 2.3K Jul 5 13:26 README.md
-rw-r--r-- 1 user group 2.3K Jul 5 13:26 config.go
-rw-r--r-- 1 user group 2.8K Jul 5 13:26 config_test.go
-rw-r--r-- 1 user group 963B Jul 5 13:26 contract.abi
-rw-r--r-- 1 user group 8.1K Jul 5 13:26 contract.go
-rw-r--r-- 1 user group 8.3K Jul 5 13:26 contract_test.go
-rw-r--r-- 1 user group 2.7K Jul 5 13:26 module.go
```

### 7. Register Precompile

### Build
In `plugin/main.go` Subnet-EVM is already imported and ready to be Run from the main package.

You can build your precompile and Subnet-EVM with `./scripts/build.sh`. This script builds Subnet-EVM, and your precompile together and generates a binary file. The binary file is compatible with AvalancheGo plugins.
All you need to do is explicitly register your precompiles to Subnet-EVM in `plugin/main.go` and build it together with Subnet-EVM.

### Run
Precompiles generated by `precompilegen` tool have a self-registering mechanism in their `module.go/init()` function.

You can run you Precompile-EVM by using the Avalanche CLI.
```go
package main
import (
"fmt"
"github.com/ava-labs/avalanchego/version"
"github.com/ava-labs/subnet-evm/plugin/evm"
"github.com/ava-labs/subnet-evm/plugin/runner"
// Each precompile generated by the precompilegen tool has a self-registering init function
// that registers the precompile with the subnet-evm. Importing the precompile package here
// will cause the precompile to be registered with the subnet-evm.
_ "github.com/ava-labs/precompile-evm/helloworld"
// ADD YOUR PRECOMPILE HERE
//_ "github.com/ava-labs/precompile-evm/holamundo"
)
```

First, create the configuration for your subnet.
### 8. Build

You can build your precompile and Subnet-EVM with
```bash
avalanche subnet create mysubnet --custom --vm $AVALANCHEGO_PLUGIN_PATH/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy --genesis ./.devcontainer/genesis-example.json
./scripts/build.sh
```
This script builds Subnet-EVM, and your precompile together and generates a binary file. The binary file is compatible with AvalancheGo plugins.

Next, launch the Subnet with your custom VM:
### 9. Run

You can now run Precompile-EVM by using the Avalanche-CLI

```bash
avalanche blockchain create myblockchain --custom --vm $AVALANCHEGO_PLUGIN_PATH/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy --genesis ./.devcontainer/genesis-example.json
```

Then launch the blockchain with your custom VM:

```bash
avalanche subnet deploy mysubnet
avalanche blockchain deploy myblockchain
```

### Test
Expand All @@ -73,16 +188,16 @@ In order to upgrade the Subnet-EVM version, you need to change the version in `g
## AvalancheGo Compatibility

```text
[v0.1.0-v0.1.1] [email protected] (Protocol Version: 26)
[v0.1.2] [email protected] (Protocol Version: 27)
[v0.1.3] [email protected] (Protocol Version: 28)
[v0.1.4] [email protected] (Protocol Version: 28)
[v0.1.5] [email protected] (Protocol Version: 29)
[v0.1.6] [email protected] (Protocol Version: 30)
[v0.1.7] [email protected] (Protocol Version: 30)
[v0.1.8] [email protected] (Protocol Version: 31)
[v0.2.0] [email protected] (Protocol Version: 33)
<<<<<<< HEAD
[v0.2.1] [email protected] (Protocol Version: 35)
[v0.2.2] [email protected] (Protocol Version: 35)
[v0.2.3] [email protected] (Protocol Version: 35)
[v0.2.4] [email protected] (Protocol Version: 37)
```
=======
[v0.2.1] [email protected] (Protocol Version: 35)
[v0.2.2] [email protected] (Protocol Version: 35)
[v0.2.3] [email protected] (Protocol Version: 35)
```
>>>>>>> 9823d9df75a3db1a5466075297b4b03f088ff390
1 change: 1 addition & 0 deletions compatibility.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"rpcChainVMProtocolVersion": {
"v0.2.4": 37,
"v0.2.3": 35,
"v0.2.2": 35,
"v0.2.1": 35,
Expand Down
9 changes: 0 additions & 9 deletions contracts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,15 +111,6 @@ Subnet-EVM must activate any precompiles used in the test in the genesis:
{
"config": {
"chainId": 43214,
"homesteadBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"istanbulBlock": 0,
"muirGlacierBlock": 0,
"feeConfig": {
"gasLimit": 8000000,
"minBaseFee": 25000000000,
Expand Down
Empty file removed contracts/contracts/.gitkeep
Empty file.
19 changes: 19 additions & 0 deletions contracts/contracts/ExampleHelloWorld.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./interfaces/IHelloWorld.sol";

address constant HELLO_WORLD_ADDRESS = 0x0300000000000000000000000000000000000000;

// ExampleHelloWorld shows how the HelloWorld precompile can be used in a smart contract.
contract ExampleHelloWorld {
IHelloWorld helloWorld = IHelloWorld(HELLO_WORLD_ADDRESS);

function sayHello() public view returns (string memory) {
return helloWorld.sayHello();
}

function setGreeting(string calldata greeting) public {
helloWorld.setGreeting(greeting);
}
}
Empty file.
13 changes: 13 additions & 0 deletions contracts/contracts/interfaces/IHelloWorld.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.0;
import "@avalabs/subnet-evm-contracts/contracts/interfaces/IAllowList.sol";

interface IHelloWorld is IAllowList {
event GreetingChanged(address indexed sender, string oldGreeting, string newGreeting);
// sayHello returns the stored greeting string
function sayHello() external view returns (string calldata result);

// setGreeting stores the greeting string
function setGreeting(string calldata response) external;
}
Empty file removed contracts/contracts/test/.gitkeep
Empty file.
42 changes: 42 additions & 0 deletions contracts/contracts/test/ExampleHelloWorldTest.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "../ExampleHelloWorld.sol";
import "../interfaces/IHelloWorld.sol";
import "@avalabs/subnet-evm-contracts/contracts/test/AllowListTest.sol";

contract ExampleHelloWorldTest is AllowListTest {
IHelloWorld helloWorld = IHelloWorld(HELLO_WORLD_ADDRESS);

function step_getDefaultHelloWorld() public {
ExampleHelloWorld example = new ExampleHelloWorld();
address exampleAddress = address(example);

assertRole(helloWorld.readAllowList(exampleAddress), AllowList.Role.None);
assertEq(example.sayHello(), "Hello World!");
}

function step_doesNotSetGreetingBeforeEnabled() public {
ExampleHelloWorld example = new ExampleHelloWorld();
address exampleAddress = address(example);

assertRole(helloWorld.readAllowList(exampleAddress), AllowList.Role.None);

try example.setGreeting("testing") {
assertTrue(false, "setGreeting should fail");
} catch {} // TODO should match on an error to make sure that this is failing in the way that's expected
}

function step_setAndGetGreeting() public {
ExampleHelloWorld example = new ExampleHelloWorld();
address exampleAddress = address(example);

assertRole(helloWorld.readAllowList(exampleAddress), AllowList.Role.None);
helloWorld.setEnabled(exampleAddress);
assertRole(helloWorld.readAllowList(exampleAddress), AllowList.Role.Enabled);

string memory greeting = "testgreeting";
example.setGreeting(greeting);
assertEq(example.sayHello(), greeting);
}
}
Loading
Loading