Skip to content

Commit

Permalink
initial code base
Browse files Browse the repository at this point in the history
  • Loading branch information
endadinh committed Jul 22, 2024
1 parent f7bd4de commit bab56ac
Show file tree
Hide file tree
Showing 16 changed files with 3,534 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/node_modules
33 changes: 33 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "wallet-kit",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "webpack serve --mode development",
"build": "webpack --mode production"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@emotion/react": "^11.13.0",
"@emotion/styled": "^11.13.0",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"framer-motion": "^11.3.8",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-modal": "^3.16.1",
"typescript": "^5.5.3",
"viem": "^2.17.9"
},
"devDependencies": {
"@types/react-modal": "^3.16.3",
"html-webpack-plugin": "^5.6.0",
"ts-loader": "^9.2.3",
"webpack": "^5.51.1",
"webpack-cli": "^4.8.0",
"webpack-dev-server": "^4.0.0"
}
}
24 changes: 24 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// src/App.tsx
import React from 'react';
import ConnectButton from './components/ConnectButton';
import styled from '@emotion/styled';

const Container = styled.div`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
background-color: #f3f4f6;
`;

const App: React.FC = () => {
return (
<Container>
<h1>Viction Kit</h1>
<ConnectButton />
</Container>
);
};

export default App;
52 changes: 52 additions & 0 deletions src/chains/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { createPublicClient, http } from "viem";
import { Chain } from "viem/chains";

const VictionMainnet: Chain = {
id: 88,
name: "Viction Mainnet",
nativeCurrency: {
decimals: 18,
name: "Viction",
symbol: "VIC",
},
rpcUrls: {
default: {
http: ["https://rpc.viction.xyz"],
webSocket: ["wss://ws.viction.xyz"],
},
},
blockExplorers: {
default: { name: "Explorer", url: "https://vicscan.xyz" },
},
testnet: true,
};

const VictionTestnet: Chain = {
id: 89,
name: "Viction Testnet",
nativeCurrency: {
decimals: 18,
name: "Viction",
symbol: "VIC",
},
rpcUrls: {
default: {
http: ["https://rpc-testnet.viction.xyz"],
webSocket: ["wss://ws-testnet.viction.xyz"],
},
},
blockExplorers: {
default: { name: "Explorer", url: "https://testnet.vicscan.xyz" },
},
testnet: true,
};

export const networks: any = {
MAINNET: VictionMainnet,
TESTNET: VictionTestnet,
};

export const client = createPublicClient({
chain: VictionMainnet,
transport: http(),
});
100 changes: 100 additions & 0 deletions src/components/ConnectButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// src/components/ConnectButton.tsx
import React, { useState } from 'react';
import MetaMaskConnector from '../connectors/MetaMaskConnector';
import WalletModal from './WalletModal';
import styled from '@emotion/styled';

const ConnectButtonStyled = styled.button`
padding: 10px 20px;
font-size: 16px;
border: none;
border-radius: 4px;
cursor: pointer;
background-color: #3b82f6;
color: white;
&:hover {
background-color: #2563eb;
}
`;

const AccountInfo = styled.div`
text-align: center;
p {
margin: 5px 0;
}
`;

const ErrorMessage = styled.div`
color: red;
margin-top: 10px;
`;

const ConnectButton: React.FC = () => {
const [account, setAccount] = useState<string | null>(null);
const [balance, setBalance] = useState<string | null>(null);
const [modalIsOpen, setModalIsOpen] = useState<boolean>(false);
const [error, setError] = useState<string | null>(null);
const connector = new MetaMaskConnector();

const connectWallet = async () => {
try {
await connector.connect();
const acc = await connector.getAccount();
setAccount(acc);
const bal = await connector.getBalance(acc);
setBalance(bal);
closeModal();
} catch (error: any) {
if (error.message === 'User rejected the request.') {
setError('Connection request was rejected by the user.');
} else {
setError(error.message);
}
}
};

const disconnectWallet = () => {
connector.disconnect();
setAccount(null);
setBalance(null);
};

const openModal = () => {
setError(null); // Clear previous errors
setModalIsOpen(true);
};

const closeModal = () => {
setModalIsOpen(false);
};

return (
<div>
{account ? (
<AccountInfo>
<p>Account: {account}</p>
<p>Balance: {balance} ETH</p>
<ConnectButtonStyled onClick={disconnectWallet}>
Disconnect
</ConnectButtonStyled>
</AccountInfo>
) : (
<div>
<ConnectButtonStyled onClick={openModal}>
Connect Wallet
</ConnectButtonStyled>
{error && <ErrorMessage>{error}</ErrorMessage>}
</div>
)}
<WalletModal
isOpen={modalIsOpen}
onRequestClose={closeModal}
connectWallet={connectWallet}
/>
</div>
);
};

export default ConnectButton;
88 changes: 88 additions & 0 deletions src/components/WalletModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// src/components/WalletModal.tsx
import React from 'react';
import Modal from 'react-modal';
import { motion } from 'framer-motion';
import styled from '@emotion/styled';

const customStyles = {
content: {
top: '50%',
left: '50%',
right: 'auto',
bottom: 'auto',
marginRight: '-50%',
transform: 'translate(-50%, -50%)',
border: 'none',
borderRadius: '8px',
padding: '0',
overflow: 'hidden',
},
overlay: {
backgroundColor: 'rgba(0, 0, 0, 0.75)',
},
};

const Container = styled.div`
width: 300px;
padding: 20px;
background-color: white;
border-radius: 8px;
`;

const Header = styled.div`
font-size: 20px;
font-weight: bold;
margin-bottom: 20px;
text-align: center;
`;

const Button = styled.button`
width: 100%;
padding: 10px;
margin: 5px 0;
font-size: 16px;
border: none;
border-radius: 4px;
cursor: pointer;
background-color: #3b82f6;
color: white;
&:hover {
background-color: #2563eb;
}
`;

interface WalletModalProps {
isOpen: boolean;
onRequestClose: () => void;
connectWallet: () => void;
}

const WalletModal: React.FC<WalletModalProps> = ({
isOpen,
onRequestClose,
connectWallet,
}) => {
return (
<Modal
isOpen={isOpen}
onRequestClose={onRequestClose}
style={customStyles}
ariaHideApp={false}
>
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.3 }}
>
<Container>
<Header>Connect Your Wallet</Header>
<Button onClick={connectWallet}>Connect MetaMask</Button>
<Button onClick={onRequestClose}>Cancel</Button>
</Container>
</motion.div>
</Modal>
);
};

export default WalletModal;
76 changes: 76 additions & 0 deletions src/connectors/MetaMaskConnector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { createPublicClient, createWalletClient, custom, http } from 'viem';
// src/connectors/MetaMaskConnector.ts
import { formatEther } from 'viem';
import { networks } from '../chains';

interface WalletConnector {
connect(): Promise<void>;
disconnect(): void;
getAccount(): Promise<string>;
getBalance(account: string): Promise<string>;
sendTransaction(tx: any): Promise<any>;
}

class MetaMaskConnector implements WalletConnector {
private client: any;
private walletClient: any;

constructor() {
this.client = createPublicClient({
chain: networks.MAINNET,
transport: http()
});
this.walletClient = createWalletClient({
chain: networks.MAINNET,
transport: custom(window.ethereum!)
})
}

async connect(): Promise<void> {
if (this.client) {
try {
await this.walletClient.requestAddresses()
} catch (error) {
console.log(`${error}`)
throw new Error('User rejected the request.');
}
} else {
throw new Error('MetaMask is not installed');
}
}

disconnect(): void {
this.client = null;
}

async getAccount(): Promise<string> {
if (!this.client) {
throw new Error('Wallet not connected');
}
const accounts = await this.client.request({ method: 'eth_accounts' });
return accounts[0];
}

async getBalance(account: string): Promise<string> {
if (!this.client) {
throw new Error('Wallet not connected');
}
const balance = await this.client.getBalance({
address: account,
})
const balanceAsEther = formatEther(balance)
return formatEther(balance);
}

async sendTransaction(tx: any): Promise<any> {
if (!this.client) {
throw new Error('Wallet not connected');
}
return this.client.request({
method: 'eth_sendTransaction',
params: [tx],
});
}
}

export default MetaMaskConnector;
4 changes: 4 additions & 0 deletions src/global.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// src/global.d.ts
interface Window {
ethereum: any;
}
Loading

0 comments on commit bab56ac

Please sign in to comment.