Skip to content

Latest commit

 

History

History
219 lines (159 loc) · 8.47 KB

File metadata and controls

219 lines (159 loc) · 8.47 KB
sidebar_position title
1
Get started

:::note

We are in the process of updating our samples to include Rewards and Slashing capabilities. The Hello World AVS example will be updated as soon as possible. Use Hello World AVS now to get familiar with EigenLayer.

For more information on Rewards and Slashing, refer to the Rewards and Slashing ELIPs, and Rewards and Slashing documentation.

For questions or support, reach out to us using the Intercom button on the bottom right side of this page or here. We will promptly follow up with support!

:::

Hello World AVS: Local Deployment

The Hello World AVS is a simple implementation designed to demonstrate the core mechanics of how AVSs work within the EigenLayer framework. This example walks you through the process of:

  • Spinning up a local chain with EigenLayer contracts and AVS contracts preconfigured.
  • Registering an Operator with both EigenLayer and the AVS.
  • Consumer client requesting work to be done by the AVS.
  • Operator listening picking up this request, performing it, and signing off on it.
  • The AVS contract verifying the operator's work.

Hello World Diagram

Key Components of Hello World AVS

  • AVS Consumer: Requests a "Hello, ___" message to be generated and signed.
  • AVS: Takes the request and emits an event for operators to handle.
  • Operators: Picks up the request, generates the message, signs it, and submits it back to the AVS.
  • Validation: Ensures the operator is registered and has the necessary stake, then accepts the submission.

Code Walkthrough

The following sections highlight a few crucial components of the Hello World example that implement core AVS functionality.

AVS Contract

HelloWorldServiceManager.sol

The contract definition declares that it implements ECDSAServiceManagerBase, which allows it to inherit the core required functionality of IServiceManager. These contracts are included from the eigenlayer-middleware repo and are required components for any AVS.

contract HelloWorldServiceManager is ECDSAServiceManagerBase, IHelloWorldServiceManager {
    using ECDSAUpgradeable for bytes32;

The following functions are responsible for the "business logic" of the AVS. In the case of hello world the business logic includes managing the lifecycle of a "task" (creation and response) with a simple name string value.

function createNewTask(
    string memory name
) external returns (Task memory) {
    // create a new task struct
    Task memory newTask;
    newTask.name = name;
    newTask.taskCreatedBlock = uint32(block.number);

    // store hash of task on-chain, emit event, and increase taskNum
    allTaskHashes[latestTaskNum] = keccak256(abi.encode(newTask));
    emit NewTaskCreated(latestTaskNum, newTask);
    latestTaskNum = latestTaskNum + 1;

    return newTask;
}

function respondToTask(
    Task calldata task,
    uint32 referenceTaskIndex,
    bytes memory signature
) external {
    // check that the task is valid, hasn't been responded to yet, and is being responded in time
    require(
        keccak256(abi.encode(task)) == allTaskHashes[referenceTaskIndex],
        "supplied task does not match the one recorded in the contract"
    );
    require(
        allTaskResponses[msg.sender][referenceTaskIndex].length == 0,
        "Operator has already responded to the task"
    );

    // The message that was signed
    bytes32 messageHash = keccak256(abi.encodePacked("Hello, ", task.name));
    bytes32 ethSignedMessageHash = messageHash.toEthSignedMessageHash();
    bytes4 magicValue = IERC1271Upgradeable.isValidSignature.selector;
    if (!(magicValue == ECDSAStakeRegistry(stakeRegistry).isValidSignature(ethSignedMessageHash,signature))){
        revert();
    }

    // updating the storage with task responses
    allTaskResponses[msg.sender][referenceTaskIndex] = signature;

    // emitting event
    emit TaskResponded(referenceTaskIndex, task, msg.sender);
}

Contract Deployment Scripts

HelloWorldDeployer.s.sol

The deployment of the HelloWorld contracts associates the quorums and their asset strategies to the AVS.

token = new ERC20Mock();
helloWorldStrategy = IStrategy(StrategyFactory(coreDeployment.strategyFactory).deployNewStrategy(token));

quorum.strategies.push(
    StrategyParams({strategy: helloWorldStrategy, multiplier: 10_000})
);

Off-chain Operator Code

index.ts

The following snippets of Operator code manage Operator registration to core EigenLayer protocol, registration to the Hello World AVS, listening and responding to tasks.

// Register Operator to EigenLayer core contracts and Hello World AVS
const registerOperator = async () => {
    
    // Registers as an Operator in EigenLayer.
    try {
        const tx1 = await delegationManager.registerAsOperator({
            __deprecated_earningsReceiver: await wallet.address,
            delegationApprover: "0x0000000000000000000000000000000000000000",
            stakerOptOutWindowBlocks: 0
        }, "");
        await tx1.wait();
        console.log("Operator registered to Core EigenLayer contracts");
    }
    
    ...
    
    
    const tx2 = await ecdsaRegistryContract.registerOperatorWithSignature(
        operatorSignatureWithSaltAndExpiry,
        wallet.address
    );
    await tx2.wait();
    console.log("Operator registered on AVS successfully");
};

// Listen for new task events on-chain
const monitorNewTasks = async () => {

    helloWorldServiceManager.on("NewTaskCreated", async (taskIndex: number, task: any) => {
        console.log(`New task detected: Hello, ${task.name}`);
        await signAndRespondToTask(taskIndex, task.taskCreatedBlock, task.name);
    });
    console.log("Monitoring for new tasks...");
};



// Generate Hello, Name message string
const signAndRespondToTask = async (taskIndex: number, taskCreatedBlock: number, taskName: string) => {
    const message = `Hello, ${taskName}`;
    const messageHash = ethers.solidityPackedKeccak256(["string"], [message]);
    const messageBytes = ethers.getBytes(messageHash);
    const signature = await wallet.signMessage(messageBytes);

    console.log(`Signing and responding to task ${taskIndex}`);

    const operators = [await wallet.getAddress()];
    const signatures = [signature];
    const signedTask = ethers.AbiCoder.defaultAbiCoder().encode(
        ["address[]", "bytes[]", "uint32"],
        [operators, signatures, ethers.toBigInt(await provider.getBlockNumber()-1)]
    );

    const tx = await helloWorldServiceManager.respondToTask(
        { name: taskName, taskCreatedBlock: taskCreatedBlock },
        taskIndex,
        signedTask
    );
    await tx.wait();
    console.log(`Responded to task.`);
};

Off-chain Task Generator

createNewTasks.ts

The following Typescript code generates new tasks at a random interval. This entity that generates tasks for the AVS is also referred to as the "AVS Consumer".

// Create a New Task (a new name to be signed as "hello, name")
async function createNewTask(taskName: string) {
  try {
    // Send a transaction to the createNewTask function
    const tx = await helloWorldServiceManager.createNewTask(taskName);
    
    // Wait for the transaction to be mined
    const receipt = await tx.wait();
    
    console.log(`Transaction successful with hash: ${receipt.hash}`);
  } catch (error) {
    console.error('Error sending transaction:', error);
  }
}

Local Deployment Test

Please follow the steps under Local Devnet Deployment to deploy an instance of Hello World locally on your machine.