Skip to content

Commit c3daffc

Browse files
committed
tilt e2e test
1 parent 7d2c99d commit c3daffc

File tree

7 files changed

+183
-29
lines changed

7 files changed

+183
-29
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,5 @@ USER bun
1717
EXPOSE 3000/tcp
1818
ENTRYPOINT [ "bun", "start" ]
1919

20-
FROM release as test
20+
FROM release AS test
2121

Dockerfile.e2e

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
FROM forge
2+
3+
FROM executor
4+
5+
WORKDIR /usr/src/app
6+
7+
COPY --from=forge /app/out evm/out
8+
9+
ENTRYPOINT [ "bun", "test" ]

Tiltfile

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,30 @@ docker_build(
2525
]
2626
)
2727

28+
docker_build(
29+
ref = "forge",
30+
context = "evm",
31+
dockerfile = "./evm/Dockerfile",
32+
only=["foundry.toml", "lib", "src"]
33+
)
34+
docker_build(
35+
ref = "e2e",
36+
context = ".",
37+
dockerfile = "./Dockerfile.e2e",
38+
only=[]
39+
)
40+
2841
k8s_yaml("k8s/executor.yaml")
2942
k8s_resource(
3043
"executor",
3144
port_forwards = 3000,
3245
resource_deps = ["anvil-eth-sepolia", "anvil-base-sepolia"],
3346
labels = ["app"],
3447
)
48+
49+
k8s_yaml("k8s/e2e.yaml")
50+
k8s_resource(
51+
"e2e",
52+
resource_deps=["anvil-eth-sepolia", "anvil-base-sepolia", "executor"],
53+
labels=["tests"]
54+
)

evm/Dockerfile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
FROM ghcr.io/foundry-rs/foundry:v1.2.3@sha256:d9133dae61c19383b72695dc7eeca29d1e7a89f1f1b5fdfd8900c660b46b4303 AS forge
2+
3+
WORKDIR /app
4+
COPY foundry.toml foundry.toml
5+
COPY lib lib
6+
COPY src src
7+
8+
RUN forge build

k8s/e2e.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
kind: Job
2+
apiVersion: batch/v1
3+
metadata:
4+
name: e2e
5+
spec:
6+
backoffLimit: 0
7+
template:
8+
metadata:
9+
labels:
10+
app: e2e
11+
spec:
12+
restartPolicy: Never
13+
containers:
14+
- name: e2e
15+
image: e2e

src/e2e.test.ts

Lines changed: 129 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
1-
import { mnemonicToAccount } from "viem/accounts";
2-
import { ANVIL_MNEMONIC } from "./consts";
3-
import { test } from "bun:test";
1+
import { serializeLayout } from "@wormhole-foundation/sdk-base";
2+
import { relayInstructionsLayout } from "@wormhole-foundation/sdk-definitions";
3+
import axios from "axios";
4+
import { sleep } from "bun";
5+
import { expect, test } from "bun:test";
46
import {
57
createPublicClient,
68
createWalletClient,
79
getContract,
810
http,
11+
isHex,
912
padHex,
1013
toHex,
14+
type Account,
15+
type Chain,
16+
type PublicClient,
17+
type WalletClient,
1118
} from "viem";
12-
import { anvil, sepolia } from "viem/chains";
13-
import { serializeLayout } from "@wormhole-foundation/sdk-base";
14-
import {
15-
quoteLayout,
16-
relayInstructionsLayout,
17-
signedQuoteLayout,
18-
} from "@wormhole-foundation/sdk-definitions";
19-
import axios from "axios";
20-
import { deserialize } from "binary-layout";
19+
import { mnemonicToAccount } from "viem/accounts";
20+
import forgeOutput from "../evm/out/ExecutorVAAv1Integration.sol/ExecutorVAAv1Integration.json";
21+
import { enabledChains } from "./chains";
22+
import { ANVIL_MNEMONIC } from "./consts";
23+
import { RelayStatus } from "./types";
2124

2225
const ABI = [
2326
{
@@ -113,7 +116,90 @@ const ABI = [
113116
},
114117
] as const;
115118

119+
async function deployIntegrationContract(
120+
publicClient: PublicClient,
121+
client: WalletClient,
122+
account: Account,
123+
viemChain: Chain,
124+
coreContractAddress: string,
125+
executorAddress: string,
126+
) {
127+
if (!isHex(forgeOutput.bytecode.object)) {
128+
throw new Error("invalid bytecode");
129+
}
130+
if (!isHex(coreContractAddress)) {
131+
throw new Error("invalid coreContractAddress");
132+
}
133+
if (!isHex(executorAddress)) {
134+
throw new Error("invalid executorAddress");
135+
}
136+
const hash = await client.deployContract({
137+
account,
138+
chain: viemChain,
139+
abi: ABI,
140+
bytecode: forgeOutput.bytecode.object,
141+
args: [coreContractAddress, executorAddress, 200],
142+
});
143+
const receipt = await publicClient.waitForTransactionReceipt({
144+
hash,
145+
});
146+
if (!isHex(receipt.contractAddress)) {
147+
throw new Error("invalid contractAddress");
148+
}
149+
return receipt.contractAddress;
150+
}
151+
116152
test("it performs a VAA v1 relay", async () => {
153+
const srcChain = enabledChains[10002]!;
154+
const dstChain = enabledChains[10004]!;
155+
const account = mnemonicToAccount(ANVIL_MNEMONIC, { addressIndex: 0 });
156+
if (!srcChain.viemChain || !dstChain.viemChain) {
157+
throw new Error("invalid viem chain");
158+
}
159+
const srcTransport = http(srcChain.rpc);
160+
const srcPublicClient = createPublicClient({
161+
chain: srcChain.viemChain,
162+
transport: srcTransport,
163+
});
164+
const srcClient = createWalletClient({
165+
account,
166+
chain: srcChain.viemChain,
167+
transport: srcTransport,
168+
});
169+
const dstTransport = http(dstChain.rpc);
170+
const dstPublicClient = createPublicClient({
171+
chain: dstChain.viemChain,
172+
transport: dstTransport,
173+
});
174+
const dstClient = createWalletClient({
175+
account,
176+
chain: dstChain.viemChain,
177+
transport: dstTransport,
178+
});
179+
const srcContract = await deployIntegrationContract(
180+
srcPublicClient,
181+
srcClient,
182+
account,
183+
srcChain.viemChain,
184+
srcChain.coreContractAddress,
185+
srcChain.executorAddress,
186+
);
187+
console.log(`Deployed source contract: ${srcContract}`);
188+
const dstContract = await deployIntegrationContract(
189+
dstPublicClient,
190+
dstClient,
191+
account,
192+
dstChain.viemChain,
193+
dstChain.coreContractAddress,
194+
dstChain.executorAddress,
195+
);
196+
console.log(`Deployed destination contract: ${dstContract}`);
197+
const dstTestContract = getContract({
198+
address: srcContract,
199+
abi: ABI,
200+
client: srcClient,
201+
});
202+
expect(await dstTestContract.read.number()).toBe(0n);
117203
const relayInstructions = toHex(
118204
serializeLayout(relayInstructionsLayout, {
119205
requests: [
@@ -127,27 +213,20 @@ test("it performs a VAA v1 relay", async () => {
127213
],
128214
}),
129215
);
130-
const response = await axios.post("http://localhost:3000/v0/quote", {
216+
const response = await axios.post("http://executor:3000/v0/quote", {
131217
srcChain: 10002,
132218
dstChain: 10004,
133219
relayInstructions,
134220
});
135-
const transport = http("http://localhost:8545");
136-
const account = mnemonicToAccount(ANVIL_MNEMONIC, { addressIndex: 0 });
137-
const client = createWalletClient({
138-
account,
139-
chain: sepolia,
140-
transport,
141-
});
142-
const testContract = getContract({
143-
address: "0x8e98Bd10a6f4c1Ee0C4b5d9F50a18D1a7E20EaF8",
221+
const srcTestContract = getContract({
222+
address: srcContract,
144223
abi: ABI,
145-
client,
224+
client: srcClient,
146225
});
147-
const tx = await testContract.write.incrementAndSend(
226+
const hash = await srcTestContract.write.incrementAndSend(
148227
[
149228
10004,
150-
padHex("0x7d77360666066967579a2235332d271587cd62dC", {
229+
padHex(dstContract, {
151230
dir: "left",
152231
size: 32,
153232
}),
@@ -160,6 +239,29 @@ test("it performs a VAA v1 relay", async () => {
160239
{ value: BigInt(response.data.estimatedCost) },
161240
);
162241
console.log(
163-
`https://wormholelabs-xyz.github.io/executor-explorer/#/chain/10002/tx/${tx}?endpoint=http%3A%2F%2Flocalhost%3A3000&env=Testnet`,
242+
`Request execution: https://wormholelabs-xyz.github.io/executor-explorer/#/chain/10002/tx/${hash}?endpoint=http%3A%2F%2Flocalhost%3A3000&env=Testnet`,
164243
);
165-
});
244+
await srcPublicClient.waitForTransactionReceipt({
245+
hash,
246+
});
247+
let statusResult;
248+
while (
249+
!statusResult ||
250+
statusResult.data?.[0].status === RelayStatus.Pending
251+
) {
252+
console.log(`Statusing tx: ${hash}`);
253+
if (statusResult) {
254+
await sleep(1000);
255+
}
256+
statusResult = await axios.post("http://executor:3000/v0/status/tx", {
257+
chainId: srcChain.wormholeChainId,
258+
txHash: hash,
259+
});
260+
if (statusResult.data.length !== 1) {
261+
throw new Error(`unexpected status result length`);
262+
}
263+
}
264+
expect(statusResult.data?.[0].status).toBe(RelayStatus.Submitted);
265+
expect(await srcTestContract.read.number()).toBe(1n);
266+
expect(await dstTestContract.read.number()).toBe(1n);
267+
}, 60000);

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,5 @@
2626
"noUnusedParameters": false,
2727
"noPropertyAccessFromIndexSignature": false
2828
},
29-
"exclude": ["evm"]
29+
"exclude": ["evm", "src/e2e.test.ts"]
3030
}

0 commit comments

Comments
 (0)