Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/publish-core.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: "11"
version: "11.0.0"

- name: Install dependencies
run: pnpm install
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/publish-indexer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,12 @@ jobs:
"down": "./scripts/down.sh ./docker/\$ENV"
},
"dependencies": {
"dotenv": "^16.4.7",
"handlebars": "^4.7.8",
"rpc-websocket-client": "^1.1.4",
"tsx": "^4.19.4",
"viem": "^2.23.5",
"zod": "^3.25.32"
"dotenv": "16.6.1",
"handlebars": "4.7.9",
"rpc-websocket-client": "1.1.4",
"tsx": "4.21.0",
"viem": "2.47.6",
"zod": "3.25.76"
}
}
PKGJSON
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-sdk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: "11"
version: "11.0.0"

- name: Install dependencies
run: pnpm install
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-simplex.yml
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: "11"
version: "11.0.0"

# Straight from GitHub releases, not apt: the runners' default apt
# mirror (azure.archive.ubuntu.com) has repeatedly hung apt-get
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-core.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:

- uses: pnpm/action-setup@v4
with:
version: 11
version: 11.0.0
run_install: false

- name: Get pnpm store directory
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-lz-endpoint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ jobs:

- uses: pnpm/action-setup@v4
with:
version: 11
version: 11.0.0
run_install: false

- name: Get pnpm store directory
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test-sdk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 11
version: 11.0.0

- name: Install dependencies
run: pnpm install
Expand Down Expand Up @@ -147,7 +147,7 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 11
version: 11.0.0

- name: Install dependencies
run: pnpm install
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-simplex-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 11
version: 11.0.0

- name: Setup Node.js
uses: actions/setup-node@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-simplex-orderbook.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 11
version: 11.0.0

- name: Install dependencies
working-directory: sdk
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-solver-inventory.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 11
version: 11.0.0

- name: Install Foundry (anvil)
uses: foundry-rs/foundry-toolchain@v1
Expand Down
14 changes: 14 additions & 0 deletions docs/ai/changelog/2026-10-22-ci-pnpm-matches-packagemanager.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# 2026-10-22 — CI installs the pnpm version the workspace names

Every workflow that sets up pnpm 11 now asks `pnpm/action-setup` for `11.0.0`, the version in
`sdk/package.json`'s `packageManager` field. Before this, the workflows asked for `11`, which
means the newest 11.x release.

When the running pnpm differs from `packageManager`, pnpm downloads `@pnpm/exe` at the named
version and switches to it. Since pnpm 11.28.7 (2026-10-21), that download goes through the
workspace's `trustPolicy: no-downgrade`. `@pnpm/exe@11.0.0` has a provenance attestation where
the releases before it had a trusted publisher, so the check refuses it and `pnpm install` fails
before resolving anything.

Running the exact version means pnpm never switches. Change the workflows and `packageManager`
together when upgrading pnpm.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# 2026-10-22 — Indexer dependencies are pinned to exact versions

Every dependency in the indexer's `package.json` names an exact version, except the
`workspace:*` link to `@hyperbridge/sdk`. None of them use `latest` or a range any more.

The pins are the versions `sdk/pnpm-lock.yaml` already resolved, so installs and the release build
are unchanged. `viem` is pinned to 2.47.6, the version the workspace override in
`pnpm-workspace.yaml` already forces.

The release tarball has its own `package.json`, written by `.github/workflows/publish-indexer.yml`.
Its six dependencies are pinned to the same versions. A deployment installs them with `npm install`
and no lockfile, so a range there would resolve fresh on every deploy.

A `latest` specifier takes whatever npm tags as latest on the day the lockfile is refreshed.
`@subql/common@5.8.3`, published on 2026-10-05, carried a credential-stealing payload and was briefly
tagged `latest`. Upgrade a dependency by changing its version here, deliberately. When the change
touches one of the release package's six dependencies, change it in the workflow too.
82 changes: 41 additions & 41 deletions sdk/packages/indexer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,52 +50,52 @@
},
"dependencies": {
"@hyperbridge/sdk": "workspace:*",
"@ethersproject/abi": "^5.7.0",
"@ethersproject/providers": "^5.7.2",
"@ethersproject/shims": "^5.7.0",
"@open-web3/orml-type-definitions": "^2.0.1",
"@polkadot/api": "latest",
"@polkadot/types": "latest",
"@polkadot/util": "latest",
"@polkadot/util-crypto": "latest",
"@subql/common": "latest",
"@ethersproject/abi": "5.8.0",
"@ethersproject/providers": "5.8.0",
"@ethersproject/shims": "5.8.0",
"@open-web3/orml-type-definitions": "2.0.1",
"@polkadot/api": "16.5.6",
"@polkadot/types": "16.5.6",
"@polkadot/util": "14.0.3",
"@polkadot/util-crypto": "14.0.3",
"@subql/common": "5.8.2",
"@subql/common-ethereum": "4.7.0",
"@subql/types-core": "latest",
"@subql/types-ethereum": "latest",
"@subql/validator": "latest",
"@types/node-fetch": "^2.6.13",
"assert": "^2.0.0",
"decimal.js": "^10.5.0",
"dotenv": "^16.4.7",
"fast-text-encoding": "^1.0.6",
"handlebars": "^4.7.8",
"@subql/types-core": "2.2.0",
"@subql/types-ethereum": "4.2.3",
"@subql/validator": "2.2.0",
"@types/node-fetch": "2.6.13",
"assert": "2.1.0",
"decimal.js": "10.6.0",
"dotenv": "16.6.1",
"fast-text-encoding": "1.0.6",
"handlebars": "4.7.9",
"node-fetch": "2.7.0",
"pg": "^8.20.0",
"safe-stable-stringify": "^2.5.0",
"scale-ts": "^1.6.1",
"viem": "^2.23.5",
"zod": "^3.25.32"
"pg": "8.20.0",
"safe-stable-stringify": "2.5.0",
"scale-ts": "1.6.1",
"viem": "2.47.6",
"zod": "3.25.76"
},
"devDependencies": {
"@subql/cli": "5.7.1-0",
"@subql/node": "latest",
"@subql/node-core": "latest",
"@subql/node-ethereum": "latest",
"@subql/testing": "latest",
"@subql/types": "latest",
"@types/handlebars": "^4.1.0",
"@types/jest": "^29.5.14",
"@types/node": "^22.13.5",
"@types/pino": "^7.0.4",
"ethers": "^5.7.2",
"fs-extra": "^11.3.0",
"prettier": "^3.5.2",
"rpc-websocket-client": "^1.1.4",
"ts-jest": "^29.2.6",
"ts-node": "^10.9.2",
"tslib": "^2.8.1",
"tsx": "^4.19.4",
"typescript": "latest"
"@subql/node": "6.4.6",
"@subql/node-core": "19.3.1",
"@subql/node-ethereum": "6.5.0",
"@subql/testing": "2.3.1",
"@subql/types": "3.15.0",
"@types/handlebars": "4.1.0",
"@types/jest": "29.5.14",
"@types/node": "22.19.17",
"@types/pino": "7.0.5",
"ethers": "5.8.0",
"fs-extra": "11.3.4",
"prettier": "3.5.2",
"rpc-websocket-client": "1.1.4",
"ts-jest": "29.4.9",
"ts-node": "10.9.2",
"tslib": "2.8.1",
"tsx": "4.21.0",
"typescript": "6.0.3"
},
"exports": {
"./substrate-chaintypes/hyperbridge": "./src/substrate-chaintypes/hyperbridge.ts"
Expand Down
2 changes: 2 additions & 0 deletions sdk/packages/lz-endpoint/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ Fees can be paid in two ways:

The `quote()` function returns the appropriate fee based on the `payInLzToken` flag.

LayerZero requires `quote()` to be `view`, so it prices a native fee by reading the host's fee oracle in a static call. Some hosts' oracles price by simulating a swap, which a static call cannot do. That's the Uniswap V3 wrapper on Ethereum and Polygon, and the V4 wrapper on Arbitrum and Base. On those chains a native-fee `quote()` reverts, so OApps there should pay in the fee token. BSC's oracle is PancakeSwap's V2 router, which quotes natively.

### Relayer fees

A per-destination relayer fee incentivizes Hyperbridge relayers to deliver messages. Defaults to $0.30 (computed from the fee token's decimals). The adapter owner can customize fees:
Expand Down
25 changes: 24 additions & 1 deletion sdk/packages/lz-endpoint/contracts/HyperbridgeLzEndpoint.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ import {ILayerZeroReceiver} from "@layerzerolabs/lz-evm-protocol-v2/contracts/in
import {ILayerZeroComposer} from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroComposer.sol";
import {SetConfigParam} from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessageLibManager.sol";

/**
* @dev The host's fee oracle, read in a static context. `HyperApp.quote` calls it with a
* state-changing `CALL`, which a `view` function cannot make.
*/
interface IUniswapV2RouterView {
function WETH() external view returns (address);

function getAmountsIn(uint256 amountOut, address[] calldata path) external view returns (uint256[] memory);
}

/**
* @title HyperbridgeLzEndpoint
* @author Polytope Labs (hello@polytope.technology)
Expand Down Expand Up @@ -341,10 +351,23 @@ contract HyperbridgeLzEndpoint is HyperApp, Ownable, Pausable, ILayerZeroEndpoin
if (_params.payInLzToken) {
return MessagingFee({nativeFee: 0, lzTokenFee: request.fee * 2});
} else {
return MessagingFee({nativeFee: quote(request) * 2, lzTokenFee: 0});
return MessagingFee({nativeFee: _nativeFee(request.fee) * 2, lzTokenFee: 0});
}
}

/**
* @dev The native amount the host's fee oracle wants for `feeTokenAmount`. LayerZero
* requires `quote` to be `view`, so this reverts on a host whose oracle prices by
* simulating a swap, such as a Uniswap V3 or V4 wrapper.
*/
function _nativeFee(uint256 feeTokenAmount) private view returns (uint256) {
address router = IDispatcher(_host).uniswapV2Router();
address[] memory path = new address[](2);
path[0] = IUniswapV2RouterView(router).WETH();
path[1] = IDispatcher(_host).feeToken();
return IUniswapV2RouterView(router).getAmountsIn(feeTokenAmount, path)[0];
}

// ==================== ISMP Callbacks ====================

/**
Expand Down
Loading
Loading