Skip to content

Commit e4da110

Browse files
authored
fix: solana routing fee (#8599)
1 parent fa08f2a commit e4da110

3 files changed

Lines changed: 88 additions & 0 deletions

File tree

.changeset/brown-peaches-peel.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@hyperlane-xyz/sdk": patch
3+
---
4+
5+
Fixed routing fee for non-evm legs

typescript/cli/src/tests/cross-chain/warp/warp-cc-evm-svm.e2e-test.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
runSolanaNode,
2222
} from '@hyperlane-xyz/sealevel-sdk/testing';
2323
import {
24+
TokenFeeType,
2425
TokenType,
2526
type WarpCoreConfig,
2627
type WarpRouteDeployConfig,
@@ -355,4 +356,75 @@ describe('hyperlane warp crossCollateral EVM+SVM e2e tests', function () {
355356
`EVM Route B should have EVM Route A enrolled as CC router on domain ${evmDomainId}`,
356357
).to.include(evmRouterAHex32);
357358
});
359+
360+
it('should deploy EVM collateral + SVM synthetic with RoutingFee tokenFee without error', async function () {
361+
const evmOwner = new Wallet(EVM_KEY).address;
362+
const svmOwner = svmSigner.getSignerAddress();
363+
const DECIMALS = 9;
364+
const SYMBOL = 'RTKN';
365+
366+
const evmToken = await deployToken(
367+
EVM_KEY,
368+
EVM_CHAIN,
369+
DECIMALS,
370+
SYMBOL,
371+
'Routing Token',
372+
REGISTRY_PATH,
373+
);
374+
375+
const warpId = createWarpRouteConfigId(SYMBOL, `${EVM_CHAIN}-${SVM_CHAIN}`);
376+
const warpDeployConfig: WarpRouteDeployConfig = {
377+
[EVM_CHAIN]: {
378+
type: TokenType.collateral,
379+
token: evmToken.address,
380+
mailbox: evmCoreAddresses.mailbox,
381+
owner: evmOwner,
382+
tokenFee: {
383+
type: TokenFeeType.RoutingFee,
384+
owner: evmOwner,
385+
feeContracts: {
386+
[SVM_CHAIN]: {
387+
type: TokenFeeType.LinearFee,
388+
bps: 50,
389+
},
390+
},
391+
},
392+
},
393+
[SVM_CHAIN]: {
394+
type: TokenType.synthetic,
395+
mailbox: svmCoreAddresses.mailbox,
396+
owner: svmOwner,
397+
name: 'Routing Token',
398+
symbol: SYMBOL,
399+
decimals: DECIMALS,
400+
metadataUri: 'https://test.example.com/rtkn-metadata.json',
401+
},
402+
};
403+
404+
writeYamlOrJson(WARP_DEPLOY_OUTPUT_PATH, warpDeployConfig);
405+
406+
// Before the fix, enrollCrossChainRouters would fail with a
407+
// RoutingFeeInputConfigSchema validation error because the EVM reader
408+
// returns empty feeContracts when no SVM routers are enrolled yet.
409+
await warpCommands.deployRaw({
410+
warpRouteId: warpId,
411+
warpDeployPath: WARP_DEPLOY_OUTPUT_PATH,
412+
skipConfirmationPrompts: true,
413+
extraArgs: [
414+
`--key.${ProtocolType.Ethereum}`,
415+
EVM_KEY,
416+
`--key.${ProtocolType.Sealevel}`,
417+
SVM_KEY,
418+
],
419+
});
420+
421+
const warpCorePath = getWarpCoreConfigPath(SYMBOL, [EVM_CHAIN, SVM_CHAIN]);
422+
const deployedConfig = await warpCommands.readConfig(
423+
EVM_CHAIN,
424+
warpCorePath,
425+
);
426+
expect(deployedConfig[EVM_CHAIN].tokenFee?.type).to.equal(
427+
TokenFeeType.RoutingFee,
428+
);
429+
});
358430
});

typescript/sdk/src/deploy/warp.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,17 @@ export async function enrollCrossChainRouters(
645645
owner: resolvedConfigMap[currentChain].owner,
646646
remoteRouters,
647647
destinationGas,
648+
// For cross-protocol routes (EVM+SVM/Cosmos), the EVM deployer
649+
// never enrolls non-EVM remote routers, so TokenRouter.domains()=[]
650+
// at this point. The reader derives RoutingFee.feeContracts from
651+
// enrolled domains, returning {} which fails
652+
// RoutingFeeInputConfigSchema validation. Use the deploy config's
653+
// tokenFee (non-empty feeContracts) so validation passes.
654+
// EvmTokenFeeModule.update() reads actual on-chain state via
655+
// routingDestinations and confirms no change is needed.
656+
...(resolvedConfigMap[currentChain].tokenFee && {
657+
tokenFee: resolvedConfigMap[currentChain].tokenFee,
658+
}),
648659
};
649660

650661
transactions = await evmWarpModule.update(expectedConfig, {

0 commit comments

Comments
 (0)