|
1 | | -# eigen-cli-plugin |
| 1 | +# EigenLayer CLI Plugin |
| 2 | + |
| 3 | +This repository contains a demo plugin for EigenLayer CLI for custom off-chain code execution for AVS registration flows. |
| 4 | + |
| 5 | +## Creating a Minimal Plugin |
| 6 | + |
| 7 | +A minimal plugin for EigenLayer CLI requires a shared library to be built that includes a symbol named `PluginCoordinator` that implement the following interface. |
| 8 | + |
| 9 | +```go |
| 10 | +type Coordinator interface { |
| 11 | + Type() string |
| 12 | + Register() error |
| 13 | + OptIn() error |
| 14 | + OptOut() error |
| 15 | + Deregister() error |
| 16 | + Status() (string, error) |
| 17 | +} |
| 18 | +``` |
| 19 | + |
| 20 | +In the above interface, the `Type()` function should return the type of the coordinator, which can be a non-empty string that identifies the coordinator implementation. |
| 21 | + |
| 22 | +The `Register()`, `OptIn()`, `OptOut()` and `Deregister()` functions are invoked when the plugin is to execute the logic for the corresponding AVS registration workflows. |
| 23 | + |
| 24 | +The `Status()` function is called to query the registration status of an operator for an AVS. |
| 25 | + |
| 26 | +### Create the Project |
| 27 | + |
| 28 | +Create a new directory to hold the project (following example uses `eigenlayer-cli-plugin-demo`). |
| 29 | + |
| 30 | +```bash |
| 31 | +$ mkdir eigenlayer-plugin-demo |
| 32 | +$ cd eigenlayer-plugin-demo |
| 33 | +``` |
| 34 | + |
| 35 | +Initialize the project as a module (following example uses `github.com/eigenlayer/eigenlayer-cli-demo`). |
| 36 | + |
| 37 | +```bash |
| 38 | +$ go mod init github.com/eigenlayer/eigenlayer-cli-demo |
| 39 | +``` |
| 40 | + |
| 41 | +### Add a Coordinator |
| 42 | + |
| 43 | +Create the plugin coordinator implementation (following is added as `coordinator.go`). |
| 44 | + |
| 45 | +```go |
| 46 | +package main |
| 47 | + |
| 48 | +import ( |
| 49 | + "fmt" |
| 50 | +) |
| 51 | + |
| 52 | +type Coordinator struct { |
| 53 | +} |
| 54 | + |
| 55 | +func (coordinator Coordinator) Type() string { |
| 56 | + return "eigenlayer-cli-demo" |
| 57 | +} |
| 58 | + |
| 59 | +func (coordinator Coordinator) Register() error { |
| 60 | + fmt.Println("eigenlayer-cli-demo:register") |
| 61 | + return nil |
| 62 | +} |
| 63 | + |
| 64 | +func (coordinator Coordinator) OptIn() error { |
| 65 | + fmt.Println("eigenlayer-cli-demo:opt-in") |
| 66 | + return nil |
| 67 | +} |
| 68 | + |
| 69 | +func (coordinator Coordinator) OptOut() error { |
| 70 | + fmt.Println("eigenlayer-cli-demo:opt-out") |
| 71 | + return nil |
| 72 | +} |
| 73 | + |
| 74 | +func (coordinator Coordinator) Deregister() error { |
| 75 | + fmt.Println("eigenlayer-cli-demo:deregister") |
| 76 | + return nil |
| 77 | +} |
| 78 | + |
| 79 | +func (coordinator Coordinator) Status() (string, error) { |
| 80 | + fmt.Println("eigenlayer-cli-demo:status") |
| 81 | + return "", nil |
| 82 | +} |
| 83 | + |
| 84 | +var PluginCoordinator Coordinator |
| 85 | +``` |
| 86 | + |
| 87 | +### Build the Plugin |
| 88 | + |
| 89 | +Build the project as a plugin (following example build `eigenlayer-cli-demo.so`). |
| 90 | + |
| 91 | +```bash |
| 92 | + $ go build -buildmode=plugin -o eigenlayer-cli-demo.so |
| 93 | +``` |
| 94 | + |
| 95 | +### Host the Plugin |
| 96 | + |
| 97 | +Host the plugin shared library so that it can be accessible from a public URL. |
| 98 | + |
| 99 | +### Use it in a Specification |
| 100 | + |
| 101 | +The plugin can be used in a specification by setting the specification's `coordinator` and `library_url` attributes. |
| 102 | + |
| 103 | +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`. |
| 104 | + |
| 105 | +```json |
| 106 | +{ |
| 107 | + "name": "eigenlayer-cli-demo", |
| 108 | + "network": "mainnet", |
| 109 | + "contract_address": "0x870679e138bcdf293b7ff14dd44b70fc97e12fc0", |
| 110 | + "coordinator": "plugin", |
| 111 | + "remote_signing": false, |
| 112 | + "library_url": "https://download.eigenlayer.xys/cli/eigenlayer-cli-demo.so" |
| 113 | +} |
| 114 | +``` |
| 115 | + |
| 116 | +## Accessing the Specification |
| 117 | + |
| 118 | +The plugin can access the specification used for launching the coordinator by including a symbol named `PluginSpecification` that implements the following interface. |
| 119 | + |
| 120 | +```go |
| 121 | +type Specification interface { |
| 122 | + Type() string |
| 123 | + Validate() error |
| 124 | +} |
| 125 | +``` |
| 126 | + |
| 127 | +The `Type()` function should return the type of the specification implementation. The `Validate()` function is called after loading the specification in order to check its validity. |
| 128 | + |
| 129 | +Note that this plugin specification can also include custom properties included in the corresponding `avs.json` file. |
| 130 | + |
| 131 | +For example, consider the following `avs.json`. |
| 132 | + |
| 133 | +```json |
| 134 | +{ |
| 135 | + "name": "eigenlayer-cli-demo", |
| 136 | + "description": "Specification for CLI Plugin Demo", |
| 137 | + "network": "mainnet", |
| 138 | + "contract_address": "0x870679e138bcdf293b7ff14dd44b70fc97e12fc0", |
| 139 | + "coordinator": "plugin", |
| 140 | + "remote_signing": false, |
| 141 | + "library_url": "https://download.eigenlayer.xys/cli/eigenlayer-cli-demo.so", |
| 142 | + "foo": "bar" |
| 143 | +} |
| 144 | +``` |
| 145 | + |
| 146 | +The plugin can access the specification as follows. |
| 147 | + |
| 148 | +```go |
| 149 | +package main |
| 150 | + |
| 151 | +import ( |
| 152 | + "errors" |
| 153 | +) |
| 154 | + |
| 155 | +type Specification struct { |
| 156 | + Name string `json:"name"` |
| 157 | + Description string `json:"description"` |
| 158 | + Network string `json:"network"` |
| 159 | + ContractAddress string `json:"contract_address"` |
| 160 | + Coordinator string `json:"coordinator"` |
| 161 | + RemoteSigning bool `json:"remote_signing"` |
| 162 | + LibraryURL string `json:"library_url"` |
| 163 | + Foo string `json:"foo"` |
| 164 | +} |
| 165 | + |
| 166 | +func (spec Specification) Type() string { |
| 167 | + return spec.Coordinator |
| 168 | +} |
| 169 | + |
| 170 | +func (spec Specification) Validate() error { |
| 171 | + if spec.Foo == "" { |
| 172 | + return errors.New("specification: foo is required") |
| 173 | + } |
| 174 | + |
| 175 | + return nil |
| 176 | +} |
| 177 | + |
| 178 | +var PluginSpecification Specification |
| 179 | +``` |
| 180 | + |
| 181 | +## Accessing Configuration Parameters |
| 182 | + |
| 183 | +The plugin can access the configuration parameters used when workflows are invoked by including a symbol named `PluginConfiguration` that implements the following interface. |
| 184 | + |
| 185 | +```go |
| 186 | +type Configuration interface { |
| 187 | + Get(key string) interface{} |
| 188 | + Set(key string, value interface{}) |
| 189 | +} |
| 190 | +``` |
| 191 | + |
| 192 | +The following is a full example. |
| 193 | + |
| 194 | +```go |
| 195 | +package main |
| 196 | + |
| 197 | +type Configuration struct { |
| 198 | + registry map[string]interface{} |
| 199 | +} |
| 200 | + |
| 201 | +func (config Configuration) Get(key string) interface{} { |
| 202 | + return config.registry[key] |
| 203 | +} |
| 204 | + |
| 205 | +func (config Configuration) Set(key string, value interface{}) { |
| 206 | + config.registry[key] = value |
| 207 | +} |
| 208 | + |
| 209 | +var PluginConfiguration Configuration |
| 210 | +``` |
0 commit comments