Skip to content

Commit

Permalink
Kat/send tokens (#71)
Browse files Browse the repository at this point in the history
* fix cors and requestData errors

* redeem mint wallet

* add dynamic user info

* add FE loading state

* add lucid-evolution and wasm compatibility

* submit endpoint

* update ui to support lucid wallet loading

* update lucid

* recent changes

* handle changes to profile switcher

* add get wallet balance func

* Fix hash

* comment out output export

* add send tokens func

* add send from user func

* add seize function

* add alert updates

* resolve merge issues

* add call to look up blacklist

* fix mint recipient and add error alert messages

* implement table scroll and updates to alertbar

* UI: Fix build

* Add ui build to CI

* Add recipient

* wst-poc-cli: Add -threaded

---------

Co-authored-by: colll78 <[email protected]>
Co-authored-by: Jann Müller <[email protected]>
  • Loading branch information
3 people authored Jan 19, 2025
1 parent 4c4ee9d commit a451fe7
Show file tree
Hide file tree
Showing 17 changed files with 779 additions and 430 deletions.
19 changes: 19 additions & 0 deletions .github/workflows/ci-ui.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: "ci-ui"
on:
pull_request:
push:
tags:
- "v*"

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 23
- run: |
cd frontend
npm install
npm run export
3 changes: 2 additions & 1 deletion frontend/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"extends": ["next/core-web-vitals", "next/typescript"],
"rules": {
"@next/next/no-page-custom-font": "off"
"@next/next/no-page-custom-font": "off",
"@typescript-eslint/no-explicit-any": "off"
}
}
494 changes: 270 additions & 224 deletions frontend/package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@
"tsconfig-paths-webpack-plugin": "^4.2.0",
"typescript": "^5"
}
}
}
95 changes: 79 additions & 16 deletions frontend/src/app/[username]/index.tsx
Original file line number Diff line number Diff line change
@@ -1,57 +1,120 @@
'use client';

//React imports
import React, { useState } from 'react';
import React, { useEffect, useState } from 'react';

//Axios imports
import axios from 'axios';

//Mui imports
import { Box, Typography } from '@mui/material';

//Local components
import useStore from '../store/store';
import { Accounts } from '../store/types';
import { getWalletBalance, signAndSentTx } from '../utils/walletUtils';
import WalletCard from '../components/Card';
import WSTTextField from '../components/WSTTextField';
import CopyTextField from '../components/CopyTextField';

export default function Profile() {
const { currentUser, userA, userB, walletUser, setAlertStatus } = useStore();
const { lucid, currentUser, mintAccount, changeAlertInfo, changeWalletAccountDetails } = useStore();
const accounts = useStore((state) => state.accounts);

useEffect(() => {
useStore.getState();
// console.log("accounts changed:", accounts);
}, [accounts]);

const getUserAccountDetails = () => {
switch (currentUser) {
case "User A": return userA;
case "User B": return userB;
case "Connected Wallet": return walletUser;
case "User A": return accounts.userA;
case "User B": return accounts.userB;
case "Connected Wallet": return accounts.walletUser;
};
};

// temp state for each text field
const [mintTokens, setMintTokens] = useState(0);
const [recipientAddress, setRecipientAddress] = useState('address');
const [sendTokenAmount, setMintTokens] = useState(0);
const [sendRecipientAddress, setsendRecipientAddress] = useState('address');

const onSend = async () => {
if (getUserAccountDetails()?.status === 'Frozen') {
changeAlertInfo({
severity: 'error',
message: 'Cannot send WST with frozen account.',
open: true,
});
return;
}
console.log('start sending tokens');
changeAlertInfo({severity: 'info', message: 'Transaction processing', open: true,});
const accountInfo = getUserAccountDetails();
if (!accountInfo) {
console.error("No valid send account found! Cannot send.");
return;
}
lucid.selectWallet.fromSeed(accountInfo.mnemonic);
const requestData = {
asset_name: Buffer.from('WST', 'utf8').toString('hex'), // Convert "WST" to hex
issuer: mintAccount.address,
quantity: sendTokenAmount,
recipient: sendRecipientAddress,
sender: accountInfo.address,
};
try {
const response = await axios.post(
'/api/v1/tx/programmable-token/transfer',
requestData,
{
headers: {
'Content-Type': 'application/json;charset=utf-8',
},
}
);
console.log('Send response:', response.data);
const tx = await lucid.fromTx(response.data.cborHex);
await signAndSentTx(lucid, tx);
await updateAccountBalance(sendRecipientAddress);
await updateAccountBalance(accountInfo.address);
changeAlertInfo({severity: 'success', message: 'Transaction sent successfully!', open: true,});
} catch (error) {
console.error('Send failed:', error);
}
};

const onSend = () => {
console.log('send tokens');
setAlertStatus(true);
const updateAccountBalance = async (address: string) => {
const newAccountBalance = await getWalletBalance(address);
const walletKey = (Object.keys(accounts) as (keyof Accounts)[]).find(
(key) => accounts[key].address === address
);
if (walletKey) {
changeWalletAccountDetails(walletKey, {
...accounts[walletKey],
balance: newAccountBalance,
});
}
};

const sendContent = <Box>
<WSTTextField
placeholder='0.0000'
value={mintTokens}
value={sendTokenAmount}
onChange={(e) => setMintTokens(Number(e.target.value))}
label="Number of Tokens to Send"
fullWidth={true}
/>
<WSTTextField
placeholder="address"
value={recipientAddress}
onChange={(e) => setRecipientAddress(e.target.value)}
value={sendRecipientAddress}
onChange={(e) => setsendRecipientAddress(e.target.value)}
label="Recipient’s Address"
fullWidth={true}
/>
</Box>;

const receiveContent = <Box>
<CopyTextField
value={getUserAccountDetails()?.address}
value={getUserAccountDetails()?.address ?? ''}
fullWidth={true}
label="Your Address"
/>
Expand Down Expand Up @@ -82,4 +145,4 @@ export default function Profile() {
</div>
</div>
);
}
}
21 changes: 11 additions & 10 deletions frontend/src/app/clientLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,26 @@ import NavDrawer from './components/NavDrawer';
//Local file
import { ThemeModeProvider } from "./styles/themeContext";
import "./styles/globals.css";
import { makeLucid, getWalletFromSeed, getWalletBalance } from "./utils/walletUtils";
import { makeLucid, getWalletFromSeed } from "./utils/walletUtils";
import useStore from './store/store';
import WSTAppBar from "./components/WSTAppBar";
import AlertBar from './components/AlertBar';

export default function ClientLayout({ children }: { children: React.ReactNode }) {
const { mintAccount, userA, userB, changeMintAccountDetails, changeWalletAAccountDetails, changeWalletBAccountDetails, setLucidInstance } = useStore();
const { mintAccount, accounts, changeMintAccountDetails, changeWalletAccountDetails, setLucidInstance } = useStore();

useEffect(() => {
const fetchUserWallets = async () => {
try {
// retrieve wallet info
const mintAuthorityWallet = await getWalletFromSeed(mintAccount.mnemonic);
const walletA = await getWalletFromSeed(userA.mnemonic);
const walletB = await getWalletFromSeed(userB.mnemonic);
const walletA = await getWalletFromSeed(accounts.userA.mnemonic);
const walletB = await getWalletFromSeed(accounts.userB.mnemonic);

const mintStartBalance = await getWalletBalance(mintAuthorityWallet.address);
// Update Zustand store with the initialized wallet information
changeMintAccountDetails({ ...mintAccount, address: mintAuthorityWallet.address, balance: mintStartBalance});
changeWalletAAccountDetails({ ...userA, address: walletA.address});
changeWalletBAccountDetails({ ...userB, address: walletB.address,});
changeMintAccountDetails({ ...mintAccount, address: mintAuthorityWallet.address});
changeWalletAccountDetails('userA', { ...accounts.userA, address: walletA.address},);
changeWalletAccountDetails('userB', { ...accounts.userB, address: walletB.address});

const initialLucid = await makeLucid();
setLucidInstance(initialLucid);
Expand All @@ -39,9 +39,9 @@ export default function ClientLayout({ children }: { children: React.ReactNode }
};

fetchUserWallets();
}, []);
},[]);

if(userB.address === '') {
if(accounts.userB.address === '') {
return <div className="mainLoadingContainer">
<div className="mainLoader" />
</div>;
Expand All @@ -54,6 +54,7 @@ export default function ClientLayout({ children }: { children: React.ReactNode }
<WSTAppBar />
<NavDrawer />
<div className="contentSection">{children}</div>
<AlertBar/>
</main>
</ThemeModeProvider>
</AppRouterCacheProvider>
Expand Down
17 changes: 6 additions & 11 deletions frontend/src/app/components/AlertBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,22 @@ import Alert from '@mui/material/Alert';
//Local components
import useStore from '../store/store';

interface AlertBarProps {
severity?: 'success' | 'error' | 'info' | 'warning';
message: string;
}

export default function AlertBar({severity = 'success', message}: AlertBarProps) {
const { alertOpen, setAlertStatus } = useStore();
export default function AlertBar() {
const { alertInfo, changeAlertInfo } = useStore();

const handleClose = () => {
setAlertStatus(false);
changeAlertInfo({ ...alertInfo, open: false });
};

return (
<Snackbar open={alertOpen} anchorOrigin={{vertical: 'top', horizontal: 'center'}} >
<Snackbar open={alertInfo.open} autoHideDuration={alertInfo.severity === 'success' ? 3000 : null} onClose={handleClose} anchorOrigin={{vertical: 'top', horizontal: 'center'}} >
<Alert
severity={severity}
severity={alertInfo.severity}
variant="filled"
sx={{ width: '100%' }}
onClose={handleClose}
>
{message}
{alertInfo.message}
</Alert>
</Snackbar>
);
Expand Down
9 changes: 4 additions & 5 deletions frontend/src/app/components/ProfileSwitcher.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

'use client'
//React Imports
import * as React from 'react';
Expand All @@ -18,9 +19,7 @@ import { selectLucidWallet, getWalletBalance } from '../utils/walletUtils';

export default function ProfileSwitcher() {
const [anchorEl, setAnchorEl] = React.useState<HTMLElement | null>(null);
const currentUser = useStore(state => state.currentUser);
const walletUser = useStore(state => state.walletUser);
const changeToLaceWallet = useStore(state => state.changeToLaceWallet);
const { currentUser, accounts, changeWalletAccountDetails } = useStore();
const lucid = useStore(state => state.lucid);
const changeUserAccount = useStore(state => state.changeUserAccount);
const router = useRouter();
Expand Down Expand Up @@ -64,8 +63,8 @@ export default function ProfileSwitcher() {
await selectLucidWallet(lucid, "Lace");
const userAddress = await lucid.wallet().address();
const userBalance = await getWalletBalance(userAddress);
changeToLaceWallet({
...walletUser,
changeWalletAccountDetails('walletUser', {
...accounts.walletUser,
address: userAddress,
balance: userBalance,
});
Expand Down
Loading

0 comments on commit a451fe7

Please sign in to comment.