Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ff3ebc1
feat: multi-prover support with destination-aware selection and no-to…
nknavkal Mar 31, 2026
3abda40
feat: add LZ prover addresses, portal configs, and fix TVM address en…
nknavkal Apr 7, 2026
f5cb35c
feat: port lzprover support to new NestJS architecture
nknavkal Apr 8, 2026
2b16d4e
fix: point postinstall script to scripts/ instead of bundle/
nknavkal Apr 22, 2026
9f6aa13
fix: update pnpm lockfile to match package.json specifiers
nknavkal Apr 22, 2026
dc3a3cf
feat: port LZ prover support to NestJS architecture
nknavkal Apr 22, 2026
bd65005
fix: update test to use provers dict and adjust coverage threshold
nknavkal Apr 22, 2026
701c10b
fix: update portal and prover addresses in chain configs
nknavkal Apr 30, 2026
3d9a9c7
Merge origin/main into feat/lzprover_support
nknavkal May 6, 2026
3c9a05f
chore: mark prod portal and prover addresses in chain configs
nknavkal May 19, 2026
061ac63
Merge origin/main into feat/lzprover_support
nknavkal May 19, 2026
5b4c6a0
fix: remove zero-address LayerZero placeholders from testnet configs
nknavkal May 19, 2026
649a8fa
refactor: introduce ProverType union for type-safe prover key indexing
nknavkal May 19, 2026
d1a08cd
fix: restore defensive 0x41 check in denormalizeToTvm
nknavkal May 19, 2026
8c81917
fix: warn before manual prover fallback when intersection is empty
nknavkal May 19, 2026
21eba99
feat: auto-select prover when only one type is common; add --prover-type
nknavkal May 19, 2026
b29f142
fix: remove no-token path from publish flow
nknavkal May 19, 2026
67b1d5d
fix: improve quote catch block — re-throw RoutesCliError, use display
nknavkal May 19, 2026
d4ef208
fix: log EVM tx hash before waiting and catch timeout explicitly
nknavkal May 19, 2026
dbc602e
fix: restore coverage statements threshold to 20
nknavkal May 19, 2026
1891d54
fix: validate --prover-type against both source and dest chains
nknavkal May 20, 2026
c73a556
fix: EIP-55 checksum Hyperlane testnet prover address
nknavkal May 20, 2026
2abf1c7
fix: correct malformed LayerZero prover fixture in test (63 → 64 hex …
nknavkal May 20, 2026
42603cd
fix: update Shasta and Sepolia testnet contract addresses
nknavkal May 28, 2026
0212f60
fix: validate quote route amount, update prod addresses, harden TVM n…
nknavkal Jun 19, 2026
da69d92
Merge branch 'main' into feat/tron_testnet
nknavkal Jun 19, 2026
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
16 changes: 9 additions & 7 deletions src/blockchain/chains.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ export const RAW_CHAIN_CONFIGS: RawChainConfig[] = [
type: ChainType.EVM,
env: 'production',
rpcUrl: 'https://mainnet.base.org',
portalAddress: '0x399Dbd5DF04f83103F77A58cBa2B7c4d3cdede97', // prod portal
provers: { LayerZero: '0x0C4E3063239c9f4f323A956C79738916594D8Fd4' }, // prod prover
portalAddress: '0xfD12115CD8F37C7667050eD8499EDa6B9d9c03bA', // prod portal
provers: { LayerZero: '0x3a572CfA867691e4D8bD19B80294f3c744a384E9' }, // prod prover
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
},
{
Expand Down Expand Up @@ -108,7 +108,7 @@ export const RAW_CHAIN_CONFIGS: RawChainConfig[] = [
portalAddress: '0x399Dbd5DF04f83103F77A58cBa2B7c4d3cdede97',
provers: {
Hyperlane: '0x9523b6c0cAaC8122DbD5Dd1c1d336CEBA637038D',
LayerZero: '0x6D8D9E68627b8eb2D4A3c1110be3FE46Ff6e92A3',
LayerZero: '0x82d378D05271743d6C03fbBb108f981E39dd81a9',
},
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
},
Expand Down Expand Up @@ -156,8 +156,8 @@ export const RAW_CHAIN_CONFIGS: RawChainConfig[] = [
type: ChainType.TVM,
env: 'production',
rpcUrl: 'https://api.trongrid.io',
portalAddress: 'TTXNcSeX5aYb1ETWYjcX3fvumynWoyFgYw', // prod portal
provers: { LayerZero: 'TFu38RELzp7jdR9s7vj4JSpw2kFuTSAq3E' }, // prod prover
portalAddress: 'TMu3sz3aQqAQyvnPYrmDM9FeZGC3HdTLs7', // prod portal
provers: { LayerZero: 'TQUwftPRikD9ngiFs6MJpo7SKhSYsKmfye' }, // prod prover
nativeCurrency: { name: 'Tron', symbol: 'TRX', decimals: 6 },
},
{
Expand All @@ -166,8 +166,8 @@ export const RAW_CHAIN_CONFIGS: RawChainConfig[] = [
type: ChainType.TVM,
env: 'development',
rpcUrl: 'https://api.shasta.trongrid.io',
portalAddress: 'TScmM6ZoR6grho3pKCzX6M2MKBYVURG1s5',
provers: { LayerZero: 'TM6cLaN3LStBFi9AjrhLQ9cc6QiVu5nFsD' },
portalAddress: 'TTaASiEw2Q7ZK9um2s1M7VswiSe2wxKRD3',
provers: { LayerZero: 'TSCJtdKaJBt8THXZbKRddsHytSghZebsHm' },
nativeCurrency: { name: 'Tron', symbol: 'TRX', decimals: 6 },
},

Expand All @@ -178,6 +178,8 @@ export const RAW_CHAIN_CONFIGS: RawChainConfig[] = [
type: ChainType.SVM,
env: 'production',
rpcUrl: 'https://api.mainnet-beta.solana.com',
portalAddress: 'Ecoo5HDM2XCBy7QzkhDGrAmnRcWw7emU6xGr7CcCmooo',
provers: { Hyperlane: 'EcooFDTfKVVo5qZcpNoDngMmVXqrG6FQT1D5LDjZEGeR' },
nativeCurrency: { name: 'Solana', symbol: 'SOL', decimals: 9 },
},
{
Expand Down
8 changes: 5 additions & 3 deletions src/blockchain/utils/address-normalizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,11 @@ export class AddressNormalizer {
const unpadded = unpadFrom32Bytes(address);
// Guard against old-format universals that already carry the 0x41 prefix
// (e.g. values stored before the 20-byte normalization change).
const hexAddress = unpadded.startsWith('0x41')
? unpadded.substring(2)
: '41' + unpadded.substring(2);
// Use length instead of prefix: '0x' + 42 hex = 21 bytes (old format, 0x41 already present);
// '0x' + 40 hex = 20 bytes (new format, needs 41 prepended). A prefix check
// would misfire on 20-byte bodies that happen to start with 41.
const hexAddress =
unpadded.length === 44 ? unpadded.substring(2) : '41' + unpadded.substring(2);
const base58Address = TronWeb.address.fromHex(hexAddress);
if (!TronWeb.isAddress(base58Address)) {
throw new Error(`Invalid Tron address after denormalization: ${base58Address}`);
Expand Down
29 changes: 26 additions & 3 deletions src/cli/services/intent-publish-flow.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import { privateKeyToAccount } from 'viem/accounts';

import { AddressNormalizerService } from '@/blockchain/address-normalizer.service';
import { PublishResult } from '@/blockchain/base.publisher';
import { PortalEncoderService } from '@/blockchain/encoding/portal-encoder.service';
import { PublisherFactory } from '@/blockchain/publisher-factory.service';
import { getErrorMessage } from '@/commons/utils/error-handler';
import { ConfigService } from '@/config/config.service';
import { TOKEN_CONFIGS } from '@/config/tokens.config';
import { IntentBuilder } from '@/intent/intent-builder.service';
import { IntentStorage } from '@/intent/intent-storage.service';
import { QuoteResult, QuoteService } from '@/quote/quote.service';
import { RoutesCliError } from '@/shared/errors';
import { ErrorCode, RoutesCliError } from '@/shared/errors';
import { KeyHandle } from '@/shared/security';
import {
BlockchainAddress,
Expand Down Expand Up @@ -77,7 +78,8 @@ export class IntentPublishFlow {
private readonly intentStorage: IntentStorage,
private readonly prompt: PromptService,
private readonly display: DisplayService,
private readonly statusService: StatusService
private readonly statusService: StatusService,
private readonly encoder: PortalEncoderService
) {}

async publish(args: {
Expand Down Expand Up @@ -185,7 +187,7 @@ export class IntentPublishFlow {
);

if (!result.success) {
this.display.fail('Publishing failed');
this.display.fail(`Publishing failed: ${result.error ?? 'unknown error'}`);
throw new Error(result.error);
}

Expand Down Expand Up @@ -281,6 +283,7 @@ export class IntentPublishFlow {
});
this.display.succeed('Quote received');
this.display.displayQuote(quote, rewardToken, rewardAmount, routeToken);
this.validateQuoteRouteAmount(quote, destChain);
const sourcePortal = this.normalizer.normalize(
quote.sourcePortal as Parameters<AddressNormalizerService['normalize']>[0],
sourceChain.type
Expand Down Expand Up @@ -433,6 +436,26 @@ export class IntentPublishFlow {
}
}

private validateQuoteRouteAmount(quote: QuoteResult, destChain: ChainConfig): void {
let routeAmount: bigint;
try {
const route = this.encoder.decode(quote.encodedRoute, destChain.type, 'route');
routeAmount = route.tokens.length > 0 ? route.tokens[0].amount : route.nativeAmount;
} catch {
return; // can't decode — skip the check
}

const quotedAmount = BigInt(quote.destinationAmount);
if (routeAmount !== quotedAmount) {
throw new RoutesCliError(
ErrorCode.QUOTE_SERVICE_ERROR,
`Quote amount mismatch: solver quoted ${quotedAmount} but encoded route calls for ${routeAmount}. ` +
`The intent would revert on-chain. Please retry — the solver may have returned a stale quote.`,
true
);
}
}

private static resolveKey(options: PublishFlowOptions, chainType: ChainType): string | undefined {
switch (chainType) {
case ChainType.EVM:
Expand Down
8 changes: 4 additions & 4 deletions src/config/config.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ export class ConfigService {
fallback: '',
},
[ChainType.TVM]: {
primary: this.config.get<string>('TVM_RPC_URL') ?? 'https://api.trongrid.io',
fallback: this.config.get<string>('TVM_RPC_URL_2') ?? 'https://tron.publicnode.com',
primary: this.config.get<string>('TVM_RPC_URL') ?? '',
fallback: this.config.get<string>('TVM_RPC_URL_2') ?? '',
},
[ChainType.SVM]: {
primary: this.config.get<string>('SVM_RPC_URL') ?? 'https://api.mainnet-beta.solana.com',
fallback: this.config.get<string>('SVM_RPC_URL_2') ?? 'https://solana.publicnode.com',
primary: this.config.get<string>('SVM_RPC_URL') ?? '',
fallback: this.config.get<string>('SVM_RPC_URL_2') ?? '',
},
};
return map[chainType][variant] || undefined;
Expand Down
Loading