@steerprotocol/curator-tools 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,7 +5,7 @@ This project implements a centralized "Top-Level Authority" for the EAS (Ethereu
5
5
  ## Components
6
6
 
7
7
  ### Smart Contracts
8
- - **SteerAuthorityResolver.sol**: An EAS Schema Resolver that enforces curator permissions. It validates that the attester is authorized for the specified vault and that the attestation is made on the correct network.
8
+ - **SteerAuthorityResolver.sol**: An EAS Schema Resolver that enforces curator permissions. It validates that the attester is authorized for the specified vault; the attestation payload includes a `targetChainId` for cross-chain overrides stored canonically on Arbitrum One.
9
9
  - **Access Control**: Uses OpenZeppelin's `Ownable2Step` for secure management of the top-level authority role.
10
10
 
11
11
  ### Management Tooling
@@ -40,25 +40,132 @@ This project implements a centralized "Top-Level Authority" for the EAS (Ethereu
40
40
  npm test
41
41
  ```
42
42
 
43
+ ## Features
44
+ - **Curator gating (per vault)**: Only admins can authorize/revoke curators per vault via `setCurator(vault, curator, status)`.
45
+ - **Cross-chain overrides (canonical on Arbitrum One)**: The payload includes a `targetChainId` (encoded in the `chainId` field) so overrides for many chains can be stored on Arbitrum One.
46
+ - **Two-step admin transfers**: Uses OpenZeppelin `Ownable2Step` (`transferOwnership` + `acceptOwnership`) to reduce lockout risk.
47
+ - **Operational scripting**: Foundry scripts for deployment, curator management, and admin migration.
48
+ - **TypeScript client** (`StrategyStore`):
49
+ - Reads admin + curator status (`isAdmin`, `isCurator`).
50
+ - Encodes admin transactions (`registerCuratorTx`) for dashboards.
51
+ - Optional strict vault validation via `VaultRegistry.getVaultDetails(...)`.
52
+ - Address resolution via `@steerprotocol/sdk` with a temporary Arbitrum resolver override.
53
+
54
+ ## EAS Schema (Option B: Cross-Chain Overrides)
55
+
56
+ Canonical overrides are stored on **Arbitrum One**, and the payload’s `chainId` field is treated as a `targetChainId`.
57
+
58
+ - **Schema string**: `address vault,uint256 chainId,uint256 strategyTokenId,string manifestCid`
59
+ - **Semantics**: `chainId` is `targetChainId` (it may differ from `block.chainid`)
60
+ - **SchemaRegistry (Arbitrum One)**: `0xA310da9c5B885E7fb3fbA9D66E9Ba6Df512b78eB`
61
+ - **Resolver (Arbitrum One)**: `0xD36E3f33c6f1814F6923835Ae7dC508FEDA14b62`
62
+ - **Schema revocable**: `true`
63
+ - **Schema UID**: `0x2a8ed2dea14b650384d87e1a9fdcd56ab7489fac437134f594f518d9538cbab9`
64
+ - **Deprecated schema (no resolver)**: `0x62b656756a16bd3d2ef501cd9493c603fd90b050d6e9cfffc8e450639ce30a27` (registered with resolver `address(0)`, so it enforces no on-chain curator checks)
65
+
66
+ ## User Flows
67
+
68
+ ### Admin
69
+ 1. Deploy `SteerAuthorityResolver`.
70
+ 2. Authorize/revoke curators per vault.
71
+ 3. Migrate admin control to a new owner (EOA or multisig) using the 2-step ownership flow.
72
+
73
+ ### Curator
74
+ 1. Create an EAS attestation for a vault override.
75
+ 2. Resolver validates curator authorization for the target vault.
76
+ 3. If valid, the attestation is recorded by EAS. The payload includes a `targetChainId` so indexers/integrators can interpret which chain the override applies to.
77
+
78
+ ### Integrator (frontend / indexer / backend)
79
+ 1. Read curator authorization per vault.
80
+ 2. (Optional) Validate vault addresses via `VaultRegistry` before trusting overrides.
81
+ 3. Build admin UI flows by generating `to`/`data` transactions via `StrategyStore`.
82
+
43
83
  ## Usage
44
84
 
85
+ ### Deploying the Resolver (Foundry)
86
+ Deploys on Arbitrum One using the official Arbitrum One EAS address hardcoded in `script/DeploySteerAuthorityResolver.s.sol`.
87
+
88
+ ```bash
89
+ export PRIVATE_KEY=<DEPLOYER_PRIVATE_KEY>
90
+ export RPC_URL=<ARBITRUM_ONE_RPC_URL>
91
+
92
+ forge script script/DeploySteerAuthorityResolver.s.sol:DeploySteerAuthorityResolver --rpc-url "$RPC_URL" --broadcast
93
+ ```
94
+
95
+ Broadcast results (including deployed address) are written under `broadcast/`.
96
+
97
+ ### Registering the Cross-Chain Schema (Foundry)
98
+ Registers a new schema on Arbitrum One’s SchemaRegistry with `SteerAuthorityResolver` as the resolver.
99
+
100
+ ```bash
101
+ export PRIVATE_KEY=<REGISTERER_PRIVATE_KEY>
102
+ export RPC_URL=<ARBITRUM_ONE_RPC_URL>
103
+ export RESOLVER_ADDRESS=<DEPLOYED_RESOLVER>
104
+
105
+ forge script script/RegisterSteerCrosschainSchema.s.sol:RegisterSteerCrosschainSchema --rpc-url "$RPC_URL" --broadcast
106
+ ```
107
+
108
+ **Encoding (Solidity)**
109
+ ```solidity
110
+ bytes memory data = abi.encode(vault, targetChainId, strategyTokenId, manifestCid);
111
+ ```
112
+
113
+ **Encoding (TypeScript)**
114
+ ```ts
115
+ const data = StrategyStore.encodeOverrideAttestationData(vault, targetChainId, 1n, manifestCid);
116
+ ```
117
+
45
118
  ### Managing Curators via CLI
46
- To authorize a curator for a vault, use the `ManageCurators` script:
119
+ Authorize/revoke a curator for a specific vault.
47
120
 
48
121
  ```bash
49
- export RESOLVER_ADDRESS=<DEPLYOED_RESOLVER>
122
+ export RESOLVER_ADDRESS=<DEPLOYED_RESOLVER>
50
123
  export VAULT=<VAULT_ADDRESS>
51
124
  export CURATOR=<CURATOR_ADDRESS>
52
125
  export STATUS=true # true to authorize, false to revoke
53
126
  export PRIVATE_KEY=<ADMIN_PRIVATE_KEY>
127
+ export RPC_URL=<ARBITRUM_ONE_RPC_URL>
128
+
129
+ forge script script/ManageCurators.s.sol:ManageCurators --rpc-url "$RPC_URL" --broadcast
130
+ ```
54
131
 
55
- forge script script/ManageCurators.s.sol --rpc-url <RPC_URL> --broadcast
132
+ ### Migrating Admin Control (Ownership)
133
+ Admin migration is a 2-step flow because the resolver uses `Ownable2Step`.
134
+
135
+ **Step 1: Initiate transfer (current admin)**
136
+ ```bash
137
+ export RESOLVER_ADDRESS=<DEPLOYED_RESOLVER>
138
+ export NEW_OWNER=<NEW_ADMIN_ADDRESS>
139
+ export PRIVATE_KEY=<CURRENT_ADMIN_PRIVATE_KEY>
140
+ export RPC_URL=<ARBITRUM_ONE_RPC_URL>
141
+
142
+ forge script script/MigrateAdminControl.s.sol:MigrateAdminControl --rpc-url "$RPC_URL" --broadcast
143
+ ```
144
+
145
+ **Step 2: Accept ownership (new admin)**
146
+ ```bash
147
+ export RESOLVER_ADDRESS=<DEPLOYED_RESOLVER>
148
+ export ONLY_ACCEPT=true
149
+ export NEW_OWNER_PRIVATE_KEY=<NEW_ADMIN_PRIVATE_KEY>
150
+ export RPC_URL=<ARBITRUM_ONE_RPC_URL>
151
+
152
+ forge script script/MigrateAdminControl.s.sol:MigrateAdminControl --rpc-url "$RPC_URL" --broadcast
153
+ ```
154
+
155
+ ### Verifying Admin Migration
156
+ Use `cast` to confirm `owner()` and `pendingOwner()` on-chain.
157
+
158
+ ```bash
159
+ export RESOLVER_ADDRESS=<DEPLOYED_RESOLVER>
160
+ export RPC_URL=<ARBITRUM_ONE_RPC_URL>
161
+
162
+ cast call "$RESOLVER_ADDRESS" "owner()(address)" --rpc-url "$RPC_URL"
163
+ cast call "$RESOLVER_ADDRESS" "pendingOwner()(address)" --rpc-url "$RPC_URL"
56
164
  ```
57
165
 
58
166
  ### Using the TypeScript Client
59
- The `@steerprotocol/curator-tools` client (provided by the `StrategyStore` class) provides helpers to interact with the authority layer. It supports automatic address resolution via the `@steerprotocol/sdk`.
167
+ The `@steerprotocol/curator-tools` client (provided by the `StrategyStore` class) provides helpers to interact with the authority layer.
60
168
 
61
- #### Basic Initialization
62
169
  ```typescript
63
170
  import { StrategyStore } from '@steerprotocol/curator-tools';
64
171
  import { ethers } from 'ethers';
@@ -68,20 +175,35 @@ const provider = new ethers.JsonRpcProvider(RPC_URL);
68
175
  // Automatic resolution for supported networks (e.g., Arbitrum: 42161)
69
176
  const store = new StrategyStore({ chainId: 42161 }, provider);
70
177
 
71
- // OR explicit address
72
- const store = new StrategyStore({
178
+ // OR explicit addresses
179
+ const store2 = new StrategyStore(
180
+ {
73
181
  resolverAddress: '0x...',
74
- registryAddress: '0x...' // Optional: Enables strict vault validation
75
- }, provider);
76
- ```
182
+ registryAddress: '0x...', // Optional: Enables strict vault validation
183
+ },
184
+ provider
185
+ );
77
186
 
78
- #### Verification & Transactions
79
- ```typescript
80
187
  // Check if a curator is authorized (includes strict vault validation if registry is set)
81
- const authorized = await store.isCurator(vaultAddress, curatorAddress);
188
+ const authorized = await store2.isCurator(vaultAddress, curatorAddress);
82
189
 
83
- // Generate transaction data for the admin dashboard (async due to registry validation)
84
- const tx = await store.registerCuratorTx(vaultAddress, curatorAddress, true);
190
+ // Generate tx data for an admin dashboard
191
+ const tx = await store2.registerCuratorTx(vaultAddress, curatorAddress, true);
192
+ ```
193
+
194
+ ### Contract Verification on Arbiscan (optional)
195
+ If you want to verify the deployed resolver source, you can use `forge verify-contract`.
196
+
197
+ ```bash
198
+ export ARBISCAN_API_KEY=<API_KEY>
199
+
200
+ forge verify-contract \
201
+ --chain-id 42161 \
202
+ --watch \
203
+ --etherscan-api-key "$ARBISCAN_API_KEY" \
204
+ <DEPLOYED_RESOLVER> \
205
+ src/SteerAuthorityResolver.sol:SteerAuthorityResolver \
206
+ --constructor-args $(cast abi-encode "constructor(address)" 0xbD75f629A22Dc1ceD33dDA0b68c546A1c035c458)
85
207
  ```
86
208
 
87
209
  ## CI/CD Pipeline
@@ -100,5 +222,6 @@ Automated versioning and NPM publishing are handled via **Semantic Release**.
100
222
 
101
223
  ## Security
102
224
  - All administrative actions are restricted to the `owner`.
103
- - Attestations are strictly validated against `block.chainid` to prevent replay attacks.
104
- - Input validation is enforced on both smart contract and client-side levels.
225
+ - Resolver validation enforces curator authorization per vault on both attestation and revocation (removing a curator prevents them from attesting *and* revoking prior attestations).
226
+ - The payload includes a `targetChainId` for cross-chain overrides stored on Arbitrum One.
227
+ - Input validation is enforced on both smart contract and client-side levels.
package/dist/index.d.mts CHANGED
@@ -6,6 +6,9 @@ interface StrategyStoreConfig {
6
6
  registryAddress?: string;
7
7
  }
8
8
  declare class StrategyStore {
9
+ static readonly ARBITRUM_ONE_RESOLVER_ADDRESS = "0xD36E3f33c6f1814F6923835Ae7dC508FEDA14b62";
10
+ static readonly ARBITRUM_ONE_CROSSCHAIN_SCHEMA_UID = "0x2a8ed2dea14b650384d87e1a9fdcd56ab7489fac437134f594f518d9538cbab9";
11
+ static encodeOverrideAttestationData(vault: string, targetChainId: number | bigint, strategyTokenId: bigint, manifestCid: string): string;
9
12
  private resolver;
10
13
  private registry?;
11
14
  private resolverAddress;
package/dist/index.mjs CHANGED
@@ -14,7 +14,16 @@ var REGISTRY_ABI = [
14
14
  "function totalVaultCount() view returns (uint256)",
15
15
  "function getVaultDetails(address _address) view returns (tuple(uint8 state, uint256 tokenId, uint256 vaultID, string payloadIpfs, address vaultAddress, string beaconName))"
16
16
  ];
17
- var StrategyStore = class {
17
+ var StrategyStore = class _StrategyStore {
18
+ static ARBITRUM_ONE_RESOLVER_ADDRESS = "0xD36E3f33c6f1814F6923835Ae7dC508FEDA14b62";
19
+ static ARBITRUM_ONE_CROSSCHAIN_SCHEMA_UID = "0x2a8ed2dea14b650384d87e1a9fdcd56ab7489fac437134f594f518d9538cbab9";
20
+ static encodeOverrideAttestationData(vault, targetChainId, strategyTokenId, manifestCid) {
21
+ const validatedVault = AddressSchema.parse(vault);
22
+ return ethers.AbiCoder.defaultAbiCoder().encode(
23
+ ["address", "uint256", "uint256", "string"],
24
+ [validatedVault, targetChainId, strategyTokenId, manifestCid]
25
+ );
26
+ }
18
27
  resolver;
19
28
  registry;
20
29
  resolverAddress;
@@ -29,7 +38,7 @@ var StrategyStore = class {
29
38
  regAddr = config.registryAddress;
30
39
  if (!resAddr && config.chainId) {
31
40
  if (config.chainId === 42161) {
32
- resAddr = "0x24847D7EF3D3AEC8cA28F6CAb723eF83E708966A";
41
+ resAddr = _StrategyStore.ARBITRUM_ONE_RESOLVER_ADDRESS;
33
42
  } else {
34
43
  resAddr = getContractAddressByChainIdAndContractName(config.chainId, "SteerAuthorityResolver");
35
44
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@steerprotocol/curator-tools",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "Steer Protocol Curator Override Tools",
5
5
  "publishConfig": {
6
6
  "access": "public"