Skip to content

Commit 9f74ef2

Browse files
committed
Migrate EVMcrispr from ethers v5 to viem v2
1 parent eba295e commit 9f74ef2

File tree

132 files changed

+1893
-2463
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

132 files changed

+1893
-2463
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# `Turborepo` Vite starter
1+
# EVMcrispr
22

33
This is an official starter Turborepo.
44

apps/evmcrispr-terminal/package.json

+3-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"private": true,
55
"type": "module",
66
"dependencies": {
7-
"@1hive/evmcrispr": "0.10.4",
7+
"@1hive/evmcrispr": "*",
88
"@chakra-ui/anatomy": "^2.2.2",
99
"@chakra-ui/icons": "^2.1.1",
1010
"@chakra-ui/merge-utils": "^2.0.6",
@@ -13,7 +13,6 @@
1313
"@emotion/react": "^11.11.4",
1414
"@emotion/styled": "^11.11.5",
1515
"@esbuild-plugins/node-globals-polyfill": "^0.2.3",
16-
"@ethersproject/providers": "^5.7.2",
1716
"@fontsource/ubuntu-mono": "^5.0.20",
1817
"@heroicons/react": "^2.1.3",
1918
"@monaco-editor/react": "^4.6.0",
@@ -22,11 +21,10 @@
2221
"@safe-global/safe-apps-sdk": "^9.1.0",
2322
"@tanstack/react-query": "^5.45.0",
2423
"@types/lodash.debounce": "^4.0.9",
25-
"@wagmi/core": "^2.11.4",
24+
"@wagmi/core": "^2.11.5",
2625
"buffer": "^6.0.3",
2726
"chakra-ui-markdown-renderer": "^4.1.0",
2827
"ethereum-blockies-base64": "^1.0.0",
29-
"ethers": "^5.7.2",
3028
"events": "^3.3.0",
3129
"framer-motion": "^11.2.10",
3230
"isomorphic-fetch": "^3.0.0",
@@ -44,7 +42,7 @@
4442
"remark-gfm": "^4.0.0",
4543
"styled-components": "6.1.11",
4644
"viem": "^2.16.1",
47-
"wagmi": "^2.10.2",
45+
"wagmi": "^2.10.8",
4846
"zustand": "4.4.7",
4947
"zustand-x": "^3.0.3"
5048
},

apps/evmcrispr-terminal/src/components/ActionButtons/index.tsx

+49-25
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,31 @@ import { useState } from "react";
22
import type { Action, TransactionAction } from "@1hive/evmcrispr";
33
import { EVMcrispr, isProviderAction, parseScript } from "@1hive/evmcrispr";
44
import type { Connector } from "wagmi";
5-
import { useConnect, useWalletClient } from "wagmi";
6-
import type { Account, Chain, Transport, WalletClient } from "viem";
5+
import { useConnect, usePublicClient, useWalletClient } from "wagmi";
6+
import type {
7+
Account,
8+
Chain,
9+
PublicClient,
10+
Transport,
11+
WalletClient,
12+
} from "viem";
13+
import { createPublicClient } from "viem";
714
import { HStack, VStack, useDisclosure } from "@chakra-ui/react";
815
import type SafeAppProvider from "@safe-global/safe-apps-sdk";
916

1017
import LogModal from "../LogModal";
1118
import ErrorMsg from "./ErrorMsg";
1219

13-
import { config } from "../../wagmi";
20+
import { config, transports } from "../../wagmi";
1421

1522
import {
1623
terminalStoreActions,
1724
useTerminalStore,
1825
} from "../TerminalEditor/use-terminal-store";
19-
import { clientToSigner } from "../../utils/ethers";
2026
import { ExecuteButton } from "./ExecuteButton";
2127

2228
type ActionButtonsType = {
23-
address: string;
29+
address: `0x${string}` | undefined;
2430
maximizeGasLimit: boolean;
2531
};
2632

@@ -40,7 +46,8 @@ export default function ActionButtons({
4046
id: "log",
4147
});
4248

43-
const { data: client, refetch } = useWalletClient();
49+
let publicClient: PublicClient | undefined = usePublicClient();
50+
const { data: walletClient } = useWalletClient();
4451
const { connectors } = useConnect();
4552
const safeConnector = connectors.find((c: Connector) => c.id === "safe");
4653

@@ -57,30 +64,45 @@ export default function ActionButtons({
5764
setLogs([]);
5865
}
5966

67+
function getChainAndTransport(chainId: number) {
68+
const chain: Chain | undefined = config.chains.find(
69+
(chain) => Number(chain.id) === chainId,
70+
);
71+
if (!chain) {
72+
throw new Error("Chain not supported");
73+
}
74+
function isValidChainId(
75+
chainId: number,
76+
): chainId is keyof typeof transports {
77+
return chainId in transports;
78+
}
79+
if (!isValidChainId(chainId)) {
80+
throw new Error("Transport not supported");
81+
}
82+
const transport = transports[chainId];
83+
return { chain, transport };
84+
}
85+
6086
const executeAction = async (
6187
action: Action,
62-
client: WalletClient<Transport, Chain, Account>,
88+
walletClient: WalletClient<Transport, Chain, Account>,
6389
maximizeGasLimit: boolean,
6490
) => {
6591
if (isProviderAction(action)) {
66-
const [chainParam] = action.params;
67-
await client.switchChain({ id: Number(chainParam.chainId) });
68-
const { data: refetchedClient } = await refetch();
69-
if (!refetchedClient) {
70-
throw new Error("Failed to refetch client after chain switch");
71-
}
72-
return refetchedClient;
92+
const chainId = Number(action.params[0].chainId);
93+
await walletClient.switchChain({ id: chainId });
94+
const { chain, transport } = getChainAndTransport(chainId);
95+
publicClient = createPublicClient({ chain, transport });
7396
} else {
74-
const chainId = await client.getChainId();
75-
await client.sendTransaction({
97+
const chainId = await walletClient.getChainId();
98+
await walletClient.sendTransaction({
7699
chain: config.chains.find((chain) => chain.id === chainId),
77100
to: action.to as `0x${string}`,
78101
from: action.from as `0x${string}`,
79102
data: action.data as `0x${string}`,
80103
value: BigInt(action.value || 0),
81104
gasLimit: maximizeGasLimit ? 10_000_000n : undefined,
82105
});
83-
return client;
84106
}
85107
};
86108

@@ -89,7 +111,8 @@ export default function ActionButtons({
89111
terminalStoreActions.isLoading(true);
90112

91113
try {
92-
if (client === undefined) throw new Error("Account not connected");
114+
if (!address || publicClient === undefined || walletClient === undefined)
115+
throw new Error("Account not connected");
93116

94117
const { ast, errors } = parseScript(script);
95118

@@ -100,20 +123,21 @@ export default function ActionButtons({
100123
}
101124

102125
const batched: TransactionAction[] = [];
103-
const getSigner = async () => {
104-
const { data: client } = await refetch();
105-
return clientToSigner(client!);
106-
};
107-
await new EVMcrispr(ast, getSigner)
126+
127+
await new EVMcrispr(
128+
ast,
129+
async () => publicClient!,
130+
async () => address,
131+
)
108132
.registerLogListener(logListener)
109-
.interpret(async (action) => {
133+
.interpret(async (action: Action) => {
110134
if (inBatch) {
111135
if (isProviderAction(action)) {
112136
throw new Error("Batching not supported for provider actions");
113137
}
114138
batched.push(action);
115139
} else {
116-
await executeAction(action, client, maximizeGasLimit);
140+
await executeAction(action, walletClient, maximizeGasLimit);
117141
}
118142
});
119143

apps/evmcrispr-terminal/src/components/TerminalEditor/autocompletion.ts

+16-15
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@ import {
1818
hasCommandsBlock,
1919
parseScript,
2020
} from "@1hive/evmcrispr";
21-
import type { providers } from "ethers";
22-
import { utils } from "ethers";
2321
import type { IRange } from "monaco-editor";
2422
import { languages } from "monaco-editor";
2523

24+
import type { PublicClient } from "viem";
25+
import { isAddress } from "viem";
26+
2627
import { DEFAULT_MODULE_BINDING } from "../../utils";
2728

2829
import { shortenAddress } from "../../utils/web3";
@@ -139,12 +140,14 @@ const buildVarCompletionItems = (
139140
// Don't return the current declared variable as a suggestion
140141
} else if (currentArgIndex === 1) {
141142
const currentVarName = currentCommandNode.args[0].value;
142-
varNames = varNames.filter((varName) => varName !== currentVarName);
143+
varNames = varNames.filter(
144+
(varName: string) => varName !== currentVarName,
145+
);
143146
}
144147
}
145148

146149
return varNames.map(
147-
(name): CompletionItem => ({
150+
(name: string): CompletionItem => ({
148151
label: name,
149152
insertText: name,
150153
kind: Variable,
@@ -177,10 +180,8 @@ const buildCurrentArgCompletionItems = (
177180
eagerBindingsManager,
178181
currentPos,
179182
)
180-
.map<languages.CompletionItem>((identifier) => ({
181-
label: utils.isAddress(identifier)
182-
? shortenAddress(identifier)
183-
: identifier,
183+
.map((identifier: string) => ({
184+
label: isAddress(identifier) ? shortenAddress(identifier) : identifier,
184185
insertText: identifier,
185186
range,
186187
sortText: "1",
@@ -321,10 +322,10 @@ const removePossibleFollowingBlock = (
321322

322323
export const createProvideCompletionItemsFn: (
323324
bindingsCache: BindingsManager,
324-
fetchers: { ipfsResolver: IPFSResolver; provider: providers.Provider },
325+
fetchers: { ipfsResolver: IPFSResolver; client: PublicClient },
325326
ast?: Cas11AST,
326327
) => languages.CompletionItemProvider["provideCompletionItems"] =
327-
(bindingsCache, { ipfsResolver, provider }, ast) =>
328+
(bindingsCache, { ipfsResolver, client }, ast) =>
328329
async (model, currPos) => {
329330
const currentLineContent = model.getLineContent(currPos.lineNumber);
330331
const { startColumn, endColumn } = model.getWordUntilPosition(currPos);
@@ -389,7 +390,7 @@ export const createProvideCompletionItemsFn: (
389390
* Filter out any command with a commands block that doesn't
390391
* contain the current caret
391392
*/
392-
.filter((c) => {
393+
.filter((c: any) => {
393394
const itHasCommandsBlock = hasCommandsBlock(c);
394395
const loc = c.loc;
395396
const currentLine = calibratedCurrPos.line;
@@ -414,7 +415,7 @@ export const createProvideCompletionItemsFn: (
414415
commandNodes,
415416
eagerBindingsManager,
416417
bindingsCache,
417-
{ provider, ipfsResolver },
418+
{ client, ipfsResolver },
418419
calibratedCurrPos,
419420
);
420421

@@ -423,7 +424,7 @@ export const createProvideCompletionItemsFn: (
423424
commandNodes.filter((c) => ["set", "std:set"].includes(c.name)),
424425
eagerBindingsManager,
425426
bindingsCache,
426-
{ ipfsResolver, provider },
427+
{ ipfsResolver, client },
427428
calibratedCurrPos,
428429
);
429430

@@ -437,7 +438,7 @@ export const createProvideCompletionItemsFn: (
437438
: filteredCommandNodes,
438439
eagerBindingsManager,
439440
bindingsCache,
440-
{ provider, ipfsResolver },
441+
{ client, ipfsResolver },
441442
calibratedCurrPos,
442443
);
443444

@@ -487,7 +488,7 @@ export const createProvideCompletionItemsFn: (
487488
.getAllBindingIdentifiers({
488489
spaceFilters: [BindingsSpace.ADDR],
489490
})
490-
.map<languages.CompletionItem>((identifier) => ({
491+
.map((identifier: string) => ({
491492
insertText: identifier,
492493
label: identifier,
493494
kind: languages.CompletionItemKind.Field,

apps/evmcrispr-terminal/src/components/TerminalEditor/index.tsx

+5-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { Monaco } from "@monaco-editor/react";
33

44
import { useEffect } from "react";
55

6-
import { useWalletClient } from "wagmi";
6+
import { usePublicClient } from "wagmi";
77

88
import { IPFSResolver } from "@1hive/evmcrispr";
99

@@ -17,7 +17,6 @@ import { createProvideCompletionItemsFn } from "./autocompletion";
1717
import { theme } from "./theme";
1818
import { terminalStoreActions, useTerminalStore } from "./use-terminal-store";
1919
import { useDebounce } from "../../hooks/useDebounce";
20-
import { clientToSigner } from "../../utils/ethers";
2120

2221
export default function TerminalEditor() {
2322
const monaco = useMonaco();
@@ -26,8 +25,7 @@ export default function TerminalEditor() {
2625

2726
const { bindingsCache, script, ast, currentModuleNames } = useTerminalStore();
2827

29-
const { data: client } = useWalletClient();
30-
const provider = client ? clientToSigner(client).provider : undefined;
28+
const publicClient = usePublicClient();
3129

3230
const debouncedScript = useDebounce(script, 200);
3331

@@ -69,15 +67,15 @@ export default function TerminalEditor() {
6967
}, [monaco, currentModuleNames, bindingsCache]);
7068

7169
useEffect(() => {
72-
if (!monaco || !provider) {
70+
if (!monaco || !publicClient) {
7371
return;
7472
}
7573
const completionProvider = monaco.languages.registerCompletionItemProvider(
7674
"evmcl",
7775
{
7876
provideCompletionItems: createProvideCompletionItemsFn(
7977
bindingsCache,
80-
{ provider, ipfsResolver },
78+
{ client: publicClient, ipfsResolver },
8179
ast,
8280
),
8381
},
@@ -86,7 +84,7 @@ export default function TerminalEditor() {
8684
return () => {
8785
completionProvider.dispose();
8886
};
89-
}, [bindingsCache, monaco, provider, ast]);
87+
}, [bindingsCache, monaco, publicClient, ast]);
9088

9189
function handleBeforeMountEditor(monaco: Monaco) {
9290
monaco.editor.defineTheme("theme", theme);

apps/evmcrispr-terminal/src/components/TerminalEditor/use-terminal-store.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import type {
77
} from "@1hive/evmcrispr";
88
import { BindingsManager, NodeType, parseScript } from "@1hive/evmcrispr";
99
import { createStore } from "zustand-x";
10-
import type { providers } from "ethers";
10+
11+
import type { PublicClient } from "viem";
1112

1213
import { runEagerExecutions } from "./autocompletion";
1314
import { DEFAULT_MODULE_BINDING } from "../../utils";
@@ -89,7 +90,7 @@ const terminalStore = createStore("terminal-store")(initialState, {
8990
});
9091
const fetchers = {} as {
9192
ipfsResolver: IPFSResolver;
92-
provider: providers.Provider;
93+
client: PublicClient;
9394
};
9495
const pos = {} as Position;
9596
const oldModules = get.currentModuleNames();
@@ -152,7 +153,9 @@ const terminalStore = createStore("terminal-store")(initialState, {
152153
]);
153154

154155
set.ast(ast);
155-
set.updateCurrentModules(commandNodes.filter((c) => c.name === "load"));
156+
set.updateCurrentModules(
157+
commandNodes.filter((c: CommandExpressionNode) => c.name === "load"),
158+
);
156159
},
157160
updateCurrentLine(currentLine: number) {
158161
set.lastLine(get.currentLine());

apps/evmcrispr-terminal/src/components/TerminalHeader/index.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ import { terminalStoreActions } from "../TerminalEditor/use-terminal-store";
2424
export default function TerminalHeader({
2525
address,
2626
}: {
27+
// TODO: It looks like it should not be here
2728
terminalStoreActions: {
2829
errors: (param: string[]) => void;
2930
};
30-
address: string;
31+
address: `0x${string}` | undefined;
3132
}) {
3233
const { disconnect } = useDisconnect();
3334
const { connect, connectors } = useConnect();
@@ -45,7 +46,8 @@ export default function TerminalHeader({
4546
terminalStoreActions.errors([]);
4647
disconnect();
4748
}
48-
const addressShortened = `${address.slice(0, 6)}..${address.slice(-4)}`;
49+
const addressShortened =
50+
address && `${address.slice(0, 6)}..${address.slice(-4)}`;
4951

5052
return (
5153
<>

0 commit comments

Comments
 (0)