Skip to content

Commit cb373c2

Browse files
committed
reorg platform handlers folders
1 parent ca6c758 commit cb373c2

File tree

10 files changed

+269
-251
lines changed

10 files changed

+269
-251
lines changed

.prettierignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +0,0 @@
1-
evm

src/api/quote.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
getTotalGasLimitAndMsgValue,
1313
signQuote,
1414
} from "../utils";
15-
import { EvmHandler } from "../relay/platform/evm";
15+
import { evmHandler } from "../relay/evm";
1616

1717
export const quoteHandler = async (req: Request, res: Response) => {
1818
const enabledChainIds = Object.keys(enabledChains);
@@ -59,7 +59,7 @@ export const quoteHandler = async (req: Request, res: Response) => {
5959
const expiryTime = new Date();
6060
expiryTime.setHours(expiryTime.getHours() + 1);
6161

62-
const dstGasPrice = await EvmHandler.getGasPrice(dstChain);
62+
const dstGasPrice = await evmHandler.getGasPrice(dstChain);
6363

6464
const quote: Quote = {
6565
quote: {

src/api/status.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
import { type Request, type Response } from "express";
22
import { messageQueue } from "../relay/queue";
3-
import {
4-
EvmHandler,
5-
type RequestForExecutionWithId,
6-
} from "../relay/platform/evm";
3+
import { evmHandler } from "../relay/evm";
74
import { enabledChains, type ChainConfig } from "../chains";
85
import {
96
RelayStatus,
107
type Capabilities,
118
type RelayRequestData,
9+
type RequestForExecutionWithId,
1210
} from "../types";
1311
import {
1412
fromHex,
@@ -88,7 +86,7 @@ export const statusHandler = async (req: Request, res: Response) => {
8886
return;
8987
}
9088

91-
const requestsForExecution = await EvmHandler.getRequestsForExecution(
89+
const requestsForExecution = await evmHandler.getRequestsForExecution(
9290
txHash,
9391
chainConfig,
9492
);

src/layouts/utils.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import {
2+
relayInstructionsLayout,
3+
type RelayInstructions,
4+
} from "@wormhole-foundation/sdk-definitions";
5+
import { deserialize } from "binary-layout";
6+
import { fromHex, getAddress, padHex, trim, type Hex } from "viem";
7+
8+
export function deserializeRelayInstructions(
9+
relayInstructionsBytes: `0x${string}`,
10+
): RelayInstructions {
11+
return deserialize(
12+
relayInstructionsLayout,
13+
fromHex(relayInstructionsBytes, "bytes"),
14+
);
15+
}
16+
export function trimToAddress(hex: Hex) {
17+
return getAddress(
18+
padHex(trim(hex, { dir: "left" }), { dir: "left", size: 20 }),
19+
);
20+
}

src/relay/evm/index.ts

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import {
2+
createPublicClient,
3+
decodeEventLog,
4+
http,
5+
isAddressEqual,
6+
isHex,
7+
toEventHash,
8+
} from "viem";
9+
import { anvil } from "viem/chains";
10+
import { RequestForExecutionLogABI } from "../../abis/requestForExecutionLog";
11+
import type { ChainConfig } from "../../chains";
12+
import type { RequestForExecutionWithId } from "../../types";
13+
import type { IProtocolHandler } from "../handler";
14+
import { relayVAAv1 } from "./vaav1";
15+
16+
const REQUEST_FOR_EXECUTION_TOPIC = toEventHash(
17+
"RequestForExecution(address,uint256,uint16,bytes32,address,bytes,bytes,bytes)",
18+
);
19+
20+
export const evmHandler: IProtocolHandler = {
21+
getGasPrice: async (chainConfig: ChainConfig): Promise<bigint> => {
22+
try {
23+
const transport = http(chainConfig.rpc);
24+
const client = createPublicClient({
25+
chain: anvil,
26+
transport,
27+
});
28+
return await client.getGasPrice();
29+
} catch (e) {
30+
throw new Error(`unable to determine gas price`);
31+
}
32+
},
33+
34+
getRequestsForExecution: async (
35+
txHash: string,
36+
chainConfig: ChainConfig,
37+
): Promise<Array<RequestForExecutionWithId>> => {
38+
const results: Array<RequestForExecutionWithId> = [];
39+
40+
if (!isHex(txHash)) {
41+
throw new Error(`Invalid txHash ${txHash}`);
42+
}
43+
44+
try {
45+
const transport = http(chainConfig.rpc);
46+
const client = createPublicClient({
47+
chain: anvil,
48+
transport,
49+
});
50+
51+
const transactionReceipt = await client.getTransactionReceipt({
52+
hash: txHash,
53+
});
54+
55+
const block = await client.getBlock({
56+
blockNumber: transactionReceipt.blockNumber,
57+
});
58+
59+
if (!transactionReceipt) return results;
60+
61+
for (
62+
let logIndex = 0;
63+
logIndex < transactionReceipt.logs.length;
64+
logIndex++
65+
) {
66+
const log = transactionReceipt.logs[logIndex];
67+
68+
if (
69+
log &&
70+
log.removed === false &&
71+
isAddressEqual(
72+
log.address,
73+
chainConfig.executorAddress as `0x${string}`,
74+
) &&
75+
log.topics.length === 2 &&
76+
log.topics[0] === REQUEST_FOR_EXECUTION_TOPIC
77+
) {
78+
const {
79+
args: {
80+
quoterAddress,
81+
amtPaid,
82+
dstChain,
83+
dstAddr,
84+
refundAddr,
85+
signedQuote: signedQuoteBytes,
86+
requestBytes,
87+
relayInstructions: relayInstructionsBytes,
88+
},
89+
} = decodeEventLog({
90+
abi: RequestForExecutionLogABI,
91+
topics: log.topics,
92+
data: log.data,
93+
});
94+
95+
results.push({
96+
id: {
97+
type: "Evm",
98+
chain: chainConfig.wormholeChainId,
99+
hash: transactionReceipt.transactionHash,
100+
logIndex: BigInt(logIndex),
101+
},
102+
amtPaid,
103+
dstAddr,
104+
dstChain: Number(dstChain),
105+
quoterAddress,
106+
refundAddr,
107+
signedQuoteBytes,
108+
requestBytes,
109+
relayInstructionsBytes,
110+
timestamp: new Date(Number(block.timestamp) * 1000),
111+
});
112+
}
113+
}
114+
} catch (e) {
115+
console.error(e);
116+
}
117+
118+
return results;
119+
},
120+
121+
relayVAAv1: relayVAAv1,
122+
};

src/relay/evm/vaav1.ts

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { createPublicClient, createWalletClient, http, toHex } from "viem";
2+
import { privateKeyToAccount } from "viem/accounts";
3+
import { anvil } from "viem/chains";
4+
import { vaaV1ReceiveWithGasDropAbi } from "../../abis/vaaV1ReceiveWithGasDropoffAbi";
5+
import type { ChainConfig } from "../../chains";
6+
import { EVM_PRIVATE_KEY } from "../../consts";
7+
import type { RelayRequestData, TxInfo } from "../../types";
8+
import {
9+
getFirstDropOffInstruction,
10+
getTotalGasLimitAndMsgValue,
11+
getTotalMsgValueFromGasInstructions,
12+
} from "../../utils";
13+
import {
14+
deserializeRelayInstructions,
15+
trimToAddress,
16+
} from "../../layouts/utils";
17+
18+
export const relayVAAv1 = async (
19+
chainConfig: ChainConfig,
20+
relayRequest: RelayRequestData,
21+
base64Vaa: string,
22+
): Promise<Array<TxInfo>> => {
23+
const transport = http(chainConfig.rpc);
24+
const publicClient = createPublicClient({
25+
chain: anvil,
26+
transport,
27+
});
28+
29+
const { maxMsgValue, gasDropOffLimit } = chainConfig.capabilities;
30+
31+
const relayInstructions = deserializeRelayInstructions(
32+
relayRequest.requestForExecution.relayInstructionsBytes,
33+
);
34+
35+
const { gasLimit } = getTotalGasLimitAndMsgValue(
36+
relayRequest.requestForExecution.relayInstructionsBytes,
37+
);
38+
39+
const relayMsgValue = getTotalMsgValueFromGasInstructions(
40+
relayInstructions,
41+
maxMsgValue,
42+
);
43+
44+
const { dropOff, recipient } = getFirstDropOffInstruction(
45+
relayInstructions,
46+
gasDropOffLimit,
47+
);
48+
49+
const account = privateKeyToAccount(EVM_PRIVATE_KEY);
50+
51+
const client = createWalletClient({
52+
account,
53+
chain: chainConfig.viemChain,
54+
transport,
55+
});
56+
57+
const payloadHex = toHex(Buffer.from(base64Vaa, "base64"));
58+
59+
const { request } = await publicClient.simulateContract({
60+
account,
61+
address: "0x13b62003C8b126Ec0748376e7ab22F79Fb8bbDF2",
62+
gas: gasLimit,
63+
value: relayMsgValue + dropOff,
64+
abi: vaaV1ReceiveWithGasDropAbi,
65+
functionName: "receiveMessage",
66+
args: [
67+
trimToAddress(relayRequest.requestForExecution.dstAddr),
68+
payloadHex,
69+
trimToAddress(toHex(recipient.address)),
70+
dropOff,
71+
],
72+
});
73+
74+
const hash = await client.writeContract(request);
75+
76+
const receipt = await publicClient.waitForTransactionReceipt({
77+
hash,
78+
});
79+
80+
const block = await publicClient.getBlock({ blockHash: receipt.blockHash });
81+
const blockTime = new Date(Number(block.timestamp) * 1000);
82+
83+
let totalCostValue =
84+
receipt.effectiveGasPrice * receipt.gasUsed + (request.value || 0n);
85+
86+
const txInfo = {
87+
txHash: receipt.transactionHash,
88+
chainId: chainConfig.wormholeChainId,
89+
blockNumber: receipt.blockNumber,
90+
blockTime,
91+
cost: totalCostValue,
92+
};
93+
return [txInfo];
94+
};

src/relay/handler.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type { ChainConfig } from "../chains";
2+
import type {
3+
RelayRequestData,
4+
RequestForExecutionWithId,
5+
TxInfo,
6+
} from "../types";
7+
8+
export interface IProtocolHandler {
9+
getGasPrice(chainConfig: ChainConfig): Promise<bigint>;
10+
11+
getRequestsForExecution(
12+
txHash: string,
13+
chainConfig: ChainConfig,
14+
): Promise<Array<RequestForExecutionWithId>>;
15+
16+
relayVAAv1(
17+
chainConfig: ChainConfig,
18+
relayRequest: RelayRequestData,
19+
base64Vaa: string,
20+
): Promise<Array<TxInfo>>;
21+
}

0 commit comments

Comments
 (0)