@gearbox-protocol/sdk 18.2.0-next.11 → 18.2.0-next.13

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 (76) hide show
  1. package/dist/cjs/abi/310/iKillSwitchProxy.js +130 -0
  2. package/dist/cjs/dev/AccountOpener.js +1 -1
  3. package/dist/cjs/dev/withdrawalUtils.js +1 -1
  4. package/dist/cjs/history/trace/extractTransfers.js +1 -1
  5. package/dist/cjs/onchain/accounts/CreditAccountsServiceV310.js +2 -2
  6. package/dist/cjs/onchain/accounts/liquidations/LiquidationsService.js +1 -1
  7. package/dist/cjs/onchain/accounts/withdrawal-compressor/RedemptionLoggerV310Contract.js +2 -1
  8. package/dist/cjs/onchain/accounts/withdrawal-compressor/WithdrawalCompressorV310Contract.js +2 -1
  9. package/dist/cjs/onchain/accounts/withdrawal-compressor/WithdrawalCompressorV311Contract.js +2 -1
  10. package/dist/cjs/onchain/accounts/withdrawal-compressor/WithdrawalCompressorV313Contract.js +1 -1
  11. package/dist/cjs/onchain/base/TokensMeta.js +3 -3
  12. package/dist/cjs/onchain/core/createAddressProvider.js +1 -1
  13. package/dist/cjs/onchain/market/adapters/contracts/AccountMigratorAdapterContract.js +1 -1
  14. package/dist/cjs/onchain/market/adapters/contracts/ERC4626AdapterContract.js +1 -1
  15. package/dist/cjs/onchain/market/credit/CreditFacadeV310BaseContract.js +1 -1
  16. package/dist/cjs/onchain/market/pool/PoolV310Contract.js +1 -1
  17. package/dist/cjs/onchain/market/rwa/createDegenNFT.js +1 -1
  18. package/dist/cjs/onchain/market/zapper/IETHZapperContract.js +2 -1
  19. package/dist/cjs/onchain/market/zapper/ZapperContract.js +1 -1
  20. package/dist/cjs/onchain/preview/preview/previewPoolPositionOperation.js +2 -2
  21. package/dist/cjs/onchain/utils/viem/simulateWithPriceUpdates.js +1 -1
  22. package/dist/cjs/permissionless/api/index.js +7 -3
  23. package/dist/cjs/permissionless/api/oracles/PermissionlessOracles.js +11 -11
  24. package/dist/cjs/permissionless/api/oracles/index.js +7 -3
  25. package/dist/cjs/permissionless/api/oracles/score/formula.js +44 -82
  26. package/dist/cjs/permissionless/api/oracles/score/index.js +6 -3
  27. package/dist/cjs/permissionless/api/oracles/score/run.js +129 -46
  28. package/dist/cjs/permissionless/api/oracles/stress/index.js +1 -0
  29. package/dist/cjs/permissionless/api/oracles/stress/run.js +5 -2
  30. package/dist/cjs/permissionless/bindings/bytecode-repository.js +8 -0
  31. package/dist/cjs/permissionless/bindings/governance/governor.js +1 -1
  32. package/dist/cjs/permissionless/bindings/index.js +7 -2
  33. package/dist/cjs/permissionless/bindings/kill-switch-proxy.js +188 -0
  34. package/dist/cjs/permissionless/bindings/market-configurator-factory.js +39 -1
  35. package/dist/cjs/permissionless/bindings/market-configurator.js +142 -0
  36. package/dist/cjs/permissionless/index.js +15 -5
  37. package/dist/cjs/permissionless/utils/index.js +1 -0
  38. package/dist/cjs/permissionless/utils/literals.js +2 -0
  39. package/dist/esm/abi/310/iKillSwitchProxy.js +129 -0
  40. package/dist/esm/permissionless/api/index.js +3 -3
  41. package/dist/esm/permissionless/api/oracles/PermissionlessOracles.js +11 -11
  42. package/dist/esm/permissionless/api/oracles/index.js +3 -3
  43. package/dist/esm/permissionless/api/oracles/score/formula.js +39 -80
  44. package/dist/esm/permissionless/api/oracles/score/index.js +2 -2
  45. package/dist/esm/permissionless/api/oracles/score/run.js +131 -48
  46. package/dist/esm/permissionless/api/oracles/stress/index.js +2 -2
  47. package/dist/esm/permissionless/api/oracles/stress/run.js +5 -3
  48. package/dist/esm/permissionless/bindings/bytecode-repository.js +8 -0
  49. package/dist/esm/permissionless/bindings/governance/governor.js +1 -1
  50. package/dist/esm/permissionless/bindings/index.js +4 -3
  51. package/dist/esm/permissionless/bindings/kill-switch-proxy.js +184 -0
  52. package/dist/esm/permissionless/bindings/market-configurator-factory.js +37 -0
  53. package/dist/esm/permissionless/bindings/market-configurator.js +143 -1
  54. package/dist/esm/permissionless/index.js +7 -6
  55. package/dist/esm/permissionless/utils/index.js +2 -2
  56. package/dist/esm/permissionless/utils/literals.js +2 -1
  57. package/dist/types/abi/310/iKillSwitchProxy.d.ts +114 -0
  58. package/dist/types/permissionless/api/index.d.ts +4 -4
  59. package/dist/types/permissionless/api/oracles/PermissionlessOracles.d.ts +11 -11
  60. package/dist/types/permissionless/api/oracles/index.d.ts +4 -4
  61. package/dist/types/permissionless/api/oracles/score/formula.d.ts +31 -38
  62. package/dist/types/permissionless/api/oracles/score/index.d.ts +3 -3
  63. package/dist/types/permissionless/api/oracles/score/run.d.ts +7 -4
  64. package/dist/types/permissionless/api/oracles/score/types.d.ts +50 -22
  65. package/dist/types/permissionless/api/oracles/stress/index.d.ts +2 -2
  66. package/dist/types/permissionless/api/oracles/stress/run.d.ts +17 -1
  67. package/dist/types/permissionless/bindings/bytecode-repository.d.ts +10 -1
  68. package/dist/types/permissionless/bindings/index.d.ts +4 -3
  69. package/dist/types/permissionless/bindings/kill-switch-proxy.d.ts +205 -0
  70. package/dist/types/permissionless/bindings/market-configurator-factory.d.ts +30 -0
  71. package/dist/types/permissionless/bindings/market-configurator.d.ts +30 -1
  72. package/dist/types/permissionless/bindings/types.d.ts +27 -0
  73. package/dist/types/permissionless/index.d.ts +8 -7
  74. package/dist/types/permissionless/utils/index.d.ts +2 -2
  75. package/dist/types/permissionless/utils/literals.d.ts +1 -0
  76. package/package.json +1 -1
@@ -1,73 +1,156 @@
1
1
  import { classifySources, sourcesByFeed } from "../stress/classify.js";
2
- import { RATE_FEED_TYPE, readForest, readRateTargets } from "../stress/run.js";
3
- import { scoreMarket } from "./formula.js";
2
+ import { RATE_FEED_TYPE, readForest, readRateTargets, readUnder } from "../stress/run.js";
3
+ import { factorOf, finalPrice, scoreOfFactor, worstOf } from "./formula.js";
4
+ import { formatUnits } from "viem";
4
5
  //#region src/permissionless/api/oracles/score/run.ts
6
+ const ZERO_TYPE = "PRICE_FEED::ZERO";
7
+ /** Groups simulated at once: one `eth_call` each, all against one node. */
8
+ const CONCURRENCY = 6;
9
+ const feedOf = (slot) => slot.priceFeed.toLowerCase();
5
10
  /**
6
- * How a source is told apart from another when two feeds are compared.
11
+ * Every asset of one chain, scored by what each of its source groups can do
12
+ * to the price its running markets hold for it.
7
13
  *
8
- * A named source by its group, since two Chainlink aggregators are one
9
- * provider failing together. An unnamed one by its contract, since nothing
10
- * says what else it fails with — so the same contract on both sides is
11
- * shared, and two different ones are not.
12
- */
13
- function toScoreSource(source) {
14
- return {
15
- key: source.identified ? `group:${source.group}` : `contract:${source.target}`,
16
- name: source.group,
17
- identified: source.identified
18
- };
19
- }
20
- /**
21
- * Every asset of one chain, scored by how its running markets price it.
22
- *
23
- * One read of every oracle's feed tree and one of the LP feeds' rate
24
- * contracts, both multicalls: the score is a reading of the trees and needs
25
- * no simulation.
14
+ * One read of the oracles' feed trees, one of the LP feeds' rate contracts,
15
+ * one of every market feed as it stands — then one `eth_call` per source
16
+ * group, with the group's sources overridden to answer
17
+ * {@link SCORE_MULTIPLIER} times what they answer now and every market feed
18
+ * read under it.
26
19
  **/
27
20
  async function runAssetScores({ client, chainId, slots, compressor, logger }) {
28
21
  const running = slots.filter((slot) => !slot.isShutdown);
29
22
  const oracles = [...new Set(running.map((slot) => slot.priceOracle.toLowerCase()))];
30
- const { nodes, unreadableOracles } = await readForest(client, compressor, oracles);
23
+ const { nodes, answers, unreadableOracles } = await readForest(client, compressor, oracles);
31
24
  for (const oracle of unreadableOracles) logger?.warn(`price oracle ${oracle} of chain ${chainId} would not report its feed tree; the markets on it are not scored`);
32
- const { targets } = await readRateTargets(client, [...nodes.values()].filter((node) => RATE_FEED_TYPE.test(node.contractType)).map((node) => node.address));
33
- const { sources } = classifySources(chainId, [...nodes.values()], targets, () => null);
34
- const byTarget = new Map(sources.filter((source) => source.perturbable).map((source) => [source.target, toScoreSource(source)]));
35
- const feedOf = (slot) => slot.priceFeed.toLowerCase();
36
- const reach = sourcesByFeed([...new Set(running.map(feedOf))], nodes, sources);
37
- const sourcesOf = (feed) => [...reach.get(feed) ?? []].map((target) => byTarget.get(target)).filter((source) => source !== void 0);
38
- const positions = /* @__PURE__ */ new Map();
25
+ const { targets, baselines: rateBaselines } = await readRateTargets(client, [...nodes.values()].filter((node) => RATE_FEED_TYPE.test(node.contractType)).map((node) => node.address));
26
+ const { sources, unresolved } = classifySources(chainId, [...nodes.values()], targets, (target) => rateBaselines.get(target) ?? answers.get(target) ?? null);
27
+ for (const node of unresolved) logger?.warn(`feed ${node.address} of chain ${chainId} would not say what it reads its rate from; its protocol is not perturbed`);
28
+ const slotsByPosition = /* @__PURE__ */ new Map();
39
29
  for (const slot of running) {
40
30
  const key = `${slot.market.toLowerCase()}:${slot.token.toLowerCase()}`;
41
- const position = positions.get(key) ?? {};
42
- position[slot.reserve ? "reserve" : "main"] = slot;
43
- positions.set(key, position);
31
+ const entry = slotsByPosition.get(key) ?? {};
32
+ entry[slot.reserve ? "reserve" : "main"] = slot;
33
+ slotsByPosition.set(key, entry);
44
34
  }
45
- const byAsset = /* @__PURE__ */ new Map();
46
- for (const { main, reserve } of positions.values()) {
35
+ const positions = [];
36
+ for (const { main, reserve } of slotsByPosition.values()) {
47
37
  if (!main || main.isUnderlying) continue;
38
+ const mainNode = nodes.get(feedOf(main));
39
+ if (!mainNode || mainNode.contractType === ZERO_TYPE) continue;
40
+ const notes = [];
41
+ let reserveFeed;
42
+ if (!reserve) notes.push("no reserve feed");
43
+ else if (nodes.get(feedOf(reserve))?.contractType === ZERO_TYPE) notes.push("reserve is a ZeroPriceFeed");
44
+ else {
45
+ reserveFeed = feedOf(reserve);
46
+ if (reserveFeed === feedOf(main)) notes.push("reserve is the main feed");
47
+ }
48
+ positions.push({
49
+ main,
50
+ reserve,
51
+ reserveFeed,
52
+ notes
53
+ });
54
+ }
55
+ const priced = [...new Set(positions.flatMap(({ main, reserveFeed }) => reserveFeed ? [feedOf(main), reserveFeed] : [feedOf(main)]))];
56
+ if (priced.length === 0) return [];
57
+ const underlyingOf = (slot) => slot.underlyingPriceFeed?.toLowerCase();
58
+ const roots = [.../* @__PURE__ */ new Set([...priced, ...positions.map(({ main }) => underlyingOf(main)).filter((feed) => feed !== void 0)])];
59
+ const updatedAt = BigInt(Math.floor(Date.now() / 1e3));
60
+ const drivable = (source) => source !== void 0 && source.perturbable && source.baseline !== null;
61
+ const fresh = sources.filter((source) => drivable(source) && source.kind === "feed");
62
+ const asIs = await readUnder(client, roots, [], 1, updatedAt);
63
+ const stale = roots.filter((root) => (asIs.get(root) ?? null) === null);
64
+ const freshened = stale.length > 0 ? await readUnder(client, stale, [], 1, updatedAt, fresh) : /* @__PURE__ */ new Map();
65
+ const base = new Map(roots.map((root) => [root, asIs.get(root) ?? freshened.get(root) ?? null]));
66
+ const asIsRoots = roots.filter((root) => (asIs.get(root) ?? null) !== null);
67
+ const freshRoots = stale.filter((root) => (freshened.get(root) ?? null) !== null);
68
+ /** Every root under a lie, each read the way its base was. */
69
+ const readLying = async (members) => {
70
+ const [plain, refreshed] = await Promise.all([asIsRoots.length > 0 ? readUnder(client, asIsRoots, members, 5, updatedAt) : /* @__PURE__ */ new Map(), freshRoots.length > 0 ? readUnder(client, freshRoots, members, 5, updatedAt, fresh) : /* @__PURE__ */ new Map()]);
71
+ return new Map([...plain, ...refreshed]);
72
+ };
73
+ for (const position of positions) if (position.reserveFeed && (base.get(position.reserveFeed) ?? null) === null) {
74
+ position.reserveFeed = void 0;
75
+ position.notes.push("reserve does not answer");
76
+ }
77
+ const reach = sourcesByFeed(roots, nodes, sources);
78
+ const sourceByTarget = new Map(sources.map((source) => [source.target, source]));
79
+ const byGroup = /* @__PURE__ */ new Map();
80
+ for (const source of sources.filter(drivable)) byGroup.set(source.group, [...byGroup.get(source.group) ?? [], source]);
81
+ const reachedGroups = /* @__PURE__ */ new Set();
82
+ for (const root of priced) for (const target of reach.get(root) ?? []) {
83
+ const source = sourceByTarget.get(target);
84
+ if (drivable(source)) reachedGroups.add(source.group);
85
+ }
86
+ const stressed = /* @__PURE__ */ new Map();
87
+ const queue = [...reachedGroups];
88
+ for (let i = 0; i < queue.length; i += CONCURRENCY) await Promise.all(queue.slice(i, i + CONCURRENCY).map(async (group) => {
89
+ stressed.set(group, await readLying(byGroup.get(group) ?? []));
90
+ }));
91
+ const groupsOf = (feed) => {
92
+ const out = /* @__PURE__ */ new Map();
93
+ for (const target of feed ? reach.get(feed) ?? [] : []) {
94
+ const source = sourceByTarget.get(target);
95
+ if (drivable(source)) out.set(source.group, [...out.get(source.group) ?? [], source]);
96
+ }
97
+ return out;
98
+ };
99
+ const byAsset = /* @__PURE__ */ new Map();
100
+ for (const { main, reserve, reserveFeed, notes } of positions) {
101
+ const mainFeed = feedOf(main);
102
+ const before = finalPrice(base.get(mainFeed) ?? null, reserveFeed ? base.get(reserveFeed) ?? null : void 0);
103
+ if (before === null || before <= 0n) {
104
+ logger?.warn(`feed ${mainFeed} of chain ${chainId} does not answer; ${main.tokenSymbol ?? main.token} in ${main.marketName ?? main.market} is not scored`);
105
+ continue;
106
+ }
107
+ const onMain = groupsOf(mainFeed);
108
+ const onReserve = groupsOf(reserveFeed);
109
+ const impacts = [.../* @__PURE__ */ new Set([...onMain.keys(), ...onReserve.keys()])].map((group) => {
110
+ const under = stressed.get(group) ?? /* @__PURE__ */ new Map();
111
+ const after = finalPrice(under.get(mainFeed) ?? null, reserveFeed ? under.get(reserveFeed) ?? null : void 0);
112
+ const factor = factorOf(before, after);
113
+ const underlying = underlyingOf(main);
114
+ const debtBefore = underlying ? base.get(underlying) ?? null : null;
115
+ const debtFactor = debtBefore === null || debtBefore <= 0n || !underlying ? null : factorOf(debtBefore, under.get(underlying) ?? null);
116
+ const ratioFactor = factor === null || debtFactor === null || debtFactor === 0 ? null : factor / debtFactor;
117
+ const members = [...onMain.get(group) ?? [], ...onReserve.get(group) ?? []];
118
+ return {
119
+ group,
120
+ identified: members.every((source) => source.identified),
121
+ feeds: [...new Set(members.map((source) => source.description))],
122
+ reaches: onMain.has(group) ? onReserve.has(group) ? "both" : "main" : "reserve",
123
+ factor,
124
+ ratioFactor,
125
+ score: scoreOfFactor(factor)
126
+ };
127
+ });
128
+ const unverified = reserveFeed !== void 0 && impacts.some((impact) => !impact.identified && impact.reaches !== "both");
129
+ const side = (slot) => ({
130
+ feed: feedOf(slot),
131
+ name: slot.priceFeedName,
132
+ contractType: nodes.get(feedOf(slot))?.contractType ?? "",
133
+ sources: [...groupsOf(feedOf(slot)).keys()]
134
+ });
48
135
  const asset = main.token.toLowerCase();
49
136
  const entry = byAsset.get(asset) ?? {
50
137
  symbol: main.tokenSymbol,
51
138
  markets: []
52
139
  };
53
140
  byAsset.set(asset, entry);
54
- const mainNode = nodes.get(feedOf(main));
55
- if (!mainNode) continue;
56
- if (mainNode.contractType === "PRICE_FEED::ZERO") continue;
57
- const side = (slot) => ({
58
- feed: {
59
- feed: feedOf(slot),
60
- name: slot.priceFeedName,
61
- contractType: nodes.get(feedOf(slot))?.contractType ?? ""
62
- },
63
- sources: sourcesOf(feedOf(slot))
64
- });
65
- entry.markets.push(scoreMarket({
141
+ entry.markets.push({
66
142
  market: main.market,
67
143
  marketName: main.marketName,
68
144
  marketConfigurator: main.marketConfigurator,
69
- curatorName: main.curatorName
70
- }, side(main), reserve ? side(reserve) : null));
145
+ curatorName: main.curatorName,
146
+ main: side(main),
147
+ reserve: reserve ? side(reserve) : null,
148
+ hasReserve: reserveFeed !== void 0,
149
+ price: Number(formatUnits(before, 8)),
150
+ unverified,
151
+ notes,
152
+ ...worstOf(impacts)
153
+ });
71
154
  }
72
155
  return [...byAsset].map(([asset, { symbol, markets }]) => {
73
156
  const sorted = markets.sort((a, b) => a.score - b.score);
@@ -1,6 +1,6 @@
1
1
  import { classifySources, sourcesByFeed } from "./classify.js";
2
2
  import { UNBOUNDED_DOWN, UNBOUNDED_UP, groupOverride, perturb, rateStub, roundDataStub } from "./override.js";
3
3
  import { scoreGroup, slotHarm, verdictOf } from "./score.js";
4
- import { DEFAULT_SCENARIOS, RATE_FEED_TYPE, readForest, readRateTargets, runStressTest } from "./run.js";
4
+ import { DEFAULT_SCENARIOS, RATE_FEED_TYPE, readForest, readRateTargets, readUnder, runStressTest } from "./run.js";
5
5
  import "./types.js";
6
- export { DEFAULT_SCENARIOS, RATE_FEED_TYPE, UNBOUNDED_DOWN, UNBOUNDED_UP, classifySources, groupOverride, perturb, rateStub, readForest, readRateTargets, roundDataStub, runStressTest, scoreGroup, slotHarm, sourcesByFeed, verdictOf };
6
+ export { DEFAULT_SCENARIOS, RATE_FEED_TYPE, UNBOUNDED_DOWN, UNBOUNDED_UP, classifySources, groupOverride, perturb, rateStub, readForest, readRateTargets, readUnder, roundDataStub, runStressTest, scoreGroup, slotHarm, sourcesByFeed, verdictOf };
@@ -160,8 +160,10 @@ async function readRateTargets(client, feeds) {
160
160
  * scenario rather than a round trip per feed. A feed that reverts comes back
161
161
  * `null`, which is an outcome and not an error.
162
162
  */
163
- async function readUnder(client, feeds, sources, multiplier, updatedAt) {
164
- const overrides = groupOverride(sources, multiplier, updatedAt);
163
+ async function readUnder(client, feeds, sources, multiplier, updatedAt, freshen = []) {
164
+ const driven = groupOverride(sources, multiplier, updatedAt);
165
+ const taken = new Set(driven.map((override) => override.address.toLowerCase()));
166
+ const overrides = [...groupOverride(freshen, 1, updatedAt).filter((override) => !taken.has(override.address.toLowerCase())), ...driven];
165
167
  const results = await client.multicall({
166
168
  contracts: feeds.map((address) => ({
167
169
  address,
@@ -424,4 +426,4 @@ async function runStressTest({ client, chainId, slots, compressor, scenarios = D
424
426
  };
425
427
  }
426
428
  //#endregion
427
- export { DEFAULT_SCENARIOS, RATE_FEED_TYPE, readForest, readRateTargets, runStressTest };
429
+ export { DEFAULT_SCENARIOS, RATE_FEED_TYPE, readForest, readRateTargets, readUnder, runStressTest };
@@ -32,6 +32,14 @@ var BytecodeRepositoryContract = class BytecodeRepositoryContract extends BaseCo
32
32
  async getLatestVersion(type_) {
33
33
  return await this.contract.read.getLatestVersion([stringToHex(type_, { size: 32 })]);
34
34
  }
35
+ /**
36
+ * Returns the latest allowed patch version of the contract type within given minor version
37
+ * @throws if no bytecode of this contract type and minor version is allowed
38
+ */
39
+ async getLatestPatchVersion(type_, minorVersion) {
40
+ const version = await this.contract.read.getLatestPatchVersion([stringToHex(type_, { size: 32 }), BigInt(minorVersion)]);
41
+ return Number(version);
42
+ }
35
43
  async getAllowedBytecodeHash(type_, version) {
36
44
  return await this.contract.read.getAllowedBytecodeHash([stringToHex(type_, { size: 32 }), BigInt(version)]);
37
45
  }
@@ -5,8 +5,8 @@ import "../../../onchain/index.js";
5
5
  import { BytecodeRepositoryContract } from "../bytecode-repository.js";
6
6
  import "../../deployment/addresses.js";
7
7
  import { TreasurySplitterContract } from "../treasury-splitter.js";
8
- import { governorAbi } from "../../../abi/governance/governor.js";
9
8
  import { MarketConfiguratorContract } from "../market-configurator.js";
9
+ import { governorAbi } from "../../../abi/governance/governor.js";
10
10
  import { BatchesChainContract } from "./batches-chain.js";
11
11
  import { encodeAbiParameters, formatEther, keccak256, toBytes } from "viem";
12
12
  import { formatAbiItem } from "viem/utils";
@@ -9,11 +9,12 @@ import { RoutingManagerContract } from "./router/routing-manager.js";
9
9
  import "./router/index.js";
10
10
  import { TreasurySplitterContract } from "./treasury-splitter.js";
11
11
  import { InstanceManagerContract } from "./instance-manager.js";
12
- import { MarketConfiguratorFactoryContract } from "./market-configurator-factory.js";
13
- import { CrossChainMultisigContract } from "./cross-chain-multisig.js";
14
12
  import { AccessMode } from "./types.js";
15
13
  import { MarketConfiguratorContract } from "./market-configurator.js";
14
+ import { KILL_SWITCH_PROXY_MINOR_VERSION, KillSwitchProxyContract, KillSwitchProxyNotAllowedError, killSwitchProxySalt } from "./kill-switch-proxy.js";
15
+ import { MarketConfiguratorFactoryContract } from "./market-configurator-factory.js";
16
+ import { CrossChainMultisigContract } from "./cross-chain-multisig.js";
16
17
  import { GovernorContract } from "./governance/governor.js";
17
18
  import { TimeLockContract } from "./governance/timelock.js";
18
19
  import "./governance/index.js";
19
- export { AccessMode, AddressProviderContract, BytecodeRepositoryContract, CrossChainMultisigContract, GovernorContract, InstanceManagerContract, MarketConfiguratorContract, MarketConfiguratorFactoryContract, PriceFeedStoreContract, RoutingManagerContract, TimeLockContract, TokenCompressorContract, TreasurySplitterContract, WithdrawalCompressorContract, priceFeedSetupParams };
20
+ export { AccessMode, AddressProviderContract, BytecodeRepositoryContract, CrossChainMultisigContract, GovernorContract, InstanceManagerContract, KILL_SWITCH_PROXY_MINOR_VERSION, KillSwitchProxyContract, KillSwitchProxyNotAllowedError, MarketConfiguratorContract, MarketConfiguratorFactoryContract, PriceFeedStoreContract, RoutingManagerContract, TimeLockContract, TokenCompressorContract, TreasurySplitterContract, WithdrawalCompressorContract, killSwitchProxySalt, priceFeedSetupParams };
@@ -0,0 +1,184 @@
1
+ import { json_stringify } from "../../onchain/utils/json.js";
2
+ import { BaseContract } from "../../onchain/base/BaseContract.js";
3
+ import "../../onchain/index.js";
4
+ import { KILL_SWITCH_PROXY } from "../utils/literals.js";
5
+ import { iKillSwitchProxyAbi } from "../../abi/310/iKillSwitchProxy.js";
6
+ import { MarketConfiguratorContract } from "./market-configurator.js";
7
+ import { BaseError, ContractFunctionRevertedError, encodeAbiParameters, isAddressEqual, keccak256, toHex, zeroAddress } from "viem";
8
+ //#region src/permissionless/bindings/kill-switch-proxy.ts
9
+ const abi = iKillSwitchProxyAbi;
10
+ /**
11
+ * Minor version of kill switch proxy, the latest allowed patch of it is deployed
12
+ */
13
+ const KILL_SWITCH_PROXY_MINOR_VERSION = 310;
14
+ /**
15
+ * Salt of kill switch proxy deployed together with market configurator,
16
+ * makes each curator's market configurator get its own proxy
17
+ */
18
+ function killSwitchProxySalt(curatorName) {
19
+ return keccak256(toHex(curatorName));
20
+ }
21
+ /**
22
+ * Thrown when bytecode repository has no allowed kill switch proxy bytecode,
23
+ * so its address can't be computed and it can't be deployed
24
+ */
25
+ var KillSwitchProxyNotAllowedError = class extends BaseError {
26
+ name = "KillSwitchProxyNotAllowedError";
27
+ bytecodeRepository;
28
+ constructor(bytecodeRepository) {
29
+ super(`${KILL_SWITCH_PROXY} v310 is not allowed in bytecode repository ${bytecodeRepository}.`, { metaMessages: ["Its bytecode must be uploaded, audited and allowed before it can be deployed."] });
30
+ this.bytecodeRepository = bytecodeRepository;
31
+ }
32
+ };
33
+ /**
34
+ * Kill switch proxy forwards calls of its `caller` via `proxyCall` until `owner` kills it.
35
+ * It is used as market configurator's emergency admin, so that emergency powers
36
+ * can be revoked instantly without going through the timelock.
37
+ */
38
+ var KillSwitchProxyContract = class KillSwitchProxyContract extends BaseContract {
39
+ constructor(addr, client) {
40
+ super({ client }, {
41
+ abi,
42
+ addr,
43
+ name: "KillSwitchProxy"
44
+ });
45
+ }
46
+ /**
47
+ * Encodes `constructor(address _owner, address _caller)` params
48
+ * @throws if any address is zero or owner equals caller (constructor reverts in that case)
49
+ */
50
+ static encodeConstructorParams(args) {
51
+ const { owner, caller } = args;
52
+ if (isAddressEqual(owner, zeroAddress) || isAddressEqual(caller, zeroAddress)) throw new Error(`${KILL_SWITCH_PROXY} owner and caller must be non-zero`);
53
+ if (isAddressEqual(owner, caller)) throw new Error(`${KILL_SWITCH_PROXY} owner must differ from caller ${caller}, provide another kill switch owner`);
54
+ const [constructorAbi] = abi;
55
+ return encodeAbiParameters(constructorAbi.inputs, [owner, caller]);
56
+ }
57
+ /**
58
+ * Returns whether some patch of kill switch proxy v3.10 is allowed in bytecode repository
59
+ */
60
+ static async isAvailable(bcr) {
61
+ try {
62
+ await KillSwitchProxyContract.#getVersion(bcr);
63
+ return true;
64
+ } catch (e) {
65
+ if (e instanceof KillSwitchProxyNotAllowedError) return false;
66
+ throw e;
67
+ }
68
+ }
69
+ /**
70
+ * Computes address of kill switch proxy and prepares its deployment via bytecode repository
71
+ * @param bcr - bytecode repository
72
+ * @param args.deployer - address that will send deployment transaction, it is mixed into the salt
73
+ * @param args.owner - address that can kill the proxy
74
+ * @param args.caller - address that can call through the proxy
75
+ * @param args.salt - deployment salt
76
+ * @throws {KillSwitchProxyNotAllowedError} if kill switch proxy is not allowed in bytecode repository
77
+ */
78
+ static async prepareDeploy(bcr, args) {
79
+ const { deployer, owner, caller, salt } = args;
80
+ const encodedParams = KillSwitchProxyContract.encodeConstructorParams({
81
+ owner,
82
+ caller
83
+ });
84
+ const version = await KillSwitchProxyContract.#getVersion(bcr);
85
+ const [isAlreadyDeployed, address] = await bcr.isAlreadyDeployedAddress({
86
+ contractType: KILL_SWITCH_PROXY,
87
+ version,
88
+ encodedParams,
89
+ salt,
90
+ owner: deployer
91
+ });
92
+ return {
93
+ address,
94
+ version,
95
+ encodedParams,
96
+ salt,
97
+ isAlreadyDeployed,
98
+ isKilled: isAlreadyDeployed ? await new KillSwitchProxyContract(address, bcr.client).isKilled() : false,
99
+ tx: bcr.deployTx({
100
+ contractType: KILL_SWITCH_PROXY,
101
+ version: BigInt(version),
102
+ encodedParams,
103
+ salt
104
+ })
105
+ };
106
+ }
107
+ static async #getVersion(bcr) {
108
+ try {
109
+ return await bcr.getLatestPatchVersion(KILL_SWITCH_PROXY, 310);
110
+ } catch (e) {
111
+ const revert = e instanceof BaseError ? e.walk((err) => err instanceof ContractFunctionRevertedError) : null;
112
+ if (revert instanceof ContractFunctionRevertedError && revert.data?.errorName === "VersionNotFoundException") throw new KillSwitchProxyNotAllowedError(bcr.address);
113
+ throw e;
114
+ }
115
+ }
116
+ async isKilled() {
117
+ return await this.contract.read.isKilled();
118
+ }
119
+ async state() {
120
+ const [caller, owner, isKilled] = await this.client.multicall({
121
+ allowFailure: false,
122
+ contracts: [
123
+ {
124
+ address: this.address,
125
+ abi,
126
+ functionName: "caller"
127
+ },
128
+ {
129
+ address: this.address,
130
+ abi,
131
+ functionName: "owner"
132
+ },
133
+ {
134
+ address: this.address,
135
+ abi,
136
+ functionName: "isKilled"
137
+ }
138
+ ],
139
+ batchSize: 0
140
+ });
141
+ return {
142
+ caller,
143
+ owner,
144
+ isKilled
145
+ };
146
+ }
147
+ /**
148
+ * Wraps transaction into `proxyCall`, must be sent by proxy's caller
149
+ */
150
+ proxyCallTx(tx) {
151
+ return this.createRawTx({
152
+ functionName: "proxyCall",
153
+ args: [tx.to, tx.callData],
154
+ description: tx.description ? `KillSwitchProxy.proxyCall(${tx.description})` : void 0
155
+ });
156
+ }
157
+ /**
158
+ * Permanently disables `proxyCall`, must be sent by proxy's owner
159
+ */
160
+ killTx() {
161
+ return this.createRawTx({ functionName: "kill" });
162
+ }
163
+ parseFunctionParams(params) {
164
+ const { functionName, args } = params;
165
+ switch (functionName) {
166
+ case "proxyCall": {
167
+ const [target, data] = args;
168
+ const decoded = new MarketConfiguratorContract(target, this.client).parseFunctionData(data);
169
+ if (decoded.functionName.startsWith("Unknown function")) return {
170
+ target,
171
+ data
172
+ };
173
+ return {
174
+ target,
175
+ functionName: decoded.functionName,
176
+ data: json_stringify(decoded.args)
177
+ };
178
+ }
179
+ default: return super.parseFunctionParams(params);
180
+ }
181
+ }
182
+ };
183
+ //#endregion
184
+ export { KILL_SWITCH_PROXY_MINOR_VERSION, KillSwitchProxyContract, KillSwitchProxyNotAllowedError, killSwitchProxySalt };
@@ -1,6 +1,10 @@
1
1
  import { BaseContract } from "../../onchain/base/BaseContract.js";
2
2
  import "../../onchain/index.js";
3
+ import { KILL_SWITCH_PROXY } from "../utils/literals.js";
4
+ import { BytecodeRepositoryContract } from "./bytecode-repository.js";
5
+ import "../deployment/addresses.js";
3
6
  import { iMarketConfiguratorFactoryAbi } from "../../abi/310/iMarketConfiguratorFactory.js";
7
+ import { KillSwitchProxyContract, killSwitchProxySalt } from "./kill-switch-proxy.js";
4
8
  //#region src/permissionless/bindings/market-configurator-factory.ts
5
9
  const abi = iMarketConfiguratorFactoryAbi;
6
10
  var MarketConfiguratorFactoryContract = class extends BaseContract {
@@ -22,6 +26,39 @@ var MarketConfiguratorFactoryContract = class extends BaseContract {
22
26
  ]
23
27
  });
24
28
  }
29
+ /**
30
+ * Builds transactions that deploy market configurator with kill switch proxy as its emergency admin:
31
+ * 1. kill switch proxy deployment via bytecode repository (skipped if already deployed)
32
+ * 2. market configurator creation with proxy as emergency admin
33
+ *
34
+ * Transactions must be sent in this order by `admin`, e.g. as one Safe batch.
35
+ * When `deployGovernor` is true, proxy also becomes governor's veto admin.
36
+ *
37
+ * @param args.admin - address that sends transactions, becomes market configurator admin
38
+ * @param args.emergencyAdmin - address that executes emergency actions through the proxy
39
+ * @param args.killSwitchOwner - address that can kill the proxy, defaults to `admin`
40
+ * @throws {KillSwitchProxyNotAllowedError} if kill switch proxy is not allowed in bytecode repository,
41
+ * use {@link createMarketConfiguratorTx} to deploy market configurator without it
42
+ */
43
+ async createMarketConfiguratorWithKillSwitchTxs(args) {
44
+ const { admin, emergencyAdmin, killSwitchOwner, ...rest } = args;
45
+ const bcr = new BytecodeRepositoryContract("0x1cE2B1BE96a082b1b1539F80d5D8f82Ec06a0f9A", this.client);
46
+ const proxy = await KillSwitchProxyContract.prepareDeploy(bcr, {
47
+ deployer: admin,
48
+ owner: killSwitchOwner ?? admin,
49
+ caller: emergencyAdmin,
50
+ salt: killSwitchProxySalt(args.curatorName)
51
+ });
52
+ if (proxy.isKilled) throw new Error(`${KILL_SWITCH_PROXY} ${proxy.address} is already deployed and killed`);
53
+ return {
54
+ killSwitchProxy: proxy.address,
55
+ isKillSwitchProxyDeployed: proxy.isAlreadyDeployed,
56
+ txs: [...proxy.isAlreadyDeployed ? [] : [proxy.tx], this.createMarketConfiguratorTx({
57
+ ...rest,
58
+ emergencyAdmin: proxy.address
59
+ })]
60
+ };
61
+ }
25
62
  async getMarketConfigurators() {
26
63
  return [...await this.contract.read.getMarketConfigurators()];
27
64
  }