Skip to content

Commit 63179a0

Browse files
authored
Aptos CCTP Support (#3181)
* first commit * fix aptos token explorer urls * fixes * bump sdk for aptos route * aptos cctp: allow petra wallet
1 parent 9cf7303 commit 63179a0

File tree

9 files changed

+299
-203
lines changed

9 files changed

+299
-203
lines changed

wormhole-connect/package-lock.json

+174-159
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

wormhole-connect/package.json

+17-17
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@
3939
"@solana/spl-token": "^0.3.9",
4040
"@solana/wallet-adapter-wallets": "^0.19.25",
4141
"@solana/web3.js": "^1.95.8",
42-
"@wormhole-foundation/sdk": "1.5.0",
43-
"@wormhole-foundation/sdk-definitions": "1.5.0",
42+
"@wormhole-foundation/sdk": "1.5.2",
43+
"@wormhole-foundation/sdk-definitions": "1.5.2",
4444
"@wormhole-foundation/sdk-definitions-ntt": "^0.6.1",
4545
"@wormhole-foundation/sdk-evm-ntt": "^0.6.1",
4646
"@wormhole-foundation/sdk-icons": "^1.0.0",
@@ -167,31 +167,31 @@
167167
"axios": "1.4.0"
168168
},
169169
"@mayanfinance/wormhole-sdk-route": {
170-
"@wormhole-foundation/sdk-connect": "1.5.0",
171-
"@wormhole-foundation/sdk-evm": "1.5.0",
172-
"@wormhole-foundation/sdk-solana": "1.5.0"
170+
"@wormhole-foundation/sdk-connect": "1.5.2",
171+
"@wormhole-foundation/sdk-evm": "1.5.2",
172+
"@wormhole-foundation/sdk-solana": "1.5.2"
173173
},
174174
"@wormhole-foundation/sdk-definitions-ntt": {
175-
"@wormhole-foundation/sdk-base": "1.5.0",
176-
"@wormhole-foundation/sdk-definitions": "1.5.0"
175+
"@wormhole-foundation/sdk-base": "1.5.2",
176+
"@wormhole-foundation/sdk-definitions": "1.5.2"
177177
},
178178
"@wormhole-foundation/sdk-evm-ntt": {
179-
"@wormhole-foundation/sdk-base": "1.5.0",
180-
"@wormhole-foundation/sdk-definitions": "1.5.0",
181-
"@wormhole-foundation/sdk-evm": "1.5.0",
182-
"@wormhole-foundation/sdk-evm-core": "1.5.0"
179+
"@wormhole-foundation/sdk-base": "1.5.2",
180+
"@wormhole-foundation/sdk-definitions": "1.5.2",
181+
"@wormhole-foundation/sdk-evm": "1.5.2",
182+
"@wormhole-foundation/sdk-evm-core": "1.5.2"
183183
},
184184
"@wormhole-foundation/sdk-route-ntt": {
185-
"@wormhole-foundation/sdk-connect": "1.5.0"
185+
"@wormhole-foundation/sdk-connect": "1.5.2"
186186
},
187187
"@wormhole-foundation/sdk-solana-ntt": {
188-
"@wormhole-foundation/sdk-base": "1.5.0",
189-
"@wormhole-foundation/sdk-definitions": "1.5.0",
190-
"@wormhole-foundation/sdk-solana": "1.5.0",
191-
"@wormhole-foundation/sdk-solana-core": "1.5.0"
188+
"@wormhole-foundation/sdk-base": "1.5.2",
189+
"@wormhole-foundation/sdk-definitions": "1.5.2",
190+
"@wormhole-foundation/sdk-solana": "1.5.2",
191+
"@wormhole-foundation/sdk-solana-core": "1.5.2"
192192
},
193193
"@wormhole-foundation/wormhole-connect": {
194-
"aptos": "1.5.0"
194+
"aptos": "1.5.2"
195195
},
196196
"@json-rpc-tools/provider": {
197197
"axios": "0.27.2"

wormhole-connect/src/config/mainnet/tokens.ts

+10
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,16 @@ export const MAINNET_TOKENS: TokenConfig[] = [
282282
tokenId: { chain: 'Aptos', address: 'native' },
283283
icon: TokenIcon.APT,
284284
},
285+
{
286+
symbol: 'USDC',
287+
decimals: 6,
288+
tokenId: {
289+
chain: 'Aptos',
290+
address:
291+
'0xbae207659db88bea0cbead6da0ed00aac12edcdda169e591cd41c94180b46f3b',
292+
},
293+
icon: TokenIcon.USDC,
294+
},
285295
{
286296
symbol: 'ETH',
287297
decimals: 18,

wormhole-connect/src/config/testnet/tokens.ts

+10
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,16 @@ export const TESTNET_TOKENS: TokenConfig[] = [
134134
icon: TokenIcon.APT,
135135
decimals: 8,
136136
},
137+
{
138+
symbol: 'USDC',
139+
decimals: 6,
140+
tokenId: {
141+
chain: 'Aptos',
142+
address:
143+
'0x69091fbab5f7d635ee7ac5098cf0c1efbe31d68fec0f2cd565e8d168daf52832',
144+
},
145+
icon: TokenIcon.USDC,
146+
},
137147
{
138148
symbol: 'KLAY',
139149
icon: TokenIcon.KLAY,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { useMemo } from 'react';
2+
import { Chain } from '@wormhole-foundation/sdk';
3+
import { WalletData } from '../store/wallet';
4+
import { type AvailableWallets } from '@aptos-labs/wallet-adapter-core';
5+
6+
export type WalletCompatibilityResult = {
7+
isCompatible: boolean;
8+
warning?: string;
9+
};
10+
11+
export const useWalletCompatibility = ({
12+
sendingWallet,
13+
receivingWallet,
14+
sourceChain,
15+
destChain,
16+
routes,
17+
}: {
18+
sendingWallet: WalletData;
19+
receivingWallet: WalletData;
20+
sourceChain?: Chain;
21+
destChain?: Chain;
22+
routes: string[];
23+
}): WalletCompatibilityResult => {
24+
return useMemo(() => {
25+
const isManualCCTPRoute = routes.length === 1 && routes[0] === 'ManualCCTP';
26+
27+
if (isManualCCTPRoute) {
28+
// Aptos CCTP requires modern (AIP-62 standard) wallets with support for signing move script transaction types
29+
const compatibleWallets: AvailableWallets[] = [
30+
'Petra',
31+
'Pontem Wallet',
32+
'Nightly',
33+
];
34+
if (
35+
(sourceChain === 'Aptos' &&
36+
!compatibleWallets.includes(
37+
sendingWallet.name as AvailableWallets,
38+
)) ||
39+
(destChain === 'Aptos' &&
40+
!compatibleWallets.includes(receivingWallet.name as AvailableWallets))
41+
) {
42+
return {
43+
isCompatible: false,
44+
warning: `Please use ${compatibleWallets.join(
45+
' or ',
46+
)} with the Aptos CCTP route.`,
47+
};
48+
}
49+
}
50+
51+
return { isCompatible: true };
52+
}, [sendingWallet, receivingWallet, sourceChain, destChain, routes]);
53+
};

wormhole-connect/src/utils/index.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -284,12 +284,18 @@ export const isEmptyObject = (value: object | null | undefined) => {
284284
return true;
285285
};
286286

287-
export const getExplorerUrl = (chain: Chain, address: string) => {
287+
export const getTokenExplorerUrl = (chain: Chain, address: string) => {
288288
const chainConfig = config.chains[chain]!;
289289
let explorerUrl = '';
290290

291291
if (chain === 'Sui') {
292292
explorerUrl = `${chainConfig.explorerUrl}coin/${address}`;
293+
} else if (chain === 'Aptos') {
294+
if (isHexString(address)) {
295+
explorerUrl = `${chainConfig.explorerUrl}fungible_asset/${address}`;
296+
} else {
297+
explorerUrl = `${chainConfig.explorerUrl}coin/${address}`;
298+
}
293299
} else {
294300
explorerUrl = `${chainConfig.explorerUrl}address/${address}`;
295301
}
+12-22
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import { Wallet } from '@xlabs-libs/wallet-aggregator-core';
22
import type { Network as AptosNetwork } from '@aptos-labs/wallet-adapter-core';
33
import { AptosWallet } from '@xlabs-libs/wallet-aggregator-aptos';
4+
import { Aptos } from '@aptos-labs/ts-sdk';
45

56
import { Network } from '@wormhole-foundation/sdk';
67
import {
78
AptosUnsignedTransaction,
89
AptosChains,
910
} from '@wormhole-foundation/sdk-aptos';
1011

11-
import config from 'config';
12-
import { InputEntryFunctionData } from '@aptos-labs/ts-sdk';
12+
import config, { getWormholeContextV2 } from 'config';
1313

1414
export function fetchOptions() {
1515
const aptosWalletConfig = {
@@ -25,25 +25,12 @@ export function fetchOptions() {
2525
return aptosWallets;
2626
}
2727

28-
function isInputEntryFunctionData(data: any): data is InputEntryFunctionData {
29-
return (
30-
data &&
31-
typeof data === 'object' &&
32-
'function' in data &&
33-
'functionArguments' in data
34-
);
35-
}
36-
3728
export async function signAndSendTransaction(
3829
request: AptosUnsignedTransaction<Network, AptosChains>,
3930
wallet: Wallet | undefined,
4031
) {
4132
const payload = request.transaction;
42-
if (!isInputEntryFunctionData(payload)) {
43-
throw new Error('Unsupported transaction type');
44-
}
4533
// The wallets do not handle Uint8Array serialization
46-
// const payload = request.transaction as Types.EntryFunctionPayload;
4734
payload.functionArguments = payload.functionArguments.map((a: any) => {
4835
if (a instanceof Uint8Array) {
4936
return Array.from(a);
@@ -54,16 +41,19 @@ export async function signAndSendTransaction(
5441
}
5542
});
5643

44+
const context = await getWormholeContextV2();
45+
const aptos = context.getPlatform('Aptos');
46+
const rpc = (await aptos.getRpc('Aptos')) as Aptos;
47+
5748
const tx = await (wallet as AptosWallet).signAndSendTransaction({
5849
data: payload,
50+
options: {
51+
// this is set to 5 minutes in case the user takes a while to sign the transaction
52+
expireTimestamp: Math.floor(Date.now() / 1000) + 60 * 5,
53+
},
5954
});
60-
/*
61-
* TODO SDKV2
62-
const aptosClient = (
63-
config.wh.getContext('Aptos') as AptosContext<WormholeContext>
64-
).aptosClient;
65-
await aptosClient.waitForTransaction(tx.id);
66-
*/
55+
56+
await rpc.waitForTransaction({ transactionHash: tx.id });
6757

6858
return tx;
6959
}

wormhole-connect/src/views/v2/Bridge/AssetPicker/TokenItem.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import TokenIcon from 'icons/TokenIcons';
1515
import { Token } from 'config/tokens';
1616

1717
import type { Chain } from '@wormhole-foundation/sdk';
18-
import { chainDisplayName, getExplorerUrl } from 'utils';
18+
import { chainDisplayName, getTokenExplorerUrl } from 'utils';
1919
import ChainIcon from 'icons/ChainIcons';
2020

2121
const useStyles = makeStyles()((theme) => ({
@@ -62,7 +62,7 @@ function TokenItem(props: TokenItemProps) {
6262
// If the token is native to the chain, show the token's address.
6363
// Otherwise, show the wrapped token's address.
6464
const address = token.tokenId?.address.toString();
65-
const explorerURL = address ? getExplorerUrl(chain, address) : undefined;
65+
const explorerURL = address ? getTokenExplorerUrl(chain, address) : undefined;
6666
const addressDisplay = `${token.shortAddress}`;
6767

6868
return (

wormhole-connect/src/views/v2/Bridge/index.tsx

+14-2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import { useSortedRoutesWithQuotes } from 'hooks/useSortedRoutesWithQuotes';
4444
import type { Chain } from '@wormhole-foundation/sdk';
4545
import { amount as sdkAmount } from '@wormhole-foundation/sdk';
4646
import { useAmountValidation } from 'hooks/useAmountValidation';
47+
import { useWalletCompatibility } from 'hooks/useWalletCompatibility';
4748
import useGetTokenBalances from 'hooks/useGetTokenBalances';
4849
import { useGetTokens } from 'hooks/useGetTokens';
4950
import { Token } from 'config/tokens';
@@ -411,20 +412,31 @@ const Bridge = () => {
411412
);
412413
}, [sourceChain, destChain, sendingWallet, receivingWallet]);
413414

415+
const { isCompatible: isWalletCompatible, warning: walletWarning } =
416+
useWalletCompatibility({
417+
sendingWallet,
418+
receivingWallet,
419+
sourceChain,
420+
destChain,
421+
routes: sortedRoutes,
422+
});
423+
414424
const hasError = !!amountValidation.error;
415425

416426
const hasEnteredAmount = amount && sdkAmount.whole(amount) > 0;
417427

418428
const hasConnectedWallets = sendingWallet.address && receivingWallet.address;
419429

420-
const showRoutes = hasConnectedWallets && hasEnteredAmount && !hasError;
430+
const showRoutes =
431+
hasConnectedWallets && isWalletCompatible && hasEnteredAmount && !hasError;
421432

422433
const reviewTransactionDisabled =
423434
!sourceChain ||
424435
!sourceToken ||
425436
!destChain ||
426437
!destToken ||
427438
!hasConnectedWallets ||
439+
!isWalletCompatible ||
428440
!selectedRoute ||
429441
!isValid ||
430442
isFetchingQuotes ||
@@ -484,7 +496,7 @@ const Bridge = () => {
484496
tokenBalance={sourceToken ? balances[sourceToken.key]?.balance : null}
485497
isFetchingTokenBalance={isFetchingBalances}
486498
error={amountValidation.error}
487-
warning={amountValidation.warning}
499+
warning={amountValidation.warning || walletWarning}
488500
/>
489501
{showRoutes && (
490502
<Routes

0 commit comments

Comments
 (0)