This repository contains a demo plugin for EigenLayer CLI for custom off-chain code execution for AVS registration flows.
A minimal plugin for EigenLayer CLI requires a shared library to be built that includes a symbol named PluginCoordinator
that implement the following interface.
type Coordinator interface {
Register() error
OptIn() error
OptOut() error
Deregister() error
Status() (string, error)
}
The Register()
, OptIn()
, OptOut()
and Deregister()
functions are invoked when the plugin is to execute the logic for the corresponding AVS registration workflows.
The Status()
function is called to query the registration status of an operator for an AVS.
Create a new directory to hold the project (following example uses eigenlayer-cli-plugin-demo
).
$ mkdir eigenlayer-plugin-demo
$ cd eigenlayer-plugin-demo
Initialize the project as a module (following example uses github.com/eigenlayer/eigenlayer-cli-demo
).
$ go mod init github.com/eigenlayer/eigenlayer-cli-demo
Create the plugin coordinator implementation (following is added as coordinator.go
).
package main
import (
"fmt"
)
type Coordinator struct {
}
func (coordinator Coordinator) Register() error {
fmt.Println("eigenlayer-cli-demo:register")
return nil
}
func (coordinator Coordinator) OptIn() error {
fmt.Println("eigenlayer-cli-demo:opt-in")
return nil
}
func (coordinator Coordinator) OptOut() error {
fmt.Println("eigenlayer-cli-demo:opt-out")
return nil
}
func (coordinator Coordinator) Deregister() error {
fmt.Println("eigenlayer-cli-demo:deregister")
return nil
}
func (coordinator Coordinator) Status() (int, error) {
fmt.Println("eigenlayer-cli-demo:status")
return 0, nil
}
var PluginCoordinator Coordinator
Build the project as a plugin (following example build eigenlayer-cli-demo.so
).
$ go build -buildmode=plugin -o eigenlayer-cli-demo.so
Host the plugin shared library so that it can be accessible from a public URL.
The plugin can be used in a specification by setting the specification's coordinator
and library_url
attributes.
The following example shows an avs.json
of a specification that uses a plugin hosted at https://download.eigenlayer.xys/cli/eigenlayer-cli-demo.so
.
{
"name": "eigenlayer-cli-demo",
"network": "mainnet",
"contract_address": "0x870679e138bcdf293b7ff14dd44b70fc97e12fc0",
"coordinator": "plugin",
"remote_signing": false,
"library_url": "https://download.eigenlayer.xys/cli/eigenlayer-cli-demo.so"
}
The plugin can access the specification used for launching the coordinator by including a symbol named PluginSpecification
that implements the following interface.
type Specification interface {
Validate() error
}
The Validate()
function is called after loading the specification in order to check its validity.
Note that this plugin specification can also include custom properties included in the corresponding avs.json
file.
For example, consider the following avs.json
.
{
"name": "eigenlayer-cli-demo",
"description": "Specification for CLI Plugin Demo",
"network": "mainnet",
"contract_address": "0x870679e138bcdf293b7ff14dd44b70fc97e12fc0",
"coordinator": "plugin",
"remote_signing": false,
"library_url": "https://download.eigenlayer.xys/cli/eigenlayer-cli-demo.so",
"foo": "bar"
}
The plugin can access the specification as follows.
package main
import (
"errors"
)
type Specification struct {
Name string `json:"name"`
Description string `json:"description"`
Network string `json:"network"`
ContractAddress string `json:"contract_address"`
Coordinator string `json:"coordinator"`
RemoteSigning bool `json:"remote_signing"`
LibraryURL string `json:"library_url"`
Foo string `json:"foo"`
}
func (spec Specification) Validate() error {
if spec.Foo == "" {
return errors.New("specification: foo is required")
}
return nil
}
var PluginSpecification Specification
The plugin can access the configuration parameters used when workflows are invoked by including a symbol named PluginConfiguration
that implements the following interface.
type Configuration interface {
Set(key string, value interface{})
}
The following is a full example.
package main
type Configuration struct {
registry map[string]interface{}
}
func (config Configuration) Set(key string, value interface{}) {
config.registry[key] = value
}
var PluginConfiguration Configuration