Skip to content

Commit

Permalink
Merge pull request #6 from ethereum-optimism/nx
Browse files Browse the repository at this point in the history
nx support
  • Loading branch information
nitaliano authored Nov 19, 2024
2 parents 4a873bf + 06e86b3 commit d381d7c
Show file tree
Hide file tree
Showing 50 changed files with 1,396 additions and 452 deletions.
28 changes: 24 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,24 @@
node_modules
dist
.env*
.DS_Store
# dependencies
**/node_modules
/.pnp
packages/*/.env
.npmrc

# testing
/coverage

# build artifacts
**/build/
**/dist/
**/tsconfig.tsbuildinfo
.nx

# misc
.DS_Store
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
60 changes: 7 additions & 53 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,54 +1,8 @@
{
"name": "super",
"version": "0.0.0",
"license": "MIT",
"bin": "dist/cli.js",
"type": "module",
"engines": {
"node": ">=18.20"
},
"scripts": {
"build": "tsup",
"build:watch": "tsup --watch",
"dev": "tsx src/cli.tsx",
"start": "node dist/cli.js",
"typecheck": "tsc --noEmit"
},
"files": [
"dist"
],
"dependencies": {
"@eth-optimism/viem": "^0.0.11",
"@inkjs/ui": "^2.0.0",
"@tanstack/react-query": "^5.59.20",
"@vitejs/plugin-react": "^4.3.3",
"abitype": "^1.0.6",
"figures": "^6.1.0",
"ink": "^5.0.1",
"pastel": "^3.0.0",
"react": "^18.2.0",
"viem": "^2.21.41",
"zod": "^3.21.4",
"zod-validation-error": "^3.4.0",
"zustand": "^5.0.1"
},
"devDependencies": {
"@types/node": "^22.9.0",
"@types/react": "^18.0.32",
"@vdemedes/prettier-config": "^2.0.1",
"esbuild-plugin-wasm": "^1.1.0",
"eslint-config-xo-react": "^0.27.0",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"ink-testing-library": "^4.0.0",
"node-gyp": "^10.2.0",
"prettier": "^2.8.7",
"resolve-tspaths": "^0.8.22",
"ts-node": "^10.9.1",
"tsup": "^8.3.5",
"tsx": "^4.19.2",
"typescript": "^5.0.3",
"vite": "^5.4.11"
},
"prettier": "@vdemedes/prettier-config"
}
"name": "super",
"private": true,
"nx": {},
"dependencies": {
"nx": "^20.1.2"
}
}
File renamed without changes.
54 changes: 54 additions & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"name": "cli",
"version": "0.0.0",
"license": "MIT",
"bin": "dist/cli.js",
"type": "module",
"engines": {
"node": ">=18.20"
},
"scripts": {
"build": "tsup",
"build:watch": "tsup --watch",
"dev": "tsx src/cli.tsx",
"start": "node dist/cli.js",
"typecheck": "tsc --noEmit"
},
"files": [
"dist"
],
"dependencies": {
"@eth-optimism/viem": "^0.0.11",
"@inkjs/ui": "^2.0.0",
"@tanstack/react-query": "^5.59.20",
"@vitejs/plugin-react": "^4.3.3",
"abitype": "^1.0.6",
"figures": "^6.1.0",
"ink": "^5.0.1",
"pastel": "^3.0.0",
"react": "^18.2.0",
"viem": "^2.21.41",
"zod": "^3.21.4",
"zod-validation-error": "^3.4.0",
"zustand": "^5.0.1"
},
"devDependencies": {
"@types/node": "^22.9.0",
"@types/react": "^18.0.32",
"@vdemedes/prettier-config": "^2.0.1",
"esbuild-plugin-wasm": "^1.1.0",
"eslint-config-xo-react": "^0.27.0",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"ink-testing-library": "^4.0.0",
"node-gyp": "^10.2.0",
"prettier": "^2.8.7",
"resolve-tspaths": "^0.8.22",
"ts-node": "^10.9.1",
"tsup": "^8.3.5",
"tsx": "^4.19.2",
"typescript": "^5.0.3",
"vite": "^5.4.11"
},
"prettier": "@vdemedes/prettier-config"
}
63 changes: 63 additions & 0 deletions packages/cli/src/bridge-wizard/BridgeWizard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import {
BridgeWizardStateVariables,
BridgeWizardStep,
bridgeWizardStepMetadatas,
indexByBridgeWizardStep,
} from '@/bridge-wizard/bridgeWizardSteps';
import {useBridgeWizardStore} from '@/bridge-wizard/bridgeWizardStore';
import {EnterAmount} from '@/bridge-wizard/EnterAmount';
import {EnterPrivateKey} from '@/bridge-wizard/EnterPrivateKey';
import {SelectChains} from '@/bridge-wizard/SelectChains';
import {SelectNetwork} from '@/bridge-wizard/SelectNetwork';
import {Box, Text} from 'ink';

const WizardProgress = ({state}: {state: BridgeWizardStep}) => {
const currentIndex = indexByBridgeWizardStep[state.step];

return (
<Box flexDirection="column">
{bridgeWizardStepMetadatas.map(({step, title, getSummary}, index) => {
const isPast = index < currentIndex;
const isCurrent = index === currentIndex;

return (
<Box key={step} gap={1} paddingX={1}>
<Text color={isPast ? 'green' : isCurrent ? 'blue' : 'gray'}>
{isPast ? '✓' : isCurrent ? '>' : '○'}
</Text>
<Text color={isCurrent ? 'blue' : 'white'}> {title}</Text>
{isPast && (
<Text color="yellow">
{getSummary(state as unknown as BridgeWizardStateVariables)}
</Text>
)}
</Box>
);
})}
</Box>
);
};

export const BridgeWizard = ({
onSubmit,
}: {
onSubmit: (form: BridgeWizardStateVariables) => void;
}) => {
const {state} = useBridgeWizardStore();

return (
<Box flexDirection="column" gap={1}>
<Text bold color="blue">
🌉 Bridge Wizard
</Text>
<WizardProgress state={state} />

<Box flexDirection="column">
{state.step === 'select-network' && <SelectNetwork />}
{state.step === 'enter-private-key' && <EnterPrivateKey />}
{state.step === 'select-chains' && <SelectChains />}
{state.step === 'enter-amount' && <EnterAmount />}
</Box>
</Box>
);
};
92 changes: 92 additions & 0 deletions packages/cli/src/bridge-wizard/EnterAmount.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import {useBridgeWizardStore} from '@/bridge-wizard/bridgeWizardStore';
import {Select, Spinner} from '@inkjs/ui';
import {useQuery} from '@tanstack/react-query';
import {Box, Text} from 'ink';
import {formatEther, parseEther} from 'viem';
import {createPublicClient, http} from 'viem';
import {Address} from 'viem';
import {mainnet, sepolia} from 'viem/chains';

const getBalanceForNetworkL1 = (network: string, address: Address) => {
// TODO support supersim
const chain = network === 'mainnet' ? mainnet : sepolia;
const client = createPublicClient({
transport: http(),
chain,
});

return client.getBalance({
address,
});
};

const useBalance = (network: string, address: Address) => {
return useQuery({
queryKey: ['balance', 'l1', network, address],
queryFn: () => getBalanceForNetworkL1(network, address),
staleTime: Infinity,
});
};

const supportedAmounts: bigint[] = [
parseEther('0.01'),
parseEther('0.05'),
parseEther('0.1'),
parseEther('0.25'),
parseEther('0.5'),
];

export const EnterAmount = () => {
const {state, setAmount} = useBridgeWizardStore();

if (state.step !== 'enter-amount') {
throw new Error('Invalid state');
}

const {data: balance, isLoading: isLoadingBalance} = useBalance(
state.network,
state.address,
);

const numChains = state.chainIds.length;

return (
<Box flexDirection="column" gap={1}>
<Text bold>
How much would you like to bridge to {numChains} chain
{numChains === 1 ? '' : 's'}?{' '}
</Text>

<Box paddingLeft={2}>
<Text dimColor>Balance on {state.network}: </Text>
{isLoadingBalance ? (
<Spinner />
) : balance ? (
<Text color="green">{formatEther(balance)} ETH</Text>
) : (
<Text color="yellow">Unable to fetch balance</Text>
)}
</Box>

<Box>
<Select
options={supportedAmounts.map(amount => {
const perChainAmount = Number(formatEther(amount)).toFixed(2);
const totalAmount = Number(
formatEther(amount * BigInt(numChains)),
).toFixed(2);
return {
label: `${perChainAmount.padStart(
4,
)} ETH × ${numChains} chains = ${totalAmount.padStart(
4,
)} ETH total`,
value: amount.toString(),
};
})}
onChange={amount => setAmount(BigInt(amount))}
/>
</Box>
</Box>
);
};
55 changes: 55 additions & 0 deletions packages/cli/src/bridge-wizard/EnterPrivateKey.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import {useBridgeWizardStore} from '@/bridge-wizard/bridgeWizardStore';
import {Box, Text} from 'ink';
import {TextInput} from '@inkjs/ui';
import {privateKeyToAccount, PrivateKeyToAccountErrorType} from 'viem/accounts';
import {useState} from 'react';
import {Account, isHex} from 'viem';

export const EnterPrivateKey = () => {
const {state, setPrivateKey} = useBridgeWizardStore();

const [errorMessage, setErrorMessage] = useState<string>('');
const [resetKey, setResetKey] = useState(0);

if (state.step !== 'enter-private-key') {
throw new Error('Invalid state');
}

return (
<Box flexDirection="column">
<Box>
<Text bold>Enter your private key for your account:</Text>
</Box>
<TextInput
key={resetKey}
onSubmit={privateKey => {
if (!isHex(privateKey)) {
setErrorMessage('Invalid private key: must start with 0x');
setResetKey(prev => prev + 1);
return;
}

let account: Account;
try {
account = privateKeyToAccount(privateKey);
} catch (err) {
const error = err as PrivateKeyToAccountErrorType;
setErrorMessage(
// @ts-expect-error
`Invalid private key: ${error.shortMessage ?? error.message}`,
);
setResetKey(prev => prev + 1);
return;
}

setPrivateKey(privateKey, account.address);
}}
/>
{errorMessage && (
<Box>
<Text color="red">{errorMessage ? `❌ ${errorMessage}` : ' '}</Text>
</Box>
)}
</Box>
);
};
Loading

0 comments on commit d381d7c

Please sign in to comment.