Project initialised with AlgoKit CLI using template: https://github.com/algorandfoundation/algokit-fullstack-template.git
This commit is contained in:
56
projects/TokenizeRWATemplate-frontend/src/App.tsx
Normal file
56
projects/TokenizeRWATemplate-frontend/src/App.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
import { SupportedWallet, WalletId, WalletManager, WalletProvider } from '@txnlab/use-wallet-react'
|
||||
import { SnackbarProvider } from 'notistack'
|
||||
import Home from './Home'
|
||||
import { getAlgodConfigFromViteEnvironment, getKmdConfigFromViteEnvironment } from './utils/network/getAlgoClientConfigs'
|
||||
|
||||
let supportedWallets: SupportedWallet[]
|
||||
if (import.meta.env.VITE_ALGOD_NETWORK === 'localnet') {
|
||||
const kmdConfig = getKmdConfigFromViteEnvironment()
|
||||
supportedWallets = [
|
||||
{
|
||||
id: WalletId.KMD,
|
||||
options: {
|
||||
baseServer: kmdConfig.server,
|
||||
token: String(kmdConfig.token),
|
||||
port: String(kmdConfig.port),
|
||||
},
|
||||
},
|
||||
]
|
||||
} else {
|
||||
supportedWallets = [
|
||||
{ id: WalletId.DEFLY },
|
||||
{ id: WalletId.PERA },
|
||||
{ id: WalletId.EXODUS },
|
||||
// If you are interested in WalletConnect v2 provider
|
||||
// refer to https://github.com/TxnLab/use-wallet for detailed integration instructions
|
||||
]
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
const algodConfig = getAlgodConfigFromViteEnvironment()
|
||||
|
||||
const walletManager = new WalletManager({
|
||||
wallets: supportedWallets,
|
||||
defaultNetwork: algodConfig.network,
|
||||
networks: {
|
||||
[algodConfig.network]: {
|
||||
algod: {
|
||||
baseServer: algodConfig.server,
|
||||
port: algodConfig.port,
|
||||
token: String(algodConfig.token),
|
||||
},
|
||||
},
|
||||
},
|
||||
options: {
|
||||
resetNetwork: true,
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<SnackbarProvider maxSnack={3}>
|
||||
<WalletProvider manager={walletManager}>
|
||||
<Home />
|
||||
</WalletProvider>
|
||||
</SnackbarProvider>
|
||||
)
|
||||
}
|
||||
76
projects/TokenizeRWATemplate-frontend/src/Home.tsx
Normal file
76
projects/TokenizeRWATemplate-frontend/src/Home.tsx
Normal file
@ -0,0 +1,76 @@
|
||||
// src/components/Home.tsx
|
||||
import { useWallet } from '@txnlab/use-wallet-react'
|
||||
import React, { useState } from 'react'
|
||||
import ConnectWallet from './components/ConnectWallet'
|
||||
import Transact from './components/Transact'
|
||||
import AppCalls from './components/AppCalls'
|
||||
|
||||
interface HomeProps {}
|
||||
|
||||
const Home: React.FC<HomeProps> = () => {
|
||||
const [openWalletModal, setOpenWalletModal] = useState<boolean>(false)
|
||||
const [openDemoModal, setOpenDemoModal] = useState<boolean>(false)
|
||||
const [appCallsDemoModal, setAppCallsDemoModal] = useState<boolean>(false)
|
||||
const { activeAddress } = useWallet()
|
||||
|
||||
const toggleWalletModal = () => {
|
||||
setOpenWalletModal(!openWalletModal)
|
||||
}
|
||||
|
||||
const toggleDemoModal = () => {
|
||||
setOpenDemoModal(!openDemoModal)
|
||||
}
|
||||
|
||||
const toggleAppCallsModal = () => {
|
||||
setAppCallsDemoModal(!appCallsDemoModal)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="hero min-h-screen bg-teal-400">
|
||||
<div className="hero-content text-center rounded-lg p-6 max-w-md bg-white mx-auto">
|
||||
<div className="max-w-md">
|
||||
<h1 className="text-4xl">
|
||||
Welcome to <div className="font-bold">AlgoKit 🙂</div>
|
||||
</h1>
|
||||
<p className="py-6">
|
||||
This starter has been generated using official AlgoKit React template. Refer to the resource below for next steps.
|
||||
</p>
|
||||
|
||||
<div className="grid">
|
||||
<a
|
||||
data-test-id="getting-started"
|
||||
className="btn btn-primary m-2"
|
||||
target="_blank"
|
||||
href="https://github.com/algorandfoundation/algokit-cli"
|
||||
>
|
||||
Getting started
|
||||
</a>
|
||||
|
||||
<div className="divider" />
|
||||
<button data-test-id="connect-wallet" className="btn m-2" onClick={toggleWalletModal}>
|
||||
Wallet Connection
|
||||
</button>
|
||||
|
||||
{activeAddress && (
|
||||
<button data-test-id="transactions-demo" className="btn m-2" onClick={toggleDemoModal}>
|
||||
Transactions Demo
|
||||
</button>
|
||||
)}
|
||||
|
||||
{activeAddress && (
|
||||
<button data-test-id="appcalls-demo" className="btn m-2" onClick={toggleAppCallsModal}>
|
||||
Contract Interactions Demo
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<ConnectWallet openModal={openWalletModal} closeModal={toggleWalletModal} />
|
||||
<Transact openModal={openDemoModal} setModalState={setOpenDemoModal} />
|
||||
<AppCalls openModal={appCallsDemoModal} setModalState={setAppCallsDemoModal} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Home
|
||||
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
@ -0,0 +1,24 @@
|
||||
import { useWallet } from '@txnlab/use-wallet-react'
|
||||
import { useMemo } from 'react'
|
||||
import { ellipseAddress } from '../utils/ellipseAddress'
|
||||
import { getAlgodConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs'
|
||||
|
||||
const Account = () => {
|
||||
const { activeAddress } = useWallet()
|
||||
const algoConfig = getAlgodConfigFromViteEnvironment()
|
||||
|
||||
const networkName = useMemo(() => {
|
||||
return algoConfig.network === '' ? 'localnet' : algoConfig.network.toLocaleLowerCase()
|
||||
}, [algoConfig.network])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<a className="text-xl" target="_blank" href={`https://lora.algokit.io/${networkName}/account/${activeAddress}/`}>
|
||||
Address: {ellipseAddress(activeAddress)}
|
||||
</a>
|
||||
<div className="text-xl">Network: {networkName}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Account
|
||||
@ -0,0 +1,98 @@
|
||||
import { useWallet } from '@txnlab/use-wallet-react'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { useState } from 'react'
|
||||
import { HelloWorldFactory } from '../contracts/HelloWorld'
|
||||
import { OnSchemaBreak, OnUpdate } from '@algorandfoundation/algokit-utils/types/app'
|
||||
import { getAlgodConfigFromViteEnvironment, getIndexerConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs'
|
||||
import { AlgorandClient } from '@algorandfoundation/algokit-utils'
|
||||
|
||||
interface AppCallsInterface {
|
||||
openModal: boolean
|
||||
setModalState: (value: boolean) => void
|
||||
}
|
||||
|
||||
const AppCalls = ({ openModal, setModalState }: AppCallsInterface) => {
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
const [contractInput, setContractInput] = useState<string>('')
|
||||
const { enqueueSnackbar } = useSnackbar()
|
||||
const { transactionSigner, activeAddress } = useWallet()
|
||||
|
||||
const algodConfig = getAlgodConfigFromViteEnvironment()
|
||||
const indexerConfig = getIndexerConfigFromViteEnvironment()
|
||||
const algorand = AlgorandClient.fromConfig({
|
||||
algodConfig,
|
||||
indexerConfig,
|
||||
})
|
||||
algorand.setDefaultSigner(transactionSigner)
|
||||
|
||||
const sendAppCall = async () => {
|
||||
setLoading(true)
|
||||
|
||||
// Please note, in typical production scenarios,
|
||||
// you wouldn't want to use deploy directly from your frontend.
|
||||
// Instead, you would deploy your contract on your backend and reference it by id.
|
||||
// Given the simplicity of the starter contract, we are deploying it on the frontend
|
||||
// for demonstration purposes.
|
||||
const factory = new HelloWorldFactory({
|
||||
defaultSender: activeAddress ?? undefined,
|
||||
algorand,
|
||||
})
|
||||
const deployResult = await factory
|
||||
.deploy({
|
||||
onSchemaBreak: OnSchemaBreak.AppendApp,
|
||||
onUpdate: OnUpdate.AppendApp,
|
||||
})
|
||||
.catch((e: Error) => {
|
||||
enqueueSnackbar(`Error deploying the contract: ${e.message}`, { variant: 'error' })
|
||||
setLoading(false)
|
||||
return undefined
|
||||
})
|
||||
|
||||
if (!deployResult) {
|
||||
return
|
||||
}
|
||||
|
||||
const { appClient } = deployResult
|
||||
|
||||
const response = await appClient.send.hello({ args: { name: contractInput } }).catch((e: Error) => {
|
||||
enqueueSnackbar(`Error calling the contract: ${e.message}`, { variant: 'error' })
|
||||
setLoading(false)
|
||||
return undefined
|
||||
})
|
||||
|
||||
if (!response) {
|
||||
return
|
||||
}
|
||||
|
||||
enqueueSnackbar(`Response from the contract: ${response.return}`, { variant: 'success' })
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<dialog id="appcalls_modal" className={`modal ${openModal ? 'modal-open' : ''} bg-slate-200`}>
|
||||
<form method="dialog" className="modal-box">
|
||||
<h3 className="font-bold text-lg">Say hello to your Algorand smart contract</h3>
|
||||
<br />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Provide input to hello function"
|
||||
className="input input-bordered w-full"
|
||||
value={contractInput}
|
||||
onChange={(e) => {
|
||||
setContractInput(e.target.value)
|
||||
}}
|
||||
/>
|
||||
<div className="modal-action ">
|
||||
<button className="btn" onClick={() => setModalState(!openModal)}>
|
||||
Close
|
||||
</button>
|
||||
<button className={`btn`} onClick={sendAppCall}>
|
||||
{loading ? <span className="loading loading-spinner" /> : 'Send application call'}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</dialog>
|
||||
)
|
||||
}
|
||||
|
||||
export default AppCalls
|
||||
@ -0,0 +1,86 @@
|
||||
import { useWallet, Wallet, WalletId } from '@txnlab/use-wallet-react'
|
||||
import Account from './Account'
|
||||
|
||||
interface ConnectWalletInterface {
|
||||
openModal: boolean
|
||||
closeModal: () => void
|
||||
}
|
||||
|
||||
const ConnectWallet = ({ openModal, closeModal }: ConnectWalletInterface) => {
|
||||
const { wallets, activeAddress } = useWallet()
|
||||
|
||||
const isKmd = (wallet: Wallet) => wallet.id === WalletId.KMD
|
||||
|
||||
return (
|
||||
<dialog id="connect_wallet_modal" className={`modal ${openModal ? 'modal-open' : ''}`}>
|
||||
<form method="dialog" className="modal-box">
|
||||
<h3 className="font-bold text-2xl">Select wallet provider</h3>
|
||||
|
||||
<div className="grid m-2 pt-5">
|
||||
{activeAddress && (
|
||||
<>
|
||||
<Account />
|
||||
<div className="divider" />
|
||||
</>
|
||||
)}
|
||||
|
||||
{!activeAddress &&
|
||||
wallets?.map((wallet) => (
|
||||
<button
|
||||
data-test-id={`${wallet.id}-connect`}
|
||||
className="btn border-teal-800 border-1 m-2"
|
||||
key={`provider-${wallet.id}`}
|
||||
onClick={() => {
|
||||
return wallet.connect()
|
||||
}}
|
||||
>
|
||||
{!isKmd(wallet) && (
|
||||
<img
|
||||
alt={`wallet_icon_${wallet.id}`}
|
||||
src={wallet.metadata.icon}
|
||||
style={{ objectFit: 'contain', width: '30px', height: 'auto' }}
|
||||
/>
|
||||
)}
|
||||
<span>{isKmd(wallet) ? 'LocalNet Wallet' : wallet.metadata.name}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="modal-action ">
|
||||
<button
|
||||
data-test-id="close-wallet-modal"
|
||||
className="btn"
|
||||
onClick={() => {
|
||||
closeModal()
|
||||
}}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
{activeAddress && (
|
||||
<button
|
||||
className="btn btn-warning"
|
||||
data-test-id="logout"
|
||||
onClick={async () => {
|
||||
if (wallets) {
|
||||
const activeWallet = wallets.find((w) => w.isActive)
|
||||
if (activeWallet) {
|
||||
await activeWallet.disconnect()
|
||||
} else {
|
||||
// Required for logout/cleanup of inactive providers
|
||||
// For instance, when you login to localnet wallet and switch network
|
||||
// to testnet/mainnet or vice verse.
|
||||
localStorage.removeItem('@txnlab/use-wallet:v3')
|
||||
window.location.reload()
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
Logout
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
</dialog>
|
||||
)
|
||||
}
|
||||
export default ConnectWallet
|
||||
@ -0,0 +1,46 @@
|
||||
import React, { ReactNode } from 'react'
|
||||
|
||||
interface ErrorBoundaryProps {
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
interface ErrorBoundaryState {
|
||||
hasError: boolean
|
||||
error: Error | null
|
||||
}
|
||||
|
||||
class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
||||
constructor(props: ErrorBoundaryProps) {
|
||||
super(props)
|
||||
this.state = { hasError: false, error: null }
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
|
||||
// Update state so the next render will show the fallback UI.
|
||||
return { hasError: true, error: error }
|
||||
}
|
||||
|
||||
render(): ReactNode {
|
||||
if (this.state.hasError) {
|
||||
// You can render any custom fallback UI
|
||||
return (
|
||||
<div className="hero min-h-screen bg-teal-400">
|
||||
<div className="hero-content text-center rounded-lg p-6 max-w-md bg-white mx-auto">
|
||||
<div className="max-w-md">
|
||||
<h1 className="text-4xl">Error occured</h1>
|
||||
<p className="py-6">
|
||||
{this.state.error?.message.includes('Attempt to get default algod configuration')
|
||||
? 'Please make sure to set up your environment variables correctly. Create a .env file based on .env.template and fill in the required values. This controls the network and credentials for connections with Algod and Indexer.'
|
||||
: this.state.error?.message}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
|
||||
export default ErrorBoundary
|
||||
@ -0,0 +1,80 @@
|
||||
import { algo, AlgorandClient } from '@algorandfoundation/algokit-utils'
|
||||
import { useWallet } from '@txnlab/use-wallet-react'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { useState } from 'react'
|
||||
import { getAlgodConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs'
|
||||
|
||||
interface TransactInterface {
|
||||
openModal: boolean
|
||||
setModalState: (value: boolean) => void
|
||||
}
|
||||
|
||||
const Transact = ({ openModal, setModalState }: TransactInterface) => {
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
const [receiverAddress, setReceiverAddress] = useState<string>('')
|
||||
|
||||
const algodConfig = getAlgodConfigFromViteEnvironment()
|
||||
const algorand = AlgorandClient.fromConfig({ algodConfig })
|
||||
|
||||
const { enqueueSnackbar } = useSnackbar()
|
||||
|
||||
const { transactionSigner, activeAddress } = useWallet()
|
||||
|
||||
const handleSubmitAlgo = async () => {
|
||||
setLoading(true)
|
||||
|
||||
if (!transactionSigner || !activeAddress) {
|
||||
enqueueSnackbar('Please connect wallet first', { variant: 'warning' })
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
enqueueSnackbar('Sending transaction...', { variant: 'info' })
|
||||
const result = await algorand.send.payment({
|
||||
signer: transactionSigner,
|
||||
sender: activeAddress,
|
||||
receiver: receiverAddress,
|
||||
amount: algo(1),
|
||||
})
|
||||
enqueueSnackbar(`Transaction sent: ${result.txIds[0]}`, { variant: 'success' })
|
||||
setReceiverAddress('')
|
||||
} catch (e) {
|
||||
enqueueSnackbar('Failed to send transaction', { variant: 'error' })
|
||||
}
|
||||
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<dialog id="transact_modal" className={`modal ${openModal ? 'modal-open' : ''} bg-slate-200`}>
|
||||
<form method="dialog" className="modal-box">
|
||||
<h3 className="font-bold text-lg">Send payment transaction</h3>
|
||||
<br />
|
||||
<input
|
||||
type="text"
|
||||
data-test-id="receiver-address"
|
||||
placeholder="Provide wallet address"
|
||||
className="input input-bordered w-full"
|
||||
value={receiverAddress}
|
||||
onChange={(e) => {
|
||||
setReceiverAddress(e.target.value)
|
||||
}}
|
||||
/>
|
||||
<div className="modal-action ">
|
||||
<button className="btn" onClick={() => setModalState(!openModal)}>
|
||||
Close
|
||||
</button>
|
||||
<button
|
||||
data-test-id="send-algo"
|
||||
className={`btn ${receiverAddress.length === 58 ? '' : 'btn-disabled'} lo`}
|
||||
onClick={handleSubmitAlgo}
|
||||
>
|
||||
{loading ? <span className="loading loading-spinner" /> : 'Send 1 Algo'}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</dialog>
|
||||
)
|
||||
}
|
||||
|
||||
export default Transact
|
||||
@ -0,0 +1,14 @@
|
||||
## How to connect my web app with Algorand smart contracts?
|
||||
|
||||
The following folder is reserved for the Algorand Application Clients. The clients are used to interact with instances of Algorand Smart Contracts (ASC1s) deployed on-chain.
|
||||
|
||||
To integrate this react frontend template with your smart contracts codebase, perform the following steps:
|
||||
|
||||
1. Generate the typed client using `algokit generate client -l typescript -o {path/to/this/folder}` or using the dedicated `link` command `algokit project link` (ensure to invoke it from the root of this react project). Using the `link` command is especially useful within workspaces that have multiple contract projects.
|
||||
2. The generated typescript client should be ready to be imported and used in this react frontend template, making it a full fledged dApp.
|
||||
|
||||
> Please note, by default this template defines `"generate:app-clients": "algokit project link --all"` which is a shortcut to automatically link TEAL code from all `contract` projects in the workspace as typed clients into the `frontend` project that is invoking the `link` command. Refer to [documentation](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/project/link.md) to read more about `link` command.
|
||||
|
||||
## **How to interact with the smart contract?**
|
||||
|
||||
The generated client provides a set of functions that can be used to interact with the ABI (Application Binary Interface) compliant Algorand smart contract. For example, if the smart contract has a function called `hello`, the generated client will have a function called `hello` that can be used to interact with the smart contract. Refer to a [full-stack end-to-end starter template](https://github.com/algorandfoundation/algokit-fullstack-template) for a reference example on invoking and interacting with typescript typed clients generated.
|
||||
@ -0,0 +1,26 @@
|
||||
import { AlgoClientConfig } from '@algorandfoundation/algokit-utils/types/network-client'
|
||||
import type { TokenHeader } from 'algosdk/dist/types/client/urlTokenBaseHTTPClient'
|
||||
|
||||
export interface AlgoViteClientConfig extends AlgoClientConfig {
|
||||
/** Base URL of the server e.g. http://localhost, https://testnet-api.algonode.cloud/, etc. */
|
||||
server: string
|
||||
/** The port to use e.g. 4001, 443, etc. */
|
||||
port: string | number
|
||||
/** The token to use for API authentication (or undefined if none needed) - can be a string, or an object with the header key => value */
|
||||
token: string | TokenHeader
|
||||
/** String representing current Algorand Network type (testnet/mainnet and etc) */
|
||||
network: string
|
||||
}
|
||||
|
||||
export interface AlgoViteKMDConfig extends AlgoClientConfig {
|
||||
/** Base URL of the server e.g. http://localhost, https://testnet-api.algonode.cloud/, etc. */
|
||||
server: string
|
||||
/** The port to use e.g. 4001, 443, etc. */
|
||||
port: string | number
|
||||
/** The token to use for API authentication (or undefined if none needed) - can be a string, or an object with the header key => value */
|
||||
token: string | TokenHeader
|
||||
/** KMD wallet name */
|
||||
wallet: string
|
||||
/** KMD wallet password */
|
||||
password: string
|
||||
}
|
||||
13
projects/TokenizeRWATemplate-frontend/src/main.tsx
Normal file
13
projects/TokenizeRWATemplate-frontend/src/main.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App'
|
||||
import './styles/main.css'
|
||||
import ErrorBoundary from './components/ErrorBoundary'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
<React.StrictMode>
|
||||
<ErrorBoundary>
|
||||
<App />
|
||||
</ErrorBoundary>
|
||||
</React.StrictMode>,
|
||||
)
|
||||
@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@ -0,0 +1,15 @@
|
||||
import { ellipseAddress } from './ellipseAddress'
|
||||
|
||||
describe('ellipseAddress', () => {
|
||||
it('should return ellipsed address with specified width', () => {
|
||||
const address = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
||||
const result = ellipseAddress(address, 4)
|
||||
expect(result).toBe('aaaa...aaaa')
|
||||
})
|
||||
|
||||
it('should return empty string when address is empty', () => {
|
||||
const address = ''
|
||||
const result = ellipseAddress(address)
|
||||
expect(result).toBe('')
|
||||
})
|
||||
})
|
||||
@ -0,0 +1,3 @@
|
||||
export function ellipseAddress(address: string | null, width = 6): string {
|
||||
return address ? `${address.slice(0, width)}...${address.slice(-width)}` : (address ?? '')
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
import { AlgoViteClientConfig, AlgoViteKMDConfig } from '../../interfaces/network'
|
||||
|
||||
export function getAlgodConfigFromViteEnvironment(): AlgoViteClientConfig {
|
||||
if (!import.meta.env.VITE_ALGOD_SERVER) {
|
||||
throw new Error('Attempt to get default algod configuration without specifying VITE_ALGOD_SERVER in the environment variables')
|
||||
}
|
||||
|
||||
return {
|
||||
server: import.meta.env.VITE_ALGOD_SERVER,
|
||||
port: import.meta.env.VITE_ALGOD_PORT,
|
||||
token: import.meta.env.VITE_ALGOD_TOKEN,
|
||||
network: import.meta.env.VITE_ALGOD_NETWORK,
|
||||
}
|
||||
}
|
||||
|
||||
export function getIndexerConfigFromViteEnvironment(): AlgoViteClientConfig {
|
||||
if (!import.meta.env.VITE_INDEXER_SERVER) {
|
||||
throw new Error('Attempt to get default algod configuration without specifying VITE_INDEXER_SERVER in the environment variables')
|
||||
}
|
||||
|
||||
return {
|
||||
server: import.meta.env.VITE_INDEXER_SERVER,
|
||||
port: import.meta.env.VITE_INDEXER_PORT,
|
||||
token: import.meta.env.VITE_INDEXER_TOKEN,
|
||||
network: import.meta.env.VITE_ALGOD_NETWORK,
|
||||
}
|
||||
}
|
||||
|
||||
export function getKmdConfigFromViteEnvironment(): AlgoViteKMDConfig {
|
||||
if (!import.meta.env.VITE_KMD_SERVER) {
|
||||
throw new Error('Attempt to get default kmd configuration without specifying VITE_KMD_SERVER in the environment variables')
|
||||
}
|
||||
|
||||
return {
|
||||
server: import.meta.env.VITE_KMD_SERVER,
|
||||
port: import.meta.env.VITE_KMD_PORT,
|
||||
token: import.meta.env.VITE_KMD_TOKEN,
|
||||
wallet: import.meta.env.VITE_KMD_WALLET,
|
||||
password: import.meta.env.VITE_KMD_PASSWORD,
|
||||
}
|
||||
}
|
||||
24
projects/TokenizeRWATemplate-frontend/src/vite-env.d.ts
vendored
Normal file
24
projects/TokenizeRWATemplate-frontend/src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_ENVIRONMENT: string
|
||||
|
||||
readonly VITE_ALGOD_TOKEN: string
|
||||
readonly VITE_ALGOD_SERVER: string
|
||||
readonly VITE_ALGOD_PORT: string
|
||||
readonly VITE_ALGOD_NETWORK: string
|
||||
|
||||
readonly VITE_INDEXER_TOKEN: string
|
||||
readonly VITE_INDEXER_SERVER: string
|
||||
readonly VITE_INDEXER_PORT: string
|
||||
|
||||
readonly VITE_KMD_TOKEN: string
|
||||
readonly VITE_KMD_SERVER: string
|
||||
readonly VITE_KMD_PORT: string
|
||||
readonly VITE_KMD_PASSWORD: string
|
||||
readonly VITE_KMD_WALLET: string
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv
|
||||
}
|
||||
Reference in New Issue
Block a user