@gearbox-protocol/sdk 17.10.1 → 17.12.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/dist/cjs/offchain/AbstractOffchainNamespace.js +31 -8
- package/dist/cjs/permissionless/api/GearboxPermissionless.js +79 -0
- package/dist/cjs/permissionless/api/chains/ChainsNamespace.js +180 -0
- package/dist/cjs/permissionless/api/chains/index.js +4 -0
- package/dist/cjs/permissionless/api/chains/schemas.js +34 -0
- package/dist/cjs/permissionless/api/chains/types.js +1 -0
- package/dist/cjs/permissionless/api/deploy/PermissionlessDeploy.js +152 -0
- package/dist/cjs/permissionless/api/deploy/effective-staleness.js +46 -0
- package/dist/cjs/permissionless/api/deploy/index.js +8 -0
- package/dist/cjs/permissionless/api/deploy/types.js +1 -0
- package/dist/cjs/permissionless/api/errors.js +82 -0
- package/dist/cjs/permissionless/api/index.js +28 -0
- package/dist/cjs/permissionless/api/oracles/PermissionlessOracles.js +84 -0
- package/dist/cjs/permissionless/api/oracles/index.js +6 -0
- package/dist/cjs/permissionless/api/oracles/schemas.js +51 -0
- package/dist/cjs/permissionless/api/oracles/types.js +1 -0
- package/dist/cjs/permissionless/api/schemas.js +28 -0
- package/dist/cjs/permissionless/api/transactions/InstanceOwnerTransactions.js +234 -0
- package/dist/cjs/permissionless/api/transactions/index.js +4 -0
- package/dist/cjs/permissionless/api/transactions/lp-price-feeds.js +86 -0
- package/dist/cjs/permissionless/api/transactions/types.js +1 -0
- package/dist/cjs/permissionless/api/types.js +1 -0
- package/dist/cjs/permissionless/bindings/cross-chain-multisig.js +1 -1
- package/dist/cjs/permissionless/bindings/index.js +3 -3
- package/dist/cjs/permissionless/bindings/instance-manager.js +3 -3
- package/dist/cjs/permissionless/bindings/price-feed-store.js +0 -2
- package/dist/cjs/permissionless/index.js +31 -8
- package/dist/cjs/permissionless/utils/governance/timelock-txs.js +1 -1
- package/dist/cjs/permissionless/utils/index.js +4 -4
- package/dist/cjs/permissionless/utils/price-update/get-price-update-tx.js +1 -1
- package/dist/cjs/permissionless/utils/price-update/index.js +1 -1
- package/dist/esm/offchain/AbstractOffchainNamespace.js +31 -8
- package/dist/esm/permissionless/api/GearboxPermissionless.js +78 -0
- package/dist/esm/permissionless/api/chains/ChainsNamespace.js +179 -0
- package/dist/esm/permissionless/api/chains/index.js +3 -0
- package/dist/esm/permissionless/api/chains/schemas.js +30 -0
- package/dist/esm/permissionless/api/chains/types.js +1 -0
- package/dist/esm/permissionless/api/deploy/PermissionlessDeploy.js +149 -0
- package/dist/esm/permissionless/api/deploy/effective-staleness.js +46 -0
- package/dist/esm/permissionless/api/deploy/index.js +4 -0
- package/dist/esm/permissionless/api/deploy/types.js +1 -0
- package/dist/esm/permissionless/api/errors.js +77 -0
- package/dist/esm/permissionless/api/index.js +13 -0
- package/dist/esm/permissionless/api/oracles/PermissionlessOracles.js +81 -0
- package/dist/esm/permissionless/api/oracles/index.js +3 -0
- package/dist/esm/permissionless/api/oracles/schemas.js +47 -0
- package/dist/esm/permissionless/api/oracles/types.js +1 -0
- package/dist/esm/permissionless/api/schemas.js +25 -0
- package/dist/esm/permissionless/api/transactions/InstanceOwnerTransactions.js +233 -0
- package/dist/esm/permissionless/api/transactions/index.js +3 -0
- package/dist/esm/permissionless/api/transactions/lp-price-feeds.js +84 -0
- package/dist/esm/permissionless/api/transactions/types.js +1 -0
- package/dist/esm/permissionless/api/types.js +1 -0
- package/dist/esm/permissionless/bindings/cross-chain-multisig.js +1 -1
- package/dist/esm/permissionless/bindings/index.js +3 -3
- package/dist/esm/permissionless/bindings/instance-manager.js +3 -3
- package/dist/esm/permissionless/bindings/price-feed-store.js +0 -2
- package/dist/esm/permissionless/index.js +17 -9
- package/dist/esm/permissionless/utils/governance/timelock-txs.js +1 -1
- package/dist/esm/permissionless/utils/index.js +4 -4
- package/dist/esm/permissionless/utils/price-update/get-price-update-tx.js +1 -1
- package/dist/esm/permissionless/utils/price-update/index.js +1 -1
- package/dist/types/offchain/AbstractOffchainNamespace.d.ts +12 -0
- package/dist/types/permissionless/api/GearboxPermissionless.d.ts +71 -0
- package/dist/types/permissionless/api/chains/ChainsNamespace.d.ts +99 -0
- package/dist/types/permissionless/api/chains/index.d.ts +3 -0
- package/dist/types/permissionless/api/chains/schemas.d.ts +49 -0
- package/dist/types/permissionless/api/chains/types.d.ts +58 -0
- package/dist/types/permissionless/api/deploy/PermissionlessDeploy.d.ts +69 -0
- package/dist/types/permissionless/api/deploy/effective-staleness.d.ts +19 -0
- package/dist/types/permissionless/api/deploy/index.d.ts +4 -0
- package/dist/types/permissionless/api/deploy/types.d.ts +87 -0
- package/dist/types/permissionless/api/errors.d.ts +63 -0
- package/dist/types/permissionless/api/index.d.ts +17 -0
- package/dist/types/permissionless/api/oracles/PermissionlessOracles.d.ts +64 -0
- package/dist/types/permissionless/api/oracles/index.d.ts +3 -0
- package/dist/types/permissionless/api/oracles/schemas.d.ts +74 -0
- package/dist/types/permissionless/api/oracles/types.d.ts +51 -0
- package/dist/types/permissionless/api/schemas.d.ts +22 -0
- package/dist/types/permissionless/api/transactions/InstanceOwnerTransactions.d.ts +68 -0
- package/dist/types/permissionless/api/transactions/index.d.ts +3 -0
- package/dist/types/permissionless/api/transactions/lp-price-feeds.d.ts +17 -0
- package/dist/types/permissionless/api/transactions/types.d.ts +58 -0
- package/dist/types/permissionless/api/types.d.ts +12 -0
- package/dist/types/permissionless/bindings/factory/credit-factory.d.ts +1 -1
- package/dist/types/permissionless/bindings/factory/loss-policy-factory.d.ts +1 -1
- package/dist/types/permissionless/bindings/factory/pool-factory.d.ts +1 -1
- package/dist/types/permissionless/bindings/factory/price-oracle-factory.d.ts +1 -1
- package/dist/types/permissionless/bindings/index.d.ts +1 -1
- package/dist/types/permissionless/core/index.d.ts +1 -1
- package/dist/types/permissionless/core/pricefeed.d.ts +0 -1
- package/dist/types/permissionless/index.d.ts +23 -10
- package/dist/types/permissionless/utils/governance/index.d.ts +1 -1
- package/dist/types/permissionless/utils/index.d.ts +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { parseAbi } from "viem";
|
|
2
|
+
//#region src/permissionless/api/deploy/effective-staleness.ts
|
|
3
|
+
const abi = parseAbi(["function stalenessPeriod() view returns (uint32)", "function latestRoundData() view returns (uint80,int256,uint256,uint256,uint80)"]);
|
|
4
|
+
/**
|
|
5
|
+
* Bound for a leg that recomputes its answer on every read and stamps it with
|
|
6
|
+
* the current block (exchange-rate adapters, NAV feeds, Pendle oracles,
|
|
7
|
+
* constant feeds). One second is the tightest value that still satisfies
|
|
8
|
+
* `block.timestamp < updatedAt + stalenessPeriod`.
|
|
9
|
+
**/
|
|
10
|
+
const CURRENT_BLOCK_STALENESS = 1;
|
|
11
|
+
/**
|
|
12
|
+
* The staleness a deployed price feed enforces on the price it reports.
|
|
13
|
+
*
|
|
14
|
+
* This is deliberately not the value the feed is registered with in the
|
|
15
|
+
* PriceFeedStore — derived feeds must be registered with 0 there, and the store
|
|
16
|
+
* rejects anything else for them with `IncorrectParameterException()`. It is
|
|
17
|
+
* the value a parent feed needs when it re-checks the leg's `updatedAt`
|
|
18
|
+
* itself.
|
|
19
|
+
*
|
|
20
|
+
* Returns `null` when it cannot be determined. Callers must surface that
|
|
21
|
+
* rather than substitute a bound: a leg constructed with a staleness its
|
|
22
|
+
* underlying oracle can never satisfy produces a feed that reverts on every
|
|
23
|
+
* read and can never be added to the store.
|
|
24
|
+
**/
|
|
25
|
+
async function deriveEffectiveStalenessPeriod(client, address) {
|
|
26
|
+
try {
|
|
27
|
+
const own = await client.readContract({
|
|
28
|
+
address,
|
|
29
|
+
abi,
|
|
30
|
+
functionName: "stalenessPeriod"
|
|
31
|
+
});
|
|
32
|
+
if (Number(own) > 0) return Number(own);
|
|
33
|
+
} catch {}
|
|
34
|
+
try {
|
|
35
|
+
const [block, round] = await Promise.all([client.getBlock(), client.readContract({
|
|
36
|
+
address,
|
|
37
|
+
abi,
|
|
38
|
+
functionName: "latestRoundData"
|
|
39
|
+
})]);
|
|
40
|
+
const updatedAt = Number(round[3]);
|
|
41
|
+
if (updatedAt > 0 && updatedAt >= Number(block.timestamp)) return CURRENT_BLOCK_STALENESS;
|
|
42
|
+
} catch {}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
export { deriveEffectiveStalenessPeriod };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { deriveEffectiveStalenessPeriod } from "./effective-staleness.js";
|
|
2
|
+
import { DEFAULT_DEPLOY_SALT, PermissionlessDeploy, priceFeedSalt } from "./PermissionlessDeploy.js";
|
|
3
|
+
import "./types.js";
|
|
4
|
+
export { DEFAULT_DEPLOY_SALT, PermissionlessDeploy, deriveEffectiveStalenessPeriod, priceFeedSalt };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { BaseError } from "viem";
|
|
2
|
+
//#region src/permissionless/api/errors.ts
|
|
3
|
+
/**
|
|
4
|
+
* Thrown when anything is read off {@link ChainsNamespace} before
|
|
5
|
+
* {@link GearboxPermissionless.attach} has completed.
|
|
6
|
+
*
|
|
7
|
+
* The chain list is the backend's answer, so there is nothing to report until
|
|
8
|
+
* it arrives. Answering with an empty list instead would be indistinguishable
|
|
9
|
+
* from a backend that serves no chains at all.
|
|
10
|
+
**/
|
|
11
|
+
var PermissionlessNotAttachedError = class extends BaseError {
|
|
12
|
+
name = "PermissionlessNotAttachedError";
|
|
13
|
+
constructor() {
|
|
14
|
+
super("The client has not attached yet.", { metaMessages: ["Await `attach()` before reading chains off the client."] });
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Thrown when a call has to read a chain the client holds no viem client for.
|
|
19
|
+
*
|
|
20
|
+
* Either the backend does not serve the chain, or it serves one this SDK
|
|
21
|
+
* carries no definition for: endpoints come from the chain definition, since
|
|
22
|
+
* the backend's own urls carry provider keys and are never handed out.
|
|
23
|
+
**/
|
|
24
|
+
var PermissionlessChainUnreachableError = class extends BaseError {
|
|
25
|
+
name = "PermissionlessChainUnreachableError";
|
|
26
|
+
/** Chain that was asked for. */
|
|
27
|
+
chainId;
|
|
28
|
+
constructor(chainId) {
|
|
29
|
+
super(`Chain ${chainId} cannot be read directly.`, { metaMessages: ["The backend either does not serve it, or serves a chain this SDK has no definition for."] });
|
|
30
|
+
this.chainId = chainId;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Thrown when a rename names a feed the PriceFeedStore does not carry. A name
|
|
35
|
+
* is part of what the store holds, so there is nothing to change on a feed
|
|
36
|
+
* that never reached it.
|
|
37
|
+
**/
|
|
38
|
+
var PriceFeedNotInStoreError = class extends BaseError {
|
|
39
|
+
name = "PriceFeedNotInStoreError";
|
|
40
|
+
/** Feed that was named. */
|
|
41
|
+
priceFeed;
|
|
42
|
+
chainId;
|
|
43
|
+
constructor(priceFeed, chainId) {
|
|
44
|
+
super(`Price feed ${priceFeed} is not in the PriceFeedStore of chain ${chainId}.`, { metaMessages: ["Only a feed the store already carries can be renamed; add it first."] });
|
|
45
|
+
this.priceFeed = priceFeed;
|
|
46
|
+
this.chainId = chainId;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Thrown when a composite feed names a leg whose own staleness bound cannot be
|
|
51
|
+
* read off the chain.
|
|
52
|
+
*
|
|
53
|
+
* Deploying with a placeholder is not an option: a composite whose check can
|
|
54
|
+
* never pass reverts on every read, and the store will not take it. The bound
|
|
55
|
+
* has to be set on the leg first.
|
|
56
|
+
**/
|
|
57
|
+
var PriceFeedStalenessUnknownError = class extends BaseError {
|
|
58
|
+
name = "PriceFeedStalenessUnknownError";
|
|
59
|
+
/** Leg whose bound could not be determined. */
|
|
60
|
+
priceFeed;
|
|
61
|
+
constructor(priceFeed) {
|
|
62
|
+
super(`Cannot determine the staleness period of price feed ${priceFeed}.`, { metaMessages: ["Deploying with a placeholder would make this composite permanently stale.", "Set the staleness period on the underlying feed first."] });
|
|
63
|
+
this.priceFeed = priceFeed;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Thrown when a price feed deploy names neither a salt nor the name one would
|
|
68
|
+
* be derived from, leaving CREATE2 nothing to distinguish it by.
|
|
69
|
+
**/
|
|
70
|
+
var PriceFeedSaltMissingError = class extends BaseError {
|
|
71
|
+
name = "PriceFeedSaltMissingError";
|
|
72
|
+
constructor() {
|
|
73
|
+
super("A price feed deploy needs either `salt` or `name`.", { metaMessages: ["`name` is hashed into the salt, which is what keeps two feeds of the", "same type and arguments from landing on the same address."] });
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
//#endregion
|
|
77
|
+
export { PermissionlessChainUnreachableError, PermissionlessNotAttachedError, PriceFeedNotInStoreError, PriceFeedSaltMissingError, PriceFeedStalenessUnknownError };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { PermissionlessChainUnreachableError, PermissionlessNotAttachedError, PriceFeedNotInStoreError, PriceFeedSaltMissingError, PriceFeedStalenessUnknownError } from "./errors.js";
|
|
2
|
+
import { ChainsNamespace } from "./chains/ChainsNamespace.js";
|
|
3
|
+
import "./chains/index.js";
|
|
4
|
+
import { deriveEffectiveStalenessPeriod } from "./deploy/effective-staleness.js";
|
|
5
|
+
import { DEFAULT_DEPLOY_SALT, PermissionlessDeploy, priceFeedSalt } from "./deploy/PermissionlessDeploy.js";
|
|
6
|
+
import "./deploy/index.js";
|
|
7
|
+
import { GET_PRICES_CHUNK_SIZE, GET_PRICES_GAS_LIMIT_BY_CHAIN_ID, PermissionlessOracles } from "./oracles/PermissionlessOracles.js";
|
|
8
|
+
import { InstanceOwnerTransactions } from "./transactions/InstanceOwnerTransactions.js";
|
|
9
|
+
import { GearboxPermissionless } from "./GearboxPermissionless.js";
|
|
10
|
+
import "./oracles/index.js";
|
|
11
|
+
import "./transactions/index.js";
|
|
12
|
+
import "./types.js";
|
|
13
|
+
export { ChainsNamespace, DEFAULT_DEPLOY_SALT, GET_PRICES_CHUNK_SIZE, GET_PRICES_GAS_LIMIT_BY_CHAIN_ID, GearboxPermissionless, InstanceOwnerTransactions, PermissionlessChainUnreachableError, PermissionlessDeploy, PermissionlessNotAttachedError, PermissionlessOracles, PriceFeedNotInStoreError, PriceFeedSaltMissingError, PriceFeedStalenessUnknownError, deriveEffectiveStalenessPeriod, priceFeedSalt };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { AbstractOffchainNamespace } from "../../../offchain/AbstractOffchainNamespace.js";
|
|
2
|
+
import { getPrices } from "../../utils/price-update/get-prices.js";
|
|
3
|
+
import { priceFeedStoreSchema, pricesSchema } from "./schemas.js";
|
|
4
|
+
//#region src/permissionless/api/oracles/PermissionlessOracles.ts
|
|
5
|
+
const GET_PRICES_GAS_LIMIT_BY_CHAIN_ID = { 143: 100000000n };
|
|
6
|
+
const GET_PRICES_CHUNK_SIZE = 200;
|
|
7
|
+
/**
|
|
8
|
+
* The price feeds of one chain: the PriceFeedStore as the backend has indexed
|
|
9
|
+
* it, and the prices behind it.
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* const store = await permissionless.oracles.store({ chainId: 1 });
|
|
13
|
+
* const { prices } = await permissionless.oracles.prices({ chainId: 1 });
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* Read-only. What the store carries is decided on chain, so putting a feed
|
|
17
|
+
* there is a batch the instance owner executes — see
|
|
18
|
+
* {@link InstanceOwnerTransactions} — not a row a client writes here.
|
|
19
|
+
**/
|
|
20
|
+
var PermissionlessOracles = class extends AbstractOffchainNamespace {
|
|
21
|
+
#chains;
|
|
22
|
+
constructor(options, chains) {
|
|
23
|
+
super("PermissionlessOracles", {
|
|
24
|
+
...options,
|
|
25
|
+
chainIds: []
|
|
26
|
+
});
|
|
27
|
+
this.#chains = chains;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The store with the assets and feeds of one chain.
|
|
31
|
+
**/
|
|
32
|
+
async store({ chainId }) {
|
|
33
|
+
return this.getData({
|
|
34
|
+
path: `/pfs/${chainId}`,
|
|
35
|
+
schema: priceFeedStoreSchema
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* A price per feed, and a market cap per feed and asset. The feeds the
|
|
40
|
+
* indexer has not seen are read off the chain by the backend, so the store
|
|
41
|
+
* is priced whole.
|
|
42
|
+
**/
|
|
43
|
+
async prices({ chainId }) {
|
|
44
|
+
return this.getData({
|
|
45
|
+
path: `/pfs/${chainId}/prices`,
|
|
46
|
+
schema: pricesSchema
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The price each of the named feeds reports, read off the chain.
|
|
51
|
+
*
|
|
52
|
+
* Where {@link prices} answers for the store as a whole and at whatever
|
|
53
|
+
* moment the backend priced it, this prices exactly the feeds asked for, at
|
|
54
|
+
* the current block. A feed the store does not carry prices the same way,
|
|
55
|
+
* which is what makes this the call to reach for after a deploy, or while a
|
|
56
|
+
* form is still choosing between candidates.
|
|
57
|
+
*
|
|
58
|
+
* Feeds needing a fresh answer before they can be read are updated inside
|
|
59
|
+
* the same simulation, so a push-based feed prices as readily as a
|
|
60
|
+
* pull-based one. `null` is a feed whose read reverted, not a zero.
|
|
61
|
+
*
|
|
62
|
+
* ```ts
|
|
63
|
+
* const prices = await permissionless.oracles.getPrices({
|
|
64
|
+
* chainId: 1,
|
|
65
|
+
* priceFeeds: [feed],
|
|
66
|
+
* });
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* @returns A price per feed, in the 8 decimals every Gearbox feed reports.
|
|
70
|
+
**/
|
|
71
|
+
async getPrices({ chainId, priceFeeds }) {
|
|
72
|
+
return getPrices({
|
|
73
|
+
client: this.#chains.client(chainId),
|
|
74
|
+
priceFeeds,
|
|
75
|
+
chunkSize: 200,
|
|
76
|
+
gasLimit: GET_PRICES_GAS_LIMIT_BY_CHAIN_ID[chainId]
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
//#endregion
|
|
81
|
+
export { GET_PRICES_CHUNK_SIZE, GET_PRICES_GAS_LIMIT_BY_CHAIN_ID, PermissionlessOracles };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { addressRecord, addressSchema } from "../schemas.js";
|
|
2
|
+
import { z } from "zod/v4";
|
|
3
|
+
//#region src/permissionless/api/oracles/schemas.ts
|
|
4
|
+
/**
|
|
5
|
+
* Runtime schemas for {@link ./types.js}.
|
|
6
|
+
*
|
|
7
|
+
* The objects are loose: the backend reads most of these rows straight off the
|
|
8
|
+
* chain through the SDK's own bindings, which attach fields the hand-written
|
|
9
|
+
* types do not declare, and dropping them would make this client lossier than
|
|
10
|
+
* the payload it decodes.
|
|
11
|
+
**/
|
|
12
|
+
/** {@link PriceFeed}, as the backend serves it. */
|
|
13
|
+
const priceFeedSchema = z.looseObject({
|
|
14
|
+
address: addressSchema,
|
|
15
|
+
contractType: z.string(),
|
|
16
|
+
version: z.number(),
|
|
17
|
+
deployedBy: addressSchema,
|
|
18
|
+
stalenessPeriod: z.number(),
|
|
19
|
+
name: z.string()
|
|
20
|
+
});
|
|
21
|
+
/** {@link PermissionlessAsset} */
|
|
22
|
+
const assetSchema = z.looseObject({
|
|
23
|
+
address: addressSchema,
|
|
24
|
+
symbol: z.string(),
|
|
25
|
+
name: z.string(),
|
|
26
|
+
decimals: z.number(),
|
|
27
|
+
owner: addressSchema,
|
|
28
|
+
priceFeeds: z.array(priceFeedSchema)
|
|
29
|
+
});
|
|
30
|
+
/** {@link PermissionlessPriceFeedStore} */
|
|
31
|
+
const priceFeedStoreSchema = z.looseObject({
|
|
32
|
+
chainId: z.number(),
|
|
33
|
+
name: z.string(),
|
|
34
|
+
isPublic: z.boolean(),
|
|
35
|
+
isActivated: z.boolean(),
|
|
36
|
+
explorerUrl: z.string(),
|
|
37
|
+
assets: z.array(assetSchema),
|
|
38
|
+
priceFeeds: z.array(priceFeedSchema)
|
|
39
|
+
});
|
|
40
|
+
/** {@link PermissionlessPrices} */
|
|
41
|
+
const pricesSchema = z.looseObject({
|
|
42
|
+
prices: addressRecord(z.string().nullable()),
|
|
43
|
+
marketCaps: addressRecord(addressRecord(z.string().nullable())),
|
|
44
|
+
time: z.number()
|
|
45
|
+
});
|
|
46
|
+
//#endregion
|
|
47
|
+
export { assetSchema, priceFeedSchema, priceFeedStoreSchema, pricesSchema };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { isAddress, isHex } from "viem";
|
|
2
|
+
import { z } from "zod/v4";
|
|
3
|
+
//#region src/permissionless/api/schemas.ts
|
|
4
|
+
/**
|
|
5
|
+
* Runtime schemas shared by the {@link GearboxPermissionless} namespaces.
|
|
6
|
+
*
|
|
7
|
+
* Addresses are validated but never rewritten: the backend serves them in the
|
|
8
|
+
* case its database holds, and its payloads key records by address, so
|
|
9
|
+
* checksumming here would silently break a lookup by the key it answered with.
|
|
10
|
+
* A caller that needs one form converts with viem's `getAddress`.
|
|
11
|
+
**/
|
|
12
|
+
/** An address, in whatever case the backend serves it. */
|
|
13
|
+
const addressSchema = z.custom((value) => typeof value === "string" && isAddress(value, { strict: false }), { message: "invalid address" });
|
|
14
|
+
/** A `0x`-prefixed hex string, as viem's `Hex`. */
|
|
15
|
+
const hexSchema = z.custom((value) => typeof value === "string" && isHex(value), { message: "invalid hex string" });
|
|
16
|
+
/**
|
|
17
|
+
* A record keyed by address. Spelled out here because zod types a record key
|
|
18
|
+
* by the schema's output, which the branded `Address` is not inferred as.
|
|
19
|
+
**/
|
|
20
|
+
function addressRecord(value) {
|
|
21
|
+
const key = z.string().refine((v) => isAddress(v, { strict: false }), { message: "invalid address" });
|
|
22
|
+
return z.record(key, value);
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { addressRecord, addressSchema, hexSchema };
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { createRawTx } from "../../../onchain/utils/createRawTx.js";
|
|
2
|
+
import { getNetworkType } from "../../../onchain/chain/chains.js";
|
|
3
|
+
import { AP_PRICE_FEED_COMPRESSOR } from "../../../onchain/constants/address-provider.js";
|
|
4
|
+
import { VERSION_RANGE_310 } from "../../../onchain/constants/versions.js";
|
|
5
|
+
import { ChainContractsRegister } from "../../../onchain/base/ChainContractsRegister.js";
|
|
6
|
+
import { OnchainSDK } from "../../../onchain/OnchainSDK.js";
|
|
7
|
+
import "../../../onchain/index.js";
|
|
8
|
+
import { PriceFeedNotInStoreError } from "../errors.js";
|
|
9
|
+
import { INSTANCE_MANAGER } from "../../utils/literals.js";
|
|
10
|
+
import { PriceFeedStoreContract } from "../../bindings/price-feed-store.js";
|
|
11
|
+
import "../../deployment/addresses.js";
|
|
12
|
+
import { AddressProviderContract } from "../../bindings/address-provider.js";
|
|
13
|
+
import { InstanceManagerContract } from "../../bindings/instance-manager.js";
|
|
14
|
+
import { convertRawTxToSafeMultisigTx, getSafeBatch } from "../../utils/governance/batch.js";
|
|
15
|
+
import { getUpdatablePriceFeeds } from "../../utils/price-update/get-updatable-feeds.js";
|
|
16
|
+
import { getFeedsToUpdate, updateBounds } from "./lp-price-feeds.js";
|
|
17
|
+
//#region src/permissionless/api/transactions/InstanceOwnerTransactions.ts
|
|
18
|
+
const lower = (value) => value.toLowerCase();
|
|
19
|
+
/**
|
|
20
|
+
* The Safe batches an instance owner executes to apply what a curator
|
|
21
|
+
* prepared: adding their private feeds to the PriceFeedStore, renaming feeds,
|
|
22
|
+
* changing staleness periods, forbidding feeds and refreshing LP bounds.
|
|
23
|
+
*
|
|
24
|
+
* Every batch is wrapped with `wrapConfigureLocal`, so the InstanceManager is
|
|
25
|
+
* the only caller the PriceFeedStore ever sees.
|
|
26
|
+
*
|
|
27
|
+
* Backend-free: a batch is built off the chain it will be executed on, and
|
|
28
|
+
* the store it reads is the one {@link PermissionlessOracles} already
|
|
29
|
+
* fetched. Nothing is sent either — a curator never executes these
|
|
30
|
+
* themselves. They prepare a batch and hand it to the instance owner, who
|
|
31
|
+
* reviews and executes it from their Safe.
|
|
32
|
+
*
|
|
33
|
+
* ```ts
|
|
34
|
+
* const txs = await permissionless.transactions.addFeeds({
|
|
35
|
+
* chainId: 1,
|
|
36
|
+
* owner: curator,
|
|
37
|
+
* feeds: [feed],
|
|
38
|
+
* pairs: [{ asset, priceFeed: feed.address }],
|
|
39
|
+
* });
|
|
40
|
+
* ```
|
|
41
|
+
**/
|
|
42
|
+
var InstanceOwnerTransactions = class {
|
|
43
|
+
#chains;
|
|
44
|
+
#oracles;
|
|
45
|
+
constructor(chains, oracles) {
|
|
46
|
+
this.#chains = chains;
|
|
47
|
+
this.#oracles = oracles;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The batch that adds the named feeds to the store and allows the named
|
|
51
|
+
* edges on it.
|
|
52
|
+
*
|
|
53
|
+
* Both lists come from the caller. Which feeds are private, and which asset
|
|
54
|
+
* each is meant to price, is held wherever the curator prepared them — the
|
|
55
|
+
* backend indexes the store, so a feed that has not reached it yet is not
|
|
56
|
+
* something this client can discover.
|
|
57
|
+
*
|
|
58
|
+
* Only feeds that actually carry an edge are added: a feed nobody priced
|
|
59
|
+
* would land in the store doing nothing.
|
|
60
|
+
**/
|
|
61
|
+
async addFeeds({ chainId, owner, feeds, pairs }) {
|
|
62
|
+
const batch = await this.#batchContext(chainId);
|
|
63
|
+
const connected = new Set(pairs.map((pair) => lower(pair.priceFeed)));
|
|
64
|
+
const feedsToAdd = feeds.filter((feed) => connected.has(lower(feed.address)));
|
|
65
|
+
const txs = [...feedsToAdd.map((feed) => batch.store.addPriceFeedTx(feed.address, feed.stalenessPeriod, feed.name)), ...pairs.map((pair) => batch.store.allowPriceFeedTx(pair.asset, pair.priceFeed))];
|
|
66
|
+
return this.#envelope({
|
|
67
|
+
batch,
|
|
68
|
+
author: owner,
|
|
69
|
+
name: "pfs-updates",
|
|
70
|
+
txs,
|
|
71
|
+
touchedFeeds: feedsToAdd.map((feed) => feed.address)
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The batch that renames feeds already in the store. A name is part of what
|
|
76
|
+
* the store holds, so changing one is a remove and a fresh add, followed by
|
|
77
|
+
* allowing the feed again on every asset that priced it.
|
|
78
|
+
**/
|
|
79
|
+
async changeNames({ chainId, owner, names }) {
|
|
80
|
+
const [batch, store] = await Promise.all([this.#batchContext(chainId), this.#oracles.store({ chainId })]);
|
|
81
|
+
const feedToAssets = await this.#feedToAssets(batch.store);
|
|
82
|
+
const inStore = new Map(store.priceFeeds.map((feed) => [lower(feed.address), feed]));
|
|
83
|
+
const txs = [];
|
|
84
|
+
for (const [address, name] of Object.entries(names)) {
|
|
85
|
+
const feed = inStore.get(lower(address));
|
|
86
|
+
if (!feed) throw new PriceFeedNotInStoreError(address, chainId);
|
|
87
|
+
txs.push(batch.store.removePriceFeedTx(feed.address), batch.store.addPriceFeedTx(feed.address, feed.stalenessPeriod, name));
|
|
88
|
+
for (const asset of feedToAssets.get(lower(address)) ?? []) txs.push(batch.store.allowPriceFeedTx(asset, feed.address));
|
|
89
|
+
}
|
|
90
|
+
return this.#envelope({
|
|
91
|
+
batch,
|
|
92
|
+
author: owner,
|
|
93
|
+
name: "pricefeed-name-changes",
|
|
94
|
+
txs,
|
|
95
|
+
touchedFeeds: Object.keys(names)
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The batch that changes how long each feed's price stays valid.
|
|
100
|
+
**/
|
|
101
|
+
async changeStalenessPeriods({ chainId, owner, periods }) {
|
|
102
|
+
const batch = await this.#batchContext(chainId);
|
|
103
|
+
const entries = Object.entries(periods);
|
|
104
|
+
const txs = entries.map(([address, period]) => batch.store.setStalenessPeriodTx(address, period));
|
|
105
|
+
return this.#envelope({
|
|
106
|
+
batch,
|
|
107
|
+
author: owner,
|
|
108
|
+
name: "pricefeed-staleness-changes",
|
|
109
|
+
txs,
|
|
110
|
+
touchedFeeds: entries.map(([address]) => address)
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* The batch that forbids feeds on the assets they were allowed for.
|
|
115
|
+
**/
|
|
116
|
+
async forbidFeeds({ chainId, owner, tokens }) {
|
|
117
|
+
const batch = await this.#batchContext(chainId);
|
|
118
|
+
const txs = Object.entries(tokens).flatMap(([token, feeds]) => feeds.map((feed) => createRawTx(batch.store.address, {
|
|
119
|
+
abi: batch.store.abi,
|
|
120
|
+
functionName: "forbidPriceFeed",
|
|
121
|
+
args: [token, feed]
|
|
122
|
+
})));
|
|
123
|
+
return this.#envelope({
|
|
124
|
+
batch,
|
|
125
|
+
author: owner,
|
|
126
|
+
name: "forbid-feeds",
|
|
127
|
+
txs,
|
|
128
|
+
touchedFeeds: []
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* The batch that pulls every drifted LP feed's lower bound back under its
|
|
133
|
+
* exchange rate. Unlike the others this takes no input: the whole store is
|
|
134
|
+
* scanned and only the feeds that drifted produce a transaction, so this
|
|
135
|
+
* one is slow.
|
|
136
|
+
**/
|
|
137
|
+
async updateBounds({ chainId, owner }) {
|
|
138
|
+
const batch = await this.#batchContext(chainId);
|
|
139
|
+
const pfCompressor = await this.#priceFeedCompressor(batch.client);
|
|
140
|
+
const known = await batch.store.getKnownPriceFeeds();
|
|
141
|
+
const feeds = await getFeedsToUpdate(batch.client, pfCompressor, known);
|
|
142
|
+
const configureTxs = [];
|
|
143
|
+
for (const feed of feeds) configureTxs.push(...await updateBounds(batch.client, feed));
|
|
144
|
+
const txs = [batch.store.configurePriceFeeds(configureTxs.map((tx) => ({
|
|
145
|
+
target: tx.to,
|
|
146
|
+
callData: tx.callData
|
|
147
|
+
})))];
|
|
148
|
+
return this.#envelope({
|
|
149
|
+
batch,
|
|
150
|
+
author: owner,
|
|
151
|
+
name: "update-bounds",
|
|
152
|
+
txs,
|
|
153
|
+
touchedFeeds: []
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* The contracts every batch of one chain is built against.
|
|
158
|
+
*
|
|
159
|
+
* Nothing here checks that the instance is activated: that is a backend
|
|
160
|
+
* read, and an instance without a deployed InstanceManager fails on the
|
|
161
|
+
* owner lookup below anyway.
|
|
162
|
+
**/
|
|
163
|
+
async #batchContext(chainId) {
|
|
164
|
+
const client = this.#chains.client(chainId);
|
|
165
|
+
const addressProvider = new AddressProviderContract("0xF7f0a609BfAb9a0A98786951ef10e5FE26cC1E38", client);
|
|
166
|
+
const instanceManager = new InstanceManagerContract(await addressProvider.getAddressOrRevert(INSTANCE_MANAGER), new ChainContractsRegister(client));
|
|
167
|
+
return {
|
|
168
|
+
chainId,
|
|
169
|
+
client,
|
|
170
|
+
store: new PriceFeedStoreContract("0x74A868AC479EE145029bB80827BB77F7B7c441cB", client),
|
|
171
|
+
instanceManager,
|
|
172
|
+
instanceOwner: await instanceManager.getOwner()
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/** The common shape every batch is returned in. */
|
|
176
|
+
async #envelope(args) {
|
|
177
|
+
const { batch, author, name, txs, touchedFeeds } = args;
|
|
178
|
+
const { chainId, client, instanceManager, instanceOwner } = batch;
|
|
179
|
+
const wrapped = txs.map((tx) => instanceManager.wrapConfigureLocal(tx));
|
|
180
|
+
const safeBatch = getSafeBatch({
|
|
181
|
+
chainId,
|
|
182
|
+
safeAddress: instanceOwner,
|
|
183
|
+
name,
|
|
184
|
+
txs: wrapped.map((tx) => convertRawTxToSafeMultisigTx(tx))
|
|
185
|
+
});
|
|
186
|
+
const [block, updatableFeeds] = await Promise.all([client.getBlock(), this.#updatableFeeds(touchedFeeds, client)]);
|
|
187
|
+
return {
|
|
188
|
+
chainId,
|
|
189
|
+
author,
|
|
190
|
+
instanceManager: instanceManager.address,
|
|
191
|
+
batches: [safeBatch.transactions],
|
|
192
|
+
createdAtBlock: Number(block.number),
|
|
193
|
+
updatableFeeds: [updatableFeeds]
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
/** Which of the touched feeds the executor has to push a price update for. */
|
|
197
|
+
async #updatableFeeds(touchedFeeds, client) {
|
|
198
|
+
if (touchedFeeds.length === 0) return [];
|
|
199
|
+
const sdk = await this.#sdk(client);
|
|
200
|
+
return (await getUpdatablePriceFeeds({
|
|
201
|
+
sdk,
|
|
202
|
+
client,
|
|
203
|
+
pfCompressor: this.#compressorFrom(sdk),
|
|
204
|
+
priceFeeds: touchedFeeds
|
|
205
|
+
})).map((contract) => contract.address);
|
|
206
|
+
}
|
|
207
|
+
async #priceFeedCompressor(client) {
|
|
208
|
+
return this.#compressorFrom(await this.#sdk(client));
|
|
209
|
+
}
|
|
210
|
+
#compressorFrom(sdk) {
|
|
211
|
+
const [compressor] = sdk.addressProvider.mustGetLatest(AP_PRICE_FEED_COMPRESSOR, VERSION_RANGE_310);
|
|
212
|
+
return compressor;
|
|
213
|
+
}
|
|
214
|
+
async #sdk(client) {
|
|
215
|
+
const chainId = client.chain?.id ?? await client.getChainId();
|
|
216
|
+
const sdk = new OnchainSDK(getNetworkType(chainId), { client });
|
|
217
|
+
await sdk.attach({ marketConfigurators: [] });
|
|
218
|
+
return sdk;
|
|
219
|
+
}
|
|
220
|
+
/** Every asset each feed is currently allowed on, keyed by lowercase feed. */
|
|
221
|
+
async #feedToAssets(store) {
|
|
222
|
+
const byFeed = /* @__PURE__ */ new Map();
|
|
223
|
+
for (const { token, priceFeeds } of await store.getTokenPriceFeedsMap()) for (const feed of priceFeeds) {
|
|
224
|
+
const key = lower(feed);
|
|
225
|
+
const assets = byFeed.get(key);
|
|
226
|
+
if (assets) assets.push(token.toLowerCase());
|
|
227
|
+
else byFeed.set(key, [token.toLowerCase()]);
|
|
228
|
+
}
|
|
229
|
+
return byFeed;
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
//#endregion
|
|
233
|
+
export { InstanceOwnerTransactions };
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { createRawTx } from "../../../onchain/utils/createRawTx.js";
|
|
2
|
+
import { simulateMulticall } from "../../../onchain/utils/viem/simulateMulticall.js";
|
|
3
|
+
import { priceFeedCompressorAbi } from "../../../abi/compressors/priceFeedCompressor.js";
|
|
4
|
+
import "../../../onchain/index.js";
|
|
5
|
+
import { decodeFunctionResult, encodeFunctionData, parseAbi } from "viem";
|
|
6
|
+
//#region src/permissionless/api/transactions/lp-price-feeds.ts
|
|
7
|
+
const LOWER_BOUND_FACTOR = 999n;
|
|
8
|
+
const ilpPriceFeedAbi = parseAbi([
|
|
9
|
+
"function contractType() external view returns (bytes32)",
|
|
10
|
+
"function setLimiter(uint256 newLimiter) external",
|
|
11
|
+
"function getLPExchangeRate() public view returns (uint256 exchangeRate)",
|
|
12
|
+
"function lowerBound() external view returns (uint256)",
|
|
13
|
+
"function upperBound() external view returns (uint256)"
|
|
14
|
+
]);
|
|
15
|
+
function currentLowerBound(client, address) {
|
|
16
|
+
return client.readContract({
|
|
17
|
+
abi: ilpPriceFeedAbi,
|
|
18
|
+
address,
|
|
19
|
+
functionName: "lowerBound"
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
function getValue(client, address) {
|
|
23
|
+
return client.readContract({
|
|
24
|
+
abi: ilpPriceFeedAbi,
|
|
25
|
+
address,
|
|
26
|
+
functionName: "getLPExchangeRate"
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function toLowerBound(value) {
|
|
30
|
+
return value * LOWER_BOUND_FACTOR / 1000n;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Moves an LP feed's lower bound back under its current exchange rate, when
|
|
34
|
+
* the rate has outgrown the bound or drifted far enough from it to be worth a
|
|
35
|
+
* transaction. Returns nothing when the bound still fits.
|
|
36
|
+
**/
|
|
37
|
+
async function updateBounds(client, address) {
|
|
38
|
+
const value = await getValue(client, address);
|
|
39
|
+
const currentLowerBoundValue = await currentLowerBound(client, address);
|
|
40
|
+
const lowerBound = toLowerBound(value);
|
|
41
|
+
const deviationFromValue = Math.floor(Number(10000n * (value - currentLowerBoundValue) / currentLowerBoundValue)) / 1e4;
|
|
42
|
+
const deviationFromBound = Math.floor(Number(10000n * (lowerBound - currentLowerBoundValue) / currentLowerBoundValue)) / 1e4;
|
|
43
|
+
const txs = [];
|
|
44
|
+
if (deviationFromValue < 0 || Math.abs(deviationFromBound) > .005) txs.push(createRawTx(address, {
|
|
45
|
+
abi: ilpPriceFeedAbi,
|
|
46
|
+
functionName: "setLimiter",
|
|
47
|
+
args: [lowerBound]
|
|
48
|
+
}, `[${address}].setLimiter(${lowerBound}); // deviationFromValue: ${(100 * deviationFromValue).toFixed(2)}%`));
|
|
49
|
+
return txs;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Of the whole feed tree behind `feeds`, the ones that are LP feeds: the
|
|
53
|
+
* probe keeps whatever answers `getLPExchangeRate`.
|
|
54
|
+
**/
|
|
55
|
+
async function getFeedsToUpdate(client, pfCompressor, feeds) {
|
|
56
|
+
const updateableFeeds = /* @__PURE__ */ new Set();
|
|
57
|
+
const { data } = await client.call({
|
|
58
|
+
to: pfCompressor,
|
|
59
|
+
data: encodeFunctionData({
|
|
60
|
+
abi: priceFeedCompressorAbi,
|
|
61
|
+
functionName: "loadPriceFeedTree",
|
|
62
|
+
args: [feeds]
|
|
63
|
+
}),
|
|
64
|
+
gas: 200000000n
|
|
65
|
+
});
|
|
66
|
+
const trees = decodeFunctionResult({
|
|
67
|
+
abi: priceFeedCompressorAbi,
|
|
68
|
+
functionName: "loadPriceFeedTree",
|
|
69
|
+
data: data ?? "0x"
|
|
70
|
+
});
|
|
71
|
+
for (const tree of trees) updateableFeeds.add(tree.baseParams.addr.toLowerCase());
|
|
72
|
+
const candidates = Array.from(updateableFeeds);
|
|
73
|
+
const { results } = await simulateMulticall(client, {
|
|
74
|
+
contracts: candidates.map((feed) => ({
|
|
75
|
+
address: feed,
|
|
76
|
+
abi: ilpPriceFeedAbi,
|
|
77
|
+
functionName: "getLPExchangeRate"
|
|
78
|
+
})),
|
|
79
|
+
allowFailure: true
|
|
80
|
+
});
|
|
81
|
+
return candidates.filter((_, index) => results[index]?.status === "success");
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
export { getFeedsToUpdate, updateBounds };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -3,9 +3,9 @@ import { BaseContract } from "../../onchain/base/BaseContract.js";
|
|
|
3
3
|
import "../../onchain/index.js";
|
|
4
4
|
import { CROSS_CHAIN_MULTISIG } from "../utils/literals.js";
|
|
5
5
|
import { normalizeSignature } from "../utils/signature.js";
|
|
6
|
-
import { crossChainMultisigAbi } from "../../abi/310/crossChainMultisig.js";
|
|
7
6
|
import "../deployment/addresses.js";
|
|
8
7
|
import { InstanceManagerContract } from "./instance-manager.js";
|
|
8
|
+
import { crossChainMultisigAbi } from "../../abi/310/crossChainMultisig.js";
|
|
9
9
|
import { MarketConfiguratorFactoryContract } from "./market-configurator-factory.js";
|
|
10
10
|
import { parseAbi, parseEventLogs, recoverTypedDataAddress, zeroAddress } from "viem";
|
|
11
11
|
//#region src/permissionless/bindings/cross-chain-multisig.ts
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { AddressProviderContract } from "./address-provider.js";
|
|
2
1
|
import { BytecodeRepositoryContract } from "./bytecode-repository.js";
|
|
2
|
+
import { priceFeedSetupParams } from "./pricefeeds/index.js";
|
|
3
|
+
import { PriceFeedStoreContract } from "./price-feed-store.js";
|
|
4
|
+
import { AddressProviderContract } from "./address-provider.js";
|
|
3
5
|
import { TokenCompressorContract } from "./compressors/token-compressor.js";
|
|
4
6
|
import { WithdrawalCompressorContract } from "./compressors/withdrawal-compressor.js";
|
|
5
7
|
import "./compressors/index.js";
|
|
6
|
-
import { priceFeedSetupParams } from "./pricefeeds/index.js";
|
|
7
|
-
import { PriceFeedStoreContract } from "./price-feed-store.js";
|
|
8
8
|
import { RoutingManagerContract } from "./router/routing-manager.js";
|
|
9
9
|
import "./router/index.js";
|
|
10
10
|
import { TreasurySplitterContract } from "./treasury-splitter.js";
|