A starter template using Svelte, SvelteKit, TailwindCSS, ethers.js, svelte-ethers-store, hardhat and hardhat-deploy. Kept simple for easy extensibility.
Prerequisites: Node (v16 LTS), pnpm and Git
Clone/fork:
git clone https://github.com/0xD006F00D/sharthat-template.git shart-project
# or
git clone [email protected]:0xD006F00D/sharthat-template.git shart-project
# navigate to dir for following commands
cd shart-project
Install dependencies:
pnpm install
In a new window start the Hardhat chain:
pnpm chain
In a new window deploy your contracts to the default network (localhost):
pnpm deploy
In a new window run the frontend dev server:
pnpm dev
Modify or add smart contracts in contracts/src/
Edit the deployment scripts in contracts/deploy/
Edit the frontend in frontend/src/routes/index.svelte
Create new components in frontend/src/components/
Open http://localhost:3000 to see the app
To compile and export the contract ABI:
pnpm export
pnpm deploy
andpnpm export
will automatically export copy the compiled contract ABI tofrontend/src/lib/common/contractAbi.json
Configure the .env:
cp .env.example .env
TEST_WALLET_ADDRESS
can be configured with a specific wallet address that should be automatically funded on deployment
DEPLOYER_PRIVATE_KEY
can be configured with a private key that should be used for contract deployments on test networks and mainnets
Get and set the Alchemy and/or POKT API keys for testnet & mainnet deployments
Run your tests and see the gas usage:
pnpm test
Create and modify waffle tests in
contracts/tests
Check you test coverage:
pnpm coverage
Generate the documentation in markdown format (Example):
pnpm dodoc
Document your solidity code using Natspec
Verify your deployed contracts for a specific network (e.g. Rinkeby):
pnpm verify:network rinkeby
The blockexplorer API keys bust be set in the
.env
file.
The hardhat-deploy task
etherscan-verify
is used by default
Deploy your contracts to a specific network (e.g. Rinkeby):
pnpm deploy:network rinkeby
Will by default only pick up scripts tagged
production
To use other networks than hardhat get your Alchemy and/or POKT API keys and set them in
frontend/src/common/constants.js
Building the frontend:
pnpm run build
Preview the production build:
pnpm run preview
By default adapter-static is used to pre-render the site as a collection of static files, allowing deployment to ipfs, surge, fleek, etc.
Deploy the frontend e.g. via Fleek
Sign up to Fleek for free and link your git repo (Documentation)
Transactor
import { Transacting, tx } from '$lib/stores/transactor';
// Automatically shows notification until transaction completed
const txSuccess = await tx($contracts.TokenVendor.withdraw, ethAmount);
// Read whether a transaction is currently happening via Store
let isTransacting = $Transacting
// Show transacting notification with specific message and set $Transacting
const notificationId = Transacting.start(message)
// Stop showing a specific transaction notification and unset $Transacting
Transacting.stop(notificationId);
Notifier
import { Notifier } from '$lib/stores/notifier';
// Automatically parses evm errors and reverts to a readable message
Notifier.danger(exception);
Notifier.warning(revert);
Notifier.info(error);
Notifier.success("Success");
Tooltip
import Tooltip from '$lib/generic/tooltip.svelte';
<Tooltip text={"Sample Text"}>?</Tooltip>
Address
import Address from '$lib/generic/address.svelte';
<Address address={$signerAddress} />
// Custom tooltip and click function
<Address address={$signerAddress} tooltipText="Add" clickAction={addToContacts} />
// Disable tooltip and clickAction
<Address address={$signerAddress} disabled />
Address Input
import Identicon from '$lib/generic/identicon.svelte';
<Identicon input address={address}>
<input placeholder="Address" bind:value={address} />
</Identicon>
Expandable Panel
import Panel from '$lib/generic/panel.svelte';
<Panel {name}>
<Address slot="header" value={contract.address} />
<Contract
readContract={ReadEvmStores.$contracts.TokenVendor}
writeContract={$contracts.TokenVendor} />
</Panel>
Popup
import Popup from '$lib/generic/panel.svelte';
let showMessage = false;
<Popup bind:show={showMessage}>
<h1>Message</h1>
<button id="transferCancelButton" on:click={() => (showMessage = false)}>
Close
</button>
</Popup>
Read-only Evm Store
Visit the svelte-ethers-store documentation for more information about available default evm stores
import {
ReadEvmStores,
ReadConnected,
ReadProvider,
ReadChainId,
ReadContracts,
ReadChainData
} from '$lib/stores/evm';
// ReadEvmStores get initialized on page load for the selected network
// Subscription to read-only provider Stores (without wallet connection)
$: readConnected = $ReadConnected;
$: readProvider = $ReadProvider;
$: readChainId = $ReadChainId;
$: readContracts = $ReadContracts;
$: readChainData = $ReadChainData;
// Getting the value in Svelte or JavaScript Files
const isConnected = ReadEvmStores.$connected;
Balances
import { NativeBalance, TokenBalance } from '$lib/stores/balances';
// use .display for eth formatted string with two decimal places
// use .value to get the actual big number value in wei
const nativeDisplay = $NativeBalance.display + $NativeBalance.symbol;
const tokenAmount = $TokenBalance.value;
Settings
Additional settings can be added in
frontend/src/lib/stores/settings
. The settings UI can be configured infrontend/src/lib/components/usersettings.svelte
.
import { Settings } from '$lib/stores/settings';
const isDebugMode = $Settings.debug;
Signature
import { Signature } from '$lib/common/signature';
// Create a new Signature Object
let signature = new Signature();
// Initialize Signature with required values
await signature.create($contracts.Money, $contracts.TokenVendor.address, tokenAmount);
// Check the state of the signature
let isSigned = signature.value;
// Send transaction that requires a EIP-712 signature
const txSuccess = await tx(
$contracts.TokenVendor.sellWithPermit,
tokenAmount,
signature.deadline,
signature.split.v,
signature.split.r,
signature.split.s
);
// Reset the signature after completion
// Assignment is needed to let svelte know the signature changed
signature = signature.reset();
Many thanks to my dog who came up with the name