Skip to content

Commit 6ceb161

Browse files
bfullamOGPoyraz
andauthored
feat: Swaps tron integration (#6862)
## Explanation Adds support for bridging and swapping tokens on the Tron blockchain. **What Changed** - Bridge and swap controllers now recognize and handle Tron transactions - Non-evm token approvals are now supported - Tron transactions are processed through the same snap-based flow as Solana and Bitcoin **Technical Notes** Tron transactions have a different format (raw_data_hex) than EVM chains, so this PR adds the necessary type guards and data extraction utilities to handle them properly. <!-- Thanks for your contribution! Take a moment to answer these questions so that reviewers have the information they need to properly understand your changes: * What is the current state of things and why does it need to change? * What is the solution your changes offer and how does it work? * Are there any changes whose purpose might not obvious to those unfamiliar with the domain? * If your primary goal was to update one package but you found you had to update another one along the way, why did you do so? * If you had to upgrade a dependency, why did you do so? --> ## References <!-- Are there any issues that this pull request is tied to? Are there other links that reviewers should consult to understand these changes better? Are there client or consumer pull requests to adopt any breaking changes? For example: * Fixes #12345 * Related to #67890 --> ## Checklist - [ ] I've updated the test suite for new or updated code as appropriate - [ ] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [ ] I've communicated my changes to consumers by [updating changelogs for packages I've changed](https://github.com/MetaMask/core/tree/main/docs/contributing.md#updating-changelogs), highlighting breaking changes as necessary - [ ] I've prepared draft pull requests for clients and consumer packages to resolve any breaking changes <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Adds Tron bridging/swapping with snap-based flow (incl. approvals), expanding types/validators, CAIP/chain utils, fee calc, and tests. > > - **Bridge Controller**: > - Add Tron chain support: `TrxScope.Mainnet`, `ChainId.TRON`, native `TRX` token, and include in `ALLOWED_BRIDGE_CHAIN_IDS` and symbol maps. > - Extend types/validators: introduce `TronTradeData`, allow `approval`/`trade` to be Tron; widen `QuoteResponse` generics. > - Utilities: add `isTronChainId`, include Tron in `isNonEvmChainId`; CAIP formatters handle Tron; new `trade-utils` (`Trade`, `isTronTrade`, `extractTradeData` hex→base64); fee calc adds Tron snap options. > - Public API: export new trade utils; update selectors/types exports accordingly. > - Tests/SSE snapshots updated to cover Tron fields (`raw_data_hex`) and flows. > - **Bridge Status Controller**: > - Support Tron submit flow (swap/bridge) with optional Tron approvals via unified Snap `signAndSendTransaction`; pass Tron-specific request options. > - Generalize non‑EVM handling to `Trade`; update tx meta building and client request helpers. > - Add extensive tests for Tron approval, swap, and bridge cases. > - **Transaction Pay Controller**: > - Adjust batch tx construction to handle multichain approvals (type casts); update changelog. > - **Changelogs**: note Tron bridging/swapping support in affected packages. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 2c81337. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: OGPoyraz <omergoktugpoyraz@gmail.com>
1 parent a5d7def commit 6ceb161

26 files changed

+1272
-91
lines changed

packages/bridge-controller/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Added support for bridging and swapping tokens on the Tron blockchain ([#6862](https://github.com/MetaMask/core/pull/6862))
13+
1014
## [60.0.0]
1115

1216
### Changed

packages/bridge-controller/src/__snapshots__/bridge-controller.sse.test.ts.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Array [
1818
"lifi|trade.gasLimit",
1919
"lifi|trade.unsignedPsbtBase64",
2020
"lifi|trade.inputsToSign",
21+
"lifi|trade.raw_data_hex",
2122
],
2223
"refresh_count": 1,
2324
"token_address_destination": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:123d1",

packages/bridge-controller/src/bridge-controller.sse.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,7 @@ describe('BridgeController SSE', function () {
656656
"lifi|trade.gasLimit",
657657
"lifi|trade.unsignedPsbtBase64",
658658
"lifi|trade.inputsToSign",
659+
"lifi|trade.raw_data_hex",
659660
],
660661
]
661662
`);

packages/bridge-controller/src/constants/bridge.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { AddressZero } from '@ethersproject/constants';
2-
import { BtcScope, SolScope } from '@metamask/keyring-api';
2+
import { BtcScope, SolScope, TrxScope } from '@metamask/keyring-api';
33
import type { Hex } from '@metamask/utils';
44

55
import { CHAIN_IDS } from './chains';
@@ -22,6 +22,7 @@ export const ALLOWED_BRIDGE_CHAIN_IDS = [
2222
CHAIN_IDS.MONAD,
2323
SolScope.Mainnet,
2424
BtcScope.Mainnet,
25+
TrxScope.Mainnet,
2526
] as const;
2627

2728
export type AllowedBridgeChainIds = (typeof ALLOWED_BRIDGE_CHAIN_IDS)[number];

packages/bridge-controller/src/constants/tokens.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { BtcScope, SolScope } from '@metamask/keyring-api';
1+
import { BtcScope, SolScope, TrxScope } from '@metamask/keyring-api';
22

33
import type { AllowedBridgeChainIds } from './bridge';
44
import { CHAIN_IDS } from './chains';
@@ -54,6 +54,7 @@ const CURRENCY_SYMBOLS = {
5454
SOL: 'SOL',
5555
SEI: 'SEI',
5656
BTC: 'BTC',
57+
TRX: 'TRX',
5758
MON: 'MON',
5859
} as const;
5960

@@ -157,6 +158,14 @@ const SEI_SWAPS_TOKEN_OBJECT = {
157158
iconUrl: '',
158159
} as const;
159160

161+
const TRX_SWAPS_TOKEN_OBJECT = {
162+
symbol: CURRENCY_SYMBOLS.TRX,
163+
name: 'Tron',
164+
address: DEFAULT_TOKEN_ADDRESS,
165+
decimals: 6,
166+
iconUrl: '',
167+
} as const;
168+
160169
const MONAD_SWAPS_TOKEN_OBJECT = {
161170
symbol: CURRENCY_SYMBOLS.MON,
162171
name: 'Mon',
@@ -185,6 +194,7 @@ export const SWAPS_CHAINID_DEFAULT_TOKEN_MAP = {
185194
[SolScope.Mainnet]: SOLANA_SWAPS_TOKEN_OBJECT,
186195
[SolScope.Devnet]: SOLANA_SWAPS_TOKEN_OBJECT,
187196
[BtcScope.Mainnet]: BTC_SWAPS_TOKEN_OBJECT,
197+
[TrxScope.Mainnet]: TRX_SWAPS_TOKEN_OBJECT,
188198
} as const;
189199

190200
export type SupportedSwapsNativeCurrencySymbols =
@@ -208,5 +218,6 @@ export const SYMBOL_TO_SLIP44_MAP: Record<
208218
AVAX: 'slip44:9000',
209219
TESTETH: 'slip44:60',
210220
SEI: 'slip44:19000118',
221+
TRX: 'slip44:195',
211222
MON: 'slip44:268435779',
212223
};

packages/bridge-controller/src/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export type {
4141
FeeData,
4242
TxData,
4343
BitcoinTradeData,
44+
TronTradeData,
4445
BridgeControllerState,
4546
BridgeControllerAction,
4647
BridgeControllerActions,
@@ -131,6 +132,13 @@ export {
131132
formatAddressToAssetId,
132133
} from './utils/caip-formatters';
133134

135+
export {
136+
extractTradeData,
137+
isBitcoinTrade,
138+
isTronTrade,
139+
type Trade,
140+
} from './utils/trade-utils';
141+
134142
export {
135143
selectBridgeQuotes,
136144
selectDefaultSlippagePercentage,

packages/bridge-controller/src/types.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import type {
3737
QuoteResponseSchema,
3838
QuoteSchema,
3939
StepSchema,
40+
TronTradeDataSchema,
4041
TxDataSchema,
4142
} from './utils/validators';
4243

@@ -251,16 +252,21 @@ export type Quote = Infer<typeof QuoteSchema>;
251252
export type TxData = Infer<typeof TxDataSchema>;
252253

253254
export type BitcoinTradeData = Infer<typeof BitcoinTradeDataSchema>;
255+
256+
export type TronTradeData = Infer<typeof TronTradeDataSchema>;
254257
/**
255258
* This is the type for the quote response from the bridge-api
256259
* TxDataType can be overriden to be a string when the quote is non-evm
260+
* ApprovalType can be overriden when you know the specific approval type (e.g., TxData for EVM-only contexts)
257261
*/
258-
export type QuoteResponse<TxDataType = TxData | string | BitcoinTradeData> =
259-
Infer<typeof QuoteResponseSchema> & {
260-
trade: TxDataType;
261-
approval?: TxData;
262-
featureId?: FeatureId;
263-
};
262+
export type QuoteResponse<
263+
TxDataType = TxData | string | BitcoinTradeData | TronTradeData,
264+
ApprovalType = TxData | TronTradeData,
265+
> = Infer<typeof QuoteResponseSchema> & {
266+
trade: TxDataType;
267+
approval?: ApprovalType;
268+
featureId?: FeatureId;
269+
};
264270

265271
export enum ChainId {
266272
ETH = 1,
@@ -274,6 +280,7 @@ export enum ChainId {
274280
LINEA = 59144,
275281
SOLANA = 1151111081099710,
276282
BTC = 20000000000001,
283+
TRON = 728126428,
277284
}
278285

279286
export type FeatureFlagsPlatformConfig = Infer<typeof PlatformConfigSchema>;

packages/bridge-controller/src/utils/bridge.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { AddressZero } from '@ethersproject/constants';
22
import { Contract } from '@ethersproject/contracts';
3-
import { BtcScope, SolScope } from '@metamask/keyring-api';
3+
import { BtcScope, SolScope, TrxScope } from '@metamask/keyring-api';
44
import { abiERC20 } from '@metamask/metamask-eth-abis';
55
import type { CaipAssetType, CaipChainId } from '@metamask/utils';
66
import { isCaipChainId, isStrictHexString, type Hex } from '@metamask/utils';
@@ -192,22 +192,33 @@ export const isBitcoinChainId = (
192192
return chainId.toString() === ChainId.BTC.toString();
193193
};
194194

195+
export const isTronChainId = (chainId: Hex | number | CaipChainId | string) => {
196+
if (isCaipChainId(chainId)) {
197+
return chainId === TrxScope.Mainnet.toString();
198+
}
199+
return chainId.toString() === ChainId.TRON.toString();
200+
};
201+
195202
/**
196203
* Checks if a chain ID represents a non-EVM blockchain supported by swaps
197-
* Currently supports Solana and Bitcoin
204+
* Currently supports Solana, Bitcoin and Tron
198205
*
199206
* @param chainId - The chain ID to check
200207
* @returns True if the chain is a supported non-EVM chain, false otherwise
201208
*/
202209
export const isNonEvmChainId = (
203210
chainId: GenericQuoteRequest['srcChainId'],
204211
): boolean => {
205-
return isSolanaChainId(chainId) || isBitcoinChainId(chainId);
212+
return (
213+
isSolanaChainId(chainId) ||
214+
isBitcoinChainId(chainId) ||
215+
isTronChainId(chainId)
216+
);
206217
};
207218

208219
export const isEvmQuoteResponse = (
209220
quoteResponse: QuoteResponse,
210-
): quoteResponse is QuoteResponse<TxData> => {
221+
): quoteResponse is QuoteResponse<TxData, TxData> => {
211222
return !isNonEvmChainId(quoteResponse.quote.srcChainId);
212223
};
213224

packages/bridge-controller/src/utils/caip-formatters.test.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { AddressZero } from '@ethersproject/constants';
2-
import { BtcScope, SolScope } from '@metamask/keyring-api';
2+
import { BtcScope, SolScope, TrxScope } from '@metamask/keyring-api';
33

44
import {
55
formatChainIdToCaip,
@@ -36,6 +36,11 @@ describe('CAIP Formatters', () => {
3636
expect(formatChainIdToCaip('20000000000001')).toBe(BtcScope.Mainnet);
3737
});
3838

39+
it('should convert Tron chainId to TrxScope.Mainnet', () => {
40+
expect(formatChainIdToCaip(ChainId.TRON)).toBe(TrxScope.Mainnet);
41+
expect(formatChainIdToCaip(TrxScope.Mainnet)).toBe(TrxScope.Mainnet);
42+
});
43+
3944
it('should convert number to CAIP format', () => {
4045
expect(formatChainIdToCaip(1)).toBe('eip155:1');
4146
});
@@ -59,6 +64,10 @@ describe('CAIP Formatters', () => {
5964
expect(formatChainIdToDec('20000000000001')).toBe(20000000000001);
6065
});
6166

67+
it('should handle Tron mainnet', () => {
68+
expect(formatChainIdToDec(TrxScope.Mainnet)).toBe(ChainId.TRON);
69+
});
70+
6271
it('should parse CAIP chainId to decimal', () => {
6372
expect(formatChainIdToDec('eip155:1')).toBe(1);
6473
});

packages/bridge-controller/src/utils/caip-formatters.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { getAddress } from '@ethersproject/address';
22
import { AddressZero } from '@ethersproject/constants';
33
import { convertHexToDecimal } from '@metamask/controller-utils';
4-
import { BtcScope, SolScope } from '@metamask/keyring-api';
4+
import { BtcScope, SolScope, TrxScope } from '@metamask/keyring-api';
55
import { toEvmCaipChainId } from '@metamask/multichain-network-controller';
66
import type { CaipAssetType } from '@metamask/utils';
77
import {
@@ -21,6 +21,7 @@ import {
2121
isBitcoinChainId,
2222
isNativeAddress,
2323
isSolanaChainId,
24+
isTronChainId,
2425
} from './bridge';
2526
import type { GenericQuoteRequest } from '../types';
2627
import { ChainId } from '../types';
@@ -46,6 +47,9 @@ export const formatChainIdToCaip = (
4647
if (isBitcoinChainId(chainId)) {
4748
return BtcScope.Mainnet;
4849
}
50+
if (isTronChainId(chainId)) {
51+
return TrxScope.Mainnet;
52+
}
4953
return toEvmCaipChainId(numberToHex(Number(chainId)));
5054
};
5155

@@ -67,6 +71,9 @@ export const formatChainIdToDec = (
6771
if (chainId === BtcScope.Mainnet) {
6872
return ChainId.BTC;
6973
}
74+
if (chainId === TrxScope.Mainnet) {
75+
return ChainId.TRON;
76+
}
7077
if (isCaipChainId(chainId)) {
7178
return Number(chainId.split(':').at(-1));
7279
}

0 commit comments

Comments
 (0)