@gearbox-protocol/sdk 3.0.0-vfour.340 → 3.0.0-vfour.341

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.
@@ -27,6 +27,8 @@ var import_protocol = require("@redstone-finance/protocol");
27
27
  var import_viem = require("viem");
28
28
  var import_base = require("../../base/index.js");
29
29
  var import_utils = require("../../utils/index.js");
30
+ const MAX_DATA_TIMESTAMP_DELAY_SECONDS = 10n * 60n;
31
+ const MAX_DATA_TIMESTAMP_AHEAD_SECONDS = 60n;
30
32
  class RedstoneUpdateTx {
31
33
  raw;
32
34
  data;
@@ -38,6 +40,17 @@ class RedstoneUpdateTx {
38
40
  const cached = this.data.cached ? " (cached)" : "";
39
41
  return `redstone feed ${this.data.dataFeedId} at ${this.data.priceFeed} with timestamp ${this.data.timestamp}${cached}`;
40
42
  }
43
+ validateTimestamp(blockTimestamp) {
44
+ const { timestamp: expectedPayloadTimestamp } = this.data;
45
+ if (blockTimestamp < expectedPayloadTimestamp) {
46
+ if (BigInt(expectedPayloadTimestamp) - blockTimestamp > MAX_DATA_TIMESTAMP_AHEAD_SECONDS) {
47
+ return "in future";
48
+ }
49
+ } else if (blockTimestamp - BigInt(expectedPayloadTimestamp) > MAX_DATA_TIMESTAMP_DELAY_SECONDS) {
50
+ return "too old";
51
+ }
52
+ return "valid";
53
+ }
41
54
  }
42
55
  class RedstoneUpdater extends import_base.SDKConstruct {
43
56
  #logger;
@@ -19,39 +19,65 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
19
19
  var simulateWithPriceUpdates_exports = {};
20
20
  __export(simulateWithPriceUpdates_exports, {
21
21
  SimulateWithPriceUpdatesError: () => SimulateWithPriceUpdatesError,
22
+ getSimulateWithPriceUpdatesError: () => getSimulateWithPriceUpdatesError,
22
23
  simulateWithPriceUpdates: () => simulateWithPriceUpdates
23
24
  });
24
25
  module.exports = __toCommonJS(simulateWithPriceUpdates_exports);
25
26
  var import_viem = require("viem");
26
27
  var import_abi = require("../../../abi/index.js");
27
- var import_hex = require("../hex.js");
28
28
  var import_simulateMulticall = require("./simulateMulticall.js");
29
+ const multicallTimestampAbi = (0, import_viem.parseAbi)([
30
+ "function getCurrentBlockTimestamp() public view returns (uint256 timestamp)"
31
+ ]);
29
32
  const updatePriceFeedAbi = [...import_abi.iUpdatablePriceFeedAbi, ...import_abi.errorAbis];
30
33
  async function simulateWithPriceUpdates(client, parameters) {
31
34
  const { contracts: restContracts, priceUpdates, ...rest } = parameters;
32
35
  if (restContracts.length === 0) {
33
- throw new SimulateWithPriceUpdatesError(
36
+ throw getSimulateWithPriceUpdatesError(
34
37
  new import_viem.BaseError("no contracts calls provided"),
35
38
  priceUpdates,
36
39
  restContracts
37
40
  );
38
41
  }
42
+ const multicallAddress = rest.multicallAddress ?? client?.chain?.contracts?.multicall3?.address;
43
+ if (!multicallAddress) {
44
+ throw new Error(
45
+ "client chain not configured. multicallAddress is required."
46
+ );
47
+ }
39
48
  try {
40
49
  const contracts = [
50
+ {
51
+ abi: multicallTimestampAbi,
52
+ address: multicallAddress,
53
+ functionName: "getCurrentBlockTimestamp",
54
+ args: []
55
+ },
41
56
  ...priceUpdates.map(rawTxToMulticallPriceUpdate),
42
57
  ...restContracts
43
58
  ];
44
59
  const resp = await (0, import_simulateMulticall.simulateMulticall)(client, {
45
60
  contracts,
46
61
  ...rest,
47
- allowFailure: false,
62
+ allowFailure: true,
48
63
  batchSize: 0
49
64
  // we cannot have price updates and compressor request in different batches
50
65
  });
51
- const restResults = resp.slice(priceUpdates.length);
66
+ if (resp.some((r) => r.status === "failure")) {
67
+ throw getSimulateWithPriceUpdatesError(
68
+ void 0,
69
+ priceUpdates,
70
+ restContracts,
71
+ resp
72
+ );
73
+ }
74
+ const restResults = resp.slice(priceUpdates.length + 1).map((r) => r.result);
52
75
  return restResults;
53
76
  } catch (e) {
54
- throw new SimulateWithPriceUpdatesError(
77
+ if (e instanceof SimulateWithPriceUpdatesError) {
78
+ throw e;
79
+ }
80
+ throw getSimulateWithPriceUpdatesError(
55
81
  e,
56
82
  priceUpdates,
57
83
  restContracts
@@ -78,57 +104,73 @@ function rawTxToMulticallPriceUpdate({
78
104
  args
79
105
  };
80
106
  }
107
+ function getSimulateWithPriceUpdatesError(cause, priceUpdates, calls, results) {
108
+ if (!results) {
109
+ return new SimulateWithPriceUpdatesError(cause, {
110
+ priceUpdates: priceUpdates.map((p) => p.pretty),
111
+ calls: calls.map((c) => `${c.address}.${c.functionName}`)
112
+ });
113
+ }
114
+ const timestamp = results[0]?.result;
115
+ const priceUpdatesResults = results.slice(1, 1 + priceUpdates.length);
116
+ const callsResults = results.slice(1 + priceUpdates.length);
117
+ const prettyPriceUpdates = priceUpdates.map((p, i) => {
118
+ const result = priceUpdatesResults[i];
119
+ let tsValid = timestamp ? p.validateTimestamp(timestamp) : "";
120
+ tsValid = tsValid === "valid" ? "" : `[timestamp ${tsValid}]`;
121
+ return [extractCallError(result), p.pretty, tsValid].filter(Boolean).join(" ");
122
+ });
123
+ const prettyCalls = callsResults.map((c, i) => {
124
+ const call = calls[i];
125
+ return [extractCallError(c), `${call.address}.${call.functionName}`].filter(Boolean).join(" ");
126
+ });
127
+ return new SimulateWithPriceUpdatesError(cause, {
128
+ timestamp,
129
+ priceUpdates: prettyPriceUpdates,
130
+ calls: prettyCalls
131
+ });
132
+ }
133
+ function extractCallError(result) {
134
+ if (result.status === "success") {
135
+ return "";
136
+ }
137
+ const err = result.error;
138
+ const error = err instanceof import_viem.BaseError ? err.walk((e) => e instanceof import_viem.ContractFunctionRevertedError) : void 0;
139
+ if (error instanceof import_viem.ContractFunctionRevertedError) {
140
+ return "[" + (error.data?.errorName ?? "reverted") + "]";
141
+ }
142
+ return err instanceof import_viem.BaseError ? `[${err.name}]` : "[error]";
143
+ }
81
144
  class SimulateWithPriceUpdatesError extends import_viem.BaseError {
82
145
  cause;
83
- priceUpdates;
84
- calls;
85
- constructor(cause, priceUpdates, calls) {
86
- let failedPriceFeed = "0x0";
146
+ timestamp;
147
+ constructor(cause, params) {
148
+ const { calls, priceUpdates, timestamp } = params;
87
149
  const base = cause instanceof import_viem.BaseError ? cause : {};
88
150
  let causeMeta = base.metaMessages ? [...base.metaMessages, " "] : [];
89
- if (base instanceof import_viem.ContractFunctionExecutionError && base.functionName === "updatePrice") {
90
- failedPriceFeed = base.contractAddress ?? "0x0";
91
- causeMeta = [
92
- `simulate multicall with ${priceUpdates.length} price updates failed`,
93
- " "
94
- ];
95
- const updateRevert = cause instanceof import_viem.BaseError ? cause.walk(
96
- (err) => err instanceof import_viem.ContractFunctionRevertedError
97
- ) : void 0;
98
- if (updateRevert) {
99
- causeMeta = [
100
- `simulate multicall with ${priceUpdates.length} price updates failed: ${updateRevert.metaMessages?.[0]}`,
101
- " "
102
- ];
103
- }
104
- }
105
- const priceUpdatesMeta = [
106
- "Price Updates:",
107
- ...priceUpdates.map(
108
- (u) => `${(0, import_hex.hexEq)(u.data.priceFeed, failedPriceFeed) ? "[FAILED] " : ""}${u.pretty}`
109
- )
110
- ];
111
- const callsMeta = [
112
- "Calls:",
113
- ...calls.map((c) => `${c.address}.${c.functionName}`)
114
- ];
115
151
  super(
116
152
  `simulate multicall with ${priceUpdates.length} price updates failed`,
117
153
  {
118
154
  cause: base,
119
- metaMessages: [...causeMeta, ...priceUpdatesMeta, ...callsMeta].filter(
120
- Boolean
121
- ),
155
+ metaMessages: [
156
+ ...causeMeta,
157
+ ...timestamp ? [" ", "Block Timestamp: " + timestamp.toString(), " "] : [],
158
+ "Price Updates:",
159
+ ...priceUpdates,
160
+ " ",
161
+ "Calls: ",
162
+ ...calls
163
+ ].filter(Boolean),
122
164
  name: "SimulateWithPriceUpdatesError"
123
165
  }
124
166
  );
125
167
  this.cause = cause;
126
- this.priceUpdates = priceUpdates;
127
- this.calls = calls;
168
+ this.timestamp = timestamp;
128
169
  }
129
170
  }
130
171
  // Annotate the CommonJS export names for ESM import in node:
131
172
  0 && (module.exports = {
132
173
  SimulateWithPriceUpdatesError,
174
+ getSimulateWithPriceUpdatesError,
133
175
  simulateWithPriceUpdates
134
176
  });
@@ -3,6 +3,8 @@ import { RedstonePayload } from "@redstone-finance/protocol";
3
3
  import { encodeAbiParameters, toBytes } from "viem";
4
4
  import { SDKConstruct } from "../../base/index.js";
5
5
  import { childLogger, retry } from "../../utils/index.js";
6
+ const MAX_DATA_TIMESTAMP_DELAY_SECONDS = 10n * 60n;
7
+ const MAX_DATA_TIMESTAMP_AHEAD_SECONDS = 60n;
6
8
  class RedstoneUpdateTx {
7
9
  raw;
8
10
  data;
@@ -14,6 +16,17 @@ class RedstoneUpdateTx {
14
16
  const cached = this.data.cached ? " (cached)" : "";
15
17
  return `redstone feed ${this.data.dataFeedId} at ${this.data.priceFeed} with timestamp ${this.data.timestamp}${cached}`;
16
18
  }
19
+ validateTimestamp(blockTimestamp) {
20
+ const { timestamp: expectedPayloadTimestamp } = this.data;
21
+ if (blockTimestamp < expectedPayloadTimestamp) {
22
+ if (BigInt(expectedPayloadTimestamp) - blockTimestamp > MAX_DATA_TIMESTAMP_AHEAD_SECONDS) {
23
+ return "in future";
24
+ }
25
+ } else if (blockTimestamp - BigInt(expectedPayloadTimestamp) > MAX_DATA_TIMESTAMP_DELAY_SECONDS) {
26
+ return "too old";
27
+ }
28
+ return "valid";
29
+ }
17
30
  }
18
31
  class RedstoneUpdater extends SDKConstruct {
19
32
  #logger;
@@ -1,38 +1,63 @@
1
1
  import {
2
2
  BaseError,
3
- ContractFunctionExecutionError,
4
3
  ContractFunctionRevertedError,
5
- decodeFunctionData
4
+ decodeFunctionData,
5
+ parseAbi
6
6
  } from "viem";
7
7
  import { errorAbis, iUpdatablePriceFeedAbi } from "../../../abi/index.js";
8
- import { hexEq } from "../hex.js";
9
8
  import { simulateMulticall } from "./simulateMulticall.js";
9
+ const multicallTimestampAbi = parseAbi([
10
+ "function getCurrentBlockTimestamp() public view returns (uint256 timestamp)"
11
+ ]);
10
12
  const updatePriceFeedAbi = [...iUpdatablePriceFeedAbi, ...errorAbis];
11
13
  async function simulateWithPriceUpdates(client, parameters) {
12
14
  const { contracts: restContracts, priceUpdates, ...rest } = parameters;
13
15
  if (restContracts.length === 0) {
14
- throw new SimulateWithPriceUpdatesError(
16
+ throw getSimulateWithPriceUpdatesError(
15
17
  new BaseError("no contracts calls provided"),
16
18
  priceUpdates,
17
19
  restContracts
18
20
  );
19
21
  }
22
+ const multicallAddress = rest.multicallAddress ?? client?.chain?.contracts?.multicall3?.address;
23
+ if (!multicallAddress) {
24
+ throw new Error(
25
+ "client chain not configured. multicallAddress is required."
26
+ );
27
+ }
20
28
  try {
21
29
  const contracts = [
30
+ {
31
+ abi: multicallTimestampAbi,
32
+ address: multicallAddress,
33
+ functionName: "getCurrentBlockTimestamp",
34
+ args: []
35
+ },
22
36
  ...priceUpdates.map(rawTxToMulticallPriceUpdate),
23
37
  ...restContracts
24
38
  ];
25
39
  const resp = await simulateMulticall(client, {
26
40
  contracts,
27
41
  ...rest,
28
- allowFailure: false,
42
+ allowFailure: true,
29
43
  batchSize: 0
30
44
  // we cannot have price updates and compressor request in different batches
31
45
  });
32
- const restResults = resp.slice(priceUpdates.length);
46
+ if (resp.some((r) => r.status === "failure")) {
47
+ throw getSimulateWithPriceUpdatesError(
48
+ void 0,
49
+ priceUpdates,
50
+ restContracts,
51
+ resp
52
+ );
53
+ }
54
+ const restResults = resp.slice(priceUpdates.length + 1).map((r) => r.result);
33
55
  return restResults;
34
56
  } catch (e) {
35
- throw new SimulateWithPriceUpdatesError(
57
+ if (e instanceof SimulateWithPriceUpdatesError) {
58
+ throw e;
59
+ }
60
+ throw getSimulateWithPriceUpdatesError(
36
61
  e,
37
62
  priceUpdates,
38
63
  restContracts
@@ -59,56 +84,72 @@ function rawTxToMulticallPriceUpdate({
59
84
  args
60
85
  };
61
86
  }
87
+ function getSimulateWithPriceUpdatesError(cause, priceUpdates, calls, results) {
88
+ if (!results) {
89
+ return new SimulateWithPriceUpdatesError(cause, {
90
+ priceUpdates: priceUpdates.map((p) => p.pretty),
91
+ calls: calls.map((c) => `${c.address}.${c.functionName}`)
92
+ });
93
+ }
94
+ const timestamp = results[0]?.result;
95
+ const priceUpdatesResults = results.slice(1, 1 + priceUpdates.length);
96
+ const callsResults = results.slice(1 + priceUpdates.length);
97
+ const prettyPriceUpdates = priceUpdates.map((p, i) => {
98
+ const result = priceUpdatesResults[i];
99
+ let tsValid = timestamp ? p.validateTimestamp(timestamp) : "";
100
+ tsValid = tsValid === "valid" ? "" : `[timestamp ${tsValid}]`;
101
+ return [extractCallError(result), p.pretty, tsValid].filter(Boolean).join(" ");
102
+ });
103
+ const prettyCalls = callsResults.map((c, i) => {
104
+ const call = calls[i];
105
+ return [extractCallError(c), `${call.address}.${call.functionName}`].filter(Boolean).join(" ");
106
+ });
107
+ return new SimulateWithPriceUpdatesError(cause, {
108
+ timestamp,
109
+ priceUpdates: prettyPriceUpdates,
110
+ calls: prettyCalls
111
+ });
112
+ }
113
+ function extractCallError(result) {
114
+ if (result.status === "success") {
115
+ return "";
116
+ }
117
+ const err = result.error;
118
+ const error = err instanceof BaseError ? err.walk((e) => e instanceof ContractFunctionRevertedError) : void 0;
119
+ if (error instanceof ContractFunctionRevertedError) {
120
+ return "[" + (error.data?.errorName ?? "reverted") + "]";
121
+ }
122
+ return err instanceof BaseError ? `[${err.name}]` : "[error]";
123
+ }
62
124
  class SimulateWithPriceUpdatesError extends BaseError {
63
125
  cause;
64
- priceUpdates;
65
- calls;
66
- constructor(cause, priceUpdates, calls) {
67
- let failedPriceFeed = "0x0";
126
+ timestamp;
127
+ constructor(cause, params) {
128
+ const { calls, priceUpdates, timestamp } = params;
68
129
  const base = cause instanceof BaseError ? cause : {};
69
130
  let causeMeta = base.metaMessages ? [...base.metaMessages, " "] : [];
70
- if (base instanceof ContractFunctionExecutionError && base.functionName === "updatePrice") {
71
- failedPriceFeed = base.contractAddress ?? "0x0";
72
- causeMeta = [
73
- `simulate multicall with ${priceUpdates.length} price updates failed`,
74
- " "
75
- ];
76
- const updateRevert = cause instanceof BaseError ? cause.walk(
77
- (err) => err instanceof ContractFunctionRevertedError
78
- ) : void 0;
79
- if (updateRevert) {
80
- causeMeta = [
81
- `simulate multicall with ${priceUpdates.length} price updates failed: ${updateRevert.metaMessages?.[0]}`,
82
- " "
83
- ];
84
- }
85
- }
86
- const priceUpdatesMeta = [
87
- "Price Updates:",
88
- ...priceUpdates.map(
89
- (u) => `${hexEq(u.data.priceFeed, failedPriceFeed) ? "[FAILED] " : ""}${u.pretty}`
90
- )
91
- ];
92
- const callsMeta = [
93
- "Calls:",
94
- ...calls.map((c) => `${c.address}.${c.functionName}`)
95
- ];
96
131
  super(
97
132
  `simulate multicall with ${priceUpdates.length} price updates failed`,
98
133
  {
99
134
  cause: base,
100
- metaMessages: [...causeMeta, ...priceUpdatesMeta, ...callsMeta].filter(
101
- Boolean
102
- ),
135
+ metaMessages: [
136
+ ...causeMeta,
137
+ ...timestamp ? [" ", "Block Timestamp: " + timestamp.toString(), " "] : [],
138
+ "Price Updates:",
139
+ ...priceUpdates,
140
+ " ",
141
+ "Calls: ",
142
+ ...calls
143
+ ].filter(Boolean),
103
144
  name: "SimulateWithPriceUpdatesError"
104
145
  }
105
146
  );
106
147
  this.cause = cause;
107
- this.priceUpdates = priceUpdates;
108
- this.calls = calls;
148
+ this.timestamp = timestamp;
109
149
  }
110
150
  }
111
151
  export {
112
152
  SimulateWithPriceUpdatesError,
153
+ getSimulateWithPriceUpdatesError,
113
154
  simulateWithPriceUpdates
114
155
  };
@@ -15,6 +15,7 @@ export declare class RedstoneUpdateTx implements IPriceUpdateTx<RedstoneUpdateTa
15
15
  readonly data: RedstoneUpdateTask;
16
16
  constructor(raw: RawTx, data: RedstoneUpdateTask);
17
17
  get pretty(): string;
18
+ validateTimestamp(blockTimestamp: bigint): "valid" | "too old" | "in future";
18
19
  }
19
20
  /**
20
21
  * Class to update multiple redstone price feeds at once
@@ -26,6 +26,7 @@ export interface IPriceUpdateTx<Data extends {
26
26
  raw: RawTx;
27
27
  data: Data;
28
28
  pretty: string;
29
+ validateTimestamp: (timestamp: bigint) => "valid" | "too old" | "in future";
29
30
  }
30
31
  export interface MultiCall {
31
32
  target: Address;
@@ -17,12 +17,17 @@ export type SimulateWithPriceUpdatesReturnType<contracts extends readonly unknow
17
17
  error: Error;
18
18
  }> = MulticallReturnType<contracts, false, options>;
19
19
  export declare function simulateWithPriceUpdates<const contracts extends readonly unknown[], chain extends Chain | undefined>(client: Client<Transport, chain>, parameters: SimulateWithPriceUpdatesParameters<contracts>): Promise<SimulateWithPriceUpdatesReturnType<contracts>>;
20
- export type SimulateWithPriceUpdatesErrorType<contracts extends readonly unknown[]> = SimulateWithPriceUpdatesError<contracts> & {
20
+ export declare function getSimulateWithPriceUpdatesError<contracts extends readonly unknown[]>(cause: Error | undefined, priceUpdates: IPriceUpdateTx[], calls: MulticallContracts<Narrow<contracts>>, results?: MulticallReturnType<Narrow<contracts>>): SimulateWithPriceUpdatesError;
21
+ export type SimulateWithPriceUpdatesErrorType = SimulateWithPriceUpdatesError & {
21
22
  name: "SimulateWithPriceUpdatesError";
22
23
  };
23
- export declare class SimulateWithPriceUpdatesError<contracts extends readonly unknown[]> extends BaseError {
24
- cause: Error;
25
- readonly priceUpdates: IPriceUpdateTx[];
26
- readonly calls: MulticallContracts<Narrow<contracts>>;
27
- constructor(cause: Error, priceUpdates: IPriceUpdateTx[], calls: MulticallContracts<Narrow<contracts>>);
24
+ export interface SimulateWithPriceUpdatesErrorParams {
25
+ timestamp?: bigint;
26
+ priceUpdates: string[];
27
+ calls: string[];
28
+ }
29
+ export declare class SimulateWithPriceUpdatesError extends BaseError {
30
+ cause?: Error;
31
+ readonly timestamp?: bigint;
32
+ constructor(cause: Error | undefined, params: SimulateWithPriceUpdatesErrorParams);
28
33
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gearbox-protocol/sdk",
3
- "version": "3.0.0-vfour.340",
3
+ "version": "3.0.0-vfour.341",
4
4
  "description": "Gearbox SDK",
5
5
  "license": "MIT",
6
6
  "main": "./dist/cjs/sdk/index.js",