@exponent-labs/exponent-vaults-fetcher 0.9.24 → 0.9.26
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/build/borshPreflight.d.ts +3 -0
- package/build/borshPreflight.js +199 -0
- package/build/borshPreflight.js.map +1 -0
- package/build/economicsAccounts.d.ts +9 -0
- package/build/economicsAccounts.js +66 -0
- package/build/economicsAccounts.js.map +1 -0
- package/build/economicsAccounts.test.d.ts +1 -0
- package/build/economicsAccounts.test.js +118 -0
- package/build/economicsAccounts.test.js.map +1 -0
- package/build/fetcher.js +41 -14
- package/build/fetcher.js.map +1 -1
- package/build/fetcher.test.d.ts +1 -0
- package/build/fetcher.test.js +225 -0
- package/build/fetcher.test.js.map +1 -0
- package/build/types.d.ts +4 -0
- package/build/types.js.map +1 -1
- package/package.json +6 -4
- package/src/fetcher.test.ts +346 -0
- package/src/fetcher.ts +47 -22
- package/src/types.ts +4 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const anchor_1 = require("@coral-xyz/anchor");
|
|
7
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
8
|
+
const node_test_1 = __importDefault(require("node:test"));
|
|
9
|
+
const exponent_vaults_pda_1 = require("@exponent-labs/exponent-vaults-pda");
|
|
10
|
+
const fetcher_1 = require("./fetcher");
|
|
11
|
+
const EXPONENT_PRICES_DISCRIMINATOR = Buffer.from([188, 97, 196, 52, 192, 41, 211, 196]);
|
|
12
|
+
const PRICE_INTERFACE_ACCOUNTS_DISCRIMINATOR = Buffer.from([166, 196, 173, 204, 35, 6, 104, 230]);
|
|
13
|
+
const PROGRAM_ID = new anchor_1.web3.PublicKey(Uint8Array.from({ length: 32 }, (_, index) => index + 1));
|
|
14
|
+
const PRICE_MINT = new anchor_1.web3.PublicKey(Uint8Array.from({ length: 32 }, (_, index) => index + 33));
|
|
15
|
+
const MAX_PRICE_MANAGERS = 8;
|
|
16
|
+
const MAX_PRICES = 1024;
|
|
17
|
+
const PUBKEY_BYTES = 32;
|
|
18
|
+
const PRICE_NODE_REGISTERS_SIZE = 16;
|
|
19
|
+
const EXPONENT_PRICE_SIZE = 224;
|
|
20
|
+
const PRICE_NODE_SIZE = PRICE_NODE_REGISTERS_SIZE + EXPONENT_PRICE_SIZE;
|
|
21
|
+
const PRICE_NODES_OFFSET = 8 + 8 + MAX_PRICE_MANAGERS * PUBKEY_BYTES + 16;
|
|
22
|
+
const FIRST_PRICE_VALUE_OFFSET = PRICE_NODES_OFFSET + PRICE_NODE_REGISTERS_SIZE;
|
|
23
|
+
const EXPONENT_PRICES_SIZE = PRICE_NODES_OFFSET + MAX_PRICES * PRICE_NODE_SIZE + 128;
|
|
24
|
+
const PRICE_DECIMALS = 6;
|
|
25
|
+
const UNDERLYING_DECIMALS = 9;
|
|
26
|
+
const SY_PRICE_ID = 0x0807060504030201n;
|
|
27
|
+
const ZERO_PUBKEY = new anchor_1.web3.PublicKey(new Uint8Array(PUBKEY_BYTES));
|
|
28
|
+
function createPublicKey(seed) {
|
|
29
|
+
const bytes = new Uint8Array(PUBKEY_BYTES).fill(255);
|
|
30
|
+
new DataView(bytes.buffer).setUint32(0, seed, true);
|
|
31
|
+
return new anchor_1.web3.PublicKey(bytes);
|
|
32
|
+
}
|
|
33
|
+
function createExponentPricesData(priceInterfaceAccounts, reservedByte = 0) {
|
|
34
|
+
const data = Buffer.alloc(EXPONENT_PRICES_SIZE);
|
|
35
|
+
EXPONENT_PRICES_DISCRIMINATOR.copy(data);
|
|
36
|
+
data.writeBigUInt64LE(BigInt(priceInterfaceAccounts.length), 8 + 8 + MAX_PRICE_MANAGERS * PUBKEY_BYTES);
|
|
37
|
+
for (const [index, priceInterfaceAccount] of priceInterfaceAccounts.entries()) {
|
|
38
|
+
const valueOffset = FIRST_PRICE_VALUE_OFFSET + index * PRICE_NODE_SIZE;
|
|
39
|
+
PRICE_MINT.toBuffer().copy(data, valueOffset);
|
|
40
|
+
priceInterfaceAccount.toBuffer().copy(data, valueOffset + 128);
|
|
41
|
+
data.writeUInt8(PRICE_DECIMALS, valueOffset + 160);
|
|
42
|
+
data.writeUInt8(UNDERLYING_DECIMALS, valueOffset + 161);
|
|
43
|
+
data.fill(reservedByte, valueOffset + 162, valueOffset + 168);
|
|
44
|
+
data.writeBigUInt64LE(SY_PRICE_ID, valueOffset + 168);
|
|
45
|
+
data.fill(reservedByte, valueOffset + 176, valueOffset + EXPONENT_PRICE_SIZE);
|
|
46
|
+
}
|
|
47
|
+
return data;
|
|
48
|
+
}
|
|
49
|
+
function createPriceInterfaceAccountsData(interfaceAccounts, scopeConfig) {
|
|
50
|
+
const data = Buffer.alloc(12 + interfaceAccounts.length * PUBKEY_BYTES + (scopeConfig ? 17 : 0));
|
|
51
|
+
PRICE_INTERFACE_ACCOUNTS_DISCRIMINATOR.copy(data);
|
|
52
|
+
data.writeUInt32LE(interfaceAccounts.length, 8);
|
|
53
|
+
interfaceAccounts.forEach((account, index) => account.toBuffer().copy(data, 12 + index * PUBKEY_BYTES));
|
|
54
|
+
if (scopeConfig) {
|
|
55
|
+
const stateOffset = 12 + interfaceAccounts.length * PUBKEY_BYTES;
|
|
56
|
+
data.writeUInt8(2, stateOffset);
|
|
57
|
+
scopeConfig.priceChain.forEach((price, index) => data.writeUInt16LE(price, stateOffset + 1 + index * 2));
|
|
58
|
+
data.writeBigUInt64LE(scopeConfig.maximumAgeSeconds, stateOffset + 9);
|
|
59
|
+
}
|
|
60
|
+
return data;
|
|
61
|
+
}
|
|
62
|
+
function createConnection(registryData, options = {}) {
|
|
63
|
+
const registryAddress = new exponent_vaults_pda_1.ExponentVaultsPDA(PROGRAM_ID).exponentPrices()[0];
|
|
64
|
+
const singleAccountReads = [];
|
|
65
|
+
const multipleAccountReads = [];
|
|
66
|
+
let activeMultipleAccountReads = 0;
|
|
67
|
+
let maxConcurrentMultipleAccountReads = 0;
|
|
68
|
+
const connection = {
|
|
69
|
+
async getAccountInfo(address) {
|
|
70
|
+
singleAccountReads.push(address);
|
|
71
|
+
if (!address.equals(registryAddress)) {
|
|
72
|
+
throw new Error(`Unexpected getAccountInfo for PriceInterfaceAccounts: ${address.toBase58()}`);
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
data: registryData,
|
|
76
|
+
executable: false,
|
|
77
|
+
lamports: 1,
|
|
78
|
+
owner: PROGRAM_ID,
|
|
79
|
+
rentEpoch: 0,
|
|
80
|
+
};
|
|
81
|
+
},
|
|
82
|
+
async getMultipleAccountsInfo(addresses) {
|
|
83
|
+
multipleAccountReads.push(addresses);
|
|
84
|
+
activeMultipleAccountReads += 1;
|
|
85
|
+
maxConcurrentMultipleAccountReads = Math.max(maxConcurrentMultipleAccountReads, activeMultipleAccountReads);
|
|
86
|
+
try {
|
|
87
|
+
await Promise.resolve();
|
|
88
|
+
if (options.rpcError)
|
|
89
|
+
throw options.rpcError;
|
|
90
|
+
const infos = addresses.map((address) => {
|
|
91
|
+
const fixture = options.accounts?.get(address.toBase58());
|
|
92
|
+
if (fixture === null || !fixture)
|
|
93
|
+
return null;
|
|
94
|
+
return {
|
|
95
|
+
data: fixture.data,
|
|
96
|
+
executable: false,
|
|
97
|
+
lamports: 1,
|
|
98
|
+
owner: fixture.owner ?? PROGRAM_ID,
|
|
99
|
+
rentEpoch: 0,
|
|
100
|
+
};
|
|
101
|
+
});
|
|
102
|
+
return options.shortResponse ? infos.slice(0, -1) : infos;
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
activeMultipleAccountReads -= 1;
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
return {
|
|
110
|
+
connection,
|
|
111
|
+
multipleAccountReads,
|
|
112
|
+
get maxConcurrentMultipleAccountReads() {
|
|
113
|
+
return maxConcurrentMultipleAccountReads;
|
|
114
|
+
},
|
|
115
|
+
registryAddress,
|
|
116
|
+
singleAccountReads,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function createAccountFixtures(keys) {
|
|
120
|
+
return new Map(keys.map((key, index) => [
|
|
121
|
+
key.toBase58(),
|
|
122
|
+
{
|
|
123
|
+
data: createPriceInterfaceAccountsData([createPublicKey(128 + index)], {
|
|
124
|
+
maximumAgeSeconds: BigInt(index + 1),
|
|
125
|
+
priceChain: [index + 1, index + 2, index + 3, index + 4],
|
|
126
|
+
}),
|
|
127
|
+
},
|
|
128
|
+
]));
|
|
129
|
+
}
|
|
130
|
+
(0, node_test_1.default)("decodes price metadata from exact wire offsets without consuming reserved bytes", async () => {
|
|
131
|
+
for (const reservedByte of [0xa5, 0x5a]) {
|
|
132
|
+
const { connection } = createConnection(createExponentPricesData([ZERO_PUBKEY], reservedByte));
|
|
133
|
+
const fetcher = new fetcher_1.ExponentVaultsFetcher(connection, PROGRAM_ID);
|
|
134
|
+
const prices = await fetcher.fetchExponentPrices();
|
|
135
|
+
const price = prices.prices[1];
|
|
136
|
+
strict_1.default.ok(price);
|
|
137
|
+
strict_1.default.equal(price.priceDecimals, PRICE_DECIMALS);
|
|
138
|
+
strict_1.default.equal(price.underlyingDecimals, UNDERLYING_DECIMALS);
|
|
139
|
+
strict_1.default.equal(price.syPriceId, SY_PRICE_ID);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
(0, node_test_1.default)("fetches unique price interface accounts sequentially in maximum 100-key batches", async () => {
|
|
143
|
+
const cases = [
|
|
144
|
+
{ count: 0, expectedBatchSizes: [] },
|
|
145
|
+
{ count: 1, expectedBatchSizes: [1] },
|
|
146
|
+
{ count: 99, expectedBatchSizes: [99] },
|
|
147
|
+
{ count: 100, expectedBatchSizes: [100] },
|
|
148
|
+
{ count: 101, expectedBatchSizes: [100, 1] },
|
|
149
|
+
{ count: 200, expectedBatchSizes: [100, 100] },
|
|
150
|
+
{ count: 201, expectedBatchSizes: [100, 100, 1] },
|
|
151
|
+
{ count: 1024, expectedBatchSizes: [100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 24] },
|
|
152
|
+
];
|
|
153
|
+
for (const { count, expectedBatchSizes } of cases) {
|
|
154
|
+
const keys = Array.from({ length: count }, (_, index) => createPublicKey(index + 1));
|
|
155
|
+
const connectionState = createConnection(createExponentPricesData(keys), { accounts: createAccountFixtures(keys) });
|
|
156
|
+
const prices = await new fetcher_1.ExponentVaultsFetcher(connectionState.connection, PROGRAM_ID).fetchExponentPrices();
|
|
157
|
+
strict_1.default.deepEqual(connectionState.multipleAccountReads.map((batch) => batch.length), expectedBatchSizes, `count ${count}`);
|
|
158
|
+
strict_1.default.ok(connectionState.multipleAccountReads.every((batch) => batch.length <= 100), `count ${count}`);
|
|
159
|
+
strict_1.default.equal(connectionState.maxConcurrentMultipleAccountReads, count === 0 ? 0 : 1, `count ${count}`);
|
|
160
|
+
strict_1.default.deepEqual(connectionState.singleAccountReads.map((address) => address.toBase58()), [connectionState.registryAddress.toBase58()], `count ${count}`);
|
|
161
|
+
strict_1.default.deepEqual(connectionState.multipleAccountReads.flat().map((address) => address.toBase58()), keys.map((key) => key.toBase58()), `count ${count}`);
|
|
162
|
+
for (const [index, key] of keys.entries()) {
|
|
163
|
+
const price = prices.prices[index + 1];
|
|
164
|
+
strict_1.default.ok(price, `count ${count}, price ${index + 1}`);
|
|
165
|
+
strict_1.default.deepEqual(price.interfaceAccounts, [createPublicKey(128 + index)]);
|
|
166
|
+
strict_1.default.deepEqual(price.scopeConfig, {
|
|
167
|
+
maximumAgeSeconds: BigInt(index + 1),
|
|
168
|
+
priceChain: [index + 1, index + 2, index + 3, index + 4],
|
|
169
|
+
});
|
|
170
|
+
strict_1.default.equal(price.priceInterfaceAccounts.toBase58(), key.toBase58());
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
(0, node_test_1.default)("deduplicates nonzero price interface accounts and hydrates every matching price", async () => {
|
|
175
|
+
const firstKey = createPublicKey(250);
|
|
176
|
+
const secondKey = createPublicKey(1);
|
|
177
|
+
const keys = [firstKey, ZERO_PUBKEY, firstKey, secondKey];
|
|
178
|
+
strict_1.default.notDeepEqual([firstKey.toBase58(), secondKey.toBase58()], [firstKey.toBase58(), secondKey.toBase58()].sort());
|
|
179
|
+
const { connection, multipleAccountReads, registryAddress, singleAccountReads } = createConnection(createExponentPricesData(keys), { accounts: createAccountFixtures([firstKey, secondKey]) });
|
|
180
|
+
const prices = await new fetcher_1.ExponentVaultsFetcher(connection, PROGRAM_ID).fetchExponentPrices();
|
|
181
|
+
strict_1.default.deepEqual(singleAccountReads.map((address) => address.toBase58()), [registryAddress.toBase58()]);
|
|
182
|
+
strict_1.default.deepEqual(multipleAccountReads.map((batch) => batch.map((key) => key.toBase58())), [[firstKey.toBase58(), secondKey.toBase58()]]);
|
|
183
|
+
strict_1.default.deepEqual(prices.prices[1]?.interfaceAccounts, [createPublicKey(128)]);
|
|
184
|
+
strict_1.default.deepEqual(prices.prices[2]?.interfaceAccounts, []);
|
|
185
|
+
strict_1.default.deepEqual(prices.prices[3]?.interfaceAccounts, [createPublicKey(128)]);
|
|
186
|
+
strict_1.default.deepEqual(prices.prices[4]?.interfaceAccounts, [createPublicKey(129)]);
|
|
187
|
+
strict_1.default.strictEqual(prices.prices[1]?.interfaceAccounts, prices.prices[3]?.interfaceAccounts);
|
|
188
|
+
strict_1.default.ok(prices.prices[1]?.scopeConfig);
|
|
189
|
+
strict_1.default.strictEqual(prices.prices[1]?.scopeConfig, prices.prices[3]?.scopeConfig);
|
|
190
|
+
});
|
|
191
|
+
(0, node_test_1.default)("preserves PriceInterfaceAccounts validation errors and malformed RPC response failures", async () => {
|
|
192
|
+
const key = createPublicKey(1);
|
|
193
|
+
const registryData = createExponentPricesData([key]);
|
|
194
|
+
await strict_1.default.rejects(() => new fetcher_1.ExponentVaultsFetcher(createConnection(registryData).connection, PROGRAM_ID).fetchExponentPrices(), new Error(`PriceInterfaceAccounts account not found: ${key.toBase58()}`));
|
|
195
|
+
await strict_1.default.rejects(() => new fetcher_1.ExponentVaultsFetcher(createConnection(registryData, {
|
|
196
|
+
accounts: new Map([
|
|
197
|
+
[key.toBase58(), { data: createPriceInterfaceAccountsData([]), owner: createPublicKey(2) }],
|
|
198
|
+
]),
|
|
199
|
+
}).connection, PROGRAM_ID).fetchExponentPrices(), new Error(`PriceInterfaceAccounts owner mismatch: expected ${PROGRAM_ID.toBase58()}, got ${createPublicKey(2).toBase58()}`));
|
|
200
|
+
await strict_1.default.rejects(() => new fetcher_1.ExponentVaultsFetcher(createConnection(registryData, { accounts: createAccountFixtures([key]), shortResponse: true }).connection, PROGRAM_ID).fetchExponentPrices(), new Error("PriceInterfaceAccounts batch size mismatch: requested 1, got 0"));
|
|
201
|
+
const rpcError = new Error("RPC unavailable");
|
|
202
|
+
await strict_1.default.rejects(() => new fetcher_1.ExponentVaultsFetcher(createConnection(registryData, { accounts: createAccountFixtures([key]), rpcError }).connection, PROGRAM_ID).fetchExponentPrices(), rpcError);
|
|
203
|
+
await strict_1.default.rejects(() => new fetcher_1.ExponentVaultsFetcher(createConnection(registryData, {
|
|
204
|
+
accounts: new Map([[key.toBase58(), { data: Buffer.alloc(1) }]]),
|
|
205
|
+
}).connection, PROGRAM_ID).fetchExponentPrices(), new Error("PriceInterfaceAccounts account too small: 1 bytes"));
|
|
206
|
+
});
|
|
207
|
+
(0, node_test_1.default)("reports a missing account at its indexed position in the second batch", async () => {
|
|
208
|
+
const keys = Array.from({ length: 102 }, (_, index) => createPublicKey(index + 1));
|
|
209
|
+
const accounts = createAccountFixtures(keys);
|
|
210
|
+
const missingKey = keys[101];
|
|
211
|
+
accounts.set(missingKey.toBase58(), null);
|
|
212
|
+
await strict_1.default.rejects(() => new fetcher_1.ExponentVaultsFetcher(createConnection(createExponentPricesData(keys), { accounts }).connection, PROGRAM_ID).fetchExponentPrices(), new Error(`PriceInterfaceAccounts account not found: ${missingKey.toBase58()}`));
|
|
213
|
+
});
|
|
214
|
+
(0, node_test_1.default)("reports an owner mismatch at its indexed position in a later batch", async () => {
|
|
215
|
+
const keys = Array.from({ length: 202 }, (_, index) => createPublicKey(index + 1));
|
|
216
|
+
const accounts = createAccountFixtures(keys);
|
|
217
|
+
const mismatchedKey = keys[201];
|
|
218
|
+
const unexpectedOwner = createPublicKey(250);
|
|
219
|
+
accounts.set(mismatchedKey.toBase58(), {
|
|
220
|
+
data: createPriceInterfaceAccountsData([]),
|
|
221
|
+
owner: unexpectedOwner,
|
|
222
|
+
});
|
|
223
|
+
await strict_1.default.rejects(() => new fetcher_1.ExponentVaultsFetcher(createConnection(createExponentPricesData(keys), { accounts }).connection, PROGRAM_ID).fetchExponentPrices(), new Error(`PriceInterfaceAccounts owner mismatch: expected ${PROGRAM_ID.toBase58()}, got ${unexpectedOwner.toBase58()}`));
|
|
224
|
+
});
|
|
225
|
+
//# sourceMappingURL=fetcher.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetcher.test.js","sourceRoot":"","sources":["../src/fetcher.test.ts"],"names":[],"mappings":"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"}
|
package/build/types.d.ts
CHANGED
|
@@ -235,9 +235,13 @@ export type ExponentPrice = {
|
|
|
235
235
|
lastUpdatedAt: bigint;
|
|
236
236
|
lastUpdatedSlot: bigint;
|
|
237
237
|
priceType: number;
|
|
238
|
+
priceDecimals: number;
|
|
239
|
+
underlyingDecimals: number;
|
|
240
|
+
syPriceId: bigint;
|
|
238
241
|
impliedApyBps: number | null;
|
|
239
242
|
impliedApy: number | null;
|
|
240
243
|
priceInterfaceAccounts: web3.PublicKey;
|
|
244
|
+
isInverse: boolean;
|
|
241
245
|
interfaceAccounts: web3.PublicKey[];
|
|
242
246
|
scopeConfig?: ScopePriceConfig | null;
|
|
243
247
|
};
|
package/build/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;AA8QA,oCAAoC;AACpC,iCAAiC"}
|
package/package.json
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exponent-labs/exponent-vaults-fetcher",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.26",
|
|
4
4
|
"main": "build/index.js",
|
|
5
5
|
"types": "build/index.d.ts",
|
|
6
6
|
"license": "AGPL-3.0",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"build": "tsc --build"
|
|
8
|
+
"build": "tsc --build",
|
|
9
|
+
"pretest": "yarn build",
|
|
10
|
+
"test": "node --test build/fetcher.test.js build/priceInterfaceAccounts.test.js"
|
|
9
11
|
},
|
|
10
12
|
"dependencies": {
|
|
11
13
|
"@coral-xyz/anchor": "0.30.1",
|
|
12
|
-
"@exponent-labs/exponent-vaults-idl": "0.9.
|
|
13
|
-
"@exponent-labs/exponent-vaults-pda": "0.9.
|
|
14
|
+
"@exponent-labs/exponent-vaults-idl": "0.9.26",
|
|
15
|
+
"@exponent-labs/exponent-vaults-pda": "0.9.26"
|
|
14
16
|
},
|
|
15
17
|
"devDependencies": {
|
|
16
18
|
"@types/bn.js": "^5.1.0",
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import { web3 } from "@coral-xyz/anchor"
|
|
2
|
+
import assert from "node:assert/strict"
|
|
3
|
+
import test from "node:test"
|
|
4
|
+
|
|
5
|
+
import { ExponentVaultsPDA } from "@exponent-labs/exponent-vaults-pda"
|
|
6
|
+
|
|
7
|
+
import { ExponentVaultsFetcher } from "./fetcher"
|
|
8
|
+
|
|
9
|
+
const EXPONENT_PRICES_DISCRIMINATOR = Buffer.from([188, 97, 196, 52, 192, 41, 211, 196])
|
|
10
|
+
const PRICE_INTERFACE_ACCOUNTS_DISCRIMINATOR = Buffer.from([166, 196, 173, 204, 35, 6, 104, 230])
|
|
11
|
+
const PROGRAM_ID = new web3.PublicKey(Uint8Array.from({ length: 32 }, (_, index) => index + 1))
|
|
12
|
+
const PRICE_MINT = new web3.PublicKey(Uint8Array.from({ length: 32 }, (_, index) => index + 33))
|
|
13
|
+
|
|
14
|
+
const MAX_PRICE_MANAGERS = 8
|
|
15
|
+
const MAX_PRICES = 1024
|
|
16
|
+
const PUBKEY_BYTES = 32
|
|
17
|
+
const PRICE_NODE_REGISTERS_SIZE = 16
|
|
18
|
+
const EXPONENT_PRICE_SIZE = 224
|
|
19
|
+
const PRICE_NODE_SIZE = PRICE_NODE_REGISTERS_SIZE + EXPONENT_PRICE_SIZE
|
|
20
|
+
const PRICE_NODES_OFFSET = 8 + 8 + MAX_PRICE_MANAGERS * PUBKEY_BYTES + 16
|
|
21
|
+
const FIRST_PRICE_VALUE_OFFSET = PRICE_NODES_OFFSET + PRICE_NODE_REGISTERS_SIZE
|
|
22
|
+
const EXPONENT_PRICES_SIZE = PRICE_NODES_OFFSET + MAX_PRICES * PRICE_NODE_SIZE + 128
|
|
23
|
+
|
|
24
|
+
const PRICE_DECIMALS = 6
|
|
25
|
+
const UNDERLYING_DECIMALS = 9
|
|
26
|
+
const SY_PRICE_ID = 0x0807060504030201n
|
|
27
|
+
const ZERO_PUBKEY = new web3.PublicKey(new Uint8Array(PUBKEY_BYTES))
|
|
28
|
+
|
|
29
|
+
type ScopeConfigFixture = {
|
|
30
|
+
maximumAgeSeconds: bigint
|
|
31
|
+
priceChain: readonly [number, number, number, number]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type AccountFixture = {
|
|
35
|
+
data: Buffer
|
|
36
|
+
owner?: web3.PublicKey
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
type ConnectionOptions = {
|
|
40
|
+
accounts?: ReadonlyMap<string, AccountFixture | null>
|
|
41
|
+
rpcError?: Error
|
|
42
|
+
shortResponse?: boolean
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function createPublicKey(seed: number): web3.PublicKey {
|
|
46
|
+
const bytes = new Uint8Array(PUBKEY_BYTES).fill(255)
|
|
47
|
+
new DataView(bytes.buffer).setUint32(0, seed, true)
|
|
48
|
+
return new web3.PublicKey(bytes)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function createExponentPricesData(priceInterfaceAccounts: readonly web3.PublicKey[], reservedByte = 0): Buffer {
|
|
52
|
+
const data = Buffer.alloc(EXPONENT_PRICES_SIZE)
|
|
53
|
+
EXPONENT_PRICES_DISCRIMINATOR.copy(data)
|
|
54
|
+
data.writeBigUInt64LE(BigInt(priceInterfaceAccounts.length), 8 + 8 + MAX_PRICE_MANAGERS * PUBKEY_BYTES)
|
|
55
|
+
|
|
56
|
+
for (const [index, priceInterfaceAccount] of priceInterfaceAccounts.entries()) {
|
|
57
|
+
const valueOffset = FIRST_PRICE_VALUE_OFFSET + index * PRICE_NODE_SIZE
|
|
58
|
+
PRICE_MINT.toBuffer().copy(data, valueOffset)
|
|
59
|
+
priceInterfaceAccount.toBuffer().copy(data, valueOffset + 128)
|
|
60
|
+
data.writeUInt8(PRICE_DECIMALS, valueOffset + 160)
|
|
61
|
+
data.writeUInt8(UNDERLYING_DECIMALS, valueOffset + 161)
|
|
62
|
+
data.fill(reservedByte, valueOffset + 162, valueOffset + 168)
|
|
63
|
+
data.writeBigUInt64LE(SY_PRICE_ID, valueOffset + 168)
|
|
64
|
+
data.fill(reservedByte, valueOffset + 176, valueOffset + EXPONENT_PRICE_SIZE)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return data
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function createPriceInterfaceAccountsData(
|
|
71
|
+
interfaceAccounts: readonly web3.PublicKey[],
|
|
72
|
+
scopeConfig?: ScopeConfigFixture,
|
|
73
|
+
): Buffer {
|
|
74
|
+
const data = Buffer.alloc(12 + interfaceAccounts.length * PUBKEY_BYTES + (scopeConfig ? 17 : 0))
|
|
75
|
+
PRICE_INTERFACE_ACCOUNTS_DISCRIMINATOR.copy(data)
|
|
76
|
+
data.writeUInt32LE(interfaceAccounts.length, 8)
|
|
77
|
+
interfaceAccounts.forEach((account, index) => account.toBuffer().copy(data, 12 + index * PUBKEY_BYTES))
|
|
78
|
+
|
|
79
|
+
if (scopeConfig) {
|
|
80
|
+
const stateOffset = 12 + interfaceAccounts.length * PUBKEY_BYTES
|
|
81
|
+
data.writeUInt8(2, stateOffset)
|
|
82
|
+
scopeConfig.priceChain.forEach((price, index) => data.writeUInt16LE(price, stateOffset + 1 + index * 2))
|
|
83
|
+
data.writeBigUInt64LE(scopeConfig.maximumAgeSeconds, stateOffset + 9)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return data
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function createConnection(registryData: Buffer, options: ConnectionOptions = {}) {
|
|
90
|
+
const registryAddress = new ExponentVaultsPDA(PROGRAM_ID).exponentPrices()[0]
|
|
91
|
+
const singleAccountReads: web3.PublicKey[] = []
|
|
92
|
+
const multipleAccountReads: web3.PublicKey[][] = []
|
|
93
|
+
let activeMultipleAccountReads = 0
|
|
94
|
+
let maxConcurrentMultipleAccountReads = 0
|
|
95
|
+
|
|
96
|
+
const connection = {
|
|
97
|
+
async getAccountInfo(address: web3.PublicKey) {
|
|
98
|
+
singleAccountReads.push(address)
|
|
99
|
+
if (!address.equals(registryAddress)) {
|
|
100
|
+
throw new Error(`Unexpected getAccountInfo for PriceInterfaceAccounts: ${address.toBase58()}`)
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
data: registryData,
|
|
104
|
+
executable: false,
|
|
105
|
+
lamports: 1,
|
|
106
|
+
owner: PROGRAM_ID,
|
|
107
|
+
rentEpoch: 0,
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
async getMultipleAccountsInfo(addresses: web3.PublicKey[]) {
|
|
111
|
+
multipleAccountReads.push(addresses)
|
|
112
|
+
activeMultipleAccountReads += 1
|
|
113
|
+
maxConcurrentMultipleAccountReads = Math.max(maxConcurrentMultipleAccountReads, activeMultipleAccountReads)
|
|
114
|
+
try {
|
|
115
|
+
await Promise.resolve()
|
|
116
|
+
if (options.rpcError) throw options.rpcError
|
|
117
|
+
|
|
118
|
+
const infos = addresses.map((address) => {
|
|
119
|
+
const fixture = options.accounts?.get(address.toBase58())
|
|
120
|
+
if (fixture === null || !fixture) return null
|
|
121
|
+
return {
|
|
122
|
+
data: fixture.data,
|
|
123
|
+
executable: false,
|
|
124
|
+
lamports: 1,
|
|
125
|
+
owner: fixture.owner ?? PROGRAM_ID,
|
|
126
|
+
rentEpoch: 0,
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
return options.shortResponse ? infos.slice(0, -1) : infos
|
|
130
|
+
} finally {
|
|
131
|
+
activeMultipleAccountReads -= 1
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
} as unknown as web3.Connection
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
connection,
|
|
138
|
+
multipleAccountReads,
|
|
139
|
+
get maxConcurrentMultipleAccountReads() {
|
|
140
|
+
return maxConcurrentMultipleAccountReads
|
|
141
|
+
},
|
|
142
|
+
registryAddress,
|
|
143
|
+
singleAccountReads,
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function createAccountFixtures(keys: readonly web3.PublicKey[]): Map<string, AccountFixture> {
|
|
148
|
+
return new Map(
|
|
149
|
+
keys.map((key, index) => [
|
|
150
|
+
key.toBase58(),
|
|
151
|
+
{
|
|
152
|
+
data: createPriceInterfaceAccountsData([createPublicKey(128 + index)], {
|
|
153
|
+
maximumAgeSeconds: BigInt(index + 1),
|
|
154
|
+
priceChain: [index + 1, index + 2, index + 3, index + 4],
|
|
155
|
+
}),
|
|
156
|
+
},
|
|
157
|
+
]),
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
test("decodes price metadata from exact wire offsets without consuming reserved bytes", async () => {
|
|
162
|
+
for (const reservedByte of [0xa5, 0x5a]) {
|
|
163
|
+
const { connection } = createConnection(createExponentPricesData([ZERO_PUBKEY], reservedByte))
|
|
164
|
+
const fetcher = new ExponentVaultsFetcher(connection, PROGRAM_ID)
|
|
165
|
+
const prices = await fetcher.fetchExponentPrices()
|
|
166
|
+
const price = prices.prices[1]
|
|
167
|
+
|
|
168
|
+
assert.ok(price)
|
|
169
|
+
assert.equal(price.priceDecimals, PRICE_DECIMALS)
|
|
170
|
+
assert.equal(price.underlyingDecimals, UNDERLYING_DECIMALS)
|
|
171
|
+
assert.equal(price.syPriceId, SY_PRICE_ID)
|
|
172
|
+
}
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
test("fetches unique price interface accounts sequentially in maximum 100-key batches", async () => {
|
|
176
|
+
const cases = [
|
|
177
|
+
{ count: 0, expectedBatchSizes: [] },
|
|
178
|
+
{ count: 1, expectedBatchSizes: [1] },
|
|
179
|
+
{ count: 99, expectedBatchSizes: [99] },
|
|
180
|
+
{ count: 100, expectedBatchSizes: [100] },
|
|
181
|
+
{ count: 101, expectedBatchSizes: [100, 1] },
|
|
182
|
+
{ count: 200, expectedBatchSizes: [100, 100] },
|
|
183
|
+
{ count: 201, expectedBatchSizes: [100, 100, 1] },
|
|
184
|
+
{ count: 1024, expectedBatchSizes: [100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 24] },
|
|
185
|
+
]
|
|
186
|
+
|
|
187
|
+
for (const { count, expectedBatchSizes } of cases) {
|
|
188
|
+
const keys = Array.from({ length: count }, (_, index) => createPublicKey(index + 1))
|
|
189
|
+
const connectionState = createConnection(createExponentPricesData(keys), { accounts: createAccountFixtures(keys) })
|
|
190
|
+
|
|
191
|
+
const prices = await new ExponentVaultsFetcher(connectionState.connection, PROGRAM_ID).fetchExponentPrices()
|
|
192
|
+
|
|
193
|
+
assert.deepEqual(
|
|
194
|
+
connectionState.multipleAccountReads.map((batch) => batch.length),
|
|
195
|
+
expectedBatchSizes,
|
|
196
|
+
`count ${count}`,
|
|
197
|
+
)
|
|
198
|
+
assert.ok(
|
|
199
|
+
connectionState.multipleAccountReads.every((batch) => batch.length <= 100),
|
|
200
|
+
`count ${count}`,
|
|
201
|
+
)
|
|
202
|
+
assert.equal(connectionState.maxConcurrentMultipleAccountReads, count === 0 ? 0 : 1, `count ${count}`)
|
|
203
|
+
assert.deepEqual(
|
|
204
|
+
connectionState.singleAccountReads.map((address) => address.toBase58()),
|
|
205
|
+
[connectionState.registryAddress.toBase58()],
|
|
206
|
+
`count ${count}`,
|
|
207
|
+
)
|
|
208
|
+
assert.deepEqual(
|
|
209
|
+
connectionState.multipleAccountReads.flat().map((address) => address.toBase58()),
|
|
210
|
+
keys.map((key) => key.toBase58()),
|
|
211
|
+
`count ${count}`,
|
|
212
|
+
)
|
|
213
|
+
for (const [index, key] of keys.entries()) {
|
|
214
|
+
const price = prices.prices[index + 1]
|
|
215
|
+
assert.ok(price, `count ${count}, price ${index + 1}`)
|
|
216
|
+
assert.deepEqual(price.interfaceAccounts, [createPublicKey(128 + index)])
|
|
217
|
+
assert.deepEqual(price.scopeConfig, {
|
|
218
|
+
maximumAgeSeconds: BigInt(index + 1),
|
|
219
|
+
priceChain: [index + 1, index + 2, index + 3, index + 4],
|
|
220
|
+
})
|
|
221
|
+
assert.equal(price.priceInterfaceAccounts.toBase58(), key.toBase58())
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
test("deduplicates nonzero price interface accounts and hydrates every matching price", async () => {
|
|
227
|
+
const firstKey = createPublicKey(250)
|
|
228
|
+
const secondKey = createPublicKey(1)
|
|
229
|
+
const keys = [firstKey, ZERO_PUBKEY, firstKey, secondKey]
|
|
230
|
+
assert.notDeepEqual([firstKey.toBase58(), secondKey.toBase58()], [firstKey.toBase58(), secondKey.toBase58()].sort())
|
|
231
|
+
const { connection, multipleAccountReads, registryAddress, singleAccountReads } = createConnection(
|
|
232
|
+
createExponentPricesData(keys),
|
|
233
|
+
{ accounts: createAccountFixtures([firstKey, secondKey]) },
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
const prices = await new ExponentVaultsFetcher(connection, PROGRAM_ID).fetchExponentPrices()
|
|
237
|
+
|
|
238
|
+
assert.deepEqual(
|
|
239
|
+
singleAccountReads.map((address) => address.toBase58()),
|
|
240
|
+
[registryAddress.toBase58()],
|
|
241
|
+
)
|
|
242
|
+
assert.deepEqual(
|
|
243
|
+
multipleAccountReads.map((batch) => batch.map((key) => key.toBase58())),
|
|
244
|
+
[[firstKey.toBase58(), secondKey.toBase58()]],
|
|
245
|
+
)
|
|
246
|
+
assert.deepEqual(prices.prices[1]?.interfaceAccounts, [createPublicKey(128)])
|
|
247
|
+
assert.deepEqual(prices.prices[2]?.interfaceAccounts, [])
|
|
248
|
+
assert.deepEqual(prices.prices[3]?.interfaceAccounts, [createPublicKey(128)])
|
|
249
|
+
assert.deepEqual(prices.prices[4]?.interfaceAccounts, [createPublicKey(129)])
|
|
250
|
+
assert.strictEqual(prices.prices[1]?.interfaceAccounts, prices.prices[3]?.interfaceAccounts)
|
|
251
|
+
assert.ok(prices.prices[1]?.scopeConfig)
|
|
252
|
+
assert.strictEqual(prices.prices[1]?.scopeConfig, prices.prices[3]?.scopeConfig)
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
test("preserves PriceInterfaceAccounts validation errors and malformed RPC response failures", async () => {
|
|
256
|
+
const key = createPublicKey(1)
|
|
257
|
+
const registryData = createExponentPricesData([key])
|
|
258
|
+
|
|
259
|
+
await assert.rejects(
|
|
260
|
+
() => new ExponentVaultsFetcher(createConnection(registryData).connection, PROGRAM_ID).fetchExponentPrices(),
|
|
261
|
+
new Error(`PriceInterfaceAccounts account not found: ${key.toBase58()}`),
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
await assert.rejects(
|
|
265
|
+
() =>
|
|
266
|
+
new ExponentVaultsFetcher(
|
|
267
|
+
createConnection(registryData, {
|
|
268
|
+
accounts: new Map([
|
|
269
|
+
[key.toBase58(), { data: createPriceInterfaceAccountsData([]), owner: createPublicKey(2) }],
|
|
270
|
+
]),
|
|
271
|
+
}).connection,
|
|
272
|
+
PROGRAM_ID,
|
|
273
|
+
).fetchExponentPrices(),
|
|
274
|
+
new Error(
|
|
275
|
+
`PriceInterfaceAccounts owner mismatch: expected ${PROGRAM_ID.toBase58()}, got ${createPublicKey(2).toBase58()}`,
|
|
276
|
+
),
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
await assert.rejects(
|
|
280
|
+
() =>
|
|
281
|
+
new ExponentVaultsFetcher(
|
|
282
|
+
createConnection(registryData, { accounts: createAccountFixtures([key]), shortResponse: true }).connection,
|
|
283
|
+
PROGRAM_ID,
|
|
284
|
+
).fetchExponentPrices(),
|
|
285
|
+
new Error("PriceInterfaceAccounts batch size mismatch: requested 1, got 0"),
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
const rpcError = new Error("RPC unavailable")
|
|
289
|
+
await assert.rejects(
|
|
290
|
+
() =>
|
|
291
|
+
new ExponentVaultsFetcher(
|
|
292
|
+
createConnection(registryData, { accounts: createAccountFixtures([key]), rpcError }).connection,
|
|
293
|
+
PROGRAM_ID,
|
|
294
|
+
).fetchExponentPrices(),
|
|
295
|
+
rpcError,
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
await assert.rejects(
|
|
299
|
+
() =>
|
|
300
|
+
new ExponentVaultsFetcher(
|
|
301
|
+
createConnection(registryData, {
|
|
302
|
+
accounts: new Map([[key.toBase58(), { data: Buffer.alloc(1) }]]),
|
|
303
|
+
}).connection,
|
|
304
|
+
PROGRAM_ID,
|
|
305
|
+
).fetchExponentPrices(),
|
|
306
|
+
new Error("PriceInterfaceAccounts account too small: 1 bytes"),
|
|
307
|
+
)
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
test("reports a missing account at its indexed position in the second batch", async () => {
|
|
311
|
+
const keys = Array.from({ length: 102 }, (_, index) => createPublicKey(index + 1))
|
|
312
|
+
const accounts = createAccountFixtures(keys)
|
|
313
|
+
const missingKey = keys[101]
|
|
314
|
+
accounts.set(missingKey.toBase58(), null)
|
|
315
|
+
|
|
316
|
+
await assert.rejects(
|
|
317
|
+
() =>
|
|
318
|
+
new ExponentVaultsFetcher(
|
|
319
|
+
createConnection(createExponentPricesData(keys), { accounts }).connection,
|
|
320
|
+
PROGRAM_ID,
|
|
321
|
+
).fetchExponentPrices(),
|
|
322
|
+
new Error(`PriceInterfaceAccounts account not found: ${missingKey.toBase58()}`),
|
|
323
|
+
)
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
test("reports an owner mismatch at its indexed position in a later batch", async () => {
|
|
327
|
+
const keys = Array.from({ length: 202 }, (_, index) => createPublicKey(index + 1))
|
|
328
|
+
const accounts = createAccountFixtures(keys)
|
|
329
|
+
const mismatchedKey = keys[201]
|
|
330
|
+
const unexpectedOwner = createPublicKey(250)
|
|
331
|
+
accounts.set(mismatchedKey.toBase58(), {
|
|
332
|
+
data: createPriceInterfaceAccountsData([]),
|
|
333
|
+
owner: unexpectedOwner,
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
await assert.rejects(
|
|
337
|
+
() =>
|
|
338
|
+
new ExponentVaultsFetcher(
|
|
339
|
+
createConnection(createExponentPricesData(keys), { accounts }).connection,
|
|
340
|
+
PROGRAM_ID,
|
|
341
|
+
).fetchExponentPrices(),
|
|
342
|
+
new Error(
|
|
343
|
+
`PriceInterfaceAccounts owner mismatch: expected ${PROGRAM_ID.toBase58()}, got ${unexpectedOwner.toBase58()}`,
|
|
344
|
+
),
|
|
345
|
+
)
|
|
346
|
+
})
|