Skip to content

Commit 8fd3bf7

Browse files
authored
feat(infra): svm igp update script (#7223)
1 parent 4d0f682 commit 8fd3bf7

File tree

15 files changed

+1713
-37
lines changed

15 files changed

+1713
-37
lines changed

.changeset/eighty-dragons-shop.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@hyperlane-xyz/sdk": minor
3+
---
4+
5+
Fixed critical schema bug where SealevelIgpData.gas_oracles was incorrectly typed as Map<number, bigint> instead of Map<number, SealevelGasOracle>, preventing proper deserialization of on-chain gas oracle state. Added SealevelRemoteGasData, SealevelGasOracle, and related Borsh schemas to match the Rust implementation. Implemented createSetGasOracleConfigsInstruction() and createSetDestinationGasOverheadsInstruction() methods on the IGP adapters, along with gasOracleMatches() helper with BigInt-safe comparison for detecting configuration drift between expected and actual on-chain values.

typescript/infra/config/environments/mainnet3/igp.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,24 @@ export function getOverheadWithOverrides(local: ChainName, remote: ChainName) {
4646

4747
function getOracleConfigWithOverrides(origin: ChainName) {
4848
const oracleConfig = storageGasOracleConfig[origin];
49-
/* apply overrides in here if needed */
49+
50+
// WORKAROUND for Sealevel IGP decimal bug (solaxy-specific):
51+
// The Rust Sealevel IGP code hardcodes SOL_DECIMALS = 9, but solaxy has 6 decimals.
52+
// Rather than trying to calculate the correct workaround values, we hardcode
53+
// the values that are already set on-chain and known to work.
54+
if (origin === 'solaxy') {
55+
oracleConfig.ethereum = {
56+
gasPrice: '9',
57+
tokenExchangeRate: '15000000000000000000',
58+
tokenDecimals: 6,
59+
};
60+
oracleConfig.solanamainnet = {
61+
gasPrice: '1',
62+
tokenExchangeRate: '15000000000000000000',
63+
tokenDecimals: 6,
64+
};
65+
}
66+
5067
return oracleConfig;
5168
}
5269

typescript/infra/scripts/gas/print-all-gas-oracles.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
1-
import { ChainMap, ProtocolAgnositicGasOracleConfig } from '@hyperlane-xyz/sdk';
1+
import { ChainMap } from '@hyperlane-xyz/sdk';
22
import { stringifyObject } from '@hyperlane-xyz/utils';
33

44
import { getChains } from '../../config/registry.js';
5+
import {
6+
type GasOracleConfigWithOverhead,
7+
OracleConfigSchema,
8+
} from '../../src/config/gas-oracle.js';
59
import { writeAndFormatJsonAtPath } from '../../src/utils/utils.js';
610
import { getArgs, withOutputFile } from '../agent-utils.js';
711
import { getEnvironmentConfig } from '../core-utils.js';
812

9-
interface GasOracleConfigWithOverhead {
10-
oracleConfig: ProtocolAgnositicGasOracleConfig;
11-
overhead?: number;
12-
}
13-
1413
async function main() {
1514
const allChainChoices = getChains();
1615

@@ -68,8 +67,9 @@ async function main() {
6867
);
6968
}
7069

70+
const validatedOracleConfig = OracleConfigSchema.parse(oracleConfig);
7171
destAcc[destination] = {
72-
oracleConfig,
72+
oracleConfig: validatedOracleConfig,
7373
overhead: igpConfig?.overhead?.[destination],
7474
};
7575
return destAcc;

typescript/infra/scripts/sealevel-helpers/print-gas-oracles.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ import {
1313
import { WarpRouteIds } from '../../config/environments/mainnet3/warp/warpIds.js';
1414
import { getChain, getWarpAddresses } from '../../config/registry.js';
1515
import { DeployEnvironment } from '../../src/config/environment.js';
16+
import { svmGasOracleConfigPath } from '../../src/utils/sealevel.js';
1617
import { writeAndFormatJsonAtPath } from '../../src/utils/utils.js';
17-
import { getArgs, withOutputFile } from '../agent-utils.js';
18+
import { getArgs, withWrite } from '../agent-utils.js';
1819
import { getEnvironmentConfig } from '../core-utils.js';
1920

2021
// This script exists to print the gas oracle configs for a given environment
@@ -26,7 +27,7 @@ interface GasOracleConfigWithOverhead {
2627
}
2728

2829
async function main() {
29-
const { environment, outFile } = await withOutputFile(getArgs()).argv;
30+
const { environment, write } = await withWrite(getArgs()).argv;
3031

3132
const environmentConfig = getEnvironmentConfig(environment);
3233

@@ -75,11 +76,12 @@ async function main() {
7576
value !== undefined,
7677
);
7778

78-
console.log(stringifyObject(gasOracles, 'json', 2));
79-
80-
if (outFile) {
81-
console.log(`Writing config to ${outFile}`);
82-
writeAndFormatJsonAtPath(outFile, gasOracles);
79+
if (write) {
80+
const filepath = svmGasOracleConfigPath(environment);
81+
console.log(`Writing config to ${filepath}`);
82+
await writeAndFormatJsonAtPath(filepath, gasOracles);
83+
} else {
84+
console.log(stringifyObject(gasOracles, 'json', 2));
8385
}
8486
}
8587

typescript/infra/scripts/sealevel-helpers/print-multisig-ism-config.ts

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,51 @@
1-
import { IsmType } from '@hyperlane-xyz/sdk';
1+
import path from 'path';
22

3+
import { ChainName, IsmType } from '@hyperlane-xyz/sdk';
4+
5+
import { Contexts } from '../../config/contexts.js';
36
import { multisigIsms } from '../../config/multisigIsm.js';
47
import { getChains } from '../../config/registry.js';
5-
import { getArgs, withContext } from '../agent-utils.js';
8+
import { DeployEnvironment } from '../../src/config/environment.js';
9+
import {
10+
assertContext,
11+
getMonorepoRoot,
12+
writeAndFormatJsonAtPath,
13+
} from '../../src/utils/utils.js';
14+
import { getArgs, withWrite } from '../agent-utils.js';
615

716
// This script exists to print the default multisig ISM validator sets for a given environment
817
// so they can easily be copied into the Sealevel tooling. :'(
918

19+
const multisigIsmConfigPath = (
20+
environment: DeployEnvironment,
21+
context: Contexts,
22+
local: ChainName,
23+
) =>
24+
path.resolve(
25+
getMonorepoRoot(),
26+
`rust/sealevel/environments/${environment}/multisig-ism-message-id/${local}/${context}/multisig-config.json`,
27+
);
28+
1029
async function main() {
11-
const args = await withContext(getArgs())
30+
const {
31+
environment,
32+
local,
33+
context = Contexts.Hyperlane,
34+
write,
35+
} = await withWrite(getArgs())
36+
.describe('context', 'write multisig ISM config to context')
37+
.choices('context', [Contexts.Hyperlane, Contexts.ReleaseCandidate])
38+
.alias('x', 'context')
1239
.describe('local', 'local chain')
1340
.choices('local', getChains())
1441
.demandOption('local').argv;
1542

1643
const config = multisigIsms(
17-
args.environment,
18-
args.local,
44+
environment,
45+
local,
1946
IsmType.MESSAGE_ID_MULTISIG,
20-
args.context,
47+
// generate for hyperlane context by default
48+
Contexts.Hyperlane,
2149
);
2250

2351
// Cap any thresholds to 4 due to the Sealevel transaction size limit.
@@ -44,7 +72,17 @@ async function main() {
4472
}
4573
}
4674

47-
console.log(JSON.stringify(config, null, 2));
75+
if (write) {
76+
// write to the context directory
77+
// we use the `hyperlane` context for all config generation
78+
// but when deploying new SVM ISMS, we deploy/configure a new "release candidate" ISM
79+
// before promoting it to the `hyperlane` context and setting it as the default ISM
80+
const filepath = multisigIsmConfigPath(environment, context, local);
81+
console.log(`Writing config to ${filepath}`);
82+
await writeAndFormatJsonAtPath(filepath, config);
83+
} else {
84+
console.log(JSON.stringify(config, null, 2));
85+
}
4886
}
4987

5088
main().catch((err) => {

0 commit comments

Comments
 (0)