@gearbox-protocol/sdk 17.10.1 → 17.11.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.
Files changed (112) hide show
  1. package/dist/cjs/offchain/AbstractOffchainNamespace.js +31 -8
  2. package/dist/cjs/permissionless/api/GearboxPermissionless.js +79 -0
  3. package/dist/cjs/permissionless/api/chains/ChainsNamespace.js +180 -0
  4. package/dist/cjs/permissionless/api/chains/index.js +4 -0
  5. package/dist/cjs/permissionless/api/chains/schemas.js +34 -0
  6. package/dist/cjs/permissionless/api/chains/types.js +1 -0
  7. package/dist/cjs/permissionless/api/deploy/PermissionlessDeploy.js +152 -0
  8. package/dist/cjs/permissionless/api/deploy/effective-staleness.js +46 -0
  9. package/dist/cjs/permissionless/api/deploy/index.js +8 -0
  10. package/dist/cjs/permissionless/api/deploy/types.js +1 -0
  11. package/dist/cjs/permissionless/api/errors.js +82 -0
  12. package/dist/cjs/permissionless/api/index.js +28 -0
  13. package/dist/cjs/permissionless/api/oracles/PermissionlessOracles.js +84 -0
  14. package/dist/cjs/permissionless/api/oracles/index.js +6 -0
  15. package/dist/cjs/permissionless/api/oracles/schemas.js +64 -0
  16. package/dist/cjs/permissionless/api/oracles/types.js +1 -0
  17. package/dist/cjs/permissionless/api/schemas.js +28 -0
  18. package/dist/cjs/permissionless/api/transactions/InstanceOwnerTransactions.js +230 -0
  19. package/dist/cjs/permissionless/api/transactions/index.js +4 -0
  20. package/dist/cjs/permissionless/api/transactions/lp-price-feeds.js +86 -0
  21. package/dist/cjs/permissionless/api/transactions/types.js +1 -0
  22. package/dist/cjs/permissionless/api/types.js +1 -0
  23. package/dist/cjs/permissionless/bindings/cross-chain-multisig.js +1 -1
  24. package/dist/cjs/permissionless/bindings/index.js +3 -3
  25. package/dist/cjs/permissionless/bindings/instance-manager.js +3 -3
  26. package/dist/cjs/permissionless/index.js +31 -8
  27. package/dist/cjs/permissionless/utils/governance/timelock-txs.js +1 -1
  28. package/dist/cjs/permissionless/utils/index.js +4 -4
  29. package/dist/cjs/permissionless/utils/price-update/get-price-update-tx.js +1 -1
  30. package/dist/cjs/permissionless/utils/price-update/index.js +1 -1
  31. package/dist/esm/dev/AccountOpener.js +1 -1
  32. package/dist/esm/dev/withdrawalUtils.js +1 -1
  33. package/dist/esm/history/trace/extractTransfers.js +1 -1
  34. package/dist/esm/offchain/AbstractOffchainNamespace.js +31 -8
  35. package/dist/esm/onchain/accounts/CreditAccountsServiceV310.js +2 -2
  36. package/dist/esm/onchain/accounts/liquidations/LiquidationsService.js +1 -1
  37. package/dist/esm/onchain/accounts/withdrawal-compressor/RedemptionLoggerV310Contract.js +1 -1
  38. package/dist/esm/onchain/accounts/withdrawal-compressor/WithdrawalCompressorV310Contract.js +1 -1
  39. package/dist/esm/onchain/accounts/withdrawal-compressor/WithdrawalCompressorV311Contract.js +1 -1
  40. package/dist/esm/onchain/accounts/withdrawal-compressor/WithdrawalCompressorV313Contract.js +1 -1
  41. package/dist/esm/onchain/base/TokensMeta.js +3 -3
  42. package/dist/esm/onchain/core/createAddressProvider.js +1 -1
  43. package/dist/esm/onchain/market/adapters/contracts/AccountMigratorAdapterContract.js +1 -1
  44. package/dist/esm/onchain/market/adapters/contracts/ERC4626AdapterContract.js +1 -1
  45. package/dist/esm/onchain/market/credit/CreditFacadeV310BaseContract.js +1 -1
  46. package/dist/esm/onchain/market/pool/PoolV310Contract.js +1 -1
  47. package/dist/esm/onchain/market/rwa/createDegenNFT.js +1 -1
  48. package/dist/esm/onchain/market/zapper/IETHZapperContract.js +1 -1
  49. package/dist/esm/onchain/market/zapper/ZapperContract.js +1 -1
  50. package/dist/esm/onchain/preview/preview/previewPoolPositionOperation.js +2 -2
  51. package/dist/esm/onchain/utils/viem/simulateWithPriceUpdates.js +1 -1
  52. package/dist/esm/permissionless/api/GearboxPermissionless.js +78 -0
  53. package/dist/esm/permissionless/api/chains/ChainsNamespace.js +179 -0
  54. package/dist/esm/permissionless/api/chains/index.js +3 -0
  55. package/dist/esm/permissionless/api/chains/schemas.js +30 -0
  56. package/dist/esm/permissionless/api/chains/types.js +1 -0
  57. package/dist/esm/permissionless/api/deploy/PermissionlessDeploy.js +149 -0
  58. package/dist/esm/permissionless/api/deploy/effective-staleness.js +46 -0
  59. package/dist/esm/permissionless/api/deploy/index.js +4 -0
  60. package/dist/esm/permissionless/api/deploy/types.js +1 -0
  61. package/dist/esm/permissionless/api/errors.js +77 -0
  62. package/dist/esm/permissionless/api/index.js +13 -0
  63. package/dist/esm/permissionless/api/oracles/PermissionlessOracles.js +81 -0
  64. package/dist/esm/permissionless/api/oracles/index.js +3 -0
  65. package/dist/esm/permissionless/api/oracles/schemas.js +59 -0
  66. package/dist/esm/permissionless/api/oracles/types.js +1 -0
  67. package/dist/esm/permissionless/api/schemas.js +25 -0
  68. package/dist/esm/permissionless/api/transactions/InstanceOwnerTransactions.js +229 -0
  69. package/dist/esm/permissionless/api/transactions/index.js +3 -0
  70. package/dist/esm/permissionless/api/transactions/lp-price-feeds.js +84 -0
  71. package/dist/esm/permissionless/api/transactions/types.js +1 -0
  72. package/dist/esm/permissionless/api/types.js +1 -0
  73. package/dist/esm/permissionless/bindings/cross-chain-multisig.js +1 -1
  74. package/dist/esm/permissionless/bindings/index.js +3 -3
  75. package/dist/esm/permissionless/bindings/instance-manager.js +3 -3
  76. package/dist/esm/permissionless/index.js +17 -9
  77. package/dist/esm/permissionless/utils/governance/timelock-txs.js +1 -1
  78. package/dist/esm/permissionless/utils/index.js +4 -4
  79. package/dist/esm/permissionless/utils/price-update/get-price-update-tx.js +1 -1
  80. package/dist/esm/permissionless/utils/price-update/index.js +1 -1
  81. package/dist/types/offchain/AbstractOffchainNamespace.d.ts +12 -0
  82. package/dist/types/permissionless/api/GearboxPermissionless.d.ts +71 -0
  83. package/dist/types/permissionless/api/chains/ChainsNamespace.d.ts +99 -0
  84. package/dist/types/permissionless/api/chains/index.d.ts +3 -0
  85. package/dist/types/permissionless/api/chains/schemas.d.ts +49 -0
  86. package/dist/types/permissionless/api/chains/types.d.ts +58 -0
  87. package/dist/types/permissionless/api/deploy/PermissionlessDeploy.d.ts +69 -0
  88. package/dist/types/permissionless/api/deploy/effective-staleness.d.ts +19 -0
  89. package/dist/types/permissionless/api/deploy/index.d.ts +4 -0
  90. package/dist/types/permissionless/api/deploy/types.d.ts +87 -0
  91. package/dist/types/permissionless/api/errors.d.ts +63 -0
  92. package/dist/types/permissionless/api/index.d.ts +17 -0
  93. package/dist/types/permissionless/api/oracles/PermissionlessOracles.d.ts +64 -0
  94. package/dist/types/permissionless/api/oracles/index.d.ts +3 -0
  95. package/dist/types/permissionless/api/oracles/schemas.d.ts +103 -0
  96. package/dist/types/permissionless/api/oracles/types.d.ts +63 -0
  97. package/dist/types/permissionless/api/schemas.d.ts +22 -0
  98. package/dist/types/permissionless/api/transactions/InstanceOwnerTransactions.d.ts +60 -0
  99. package/dist/types/permissionless/api/transactions/index.d.ts +3 -0
  100. package/dist/types/permissionless/api/transactions/lp-price-feeds.d.ts +17 -0
  101. package/dist/types/permissionless/api/transactions/types.d.ts +36 -0
  102. package/dist/types/permissionless/api/types.d.ts +12 -0
  103. package/dist/types/permissionless/bindings/factory/credit-factory.d.ts +1 -1
  104. package/dist/types/permissionless/bindings/factory/loss-policy-factory.d.ts +1 -1
  105. package/dist/types/permissionless/bindings/factory/pool-factory.d.ts +1 -1
  106. package/dist/types/permissionless/bindings/factory/price-oracle-factory.d.ts +1 -1
  107. package/dist/types/permissionless/bindings/index.d.ts +1 -1
  108. package/dist/types/permissionless/core/index.d.ts +1 -1
  109. package/dist/types/permissionless/index.d.ts +23 -10
  110. package/dist/types/permissionless/utils/governance/index.d.ts +1 -1
  111. package/dist/types/permissionless/utils/index.d.ts +1 -1
  112. package/package.json +1 -1
@@ -0,0 +1,179 @@
1
+ import { getChain } from "../../../onchain/chain/chains.js";
2
+ import { AbstractOffchainNamespace } from "../../../offchain/AbstractOffchainNamespace.js";
3
+ import { PermissionlessChainUnreachableError, PermissionlessNotAttachedError } from "../errors.js";
4
+ import { chainListSchema, chainSummaryListSchema } from "./schemas.js";
5
+ import { createPublicClient, fallback, http } from "viem";
6
+ //#region src/permissionless/api/chains/ChainsNamespace.ts
7
+ /**
8
+ * The chains the permissionless stack runs on, and the viem clients that
9
+ * reach them.
10
+ *
11
+ * Both halves live here because they answer the same question. Which chains
12
+ * exist is the backend's to decide — it holds rows for chains it does not
13
+ * serve, and those must not be reachable — so the list is also the only
14
+ * honest basis for deciding which clients to build. Nothing is passed in: a
15
+ * caller that had to name the chains could name one the backend does not
16
+ * serve, or miss one it does.
17
+ *
18
+ * The list is fetched once, by {@link attach}, and kept for the client's
19
+ * lifetime:
20
+ *
21
+ * ```ts
22
+ * await permissionless.attach();
23
+ * const chains = permissionless.chains.list();
24
+ * ```
25
+ *
26
+ * Every read before that throws {@link PermissionlessNotAttachedError} rather
27
+ * than answering with an empty list, which would be indistinguishable from a
28
+ * backend that serves nothing.
29
+ *
30
+ * What {@link attach} reads is only what identifies a chain. The counters a
31
+ * list renders next to one are {@link summary}, a second read, because they
32
+ * aggregate over every market the backend knows and nothing should wait on
33
+ * that to start.
34
+ **/
35
+ var ChainsNamespace = class extends AbstractOffchainNamespace {
36
+ #clients = /* @__PURE__ */ new Map();
37
+ #chains;
38
+ #attaching;
39
+ /** The scope {@link attach} settled on, which {@link summary} follows. */
40
+ #scope;
41
+ constructor(options) {
42
+ super("ChainsNamespace", {
43
+ ...options,
44
+ chainIds: []
45
+ });
46
+ }
47
+ /**
48
+ * Reads the chain list off the backend and builds a client for each chain
49
+ * it names.
50
+ *
51
+ * Idempotent, and joins a call already in flight, so a double invocation —
52
+ * React's StrictMode, two components mounting at once — costs one request.
53
+ * A second call with a different `scope` is a no-op: the list is decided
54
+ * once.
55
+ **/
56
+ async attach({ scope } = {}) {
57
+ if (this.#chains) return;
58
+ this.#attaching ??= this.#load(scope).finally(() => {
59
+ this.#attaching = void 0;
60
+ });
61
+ return this.#attaching;
62
+ }
63
+ /**
64
+ * Whether {@link attach} has completed.
65
+ **/
66
+ get isAttached() {
67
+ return this.#chains !== void 0;
68
+ }
69
+ /**
70
+ * Every chain the backend serves, in the order it listed them.
71
+ **/
72
+ list() {
73
+ if (!this.#chains) throw new PermissionlessNotAttachedError();
74
+ return this.#chains;
75
+ }
76
+ /**
77
+ * One chain by id, or nothing when the backend does not serve it.
78
+ *
79
+ * Reads the attached list rather than a route of its own, which is also
80
+ * what makes "not served" and "not activated" distinguishable: the first is
81
+ * absence here, the second is {@link PermissionlessChain.isActivated}.
82
+ **/
83
+ find(chainId) {
84
+ return this.list().find((chain) => chain.chainId === chainId);
85
+ }
86
+ /**
87
+ * The same chains, with the counters a list renders next to them.
88
+ *
89
+ * A read of its own, and not part of {@link attach}: the counters aggregate
90
+ * over every market configurator and market the backend knows, which is
91
+ * slow enough that waiting on it would hold up the whole client. A list
92
+ * renders off {@link list} and fills the numbers in when this resolves.
93
+ *
94
+ * Nothing is cached — each call is a request — so a caller that renders
95
+ * this should hold it in whatever already owns its async state.
96
+ *
97
+ * Covers the scope {@link attach} settled on unless one is named here, so
98
+ * the counters describe the list that is already on screen rather than a
99
+ * different set of chains.
100
+ *
101
+ * ```ts
102
+ * await permissionless.attach();
103
+ * const chains = permissionless.chains.list();
104
+ * const withCounters = await permissionless.chains.summary();
105
+ * ```
106
+ **/
107
+ async summary({ scope } = {}) {
108
+ return this.getData({
109
+ path: "/chain/summary",
110
+ query: { scope: scope ?? this.#scope },
111
+ schema: chainSummaryListSchema
112
+ });
113
+ }
114
+ /**
115
+ * Chains a client could be built for. A subset of {@link list} — see
116
+ * {@link client}.
117
+ **/
118
+ get chainIds() {
119
+ if (!this.#chains) throw new PermissionlessNotAttachedError();
120
+ return [...this.#clients.keys()];
121
+ }
122
+ /**
123
+ * The client for one chain.
124
+ *
125
+ * @throws {@link PermissionlessChainUnreachableError} when the backend does
126
+ * not serve the chain, or serves one this SDK carries no definition for.
127
+ **/
128
+ client(chainId) {
129
+ if (!this.#chains) throw new PermissionlessNotAttachedError();
130
+ const client = this.#clients.get(chainId);
131
+ if (!client) throw new PermissionlessChainUnreachableError(chainId);
132
+ return client;
133
+ }
134
+ async #load(scope) {
135
+ const chains = await this.getData({
136
+ path: "/chain/list",
137
+ query: { scope },
138
+ schema: chainListSchema
139
+ });
140
+ this.#scope = scope;
141
+ this.#clients.clear();
142
+ for (const { chainId } of chains) {
143
+ const client = this.#client(chainId);
144
+ if (client) this.#clients.set(chainId, client);
145
+ }
146
+ this.#chains = chains;
147
+ }
148
+ /**
149
+ * A client over the chain's own public endpoints, or nothing when this SDK
150
+ * has no definition for it.
151
+ *
152
+ * The backend will not hand over its endpoints — they carry provider keys —
153
+ * so the chain definition is all there is to go on. A chain the SDK does
154
+ * not know stays in {@link list}, because the backend serves it and its
155
+ * rows are readable through the backend; only the calls that have to reach
156
+ * it directly fail, and they say which chain they could not reach.
157
+ **/
158
+ #client(chainId) {
159
+ let chain;
160
+ try {
161
+ chain = getChain(chainId);
162
+ } catch {
163
+ this.logger?.warn(`no chain definition for ${chainId}: it will be listed, but nothing can be read off it directly`);
164
+ return;
165
+ }
166
+ const urls = chain.rpcUrls.default.http;
167
+ if (urls.length === 0) {
168
+ this.logger?.warn(`no public endpoint for chain ${chainId}`);
169
+ return;
170
+ }
171
+ const transports = urls.map((url) => http(url));
172
+ return createPublicClient({
173
+ chain,
174
+ transport: transports.length > 1 ? fallback(transports) : transports[0]
175
+ });
176
+ }
177
+ };
178
+ //#endregion
179
+ export { ChainsNamespace };
@@ -0,0 +1,3 @@
1
+ import { ChainsNamespace } from "./ChainsNamespace.js";
2
+ import "./types.js";
3
+ export { ChainsNamespace };
@@ -0,0 +1,30 @@
1
+ import { z } from "zod/v4";
2
+ //#region src/permissionless/api/chains/schemas.ts
3
+ /**
4
+ * Runtime schema for {@link PermissionlessChain}.
5
+ **/
6
+ const chainSchema = z.looseObject({
7
+ chainId: z.number(),
8
+ name: z.string(),
9
+ isPublic: z.boolean(),
10
+ isActivated: z.boolean(),
11
+ explorerUrl: z.string()
12
+ });
13
+ /** What `GET /chain/list` answers with. */
14
+ const chainListSchema = z.array(chainSchema);
15
+ /**
16
+ * Runtime schema for {@link PermissionlessChainSummary}.
17
+ *
18
+ * Strict about the two counters on purpose: they are the only reason this
19
+ * read exists, and a backend that answered without them would render as
20
+ * dashes where the numbers belong — a skew worth failing on rather than
21
+ * displaying.
22
+ **/
23
+ const chainSummarySchema = chainSchema.extend({
24
+ riskCuratorsQty: z.number(),
25
+ marketsQty: z.number()
26
+ });
27
+ /** What `GET /chain/summary` answers with. */
28
+ const chainSummaryListSchema = z.array(chainSummarySchema);
29
+ //#endregion
30
+ export { chainListSchema, chainSchema, chainSummaryListSchema, chainSummarySchema };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,149 @@
1
+ import { PriceFeedSaltMissingError, PriceFeedStalenessUnknownError } from "../errors.js";
2
+ import { deriveEffectiveStalenessPeriod } from "./effective-staleness.js";
3
+ import { BytecodeRepositoryContract } from "../../bindings/bytecode-repository.js";
4
+ import { PriceFeedStoreContract } from "../../bindings/price-feed-store.js";
5
+ import "../../deployment/addresses.js";
6
+ import { encodeFunctionParams } from "../../utils/abi-encoder.js";
7
+ import { keccak256, stringToHex, toHex } from "viem";
8
+ //#region src/permissionless/api/deploy/PermissionlessDeploy.ts
9
+ /**
10
+ * Contract type whose first leg needs a bound this client reads off the chain,
11
+ * see {@link PermissionlessDeploy.priceFeed}.
12
+ **/
13
+ const COMPOSITE = "PRICE_FEED::COMPOSITE";
14
+ /** Label the composite's first leg arrives under. */
15
+ const COMPOSITE_FIRST_LEG = "underlyingPriceFeed-0";
16
+ /**
17
+ * Salt a generic contract deploy gets when the caller does not name one. Two
18
+ * deploys of the same type and arguments by the same owner collide under it,
19
+ * which for a singleton like a pause committee is the point.
20
+ **/
21
+ const DEFAULT_DEPLOY_SALT = stringToHex("SALT", { size: 32 });
22
+ /**
23
+ * The salt a price feed is deployed under, derived from its name so that two
24
+ * feeds of the same type and arguments get different addresses.
25
+ **/
26
+ function priceFeedSalt(name) {
27
+ return keccak256(toHex(name));
28
+ }
29
+ /**
30
+ * Deploys through the BytecodeRepository: the constructor arguments it wants,
31
+ * the CREATE2 address those produce, and the transaction that gets there.
32
+ *
33
+ * Entirely on-chain — no backend is involved, every answer is derived from the
34
+ * target chain. Nothing is sent either: a browser holds the only wallet, so a
35
+ * call prepares the deploy and the caller sends
36
+ * {@link PreparedDeployment.tx} with whatever signs for them.
37
+ *
38
+ * ```ts
39
+ * const feed = await permissionless.deploy.priceFeed({
40
+ * chainId: 1,
41
+ * owner: account,
42
+ * contractType: "PRICE_FEED::CHAINLINK",
43
+ * version: 310,
44
+ * name: "WETH / USD",
45
+ * values,
46
+ * });
47
+ * if (!feed.isAlreadyDeployed) {
48
+ * await sendRawTx(wallet, { tx: feed.tx });
49
+ * }
50
+ * ```
51
+ **/
52
+ var PermissionlessDeploy = class {
53
+ #chains;
54
+ constructor(chains) {
55
+ this.#chains = chains;
56
+ }
57
+ /**
58
+ * Prepares a price feed deploy from the values a form collected: encodes
59
+ * them the way the PriceFeedStore reads them back, and reports where the
60
+ * feed will land.
61
+ *
62
+ * A COMPOSITE's first leg is the one case where the values are not taken as
63
+ * given. It arrives with `stalenessPeriod` 0 whenever it is a derived feed,
64
+ * because the store registers those with 0 and rejects anything else — which
65
+ * says nothing about what the leg itself enforces. A composite built on that
66
+ * 0 can never pass its own staleness check, so the real bound is read off
67
+ * the leg, see {@link deriveEffectiveStalenessPeriod}.
68
+ *
69
+ * The deployed feed reaches {@link PermissionlessOracles.store} only once
70
+ * it is in the PriceFeedStore, which is a batch the instance owner
71
+ * executes, see {@link InstanceOwnerTransactions.addFeeds}.
72
+ **/
73
+ async priceFeed(args) {
74
+ const client = this.#chains.client(args.chainId);
75
+ const salt = this.#priceFeedSalt(args);
76
+ const values = args.contractType === COMPOSITE ? await this.#withDerivedStaleness(client, args.values) : args.values;
77
+ const encodedParams = await new PriceFeedStoreContract("0x74A868AC479EE145029bB80827BB77F7B7c441cB", client).encodeConstructorParams(args.contractType, args.version, values);
78
+ return {
79
+ ...await this.#prepare(client, {
80
+ contractType: args.contractType,
81
+ version: args.version,
82
+ owner: args.owner,
83
+ encodedParams,
84
+ salt
85
+ }),
86
+ values
87
+ };
88
+ }
89
+ /**
90
+ * Prepares a generic contract deploy from ABI type/value pairs.
91
+ *
92
+ * Unlike a price feed, the arguments here are already typed by the caller,
93
+ * so nothing is looked up: they are encoded as given and the address they
94
+ * produce is reported.
95
+ **/
96
+ async contract(args) {
97
+ const client = this.#chains.client(args.chainId);
98
+ return this.#prepare(client, {
99
+ contractType: args.contractType,
100
+ version: args.version,
101
+ owner: args.owner,
102
+ encodedParams: encodeFunctionParams(args.values),
103
+ salt: args.salt ?? DEFAULT_DEPLOY_SALT
104
+ });
105
+ }
106
+ /**
107
+ * Where the encoded deploy lands, whether it is already there, and the
108
+ * transaction that puts it there.
109
+ **/
110
+ async #prepare(client, args) {
111
+ const repository = new BytecodeRepositoryContract("0x1cE2B1BE96a082b1b1539F80d5D8f82Ec06a0f9A", client);
112
+ const [isAlreadyDeployed, address] = await repository.isAlreadyDeployedAddress(args);
113
+ return {
114
+ ...args,
115
+ address,
116
+ isAlreadyDeployed,
117
+ tx: repository.deployTx({
118
+ contractType: args.contractType,
119
+ version: BigInt(args.version),
120
+ encodedParams: args.encodedParams,
121
+ salt: args.salt
122
+ })
123
+ };
124
+ }
125
+ /**
126
+ * The values with the composite's first leg given the bound it actually
127
+ * enforces, when it came in without one.
128
+ **/
129
+ async #withDerivedStaleness(client, values) {
130
+ const leg = values[COMPOSITE_FIRST_LEG];
131
+ if (!leg || typeof leg !== "object" || !leg.address || leg.stalenessPeriod !== 0) return values;
132
+ const derived = await deriveEffectiveStalenessPeriod(client, leg.address);
133
+ if (derived === null) throw new PriceFeedStalenessUnknownError(leg.address);
134
+ return {
135
+ ...values,
136
+ [COMPOSITE_FIRST_LEG]: {
137
+ ...leg,
138
+ stalenessPeriod: derived
139
+ }
140
+ };
141
+ }
142
+ #priceFeedSalt(args) {
143
+ if (args.salt) return args.salt;
144
+ if (!args.name) throw new PriceFeedSaltMissingError();
145
+ return priceFeedSalt(args.name);
146
+ }
147
+ };
148
+ //#endregion
149
+ export { DEFAULT_DEPLOY_SALT, PermissionlessDeploy, priceFeedSalt };
@@ -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,3 @@
1
+ import { GET_PRICES_CHUNK_SIZE, GET_PRICES_GAS_LIMIT_BY_CHAIN_ID, PermissionlessOracles } from "./PermissionlessOracles.js";
2
+ import "./types.js";
3
+ export { GET_PRICES_CHUNK_SIZE, GET_PRICES_GAS_LIMIT_BY_CHAIN_ID, PermissionlessOracles };
@@ -0,0 +1,59 @@
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
+ isInStore: z.boolean(),
19
+ stalenessPeriod: z.number(),
20
+ name: z.string()
21
+ });
22
+ /** {@link PermissionlessPriceFeedStatus} */
23
+ const priceFeedStatusSchema = z.enum([
24
+ "private",
25
+ "inStore",
26
+ "pendingForbidden",
27
+ "forbidden"
28
+ ]);
29
+ /** {@link PermissionlessAsset} */
30
+ const assetSchema = z.looseObject({
31
+ address: addressSchema,
32
+ symbol: z.string(),
33
+ name: z.string(),
34
+ decimals: z.number(),
35
+ isInStore: z.boolean(),
36
+ owner: addressSchema,
37
+ priceFeeds: z.array(z.looseObject({
38
+ pricefeed: priceFeedSchema,
39
+ status: priceFeedStatusSchema
40
+ }))
41
+ });
42
+ /** {@link PermissionlessPriceFeedStore} */
43
+ const priceFeedStoreSchema = z.looseObject({
44
+ chainId: z.number(),
45
+ name: z.string(),
46
+ isPublic: z.boolean(),
47
+ isActivated: z.boolean(),
48
+ explorerUrl: z.string(),
49
+ assets: z.array(assetSchema),
50
+ priceFeeds: z.array(priceFeedSchema)
51
+ });
52
+ /** {@link PermissionlessPrices} */
53
+ const pricesSchema = z.looseObject({
54
+ prices: addressRecord(z.string().nullable()),
55
+ marketCaps: addressRecord(addressRecord(z.string().nullable())),
56
+ time: z.number()
57
+ });
58
+ //#endregion
59
+ export { assetSchema, priceFeedSchema, priceFeedStatusSchema, priceFeedStoreSchema, pricesSchema };
@@ -0,0 +1 @@
1
+ export {};