Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added assets/networks/passethub.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions assets/networks/westend.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions examples/demo-inkv6/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
"@typescript-eslint/no-explicit-any": "off"
},
}
10 changes: 10 additions & 0 deletions examples/demo-inkv6/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Example Dapp

- Start the application by running:
```shell
# From examples/demo folder
yarn dev

# From the project root folder
yarn workspace demo-inkv6 dev
```
13 changes: 13 additions & 0 deletions examples/demo-inkv6/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Typink Demo - ink! v6</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
41 changes: 41 additions & 0 deletions examples/demo-inkv6/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"name": "demo-inkv6",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
"typink": "npx dedot typink -m ./src/contracts/artifacts/psp22/psp22.contract -o ./src/contracts/types && npx dedot typink -m ./src/contracts/artifacts/greeter/greeter.contract -o ./src/contracts/types"
},
"dependencies": {
"@chakra-ui/icons": "2.2.5",
"@chakra-ui/react": "^2.10.5",
"@chakra-ui/system": "^2.6.2",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0",
"dedot": "^0.14.1",
"framer-motion": "^10.18.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-toastify": "^10.0.6",
"react-use": "^17.6.0",
"typink": "workspace:*"
},
"devDependencies": {
"@dedot/chaintypes": "^0.74.0",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"@vitejs/plugin-react": "^4.3.4",
"eslint": "^8.57.1",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-refresh": "^0.4.16",
"typescript": "^5.5.2",
"vite": "^6.0.5",
"vite-tsconfig-paths": "^5.1.4"
}
}
Binary file added examples/demo-inkv6/public/favicon.ico
Binary file not shown.
Binary file added examples/demo-inkv6/public/typink-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
46 changes: 46 additions & 0 deletions examples/demo-inkv6/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Box, Flex, Tab, TabList, TabPanel, TabPanels, Tabs } from '@chakra-ui/react';
import { useState } from 'react';
import { useSearchParam } from 'react-use';
import BalanceInsufficientAlert from '@/components/shared/BalanceInsufficientAlert.tsx';
import MainFooter from '@/components/shared/MainFooter';
import MainHeader from '@/components/shared/MainHeader';
import FlipperBoard from '@/components/FlipperBoard.tsx';
import { ContractDeployerBoard } from '@/components/ContractDeployerBoard.tsx';

function App() {
const tab = useSearchParam('tab');
const tabIndex = tab ? parseInt(tab) : 0;
const [index, setIndex] = useState(tabIndex);

const handleTabsChange = (index: number) => {
setIndex(index);
history.pushState({}, '', location.pathname + `?tab=${index}`);
};

return (
<Flex direction='column' minHeight='100vh'>
<MainHeader />
<Box maxWidth='760px' mx='auto' my={4} px={4} flex={1} w='full'>
<BalanceInsufficientAlert />
<Tabs index={index} onChange={handleTabsChange}>
<TabList>
<Tab>Flipper Contract</Tab>
<Tab>Deploy Flipper Contracts</Tab>
</TabList>

<TabPanels>
<TabPanel>
<FlipperBoard />
</TabPanel>
<TabPanel>
<ContractDeployerBoard />
</TabPanel>
</TabPanels>
</Tabs>
</Box>
<MainFooter />
</Flex>
);
}

export default App;
71 changes: 71 additions & 0 deletions examples/demo-inkv6/src/components/AccountSelection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { Box, Button, Flex, Menu, MenuButton, MenuDivider, MenuItem, MenuList, Text } from '@chakra-ui/react';
import { useEffect, useMemo } from 'react';
import ConnectedWallet from '@/components/dialog/ConnectedWallet.tsx';
import WalletSelection, { ButtonStyle } from '@/components/dialog/WalletSelection.tsx';
import { shortenAddress } from '@/utils/string.ts';
import { formatBalance, useBalances, useTypink } from 'typink';

export default function AccountSelection() {
const { accounts, connectedAccount, setConnectedAccount, disconnect, network } = useTypink();
const addresses = useMemo(() => accounts.map((a) => a.address), [accounts]);
const balances = useBalances(addresses);

useEffect(() => {
if (connectedAccount && accounts.map((one) => one.address).includes(connectedAccount.address)) {
return;
}

setConnectedAccount(accounts[0]);
}, [accounts]);

if (!connectedAccount) {
return <></>;
}

const { name, address } = connectedAccount;

return (
<Box>
<Menu autoSelect={false}>
<MenuButton as={Button} variant='outline'>
<Flex align='center' gap={2}>
<Text fontWeight='semi-bold' fontSize='md'>
{name}
</Text>
<Text fontSize='sm' fontWeight='400'>
({shortenAddress(address)})
</Text>
</Flex>
</MenuButton>

<MenuList>
<ConnectedWallet />

{accounts.map((one) => (
<MenuItem
backgroundColor={one.address === address ? 'gray.200' : ''}
gap={2}
key={one.address}
onClick={() => setConnectedAccount(one)}>
<Flex direction='column'>
<Text fontWeight='500'>{one.name}</Text>

<Text fontSize='xs'>Address: {shortenAddress(one.address)}</Text>
<Text fontSize='xs'>Balance: {formatBalance(balances[one.address]?.free, network)}</Text>
</Flex>
</MenuItem>
))}
<MenuDivider />
<WalletSelection
buttonStyle={ButtonStyle.MENU_ITEM}
buttonLabel='Switch Wallet'
buttonProps={{ color: 'primary.500' }}
/>
<MenuItem onClick={disconnect} color='red.500'>
Sign Out
</MenuItem>
</MenuList>
</Menu>
</Box>
);
}
80 changes: 80 additions & 0 deletions examples/demo-inkv6/src/components/ContractDeployerBoard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { Box, Button, Heading, Table, TableContainer, Tbody, Td, Th, Thead, Tr } from '@chakra-ui/react';
import { SubstrateAddress, useDeployer, useDeployerTx } from 'typink';
import { flipperMetadata } from '@/contracts/deployments.ts';
import { txToaster } from '@/utils/txToaster.tsx';
import { generateRandomHex } from 'dedot/utils';
import { useLocalStorage } from 'react-use';
import { Flipper6ContractApi } from '@/contracts/types/flipper6';

interface DeployedContract {
address: SubstrateAddress;
at: number;
}

export function ContractDeployerBoard() {
const wasmHash = flipperMetadata.source.hash;
const { deployer } = useDeployer<Flipper6ContractApi>(flipperMetadata, wasmHash);
const newFlipperTx = useDeployerTx(deployer, 'new');
const [deployedContracts, setDeployedContracts] = useLocalStorage<DeployedContract[]>('DEPLOYED_CONTRACTS', []);

const doDeploy = async () => {
if (!deployer) return;

const toaster = txToaster();
try {
const salt = generateRandomHex();
await newFlipperTx.signAndSend({
args: [true],
txOptions: { salt },
callback: ({ status }, contractAddress) => {
console.log(status);

// TODO improve this?
if (contractAddress) {
console.log('Contract is deployed at address', contractAddress);
setDeployedContracts((prev) => [{ address: contractAddress, at: Date.now() }, ...(prev || [])]);
}

toaster.updateTxStatus(status);
},
});
} catch (e: any) {
console.error(e);
toaster.onError(e);
}
};

return (
<Box>
<Heading size='md' mb={4}>
Deploy Flipper Contract
</Heading>
<Button my={4} isLoading={newFlipperTx.inBestBlockProgress} onClick={doDeploy}>
Deploy
</Button>
<Box mt={4}>
<Heading size='sm' mb={4}>
Deployed Contracts
</Heading>
<TableContainer>
<Table variant='simple' size='sm'>
<Thead>
<Tr>
<Th>Contract Address</Th>
<Th>Deployed at</Th>
</Tr>
</Thead>
<Tbody>
{deployedContracts!.map(({ address, at }) => (
<Tr key={address}>
<Td>{address}</Td>
<Td>{new Date(at).toLocaleString()}</Td>
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
</Box>
</Box>
);
}
53 changes: 53 additions & 0 deletions examples/demo-inkv6/src/components/FlipperBoard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Box, Button, Flex, Heading, Text } from '@chakra-ui/react';
import PendingText from '@/components/shared/PendingText.tsx';
import { ContractId } from 'contracts/deployments';
import { useContract, useContractQuery, useContractTx } from 'typink';
import { txToaster } from '@/utils/txToaster.tsx';
import { Flipper6ContractApi } from '@/contracts/types/flipper6';

export default function FlipperBoard() {
const { contract } = useContract<Flipper6ContractApi>(ContractId.FLIPPER);
const setMessageTx = useContractTx(contract, 'flip');

const { data: value, isLoading } = useContractQuery({
contract,
fn: 'get',
watch: true,
});

console.log('value', value);

const handleFlip = async () => {
if (!contract) return;

const toaster = txToaster('Signing transaction...');

try {
await setMessageTx.signAndSend({
callback: ({ status }) => {
console.log(status);

toaster.updateTxStatus(status);
},
});
} catch (e: any) {
console.error(e, e.message);
toaster.onError(e);
}
};

return (
<Box>
<Heading size='md'>Flipper Contract</Heading>
<Flex my={4} gap={2}>
<Text>Flipper Value:</Text>
<PendingText fontWeight='600' isLoading={isLoading} color='primary.500'>
{value === undefined ? '---' : value ? 'TRUE' : 'FALSE'}
</PendingText>
</Flex>
<Button mt={2} isLoading={setMessageTx.inBestBlockProgress} onClick={handleFlip}>
Flip!
</Button>
</Box>
);
}
15 changes: 15 additions & 0 deletions examples/demo-inkv6/src/components/dialog/ConnectedWallet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Flex, Text } from '@chakra-ui/react';
import { useTypink } from 'typink';

export default function ConnectedWallet() {
const { connectedWallet } = useTypink();

return (
<Flex align='center' gap={3} flex={1} justify='center' pb={2}>
<img src={connectedWallet?.logo} alt={connectedWallet?.name} width={24} />
<Text fontWeight='600' fontSize='14'>
{connectedWallet?.name} - v{connectedWallet?.version}
</Text>
</Flex>
);
}
Loading