@steerprotocol/curator-tools 1.7.0 → 1.8.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 +37 -4
- package/dist/index.d.mts +87 -4
- package/dist/index.mjs +374 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -61,7 +61,7 @@ Canonical overrides are stored on **Arbitrum One**, and the payload’s `chainId
|
|
|
61
61
|
- **Resolver (Arbitrum One)**: `0x6628d1Bf82F34de132d0e1c60DEB574C1352d5A7`
|
|
62
62
|
- **Schema revocable**: `true`
|
|
63
63
|
- **Schema UID**: `0xf1a6a394d1f3dd832a15e5b750963d929812e2b74d2dcea68ddf9f75691f16bf`
|
|
64
|
-
- **Deprecated schema (no resolver)**: `0x62b656756a16bd3d2ef501cd9493c603fd90b050d6e9cfffc8e450639ce30a27` (registered with resolver `address(0)
|
|
64
|
+
- **Deprecated testing-only schema (no resolver guards)**: `0x62b656756a16bd3d2ef501cd9493c603fd90b050d6e9cfffc8e450639ce30a27` (registered with resolver `address(0)`)
|
|
65
65
|
|
|
66
66
|
## User Flows
|
|
67
67
|
|
|
@@ -115,6 +115,20 @@ bytes memory data = abi.encode(vault, targetChainId, strategyTokenId, manifestCi
|
|
|
115
115
|
const data = StrategyStore.encodeOverrideAttestationData(vault, targetChainId, 1n, manifestCid);
|
|
116
116
|
```
|
|
117
117
|
|
|
118
|
+
**Full EAS request defaults (TypeScript)**
|
|
119
|
+
```ts
|
|
120
|
+
const request = StrategyStore.buildOverrideAttestationRequest(
|
|
121
|
+
schemaUID,
|
|
122
|
+
vault,
|
|
123
|
+
targetChainId,
|
|
124
|
+
1n,
|
|
125
|
+
manifestCid
|
|
126
|
+
);
|
|
127
|
+
// request.data.recipient = computeVaultIdentifier(vault, targetChainId)
|
|
128
|
+
// request.data.revocable = false
|
|
129
|
+
// request.data.expirationTime = 0n
|
|
130
|
+
```
|
|
131
|
+
|
|
118
132
|
### Managing Curators via CLI
|
|
119
133
|
Authorize/revoke a curator for a specific vault.
|
|
120
134
|
|
|
@@ -172,15 +186,15 @@ import { ethers } from 'ethers';
|
|
|
172
186
|
|
|
173
187
|
const provider = new ethers.JsonRpcProvider(RPC_URL);
|
|
174
188
|
|
|
175
|
-
// Automatic resolution
|
|
176
|
-
const store = new StrategyStore({
|
|
189
|
+
// Automatic resolution defaults to Base mainnet (8453)
|
|
190
|
+
const store = new StrategyStore({}, provider);
|
|
177
191
|
|
|
178
192
|
// OR explicit addresses
|
|
179
193
|
const store2 = new StrategyStore(
|
|
180
194
|
{
|
|
181
195
|
resolverAddress: '0x...',
|
|
182
196
|
registryAddress: '0x...', // Optional: Enables strict vault validation
|
|
183
|
-
chainId:
|
|
197
|
+
chainId: 8453, // Optional; defaults to Base when omitted
|
|
184
198
|
},
|
|
185
199
|
provider
|
|
186
200
|
);
|
|
@@ -190,6 +204,25 @@ const authorized = await store2.isCurator(vaultAddress, curatorAddress);
|
|
|
190
204
|
|
|
191
205
|
// Generate tx data for an admin dashboard
|
|
192
206
|
const tx = await store2.registerCuratorTx(vaultAddress, curatorAddress, true);
|
|
207
|
+
|
|
208
|
+
// Curator flow: check auth, build request, and submit EAS attestation
|
|
209
|
+
const submit = await store2.submitOverrideAttestation(
|
|
210
|
+
vaultAddress,
|
|
211
|
+
targetChainId,
|
|
212
|
+
1n,
|
|
213
|
+
manifestCid
|
|
214
|
+
);
|
|
215
|
+
console.log(submit.txHash, submit.uid);
|
|
216
|
+
|
|
217
|
+
// Read full decoded history for a vault+targetChain
|
|
218
|
+
const history = await store2.getOverrideAttestationHistory(vaultAddress, targetChainId, {
|
|
219
|
+
graphqlEndpoint: 'https://base.easscan.org/graphql',
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// Read latest active attestation (not revoked, not expired)
|
|
223
|
+
const latest = await store2.getLatestOverrideAttestation(vaultAddress, targetChainId, {
|
|
224
|
+
graphqlEndpoint: 'https://base.easscan.org/graphql',
|
|
225
|
+
});
|
|
193
226
|
```
|
|
194
227
|
|
|
195
228
|
### Contract Verification on Arbiscan (optional)
|
package/dist/index.d.mts
CHANGED
|
@@ -42,20 +42,84 @@ interface StrategyStoreConfig {
|
|
|
42
42
|
resolverAddress?: string;
|
|
43
43
|
registryAddress?: string;
|
|
44
44
|
}
|
|
45
|
+
interface EASAttestationRequestData {
|
|
46
|
+
recipient: Address;
|
|
47
|
+
expirationTime: bigint;
|
|
48
|
+
revocable: boolean;
|
|
49
|
+
refUID: string;
|
|
50
|
+
data: string;
|
|
51
|
+
value: bigint;
|
|
52
|
+
}
|
|
53
|
+
interface EASAttestationRequest {
|
|
54
|
+
schema: string;
|
|
55
|
+
data: EASAttestationRequestData;
|
|
56
|
+
}
|
|
57
|
+
interface SubmitOverrideAttestationOptions {
|
|
58
|
+
schemaUID?: string;
|
|
59
|
+
easAddress?: string;
|
|
60
|
+
refUID?: string;
|
|
61
|
+
skipCuratorCheck?: boolean;
|
|
62
|
+
easContract?: ethers.Contract;
|
|
63
|
+
}
|
|
64
|
+
interface SubmitOverrideAttestationResult {
|
|
65
|
+
request: EASAttestationRequest;
|
|
66
|
+
txHash: string;
|
|
67
|
+
uid?: string;
|
|
68
|
+
}
|
|
69
|
+
interface GetOverrideAttestationsOptions {
|
|
70
|
+
schemaUID?: string;
|
|
71
|
+
graphqlEndpoint?: string;
|
|
72
|
+
strategyTokenId?: bigint;
|
|
73
|
+
includeRevoked?: boolean;
|
|
74
|
+
includeExpired?: boolean;
|
|
75
|
+
pageSize?: number;
|
|
76
|
+
maxPages?: number;
|
|
77
|
+
timeoutMs?: number;
|
|
78
|
+
retries?: number;
|
|
79
|
+
fetchFn?: typeof fetch;
|
|
80
|
+
}
|
|
81
|
+
interface OverrideAttestationRecord {
|
|
82
|
+
uid: string;
|
|
83
|
+
attester: Address;
|
|
84
|
+
recipient: Address;
|
|
85
|
+
refUID: string;
|
|
86
|
+
revocable: boolean;
|
|
87
|
+
revocationTime: bigint;
|
|
88
|
+
expirationTime: bigint;
|
|
89
|
+
createdAt: bigint;
|
|
90
|
+
data: string;
|
|
91
|
+
vault: Address;
|
|
92
|
+
targetChainId: bigint;
|
|
93
|
+
strategyTokenId: bigint;
|
|
94
|
+
manifestCid: string;
|
|
95
|
+
isActive: boolean;
|
|
96
|
+
}
|
|
45
97
|
declare class StrategyStore {
|
|
46
|
-
static readonly
|
|
47
|
-
static readonly
|
|
98
|
+
static readonly BASE_MAINNET_EAS_ADDRESS: Address;
|
|
99
|
+
static readonly BASE_MAINNET_RESOLVER_ADDRESS: Address;
|
|
100
|
+
static readonly BASE_MAINNET_CROSSCHAIN_SCHEMA_UID: string;
|
|
101
|
+
/** @deprecated Use BASE_MAINNET_EAS_ADDRESS */
|
|
102
|
+
static readonly ARBITRUM_ONE_EAS_ADDRESS: `0x${string}`;
|
|
103
|
+
/** @deprecated Use BASE_MAINNET_RESOLVER_ADDRESS */
|
|
104
|
+
static readonly ARBITRUM_ONE_RESOLVER_ADDRESS: `0x${string}`;
|
|
105
|
+
/** @deprecated Use BASE_MAINNET_CROSSCHAIN_SCHEMA_UID */
|
|
106
|
+
static readonly ARBITRUM_ONE_CROSSCHAIN_SCHEMA_UID: string;
|
|
48
107
|
static encodeOverrideAttestationData(vault: string, targetChainId: number | bigint, strategyTokenId: bigint, manifestCid: string): string;
|
|
108
|
+
static buildOverrideAttestationRequest(schemaUID: string, vault: string, targetChainId: number | bigint, strategyTokenId: bigint, manifestCid: string, refUID?: string): EASAttestationRequest;
|
|
49
109
|
private resolver;
|
|
50
110
|
private registry?;
|
|
51
111
|
private resolverAddress;
|
|
52
112
|
private registryAddress?;
|
|
53
|
-
private chainId
|
|
113
|
+
private chainId;
|
|
114
|
+
private readonly runner;
|
|
54
115
|
constructor(config: string | StrategyStoreConfig, providerOrSigner: ethers.Provider | ethers.Signer, overrides?: {
|
|
55
116
|
resolver?: any;
|
|
56
117
|
registry?: any;
|
|
57
118
|
});
|
|
58
119
|
private resolveChainId;
|
|
120
|
+
private getDefaultSchemaUID;
|
|
121
|
+
private getDefaultEASAddress;
|
|
122
|
+
private getSignerAddress;
|
|
59
123
|
/**
|
|
60
124
|
* Checks if an address is the top-level admin.
|
|
61
125
|
* @param address The address to check.
|
|
@@ -79,6 +143,25 @@ declare class StrategyStore {
|
|
|
79
143
|
to: string;
|
|
80
144
|
data: string;
|
|
81
145
|
}>;
|
|
146
|
+
/**
|
|
147
|
+
* Checks curator auth, builds an EAS request, and submits the attestation transaction.
|
|
148
|
+
* Uses computed recipient format, revocable=false, expirationTime=0 by default.
|
|
149
|
+
*/
|
|
150
|
+
submitOverrideAttestation(vault: string, targetChainId: number | bigint, strategyTokenId: bigint, manifestCid: string, options?: SubmitOverrideAttestationOptions): Promise<SubmitOverrideAttestationResult>;
|
|
151
|
+
private static toBigInt;
|
|
152
|
+
private static isActiveAttestation;
|
|
153
|
+
private queryEASSubgraph;
|
|
154
|
+
private decodeOverrideAttestationRecord;
|
|
155
|
+
/**
|
|
156
|
+
* Returns full decoded override attestation history for a (vault, targetChainId) pair.
|
|
157
|
+
* Results are returned newest first according to subgraph ordering.
|
|
158
|
+
*/
|
|
159
|
+
getOverrideAttestationHistory(vault: string, targetChainId: number | bigint, options?: GetOverrideAttestationsOptions): Promise<OverrideAttestationRecord[]>;
|
|
160
|
+
/**
|
|
161
|
+
* Returns the latest active override attestation for a (vault, targetChainId) pair.
|
|
162
|
+
* Active means not revoked and not expired.
|
|
163
|
+
*/
|
|
164
|
+
getLatestOverrideAttestation(vault: string, targetChainId: number | bigint, options?: GetOverrideAttestationsOptions): Promise<OverrideAttestationRecord | null>;
|
|
82
165
|
}
|
|
83
166
|
|
|
84
|
-
export { CHAINS, type ChainConfig, StrategyStore, type StrategyStoreConfig, computeVaultIdentifier };
|
|
167
|
+
export { CHAINS, type ChainConfig, type EASAttestationRequest, type EASAttestationRequestData, type GetOverrideAttestationsOptions, type OverrideAttestationRecord, StrategyStore, type StrategyStoreConfig, type SubmitOverrideAttestationOptions, type SubmitOverrideAttestationResult, computeVaultIdentifier };
|
package/dist/index.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { ethers } from "ethers";
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { getContractAddressByChainIdAndContractName } from "@steerprotocol/sdk";
|
|
5
5
|
import { encodePacked, keccak256 } from "viem";
|
|
6
|
-
import { base } from "viem/chains";
|
|
6
|
+
import { arbitrum, base } from "viem/chains";
|
|
7
7
|
|
|
8
8
|
// deployments/8453.json
|
|
9
9
|
var __default = {
|
|
@@ -676,7 +676,66 @@ var REGISTRY_ABI = [
|
|
|
676
676
|
"function totalVaultCount() view returns (uint256)",
|
|
677
677
|
"function getVaultDetails(address _address) view returns (tuple(uint8 state, uint256 tokenId, uint256 vaultID, string payloadIpfs, address vaultAddress, string beaconName))"
|
|
678
678
|
];
|
|
679
|
+
var EAS_ABI = [
|
|
680
|
+
"event Attested(address indexed recipient, address indexed attester, bytes32 uid, bytes32 indexed schemaUID)",
|
|
681
|
+
"function attest((bytes32 schema,(address recipient,uint64 expirationTime,bool revocable,bytes32 refUID,bytes data,uint256 value) data) request) payable returns (bytes32)"
|
|
682
|
+
];
|
|
683
|
+
var DEFAULT_EAS_SUBGRAPH_ENDPOINT = "https://base.easscan.org/graphql";
|
|
684
|
+
var DEFAULT_SUBGRAPH_PAGE_SIZE = 100;
|
|
685
|
+
var DEFAULT_SUBGRAPH_MAX_PAGES = 20;
|
|
686
|
+
var DEFAULT_SUBGRAPH_TIMEOUT_MS = 15e3;
|
|
687
|
+
var DEFAULT_SUBGRAPH_RETRIES = 2;
|
|
688
|
+
var DEFAULT_CHAIN_ID = __default.chainId;
|
|
689
|
+
var EAS_ATTESTATION_HISTORY_QUERY = `
|
|
690
|
+
query AttestationHistory($schemaUID: String!, $recipient: String!, $take: Int!, $skip: Int!) {
|
|
691
|
+
attestations(
|
|
692
|
+
where: { schemaId: { equals: $schemaUID }, recipient: { equals: $recipient } }
|
|
693
|
+
orderBy: [{ timeCreated: desc }]
|
|
694
|
+
take: $take
|
|
695
|
+
skip: $skip
|
|
696
|
+
) {
|
|
697
|
+
id
|
|
698
|
+
attester
|
|
699
|
+
recipient
|
|
700
|
+
refUID
|
|
701
|
+
revocable
|
|
702
|
+
revocationTime
|
|
703
|
+
expirationTime
|
|
704
|
+
time
|
|
705
|
+
timeCreated
|
|
706
|
+
data
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
`;
|
|
710
|
+
var EAS_ATTESTATION_HISTORY_FALLBACK_QUERY = `
|
|
711
|
+
query AttestationHistoryFallback($take: Int!, $skip: Int!) {
|
|
712
|
+
attestations(take: $take, skip: $skip) {
|
|
713
|
+
id
|
|
714
|
+
attester
|
|
715
|
+
recipient
|
|
716
|
+
refUID
|
|
717
|
+
revocable
|
|
718
|
+
revocationTime
|
|
719
|
+
expirationTime
|
|
720
|
+
time
|
|
721
|
+
timeCreated
|
|
722
|
+
data
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
`;
|
|
726
|
+
var ARBITRUM_ONE_DEPLOYMENT = {
|
|
727
|
+
chainId: 42161,
|
|
728
|
+
chainName: "Arbitrum One",
|
|
729
|
+
chain: arbitrum,
|
|
730
|
+
rpcUrl: "https://arb1.arbitrum.io/rpc",
|
|
731
|
+
explorerUrl: "https://arbiscan.io",
|
|
732
|
+
easAddress: "0xbD75f629A22Dc1ceD33dDA0b68c546A1c035c458",
|
|
733
|
+
schemaRegistry: "0xA310da9c5B885E7fb3fbA9D66E9Ba6Df512b78eB",
|
|
734
|
+
resolverAddress: "0x6628d1Bf82F34de132d0e1c60DEB574C1352d5A7",
|
|
735
|
+
schemaUID: "0xf1a6a394d1f3dd832a15e5b750963d929812e2b74d2dcea68ddf9f75691f16bf"
|
|
736
|
+
};
|
|
679
737
|
var CHAINS = {
|
|
738
|
+
ARBITRUM_ONE: ARBITRUM_ONE_DEPLOYMENT,
|
|
680
739
|
BASE_MAINNET: {
|
|
681
740
|
chainId: __default.chainId,
|
|
682
741
|
chainName: __default.chainName,
|
|
@@ -700,9 +759,19 @@ function computeVaultIdentifier(vault, chainId) {
|
|
|
700
759
|
const hash = keccak256(packed);
|
|
701
760
|
return `0x${hash.slice(-40)}`;
|
|
702
761
|
}
|
|
762
|
+
var Bytes32Schema = z.string().regex(/^0x[a-fA-F0-9]{64}$/, {
|
|
763
|
+
message: "Invalid bytes32 hex string"
|
|
764
|
+
});
|
|
703
765
|
var StrategyStore = class _StrategyStore {
|
|
704
|
-
static
|
|
705
|
-
static
|
|
766
|
+
static BASE_MAINNET_EAS_ADDRESS = __default.easAddress;
|
|
767
|
+
static BASE_MAINNET_RESOLVER_ADDRESS = __default.deployments.SteerAuthorityResolver.address;
|
|
768
|
+
static BASE_MAINNET_CROSSCHAIN_SCHEMA_UID = __default.schema.uid;
|
|
769
|
+
/** @deprecated Use BASE_MAINNET_EAS_ADDRESS */
|
|
770
|
+
static ARBITRUM_ONE_EAS_ADDRESS = ARBITRUM_ONE_DEPLOYMENT.easAddress;
|
|
771
|
+
/** @deprecated Use BASE_MAINNET_RESOLVER_ADDRESS */
|
|
772
|
+
static ARBITRUM_ONE_RESOLVER_ADDRESS = ARBITRUM_ONE_DEPLOYMENT.resolverAddress;
|
|
773
|
+
/** @deprecated Use BASE_MAINNET_CROSSCHAIN_SCHEMA_UID */
|
|
774
|
+
static ARBITRUM_ONE_CROSSCHAIN_SCHEMA_UID = ARBITRUM_ONE_DEPLOYMENT.schemaUID;
|
|
706
775
|
static encodeOverrideAttestationData(vault, targetChainId, strategyTokenId, manifestCid) {
|
|
707
776
|
const validatedVault = AddressSchema.parse(vault);
|
|
708
777
|
return ethers.AbiCoder.defaultAbiCoder().encode(
|
|
@@ -710,35 +779,61 @@ var StrategyStore = class _StrategyStore {
|
|
|
710
779
|
[validatedVault, targetChainId, strategyTokenId, manifestCid]
|
|
711
780
|
);
|
|
712
781
|
}
|
|
782
|
+
static buildOverrideAttestationRequest(schemaUID, vault, targetChainId, strategyTokenId, manifestCid, refUID = ethers.ZeroHash) {
|
|
783
|
+
const validatedSchemaUID = Bytes32Schema.parse(schemaUID);
|
|
784
|
+
const validatedVault = AddressSchema.parse(vault);
|
|
785
|
+
const validatedRefUID = Bytes32Schema.parse(refUID);
|
|
786
|
+
const normalizedTargetChainId = BigInt(targetChainId);
|
|
787
|
+
const data = _StrategyStore.encodeOverrideAttestationData(
|
|
788
|
+
validatedVault,
|
|
789
|
+
normalizedTargetChainId,
|
|
790
|
+
strategyTokenId,
|
|
791
|
+
manifestCid
|
|
792
|
+
);
|
|
793
|
+
return {
|
|
794
|
+
schema: validatedSchemaUID,
|
|
795
|
+
data: {
|
|
796
|
+
recipient: computeVaultIdentifier(validatedVault, normalizedTargetChainId),
|
|
797
|
+
expirationTime: 0n,
|
|
798
|
+
revocable: false,
|
|
799
|
+
refUID: validatedRefUID,
|
|
800
|
+
data,
|
|
801
|
+
value: 0n
|
|
802
|
+
}
|
|
803
|
+
};
|
|
804
|
+
}
|
|
713
805
|
resolver;
|
|
714
806
|
registry;
|
|
715
807
|
resolverAddress;
|
|
716
808
|
registryAddress;
|
|
717
|
-
chainId;
|
|
809
|
+
chainId = DEFAULT_CHAIN_ID;
|
|
810
|
+
runner;
|
|
718
811
|
constructor(config, providerOrSigner, overrides) {
|
|
719
812
|
let resAddr;
|
|
720
813
|
let regAddr;
|
|
814
|
+
this.runner = providerOrSigner;
|
|
721
815
|
if (typeof config === "string") {
|
|
722
816
|
resAddr = config;
|
|
817
|
+
this.chainId = DEFAULT_CHAIN_ID;
|
|
723
818
|
} else {
|
|
724
819
|
resAddr = config.resolverAddress;
|
|
725
820
|
regAddr = config.registryAddress;
|
|
726
|
-
this.chainId = config.chainId;
|
|
727
|
-
const chainConfig =
|
|
821
|
+
this.chainId = config.chainId ?? DEFAULT_CHAIN_ID;
|
|
822
|
+
const chainConfig = getChainConfigByChainId(this.chainId);
|
|
728
823
|
if (!resAddr && chainConfig?.resolverAddress) {
|
|
729
824
|
resAddr = chainConfig.resolverAddress;
|
|
730
825
|
}
|
|
731
|
-
if (!resAddr
|
|
732
|
-
if (
|
|
826
|
+
if (!resAddr) {
|
|
827
|
+
if (this.chainId === 42161) {
|
|
733
828
|
resAddr = _StrategyStore.ARBITRUM_ONE_RESOLVER_ADDRESS;
|
|
734
829
|
} else {
|
|
735
|
-
resAddr = getContractAddressByChainIdAndContractName(
|
|
830
|
+
resAddr = getContractAddressByChainIdAndContractName(this.chainId, "SteerAuthorityResolver");
|
|
736
831
|
}
|
|
737
832
|
}
|
|
738
|
-
if (!regAddr && config.chainId) {
|
|
739
|
-
regAddr = getContractAddressByChainIdAndContractName(
|
|
833
|
+
if (!regAddr && config.chainId !== void 0) {
|
|
834
|
+
regAddr = getContractAddressByChainIdAndContractName(this.chainId, "VaultRegistry");
|
|
740
835
|
if (!regAddr) {
|
|
741
|
-
throw new Error(`VaultRegistry address not found for chainId ${
|
|
836
|
+
throw new Error(`VaultRegistry address not found for chainId ${this.chainId}`);
|
|
742
837
|
}
|
|
743
838
|
}
|
|
744
839
|
}
|
|
@@ -753,11 +848,20 @@ var StrategyStore = class _StrategyStore {
|
|
|
753
848
|
}
|
|
754
849
|
}
|
|
755
850
|
resolveChainId(chainId) {
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
851
|
+
return chainId ?? this.chainId;
|
|
852
|
+
}
|
|
853
|
+
getDefaultSchemaUID() {
|
|
854
|
+
return _StrategyStore.BASE_MAINNET_CROSSCHAIN_SCHEMA_UID;
|
|
855
|
+
}
|
|
856
|
+
getDefaultEASAddress() {
|
|
857
|
+
const chainConfig = getChainConfigByChainId(this.chainId);
|
|
858
|
+
return chainConfig?.easAddress ?? _StrategyStore.BASE_MAINNET_EAS_ADDRESS;
|
|
859
|
+
}
|
|
860
|
+
async getSignerAddress() {
|
|
861
|
+
if (!("getAddress" in this.runner) || typeof this.runner.getAddress !== "function") {
|
|
862
|
+
throw new Error("A signer is required to submit attestations");
|
|
759
863
|
}
|
|
760
|
-
return
|
|
864
|
+
return await this.runner.getAddress();
|
|
761
865
|
}
|
|
762
866
|
/**
|
|
763
867
|
* Checks if an address is the top-level admin.
|
|
@@ -817,6 +921,260 @@ var StrategyStore = class _StrategyStore {
|
|
|
817
921
|
data
|
|
818
922
|
};
|
|
819
923
|
}
|
|
924
|
+
/**
|
|
925
|
+
* Checks curator auth, builds an EAS request, and submits the attestation transaction.
|
|
926
|
+
* Uses computed recipient format, revocable=false, expirationTime=0 by default.
|
|
927
|
+
*/
|
|
928
|
+
async submitOverrideAttestation(vault, targetChainId, strategyTokenId, manifestCid, options) {
|
|
929
|
+
const resolvedSchemaUID = options?.schemaUID ?? this.getDefaultSchemaUID();
|
|
930
|
+
const resolvedEASAddress = options?.easAddress ?? this.getDefaultEASAddress();
|
|
931
|
+
const signerAddress = await this.getSignerAddress();
|
|
932
|
+
if (!options?.skipCuratorCheck) {
|
|
933
|
+
const authorized = await this.isCurator(vault, signerAddress, targetChainId);
|
|
934
|
+
if (!authorized) {
|
|
935
|
+
throw new Error(
|
|
936
|
+
`Address ${signerAddress} is not authorized as curator for vault ${vault} on chain ${targetChainId}`
|
|
937
|
+
);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
const request = _StrategyStore.buildOverrideAttestationRequest(
|
|
941
|
+
resolvedSchemaUID,
|
|
942
|
+
vault,
|
|
943
|
+
targetChainId,
|
|
944
|
+
strategyTokenId,
|
|
945
|
+
manifestCid,
|
|
946
|
+
options?.refUID
|
|
947
|
+
);
|
|
948
|
+
const eas = options?.easContract || new ethers.Contract(AddressSchema.parse(resolvedEASAddress), EAS_ABI, this.runner);
|
|
949
|
+
const tx = await eas.attest(request);
|
|
950
|
+
const receipt = await tx.wait();
|
|
951
|
+
let uid;
|
|
952
|
+
for (const log of receipt.logs) {
|
|
953
|
+
try {
|
|
954
|
+
const parsed = eas.interface.parseLog(log);
|
|
955
|
+
if (!parsed || parsed.name !== "Attested") {
|
|
956
|
+
continue;
|
|
957
|
+
}
|
|
958
|
+
const eventAttester = String(parsed.args[1]).toLowerCase();
|
|
959
|
+
const eventUid = String(parsed.args[2]);
|
|
960
|
+
const eventSchemaUID = String(parsed.args[3]).toLowerCase();
|
|
961
|
+
if (eventAttester === signerAddress.toLowerCase() && eventSchemaUID === resolvedSchemaUID.toLowerCase()) {
|
|
962
|
+
uid = eventUid;
|
|
963
|
+
break;
|
|
964
|
+
}
|
|
965
|
+
} catch {
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
return {
|
|
969
|
+
request,
|
|
970
|
+
txHash: tx.hash,
|
|
971
|
+
uid
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
static toBigInt(value) {
|
|
975
|
+
if (value === null || value === void 0) {
|
|
976
|
+
return 0n;
|
|
977
|
+
}
|
|
978
|
+
if (typeof value === "bigint") {
|
|
979
|
+
return value;
|
|
980
|
+
}
|
|
981
|
+
if (typeof value === "number") {
|
|
982
|
+
return BigInt(value);
|
|
983
|
+
}
|
|
984
|
+
if (value.startsWith("0x") || value.startsWith("0X")) {
|
|
985
|
+
return BigInt(value);
|
|
986
|
+
}
|
|
987
|
+
return BigInt(value || "0");
|
|
988
|
+
}
|
|
989
|
+
static isActiveAttestation(record) {
|
|
990
|
+
const now = BigInt(Math.floor(Date.now() / 1e3));
|
|
991
|
+
const notRevoked = record.revocationTime === 0n;
|
|
992
|
+
const notExpired = record.expirationTime === 0n || record.expirationTime > now;
|
|
993
|
+
return notRevoked && notExpired;
|
|
994
|
+
}
|
|
995
|
+
async queryEASSubgraph(query, variables, endpoint, timeoutMs, retries, fetchFn) {
|
|
996
|
+
const resolvedFetchFn = fetchFn ?? globalThis.fetch;
|
|
997
|
+
if (!resolvedFetchFn) {
|
|
998
|
+
throw new Error("No fetch implementation available for subgraph requests");
|
|
999
|
+
}
|
|
1000
|
+
let lastError;
|
|
1001
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
1002
|
+
const controller = new AbortController();
|
|
1003
|
+
const timeoutHandle = setTimeout(() => controller.abort(), timeoutMs);
|
|
1004
|
+
try {
|
|
1005
|
+
const response = await resolvedFetchFn(endpoint, {
|
|
1006
|
+
method: "POST",
|
|
1007
|
+
headers: {
|
|
1008
|
+
"content-type": "application/json"
|
|
1009
|
+
},
|
|
1010
|
+
body: JSON.stringify({ query, variables }),
|
|
1011
|
+
signal: controller.signal
|
|
1012
|
+
});
|
|
1013
|
+
if (!response.ok) {
|
|
1014
|
+
throw new Error(`Subgraph HTTP ${response.status} ${response.statusText}`);
|
|
1015
|
+
}
|
|
1016
|
+
const payload = await response.json();
|
|
1017
|
+
if (payload.errors && payload.errors.length > 0) {
|
|
1018
|
+
const errorMessages = payload.errors.map((error) => error.message || "Unknown GraphQL error").join("; ");
|
|
1019
|
+
throw new Error(`Subgraph GraphQL error: ${errorMessages}`);
|
|
1020
|
+
}
|
|
1021
|
+
if (!payload.data) {
|
|
1022
|
+
throw new Error("Subgraph response missing data");
|
|
1023
|
+
}
|
|
1024
|
+
return payload.data;
|
|
1025
|
+
} catch (error) {
|
|
1026
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
1027
|
+
if (attempt === retries) {
|
|
1028
|
+
break;
|
|
1029
|
+
}
|
|
1030
|
+
const backoffMs = 300 * (attempt + 1);
|
|
1031
|
+
await new Promise((resolve) => setTimeout(resolve, backoffMs));
|
|
1032
|
+
} finally {
|
|
1033
|
+
clearTimeout(timeoutHandle);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
throw new Error(`Failed to query EAS subgraph after ${retries + 1} attempts: ${lastError?.message || "unknown error"}`);
|
|
1037
|
+
}
|
|
1038
|
+
decodeOverrideAttestationRecord(raw) {
|
|
1039
|
+
try {
|
|
1040
|
+
const decoded = ethers.AbiCoder.defaultAbiCoder().decode(
|
|
1041
|
+
["address", "uint256", "uint256", "string"],
|
|
1042
|
+
raw.data
|
|
1043
|
+
);
|
|
1044
|
+
const vault = decoded[0];
|
|
1045
|
+
const targetChainId = decoded[1];
|
|
1046
|
+
const strategyTokenId = decoded[2];
|
|
1047
|
+
const manifestCid = decoded[3];
|
|
1048
|
+
const record = {
|
|
1049
|
+
uid: raw.id,
|
|
1050
|
+
attester: AddressSchema.parse(raw.attester),
|
|
1051
|
+
recipient: AddressSchema.parse(raw.recipient),
|
|
1052
|
+
refUID: raw.refUID,
|
|
1053
|
+
revocable: raw.revocable,
|
|
1054
|
+
revocationTime: _StrategyStore.toBigInt(raw.revocationTime),
|
|
1055
|
+
expirationTime: _StrategyStore.toBigInt(raw.expirationTime),
|
|
1056
|
+
createdAt: _StrategyStore.toBigInt(raw.timeCreated ?? raw.time),
|
|
1057
|
+
data: raw.data,
|
|
1058
|
+
vault: AddressSchema.parse(vault),
|
|
1059
|
+
targetChainId: _StrategyStore.toBigInt(targetChainId),
|
|
1060
|
+
strategyTokenId: _StrategyStore.toBigInt(strategyTokenId),
|
|
1061
|
+
manifestCid,
|
|
1062
|
+
isActive: false
|
|
1063
|
+
};
|
|
1064
|
+
record.isActive = _StrategyStore.isActiveAttestation(record);
|
|
1065
|
+
return record;
|
|
1066
|
+
} catch {
|
|
1067
|
+
return null;
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Returns full decoded override attestation history for a (vault, targetChainId) pair.
|
|
1072
|
+
* Results are returned newest first according to subgraph ordering.
|
|
1073
|
+
*/
|
|
1074
|
+
async getOverrideAttestationHistory(vault, targetChainId, options) {
|
|
1075
|
+
const validatedVault = AddressSchema.parse(vault);
|
|
1076
|
+
const normalizedTargetChainId = BigInt(targetChainId);
|
|
1077
|
+
const recipient = computeVaultIdentifier(validatedVault, normalizedTargetChainId);
|
|
1078
|
+
const schemaUID = options?.schemaUID ?? this.getDefaultSchemaUID();
|
|
1079
|
+
const endpoint = options?.graphqlEndpoint ?? DEFAULT_EAS_SUBGRAPH_ENDPOINT;
|
|
1080
|
+
const pageSize = options?.pageSize ?? DEFAULT_SUBGRAPH_PAGE_SIZE;
|
|
1081
|
+
const maxPages = options?.maxPages ?? DEFAULT_SUBGRAPH_MAX_PAGES;
|
|
1082
|
+
const timeoutMs = options?.timeoutMs ?? DEFAULT_SUBGRAPH_TIMEOUT_MS;
|
|
1083
|
+
const retries = options?.retries ?? DEFAULT_SUBGRAPH_RETRIES;
|
|
1084
|
+
const includeRevoked = options?.includeRevoked ?? true;
|
|
1085
|
+
const includeExpired = options?.includeExpired ?? true;
|
|
1086
|
+
const all = [];
|
|
1087
|
+
let useFallbackQuery = false;
|
|
1088
|
+
for (let page = 0; page < maxPages; page++) {
|
|
1089
|
+
const skip = page * pageSize;
|
|
1090
|
+
let response;
|
|
1091
|
+
if (!useFallbackQuery) {
|
|
1092
|
+
try {
|
|
1093
|
+
response = await this.queryEASSubgraph(
|
|
1094
|
+
EAS_ATTESTATION_HISTORY_QUERY,
|
|
1095
|
+
{
|
|
1096
|
+
schemaUID,
|
|
1097
|
+
recipient,
|
|
1098
|
+
take: pageSize,
|
|
1099
|
+
skip
|
|
1100
|
+
},
|
|
1101
|
+
endpoint,
|
|
1102
|
+
timeoutMs,
|
|
1103
|
+
retries,
|
|
1104
|
+
options?.fetchFn
|
|
1105
|
+
);
|
|
1106
|
+
} catch {
|
|
1107
|
+
useFallbackQuery = true;
|
|
1108
|
+
response = await this.queryEASSubgraph(
|
|
1109
|
+
EAS_ATTESTATION_HISTORY_FALLBACK_QUERY,
|
|
1110
|
+
{
|
|
1111
|
+
take: pageSize,
|
|
1112
|
+
skip
|
|
1113
|
+
},
|
|
1114
|
+
endpoint,
|
|
1115
|
+
timeoutMs,
|
|
1116
|
+
retries,
|
|
1117
|
+
options?.fetchFn
|
|
1118
|
+
);
|
|
1119
|
+
}
|
|
1120
|
+
} else {
|
|
1121
|
+
response = await this.queryEASSubgraph(
|
|
1122
|
+
EAS_ATTESTATION_HISTORY_FALLBACK_QUERY,
|
|
1123
|
+
{
|
|
1124
|
+
take: pageSize,
|
|
1125
|
+
skip
|
|
1126
|
+
},
|
|
1127
|
+
endpoint,
|
|
1128
|
+
timeoutMs,
|
|
1129
|
+
retries,
|
|
1130
|
+
options?.fetchFn
|
|
1131
|
+
);
|
|
1132
|
+
}
|
|
1133
|
+
const pageRows = response.attestations || [];
|
|
1134
|
+
for (const row of pageRows) {
|
|
1135
|
+
const decoded = this.decodeOverrideAttestationRecord(row);
|
|
1136
|
+
if (!decoded) {
|
|
1137
|
+
continue;
|
|
1138
|
+
}
|
|
1139
|
+
if (decoded.vault.toLowerCase() !== validatedVault.toLowerCase()) {
|
|
1140
|
+
continue;
|
|
1141
|
+
}
|
|
1142
|
+
if (decoded.targetChainId !== normalizedTargetChainId) {
|
|
1143
|
+
continue;
|
|
1144
|
+
}
|
|
1145
|
+
if (decoded.recipient.toLowerCase() !== recipient.toLowerCase()) {
|
|
1146
|
+
continue;
|
|
1147
|
+
}
|
|
1148
|
+
if (options?.strategyTokenId !== void 0 && decoded.strategyTokenId !== options.strategyTokenId) {
|
|
1149
|
+
continue;
|
|
1150
|
+
}
|
|
1151
|
+
if (!includeRevoked && decoded.revocationTime !== 0n) {
|
|
1152
|
+
continue;
|
|
1153
|
+
}
|
|
1154
|
+
if (!includeExpired && decoded.expirationTime !== 0n && decoded.expirationTime <= BigInt(Math.floor(Date.now() / 1e3))) {
|
|
1155
|
+
continue;
|
|
1156
|
+
}
|
|
1157
|
+
all.push(decoded);
|
|
1158
|
+
}
|
|
1159
|
+
if (pageRows.length < pageSize) {
|
|
1160
|
+
break;
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
return all;
|
|
1164
|
+
}
|
|
1165
|
+
/**
|
|
1166
|
+
* Returns the latest active override attestation for a (vault, targetChainId) pair.
|
|
1167
|
+
* Active means not revoked and not expired.
|
|
1168
|
+
*/
|
|
1169
|
+
async getLatestOverrideAttestation(vault, targetChainId, options) {
|
|
1170
|
+
const history = await this.getOverrideAttestationHistory(vault, targetChainId, options);
|
|
1171
|
+
for (const item of history) {
|
|
1172
|
+
if (item.isActive) {
|
|
1173
|
+
return item;
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
return null;
|
|
1177
|
+
}
|
|
820
1178
|
};
|
|
821
1179
|
export {
|
|
822
1180
|
CHAINS,
|