@steerprotocol/curator-tools 1.2.0 → 1.3.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,130 @@ 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
+ - **Schema revocable**: `true`
62
+ - **Schema UID**: (run `script/RegisterSteerCrosschainSchema.s.sol` to print the UID)
63
+
64
+ ## User Flows
65
+
66
+ ### Admin
67
+ 1. Deploy `SteerAuthorityResolver`.
68
+ 2. Authorize/revoke curators per vault.
69
+ 3. Migrate admin control to a new owner (EOA or multisig) using the 2-step ownership flow.
70
+
71
+ ### Curator
72
+ 1. Create an EAS attestation for a vault override.
73
+ 2. Resolver validates curator authorization for the target vault.
74
+ 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.
75
+
76
+ ### Integrator (frontend / indexer / backend)
77
+ 1. Read curator authorization per vault.
78
+ 2. (Optional) Validate vault addresses via `VaultRegistry` before trusting overrides.
79
+ 3. Build admin UI flows by generating `to`/`data` transactions via `StrategyStore`.
80
+
43
81
  ## Usage
44
82
 
83
+ ### Deploying the Resolver (Foundry)
84
+ Deploys on Arbitrum One using the official Arbitrum One EAS address hardcoded in `script/DeploySteerAuthorityResolver.s.sol`.
85
+
86
+ ```bash
87
+ export PRIVATE_KEY=<DEPLOYER_PRIVATE_KEY>
88
+ export RPC_URL=<ARBITRUM_ONE_RPC_URL>
89
+
90
+ forge script script/DeploySteerAuthorityResolver.s.sol:DeploySteerAuthorityResolver --rpc-url "$RPC_URL" --broadcast
91
+ ```
92
+
93
+ Broadcast results (including deployed address) are written under `broadcast/`.
94
+
95
+ ### Registering the Cross-Chain Schema (Foundry)
96
+ Registers a new schema on Arbitrum One’s SchemaRegistry with `SteerAuthorityResolver` as the resolver.
97
+
98
+ ```bash
99
+ export PRIVATE_KEY=<REGISTERER_PRIVATE_KEY>
100
+ export RPC_URL=<ARBITRUM_ONE_RPC_URL>
101
+ export RESOLVER_ADDRESS=<DEPLOYED_RESOLVER>
102
+
103
+ forge script script/RegisterSteerCrosschainSchema.s.sol:RegisterSteerCrosschainSchema --rpc-url "$RPC_URL" --broadcast
104
+ ```
105
+
106
+ **Encoding (Solidity)**
107
+ ```solidity
108
+ bytes memory data = abi.encode(vault, targetChainId, strategyTokenId, manifestCid);
109
+ ```
110
+
111
+ **Encoding (TypeScript)**
112
+ ```ts
113
+ const data = StrategyStore.encodeOverrideAttestationData(vault, targetChainId, 1n, manifestCid);
114
+ ```
115
+
45
116
  ### Managing Curators via CLI
46
- To authorize a curator for a vault, use the `ManageCurators` script:
117
+ Authorize/revoke a curator for a specific vault.
47
118
 
48
119
  ```bash
49
- export RESOLVER_ADDRESS=<DEPLYOED_RESOLVER>
120
+ export RESOLVER_ADDRESS=<DEPLOYED_RESOLVER>
50
121
  export VAULT=<VAULT_ADDRESS>
51
122
  export CURATOR=<CURATOR_ADDRESS>
52
123
  export STATUS=true # true to authorize, false to revoke
53
124
  export PRIVATE_KEY=<ADMIN_PRIVATE_KEY>
125
+ export RPC_URL=<ARBITRUM_ONE_RPC_URL>
126
+
127
+ forge script script/ManageCurators.s.sol:ManageCurators --rpc-url "$RPC_URL" --broadcast
128
+ ```
129
+
130
+ ### Migrating Admin Control (Ownership)
131
+ Admin migration is a 2-step flow because the resolver uses `Ownable2Step`.
132
+
133
+ **Step 1: Initiate transfer (current admin)**
134
+ ```bash
135
+ export RESOLVER_ADDRESS=<DEPLOYED_RESOLVER>
136
+ export NEW_OWNER=<NEW_ADMIN_ADDRESS>
137
+ export PRIVATE_KEY=<CURRENT_ADMIN_PRIVATE_KEY>
138
+ export RPC_URL=<ARBITRUM_ONE_RPC_URL>
54
139
 
55
- forge script script/ManageCurators.s.sol --rpc-url <RPC_URL> --broadcast
140
+ forge script script/MigrateAdminControl.s.sol:MigrateAdminControl --rpc-url "$RPC_URL" --broadcast
141
+ ```
142
+
143
+ **Step 2: Accept ownership (new admin)**
144
+ ```bash
145
+ export RESOLVER_ADDRESS=<DEPLOYED_RESOLVER>
146
+ export ONLY_ACCEPT=true
147
+ export NEW_OWNER_PRIVATE_KEY=<NEW_ADMIN_PRIVATE_KEY>
148
+ export RPC_URL=<ARBITRUM_ONE_RPC_URL>
149
+
150
+ forge script script/MigrateAdminControl.s.sol:MigrateAdminControl --rpc-url "$RPC_URL" --broadcast
151
+ ```
152
+
153
+ ### Verifying Admin Migration
154
+ Use `cast` to confirm `owner()` and `pendingOwner()` on-chain.
155
+
156
+ ```bash
157
+ export RESOLVER_ADDRESS=<DEPLOYED_RESOLVER>
158
+ export RPC_URL=<ARBITRUM_ONE_RPC_URL>
159
+
160
+ cast call "$RESOLVER_ADDRESS" "owner()(address)" --rpc-url "$RPC_URL"
161
+ cast call "$RESOLVER_ADDRESS" "pendingOwner()(address)" --rpc-url "$RPC_URL"
56
162
  ```
57
163
 
58
164
  ### 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`.
165
+ The `@steerprotocol/curator-tools` client (provided by the `StrategyStore` class) provides helpers to interact with the authority layer.
60
166
 
61
- #### Basic Initialization
62
167
  ```typescript
63
168
  import { StrategyStore } from '@steerprotocol/curator-tools';
64
169
  import { ethers } from 'ethers';
@@ -68,20 +173,35 @@ const provider = new ethers.JsonRpcProvider(RPC_URL);
68
173
  // Automatic resolution for supported networks (e.g., Arbitrum: 42161)
69
174
  const store = new StrategyStore({ chainId: 42161 }, provider);
70
175
 
71
- // OR explicit address
72
- const store = new StrategyStore({
176
+ // OR explicit addresses
177
+ const store2 = new StrategyStore(
178
+ {
73
179
  resolverAddress: '0x...',
74
- registryAddress: '0x...' // Optional: Enables strict vault validation
75
- }, provider);
76
- ```
180
+ registryAddress: '0x...', // Optional: Enables strict vault validation
181
+ },
182
+ provider
183
+ );
77
184
 
78
- #### Verification & Transactions
79
- ```typescript
80
185
  // Check if a curator is authorized (includes strict vault validation if registry is set)
81
- const authorized = await store.isCurator(vaultAddress, curatorAddress);
186
+ const authorized = await store2.isCurator(vaultAddress, curatorAddress);
187
+
188
+ // Generate tx data for an admin dashboard
189
+ const tx = await store2.registerCuratorTx(vaultAddress, curatorAddress, true);
190
+ ```
82
191
 
83
- // Generate transaction data for the admin dashboard (async due to registry validation)
84
- const tx = await store.registerCuratorTx(vaultAddress, curatorAddress, true);
192
+ ### Contract Verification on Arbiscan (optional)
193
+ If you want to verify the deployed resolver source, you can use `forge verify-contract`.
194
+
195
+ ```bash
196
+ export ARBISCAN_API_KEY=<API_KEY>
197
+
198
+ forge verify-contract \
199
+ --chain-id 42161 \
200
+ --watch \
201
+ --etherscan-api-key "$ARBISCAN_API_KEY" \
202
+ <DEPLOYED_RESOLVER> \
203
+ src/SteerAuthorityResolver.sol:SteerAuthorityResolver \
204
+ --constructor-args $(cast abi-encode "constructor(address)" 0xbD75f629A22Dc1ceD33dDA0b68c546A1c035c458)
85
205
  ```
86
206
 
87
207
  ## CI/CD Pipeline
@@ -100,5 +220,5 @@ Automated versioning and NPM publishing are handled via **Semantic Release**.
100
220
 
101
221
  ## Security
102
222
  - 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.
223
+ - Resolver validation enforces curator authorization per vault; the payload includes a `targetChainId` for cross-chain overrides stored on Arbitrum One.
224
+ - 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.3.0",
4
4
  "description": "Steer Protocol Curator Override Tools",
5
5
  "publishConfig": {
6
6
  "access": "public"