Skip to content

Commit 9927f8a

Browse files
committed
Merge branch 'main' into pyth-stylus-governance-impl
2 parents 5de37ea + 57cdeed commit 9927f8a

File tree

30 files changed

+1379
-762
lines changed

30 files changed

+1379
-762
lines changed

.github/workflows/ci-lazer-rust.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ jobs:
4141
- name: Clippy check
4242
run: cargo clippy -p pyth-lazer-protocol -p pyth-lazer-client -p pyth-lazer-publisher-sdk --all-targets -- --deny warnings
4343
if: success() || failure()
44+
- name: Clippy check with mry
45+
run: cargo clippy -F mry -p pyth-lazer-protocol --all-targets -- --deny warnings
46+
if: success() || failure()
4447
- name: test
4548
run: cargo test -p pyth-lazer-protocol -p pyth-lazer-client -p pyth-lazer-publisher-sdk
4649
if: success() || failure()
50+
- name: test with mry
51+
run: cargo test -F mry -p pyth-lazer-protocol
52+
if: success() || failure()

Cargo.lock

Lines changed: 54 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/entropy-tester/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pythnetwork/entropy-tester",
3-
"version": "1.1.0",
3+
"version": "1.2.0",
44
"description": "Utility to test entropy provider callbacks",
55
"private": true,
66
"type": "module",

apps/entropy-tester/src/index.ts

Lines changed: 60 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@ import yargs from "yargs";
1111
import { hideBin } from "yargs/helpers";
1212
import { z } from "zod";
1313

14+
const DEFAULT_RETRIES = 3;
15+
1416
type LoadedConfig = {
1517
contract: EvmEntropyContract;
1618
interval: number;
19+
retries: number;
1720
};
1821

1922
function timeToSeconds(timeStr: string): number {
@@ -46,6 +49,7 @@ async function loadConfig(configPath: string): Promise<LoadedConfig[]> {
4649
"chain-id": z.string(),
4750
interval: z.string(),
4851
"rpc-endpoint": z.string().optional(),
52+
retries: z.number().default(DEFAULT_RETRIES),
4953
}),
5054
);
5155
const configContent = (await import(configPath, {
@@ -78,7 +82,7 @@ async function loadConfig(configPath: string): Promise<LoadedConfig[]> {
7882
evmChain.networkId,
7983
);
8084
}
81-
return { contract: firstContract, interval };
85+
return { contract: firstContract, interval, retries: config.retries };
8286
});
8387
return loadedConfigs;
8488
}
@@ -188,31 +192,63 @@ export const main = function () {
188192
privateKeyFileContent.replace("0x", "").trimEnd(),
189193
);
190194
logger.info("Running");
191-
const promises = configs.map(async ({ contract, interval }) => {
192-
const child = logger.child({ chain: contract.chain.getId() });
193-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
194-
while (true) {
195-
try {
196-
await Promise.race([
197-
testLatency(contract, privateKey, child),
198-
new Promise((_, reject) =>
199-
setTimeout(() => {
200-
reject(
201-
new Error(
202-
"Timeout: 120s passed but testLatency function was not resolved",
203-
),
195+
const promises = configs.map(
196+
async ({ contract, interval, retries }) => {
197+
const child = logger.child({ chain: contract.chain.getId() });
198+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
199+
while (true) {
200+
let lastError: Error | undefined;
201+
let success = false;
202+
203+
for (let attempt = 1; attempt <= retries; attempt++) {
204+
try {
205+
await Promise.race([
206+
testLatency(contract, privateKey, child),
207+
new Promise((_, reject) =>
208+
setTimeout(() => {
209+
reject(
210+
new Error(
211+
"Timeout: 120s passed but testLatency function was not resolved",
212+
),
213+
);
214+
}, 120_000),
215+
),
216+
]);
217+
success = true;
218+
break;
219+
} catch (error) {
220+
lastError = error as Error;
221+
child.warn(
222+
{ attempt, maxRetries: retries, error: error },
223+
`Attempt ${attempt.toString()}/${retries.toString()} failed, ${attempt < retries ? "retrying..." : "all retries exhausted"}`,
224+
);
225+
226+
if (attempt < retries) {
227+
// Wait a bit before retrying (exponential backoff, max 10s)
228+
const backoffDelay = Math.min(
229+
2000 * Math.pow(2, attempt - 1),
230+
10_000,
231+
);
232+
await new Promise((resolve) =>
233+
setTimeout(resolve, backoffDelay),
204234
);
205-
}, 120_000),
206-
),
207-
]);
208-
} catch (error) {
209-
child.error(error, "Error testing latency");
235+
}
236+
}
237+
}
238+
239+
if (!success && lastError) {
240+
child.error(
241+
{ error: lastError, retriesExhausted: retries },
242+
"All retries exhausted, callback was not called.",
243+
);
244+
}
245+
246+
await new Promise((resolve) =>
247+
setTimeout(resolve, interval * 1000),
248+
);
210249
}
211-
await new Promise((resolve) =>
212-
setTimeout(resolve, interval * 1000),
213-
);
214-
}
215-
});
250+
},
251+
);
216252
await Promise.all(promises);
217253
},
218254
)

apps/fortuna/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "fortuna"
3-
version = "8.2.0"
3+
version = "8.2.1"
44
edition = "2021"
55

66
[lib]

apps/fortuna/src/eth_utils/utils.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use {
88
ethers::{
99
contract::{ContractCall, ContractError},
1010
middleware::Middleware,
11-
providers::ProviderError,
11+
providers::{MiddlewareError, ProviderError},
1212
signers::Signer,
1313
types::{
1414
transaction::eip2718::TypedTransaction, TransactionReceipt, TransactionRequest, U256,
@@ -253,7 +253,19 @@ pub async fn submit_tx<T: Middleware + NonceManaged + 'static>(
253253
client
254254
.fill_transaction(&mut transaction, None)
255255
.await
256-
.map_err(|e| backoff::Error::transient(SubmitTxError::GasPriceEstimateError(e)))?;
256+
.map_err(|e| {
257+
// If there is revert data, the contract reverted during gas usage estimation.
258+
if let Some(e) = e.as_error_response() {
259+
if let Some(e) = e.as_revert_data() {
260+
return backoff::Error::transient(SubmitTxError::GasUsageEstimateError(
261+
ContractError::Revert(e.clone()),
262+
));
263+
}
264+
}
265+
266+
// If there is no revert data, there was likely an error during gas price polling.
267+
backoff::Error::transient(SubmitTxError::GasPriceEstimateError(e))
268+
})?;
257269

258270
// Apply the fee escalation policy. Note: the unwrap_or_default should never default as we have a gas oracle
259271
// in the client that sets the gas price.

apps/fortuna/src/history.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,18 @@ impl History {
357357
}
358358
};
359359
if let Err(e) = result {
360-
tracing::error!("Failed to update request status: {}", e);
360+
match e.as_database_error() {
361+
Some(db_error) if db_error.is_unique_violation() => {
362+
tracing::info!(
363+
"Failed to insert request, request already exists: Chain ID: {}, Sequence: {}",
364+
network_id,
365+
sequence
366+
);
367+
}
368+
_ => {
369+
tracing::error!("Failed to update request status: {}", e);
370+
}
371+
}
361372
}
362373
}
363374

apps/fortuna/src/keeper/fee.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@ async fn calculate_fair_fee_withdrawal_amount<M: Middleware + 'static>(
4444
.await
4545
.map_err(|e| anyhow!("Error while getting current keeper balance. error: {:?}", e))?;
4646

47+
tracing::info!(
48+
"Contract has available fees: {:?}, current keeper ({:?}) has balance: {:?}",
49+
available_fees,
50+
keeper_address,
51+
current_balance
52+
);
53+
4754
// Calculate total funds across all keepers + available fees
4855
let mut total_funds = current_balance + available_fees;
4956

@@ -55,6 +62,7 @@ async fn calculate_fair_fee_withdrawal_amount<M: Middleware + 'static>(
5562
e
5663
)
5764
})?;
65+
tracing::info!("Keeper address {:?} has balance: {:?}", address, balance);
5866
total_funds += balance;
5967
}
6068

@@ -174,7 +182,7 @@ pub async fn withdraw_fees_if_necessary(
174182
if withdrawal_amount < min_withdrawal_amount {
175183
// We don't have enough to meaningfully top up the balance.
176184
// NOTE: This log message triggers a grafana alert. If you want to change the text, please change the alert also.
177-
tracing::warn!("Keeper balance {:?} is too low (< {:?}) but provider fees are not sufficient to top-up.", keeper_balance, min_balance);
185+
tracing::warn!("Keeper balance {:?} is too low (< {:?}) but provider fees are not sufficient to top-up. (withdrawal_amount={:?} < min_withdrawal_amount={:?})", keeper_balance, min_balance, withdrawal_amount, min_withdrawal_amount);
178186
return Ok(());
179187
}
180188

governance/pyth_staking_sdk/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pythnetwork/staking-sdk",
3-
"version": "0.2.3",
3+
"version": "0.2.4",
44
"description": "Pyth staking SDK",
55
"type": "module",
66
"exports": {
@@ -13,7 +13,7 @@
1313
"dist/**/*"
1414
],
1515
"engines": {
16-
"node": "20 || 22"
16+
"node": "20 || 22 || 24"
1717
},
1818
"publishConfig": {
1919
"access": "public"

0 commit comments

Comments
 (0)