Skip to content
Merged
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
29 changes: 20 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ default: build
CARGO=cargo

## bindings: generating bindings
bindings:
bindings: _build_forge
# Generate new bindings
@forge bind --bindings-path ./crates/bindings --crate-name bindings --overwrite \
--alloy --alloy-version v0.9.2
@$(CARGO) fmt --manifest-path ./crates/bindings/Cargo.toml

## build: building the project
build: bindings wasi-build
build: _build_forge bindings wasi-build
@$(CARGO) build --target-dir ./target --manifest-path ./app/Cargo.toml

## wasi-build: building the WAVS wasi component(s)
Expand All @@ -25,19 +26,19 @@ wasi-build:
@mkdir -p ./compiled
@cp ./target/wasm32-wasip1/release/*.wasm ./compiled/

## build-release: building the project in release mode
build-release: bindings
@$(CARGO) build --release

## update-submodules: update the git submodules
update-submodules:
@git submodule update --init --recursive

## clean: cleaning the project files
clean:
clean: clean-docker
@forge clean
@$(CARGO) clean

## clean-docker: remove unused docker containers
clean-docker:
@docker rm -v $(shell docker ps --filter status=exited -q) || true

## fmt: formatting solidity and rust code
fmt:
@forge fmt --check
Expand All @@ -46,15 +47,25 @@ fmt:
## test: running forge and rust tests
test:
@forge test
@$(CARGO) test
@$(CARGO) test --manifest-path ./app/Cargo.toml

## setup: installing forge dependencies
setup:
@forge install

## start-all: starting anvil and WAVS with docker compose
start-all: clean-docker
@trap 'kill $(jobs -pr)' EXIT
# running anvil out of compose is a temp work around for MacOS
@anvil &
@docker compose up
@wait

_build_forge:
@forge build

# Declare phony targets
.PHONY: build build-release clean fmt bindings test
.PHONY: build clean fmt bindings test

.PHONY: help
help: Makefile
Expand Down
86 changes: 30 additions & 56 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,117 +18,90 @@ Create a new project using this template:

```bash
# If you don't have forge: `curl -L https://foundry.paradigm.xyz | bash`

forge init --template Lay3rLabs/wavs-foundry-template my-wavs
```

## Testing

Given the repository contains both Solidity and Rust code, there's 2 different
workflows.

### Setting up the Environment

Initialize the submodule dependencies:
### Solidity

```bash
# Initialize the submodule dependencies
forge install
```

Build the contracts:

```bash
# Build the contracts
forge build
```

Run the tests:

```bash
# Run the solidity tests. alias: `make test`
forge test
```

## Rust
> You can also use `make build` to build the contracts, bindings, and components.

Rust bindings to the contracts can be generated via `forge bind`, which requires
first building your contracts:
## Rust

```bash
# Generate new bindings from your contract(s)
make bindings
```

Then, you can run the tests:

```bash
cargo test
# Run rust tests
make test
```

## WAVS

Build the latest solidity:
### Install the WAVS CLI

```bash
make build
```

Install the WAVS CLI:

```bash
# MacOS: if you get permission errors: eval `ssh-agent -s`; ssh-add
# MacOS: if you get permission errors: eval `ssh-agent -s` && ssh-add
(cd lib/WAVS; cargo install --path ./packages/cli)
```

### Start Anvil, WAVS, and Deploy Eigenlayer

```bash
# copy over the .env file
cp .env.example .env

# [!] Get your key from: https://openweathermap.org/
# Update the WAVS_ENV_OPEN_WEATHER_API_KEY in the .env file with your key`

cp ./lib/WAVS/packages/wavs/wavs.toml .
cp ./lib/WAVS/packages/cli/cli.toml .

# MacOS Docker:
# Docker Engine -> Settings -> Resources -> Network -> 'Enable Host Networking'
# or
# brew install chipmk/tap/docker-mac-net-connect && sudo brew services start chipmk/tap/docker-mac-net-connect

# TODO: this is a temp workaround for MacOS (running anvil out of compose)
anvil

docker compose up --build
make start-all
```

Deploy Eigenlayer and upload your WAVS Service contract
### Upload your WAVS Service Manager

```bash
docker_cmd="docker exec -it wavs bash -c"
export CLI_EIGEN_CORE_DELEGATION_MANAGER=`${docker_cmd} 'jq -r .eigen_core.local.delegation_manager ~/wavs/cli/deployments.json' | tr -d '\r'`
export CLI_EIGEN_CORE_REWARDS_COORDINATOR=`${docker_cmd} 'jq -r .eigen_core.local.rewards_coordinator ~/wavs/cli/deployments.json' | tr -d '\r'`
export CLI_EIGEN_CORE_AVS_DIRECTORY=`${docker_cmd} 'jq -r .eigen_core.local.avs_directory ~/wavs/cli/deployments.json' | tr -d '\r'`
export FOUNDRY_ANVIL_PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

# Deploy (override: FOUNDRY_ANVIL_PRIVATE_KEY)
forge script ./script/WavsServiceManager.s.sol --rpc-url http://localhost:8545 --broadcast

# set this in the your terminal from the script output
export SERVICE_MANAGER_ADDRESS=0x851356ae760d987E095750cCeb3bC6014560891C
# Grab deployed service manager address by name
BROADCAST_FILE=./broadcast/WavsServiceManager.s.sol/31337/run-latest.json
export SERVICE_MANAGER_ADDRESS=`jq -r '.transactions[] | select(.contractName == "WavsServiceManager") | .contractAddress' "${BROADCAST_FILE}"`
echo "Service Manager Address: $SERVICE_MANAGER_ADDRESS"
```

Build WAVS WASI component(s)
### Build WASI components

> Install `cargo binstall cargo-component` if you have not already. -- https://github.com/bytecodealliance/cargo-component#installation

```bash
# build all components/*
# https://github.com/bytecodealliance/cargo-component#installation / cargo binstall cargo-component
make wasi-build

# Verify execution works as expected without deploying
# TODO: currently broken upstream
# Verify execution works as expected without deploying
# wavs-cli exec --component $(pwd)/compiled/eth_trigger_weather.wasm --input Nashville,TN
```

Deploy service and verify with adding a task
## Deploy Service and Verify

```bash
# add read-write access
sudo chmod 0666 .docker/cli/deployments.json

# Contract trigger function signature to listen for
trigger_event=$(cast sig-event "NewTrigger(bytes)"); echo $trigger_event

service_info=`wavs-cli deploy-service --log-level=error --data ./.docker/cli --component $(pwd)/compiled/eth_trigger_weather.wasm \
Expand All @@ -139,10 +112,11 @@ service_info=`wavs-cli deploy-service --log-level=error --data ./.docker/cli --c

echo "Service info: $service_info"

# Submit AVS request -> chain
SERVICE_ID=`echo $service_info | jq -r .service[0]`; echo "Service ID: $SERVICE_ID"
wavs-cli add-task --input "Nashville,TN" --data ./.docker/cli --service-id ${SERVICE_ID}

# Where the call address is the service manager in ./.docker/cli/deployments.json
# Grab data from the contract directly
hex_bytes=$(cast decode-abi "getData(uint64)(bytes)" `cast call ${SERVICE_MANAGER_ADDRESS} "getData(uint64)" 1`)
echo `cast --to-ascii $hex_bytes`
```
1 change: 1 addition & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ libs = ['lib']
solidity_version = '0.8.22'
evm_version = 'shanghai'
via_ir = true
fs_permissions = [{ access = "read", path = "./" },]

# See more config options https://github.com/foundry-rs/foundry/tree/master/config
45 changes: 35 additions & 10 deletions script/WavsServiceManager.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,38 @@ import {IDelegationManager} from
"@eigenlayer/middleware/lib/eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol";
import {Quorum, StrategyParams} from "@eigenlayer/middleware/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol";
import {IStrategy} from "@eigenlayer/middleware/lib/eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol";
import {stdJson} from "forge-std/StdJson.sol";

struct EigenContracts {
address delegation_manager;
address rewards_coordinator;
address avs_directory;
}

// forge script ./script/WavsServiceManager.s.sol --rpc-url http://localhost:8545 --broadcast --var-ir
contract WavsServiceManagerScript is Script {
address public delegation_manager = vm.envAddress("CLI_EIGEN_CORE_DELEGATION_MANAGER");
address public rewards_coordinator = vm.envAddress("CLI_EIGEN_CORE_REWARDS_COORDINATOR");
address public avs_directory = vm.envAddress("CLI_EIGEN_CORE_AVS_DIRECTORY");
using stdJson for string;

uint256 privateKey = vm.envUint("FOUNDRY_ANVIL_PRIVATE_KEY");
uint256 privateKey = vm.envOr(
"FOUNDRY_ANVIL_PRIVATE_KEY", uint256(0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80)
);

function setUp() public {}

function run() public {
vm.startBroadcast(privateKey);

ECDSAStakeRegistry ecdsa_registry = new ECDSAStakeRegistry(IDelegationManager(delegation_manager));
EigenContracts memory eigen = loadEigenContractsFromFS("deployments.json");

console.log("delegation_manager:", delegation_manager);
console.log("rewards_coordinator:", rewards_coordinator);
console.log("avs_directory:", avs_directory);
ECDSAStakeRegistry ecdsa_registry = new ECDSAStakeRegistry(IDelegationManager(eigen.delegation_manager));

WavsServiceManager sm =
new WavsServiceManager(avs_directory, address(ecdsa_registry), rewards_coordinator, delegation_manager);
console.log("delegation_manager:", eigen.delegation_manager);
console.log("rewards_coordinator:", eigen.rewards_coordinator);
console.log("avs_directory:", eigen.avs_directory);

WavsServiceManager sm = new WavsServiceManager(
eigen.avs_directory, address(ecdsa_registry), eigen.rewards_coordinator, eigen.delegation_manager
);

IStrategy mockStrategy = IStrategy(address(0x1234));
Quorum memory quorum = Quorum({strategies: new StrategyParams[](1)});
Expand All @@ -41,4 +51,19 @@ contract WavsServiceManagerScript is Script {
console.log("ServiceManager:", address(sm));
console.log("ecdssa_registry (deployed):", address(ecdsa_registry));
}

function loadEigenContractsFromFS(string memory fileName) public view returns (EigenContracts memory) {
string memory root = vm.projectRoot();
string memory path = string.concat(root, "/.docker/cli/", fileName);
string memory json = vm.readFile(path);

address dm = address(uint160(bytes20(json.readBytes(".eigen_core.local.delegation_manager"))));
address rc = address(uint160(bytes20(json.readBytes(".eigen_core.local.rewards_coordinator"))));
address avs = address(uint160(bytes20(json.readBytes(".eigen_core.local.avs_directory"))));

EigenContracts memory fixture =
EigenContracts({delegation_manager: dm, rewards_coordinator: rc, avs_directory: avs});

return fixture;
}
}