Skip to content

Commit bd6f993

Browse files
authored
Tb exec route (#3632)
* feat: added executor token bridge route support * fix: solana priority fee extra instr
1 parent bebfd76 commit bd6f993

File tree

10 files changed

+323
-244
lines changed

10 files changed

+323
-244
lines changed

package-lock.json

Lines changed: 206 additions & 206 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@
5555
"@solana/spl-token": "0.4.0",
5656
"@solana/wallet-adapter-wallets": "0.19.32",
5757
"@solana/web3.js": "1.98.0",
58-
"@wormhole-foundation/sdk": "2.4.0",
59-
"@wormhole-foundation/sdk-definitions": "2.4.0",
58+
"@wormhole-foundation/sdk": "3.0.0",
59+
"@wormhole-foundation/sdk-definitions": "3.0.0",
6060
"@wormhole-foundation/sdk-definitions-ntt": "1.0.1",
6161
"@wormhole-foundation/sdk-evm-ntt": "1.0.1",
6262
"@wormhole-foundation/sdk-icons": "^1.0.1",
@@ -199,39 +199,39 @@
199199
"axios": "1.4.0"
200200
},
201201
"@mayanfinance/wormhole-sdk-route": {
202-
"@wormhole-foundation/sdk-connect": "2.4.0",
203-
"@wormhole-foundation/sdk-evm": "2.4.0",
204-
"@wormhole-foundation/sdk-solana": "2.4.0",
205-
"@wormhole-foundation/sdk-sui": "2.4.0"
202+
"@wormhole-foundation/sdk-connect": "3.0.0",
203+
"@wormhole-foundation/sdk-evm": "3.0.0",
204+
"@wormhole-foundation/sdk-solana": "3.0.0",
205+
"@wormhole-foundation/sdk-sui": "3.0.0"
206206
},
207207
"@wormhole-foundation/sdk-definitions-ntt": {
208-
"@wormhole-foundation/sdk-base": "2.4.0",
209-
"@wormhole-foundation/sdk-definitions": "2.4.0"
208+
"@wormhole-foundation/sdk-base": "3.0.0",
209+
"@wormhole-foundation/sdk-definitions": "3.0.0"
210210
},
211211
"@wormhole-foundation/sdk-evm-ntt": {
212-
"@wormhole-foundation/sdk-base": "2.4.0",
213-
"@wormhole-foundation/sdk-definitions": "2.4.0",
214-
"@wormhole-foundation/sdk-evm": "2.4.0",
215-
"@wormhole-foundation/sdk-evm-core": "2.4.0"
212+
"@wormhole-foundation/sdk-base": "3.0.0",
213+
"@wormhole-foundation/sdk-definitions": "3.0.0",
214+
"@wormhole-foundation/sdk-evm": "3.0.0",
215+
"@wormhole-foundation/sdk-evm-core": "3.0.0"
216216
},
217217
"@wormhole-foundation/sdk-route-ntt": {
218-
"@wormhole-foundation/sdk-connect": "2.4.0",
218+
"@wormhole-foundation/sdk-connect": "3.0.0",
219219
"axios": "1.4.0"
220220
},
221221
"@wormhole-foundation/sdk-solana-ntt": {
222-
"@wormhole-foundation/sdk-base": "2.4.0",
223-
"@wormhole-foundation/sdk-definitions": "2.4.0",
224-
"@wormhole-foundation/sdk-solana": "2.4.0",
225-
"@wormhole-foundation/sdk-solana-core": "2.4.0"
222+
"@wormhole-foundation/sdk-base": "3.0.0",
223+
"@wormhole-foundation/sdk-definitions": "3.0.0",
224+
"@wormhole-foundation/sdk-solana": "3.0.0",
225+
"@wormhole-foundation/sdk-solana-core": "3.0.0"
226226
},
227227
"@wormhole-labs/cctp-executor-route": {
228-
"@wormhole-foundation/sdk-aptos": "2.4.0",
229-
"@wormhole-foundation/sdk-connect": "2.4.0",
230-
"@wormhole-foundation/sdk-evm": "2.4.0",
231-
"@wormhole-foundation/sdk-solana": "2.4.0",
232-
"@wormhole-foundation/sdk-solana-cctp": "2.4.0",
233-
"@wormhole-foundation/sdk-sui": "2.4.0",
234-
"@wormhole-foundation/sdk-sui-cctp": "2.4.0"
228+
"@wormhole-foundation/sdk-aptos": "3.0.0",
229+
"@wormhole-foundation/sdk-connect": "3.0.0",
230+
"@wormhole-foundation/sdk-evm": "3.0.0",
231+
"@wormhole-foundation/sdk-solana": "3.0.0",
232+
"@wormhole-foundation/sdk-solana-cctp": "3.0.0",
233+
"@wormhole-foundation/sdk-sui": "3.0.0",
234+
"@wormhole-foundation/sdk-sui-cctp": "3.0.0"
235235
},
236236
"@wormhole-foundation/wormhole-connect": {
237237
"aptos": "1.5.2"

src/components/SampleApp/index.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { compressToBase64, decompressFromBase64 } from 'lz-string';
1919
* - DEFAULT_ROUTES
2020
* - nttRoutes
2121
* - AutomaticTokenBridgeRoute
22+
* - TokenBridgeExecutorRoute
2223
* - TokenBridgeRoute
2324
* - AutomaticCCTPRoute
2425
* - ManualCCTPRoute
@@ -88,6 +89,8 @@ const parseConfig = (config: string): WormholeConnectConfig => {
8889
window.cctpV2StandardExecutorRoute = cctpV2StandardExecutorRoute;
8990
/* @ts-ignore */
9091
window.cctpV2FastExecutorRoute = cctpV2FastExecutorRoute;
92+
/* @ts-ignore */
93+
window.executorTokenBridgeRoute = routes.executorTokenBridgeRoute;
9194

9295
return eval(
9396
`(function() { return ${config} })()`,
@@ -313,6 +316,12 @@ function SampleApp() {
313316
<pre>cctpV2FastExecutorRoute</pre>
314317
<i>{'(CCTPExecutorRoute.Config) -> RouteConstructor'}</i>
315318
</li>
319+
<li>
320+
<pre>executorTokenBridgeRoute</pre>
321+
<i>
322+
{'(ExecutorTokenBridgeRoute.Config) -> RouteConstructor'}
323+
</i>
324+
</li>
316325
</ul>
317326
</div>
318327
<div>

src/config/mainnet/chains.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ export const MAINNET_CHAINS: ChainsConfig = {
2828
Avalanche: {
2929
displayName: 'Avalanche',
3030
sdkName: 'Avalanche',
31-
explorerUrl: 'https://avascan.info/blockchain/c/',
32-
explorerName: 'Avascan',
31+
explorerUrl: 'https://snowtrace.io/',
32+
explorerName: 'Snowtrace',
3333
icon: 'Avalanche',
3434
symbol: 'AVAX',
3535
},

src/config/testnet/chains.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ export const TESTNET_CHAINS: ChainsConfig = {
1111
},
1212
Avalanche: {
1313
displayName: 'Fuji',
14-
explorerUrl: 'https://testnet.avascan.info/blockchain/c/',
15-
explorerName: 'Avascan',
14+
explorerUrl: 'https://testnet.snowtrace.io/',
15+
explorerName: 'Snowtrace',
1616
icon: 'Avalanche',
1717
symbol: 'AVAX',
1818
sdkName: 'Avalanche',

src/exports/executor.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@ export {
33
cctpV2StandardExecutorRoute,
44
cctpV2FastExecutorRoute,
55
} from '@wormhole-labs/cctp-executor-route';
6+
7+
import { routes } from '@wormhole-foundation/sdk';
8+
const { executorTokenBridgeRoute } = routes;
9+
export { executorTokenBridgeRoute };

src/hooks/useFetchSupportedRoutes.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,11 @@ const useFetchSupportedRoutes = ({
9999
if (_routes.includes('ManualTBTC')) {
100100
_routes = _routes.filter(
101101
(route) =>
102-
!['ManualTokenBridge', 'AutomaticTokenBridge'].includes(route),
102+
![
103+
'ManualTokenBridge',
104+
'AutomaticTokenBridge',
105+
'TokenBridgeExecutorRoute',
106+
].includes(route),
103107
);
104108
}
105109

src/routes/sdkv2/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export class SDKv2Route {
3434
this.IS_TOKEN_BRIDGE_ROUTE = [
3535
'ManualTokenBridge',
3636
'AutomaticTokenBridge',
37-
'CosmosGateway',
37+
'TokenBridgeExecutorRoute',
3838
].includes(rc.meta.name);
3939
}
4040

src/utils/sdkv2.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
ChainContext,
1717
nativeTokenId,
1818
TBTCBridge,
19+
ExecutorTokenBridge,
1920
} from '@wormhole-foundation/sdk';
2021
import { NttRoute } from '@wormhole-foundation/sdk-route-ntt';
2122
import { CCTPv2ExecutorRoute } from '@wormhole-labs/cctp-executor-route';
@@ -131,6 +132,10 @@ export async function parseReceipt(
131132
return await parseTokenBridgeReceipt(
132133
receipt as ReceiptWithAttestation<TokenBridge.TransferVAA>,
133134
);
135+
case 'TokenBridgeExecutorRoute':
136+
return await parseExecutorTokenBridgeReceipt(
137+
receipt as ReceiptWithAttestation<ExecutorTokenBridge.VAA>,
138+
);
134139
case 'ManualCCTP':
135140
return await parseCCTPReceipt(
136141
receipt as ReceiptWithAttestation<CircleTransfer.CircleAttestationReceipt>,
@@ -233,6 +238,23 @@ const parseTokenBridgeReceipt = async (
233238
return txData as TransferInfo;
234239
};
235240

241+
const parseExecutorTokenBridgeReceipt = async (
242+
receipt: ReceiptWithAttestation<ExecutorTokenBridge.VAA>,
243+
): Promise<TransferInfo> => {
244+
const txData = await parseTokenBridgeReceipt(
245+
// This is fine since the VAA type is transfer with payload
246+
receipt as unknown as ReceiptWithAttestation<TokenBridge.TransferVAA>,
247+
);
248+
249+
txData.recipient =
250+
// @ts-ignore
251+
receipt.attestation.attestation.payload.payload.targetRecipient
252+
.toNative(txData.toChain)
253+
.toString();
254+
255+
return txData;
256+
};
257+
236258
const parseCCTPReceipt = async (
237259
receipt: ReceiptWithAttestation<CircleTransfer.CircleAttestationReceipt>,
238260
): Promise<TransferInfo> => {

src/utils/solana.ts

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export async function setPriorityFeeInstructions(
6666

6767
// Remove existing compute budget instructions if they were added by the SDK
6868
message.instructions = message.instructions.filter(computeBudgetIxFilter);
69+
unsignedTx.message = message.compileToV0Message(luts);
6970
message.instructions.push(
7071
...(await createPriorityFeeInstructions(connection, unsignedTx)),
7172
);
@@ -100,23 +101,62 @@ async function createPriorityFeeInstructions(
100101
let unitsUsed = 200_000;
101102
let simulationAttempts = 0;
102103

104+
// The simulation can fail if the compute budget is too low
105+
// So we set a higher compute budget for the simulation and
106+
// will set the transaction's compute budget based on the actual units used
107+
// during the simulation.
108+
const simComputeBudgetIx = ComputeBudgetProgram.setComputeUnitLimit({
109+
units: 300_000,
110+
});
111+
112+
// Create a copy of the transaction for simulation to avoid mutation
113+
let simulationTx: Transaction | VersionedTransaction;
114+
115+
if (isVersionedTransaction(transaction)) {
116+
const luts = (
117+
await Promise.all(
118+
transaction.message.addressTableLookups.map((acc) =>
119+
connection.getAddressLookupTable(acc.accountKey),
120+
),
121+
)
122+
)
123+
.map((lut) => lut.value)
124+
.filter((lut) => lut !== null) as AddressLookupTableAccount[];
125+
const message = TransactionMessage.decompile(transaction.message, {
126+
addressLookupTableAccounts: luts,
127+
});
128+
message.instructions = [simComputeBudgetIx, ...message.instructions];
129+
130+
// Create a new VersionedTransaction with the modified message
131+
simulationTx = new VersionedTransaction(message.compileToV0Message(luts));
132+
simulationTx.signatures = [...transaction.signatures];
133+
} else {
134+
// Clone the transaction for simulation
135+
const txCopy = new Transaction();
136+
txCopy.recentBlockhash = transaction.recentBlockhash;
137+
txCopy.feePayer = transaction.feePayer;
138+
txCopy.instructions = [simComputeBudgetIx, ...transaction.instructions];
139+
txCopy.signatures = [...transaction.signatures];
140+
simulationTx = txCopy;
141+
}
142+
103143
simulationLoop: while (true) {
104144
if (
105-
isVersionedTransaction(transaction) &&
106-
!transaction.message.recentBlockhash
145+
isVersionedTransaction(simulationTx) &&
146+
!simulationTx.message.recentBlockhash
107147
) {
108148
// This is required for versioned transactions
109149
// SimulateTransaction throws if recentBlockhash is an empty string
110150
const { blockhash } = await connection.getLatestBlockhash(commitment);
111-
transaction.message.recentBlockhash = blockhash;
151+
simulationTx.message.recentBlockhash = blockhash;
112152
}
113153

114-
const response = await (isVersionedTransaction(transaction)
115-
? connection.simulateTransaction(transaction, {
154+
const response = await (isVersionedTransaction(simulationTx)
155+
? connection.simulateTransaction(simulationTx, {
116156
commitment,
117157
replaceRecentBlockhash: true,
118158
})
119-
: connection.simulateTransaction(transaction));
159+
: connection.simulateTransaction(simulationTx));
120160

121161
if (response.value.err) {
122162
if (checkKnownSimulationError(response.value)) {
@@ -149,7 +189,7 @@ async function createPriorityFeeInstructions(
149189
}
150190
}
151191

152-
const unitBudget = Math.floor(unitsUsed * 1.2); // Budget in 20% headroom
192+
const unitBudget = Math.floor(unitsUsed * 1.2); // Budget in 20% headroom or more if retries occurred
153193

154194
const instructions: TransactionInstruction[] = [];
155195
instructions.push(
@@ -173,7 +213,7 @@ async function createPriorityFeeInstructions(
173213
rpcProvider?: SolanaRpcProvider,
174214
): Promise<{ fee: number; methodUsed: 'triton' | 'default' | 'minimum' }> => {
175215
if (rpcProvider === 'triton') {
176-
// Triton has an experimental RPC method that accepts a percentile paramater
216+
// Triton has an experimental RPC method that accepts a percentile parameter
177217
// and usually gives more accurate fee numbers.
178218
try {
179219
const fee = await determinePriorityFeeTritonOne(

0 commit comments

Comments
 (0)