Skip to content

Commit

Permalink
docs(example-cbdc): update frontend to match new functionality
Browse files Browse the repository at this point in the history
* Separated Transfer functionality into local blockchain transfers
and cross-chain transfers
* Added new table with the state of token approvals made to the bridge
* Highlight cross-chain functionality with red coloring
* add helper page with instructions on how to use app

Signed-off-by: André Augusto <andre.augusto@tecnico.ulisboa.pt>
  • Loading branch information
AndreAugusto11 committed Sep 16, 2024
1 parent c335e2c commit 270b9e5
Show file tree
Hide file tree
Showing 21 changed files with 694 additions and 232 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ class TokenERC20Contract extends Contract {
* @param {String} spender The spender who are able to transfer the tokens
* @returns {Number} Return the amount of remaining tokens allowed to spent
*/
@Transaction(false)
async Allowance(
ctx: Context,
owner: string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"axios": "1.6.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.2.1",
"react-scripts": "5.0.1",
"typescript": "5.5.2",
"uuid": "10.0.0",
Expand Down
16 changes: 10 additions & 6 deletions examples/cactus-example-cbdc-bridging-frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import React from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import HomePage from "./pages/HomePage";
import { CssBaseline } from "@mui/material";
import Helper from "./pages/Helper";
import "./App.css";

function App() {
return (
<>
<CssBaseline />
<HomePage />
</>
<BrowserRouter>
<Routes>
<Route path="/">
<Route index element={<HomePage />} />
<Route path="help" element={<Helper />} />
</Route>
</Routes>
</BrowserRouter>
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,36 @@ export async function authorizeNTokensBesu(
}
}

export async function fetchAmountApprovedToBridge(frontendUser: string) {
const response = await fetch("http://localhost:9999/wrapper-address");
const data = await response.json();
const wrapperAddress = data.address;

try {
const from = getEthAddress(frontendUser);
const response = await axios.post(
"http://localhost:4100/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-besu/invoke-contract",
{
contractName: BESU_CONTRACT_CBDC_ERC20_NAME,
invocationType: "CALL",
methodName: "allowance",
gas: 1000000,
params: [from, wrapperAddress],
signingCredential: {
ethAccount: from,
secret: getEthUserPrKey(frontendUser),
type: "PRIVATE_KEY_HEX",
},
keychainId: CryptoMaterial.keychains.keychain2.id,
},
);
return parseInt(response.data.callOutput);
} catch (error) {
// there is no allowance, so we will return 0
return 0;
}
}

export async function transferTokensBesu(
frontendUserFrom: string,
frontendUserTo: string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ export async function getFabricBalance(frontendUser: string) {
console.error(error.msg);
return -1;
}
console.log(response);

return parseInt(response.data.functionOutput);
}
Expand Down Expand Up @@ -219,6 +218,33 @@ export async function authorizeNTokensFabric(user: string, amount: string) {
);
}

export async function fetchAmountApprovedToBridge(frontendUser: string) {
const owner = getFabricId(frontendUser);
let response;

try {
response = await axios.post(
"http://localhost:4000/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-fabric/run-transaction",
{
contractName: FABRIC_CONTRACT_CBDC_ERC20_NAME,
channelName: FABRIC_CHANNEL_NAME,
params: [owner, CryptoMaterial.accounts.bridge.fabricID],
methodName: "Allowance",
invocationType: "FabricContractInvocationType.CALL",
signingCredential: {
keychainId: CryptoMaterial.keychains.keychain1.id,
keychainRef: getUserFromPseudonim(frontendUser),
},
},
);
} catch (error) {
// there is no allowance, so we will return 0
return 0;
}

return parseInt(response.data.functionOutput);
}

export function getUserFromFabricId(fabricID: string): string {
switch (fabricID) {
case CryptoMaterial.accounts["userA"].fabricID:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,14 @@ export async function bridgeTokens(
sender: string,
recipient: string,
sourceChain: string,
destinyChain: string,
originAmount: number,
destinyAmount: number,
destinationChain: string,
amount: number,
) {
let senderAddress;
let receiverAddress;
let port;
let sourceAsset;
let destinyAsset;
let destinationAsset;
//only way we found to pass contract address from backend to frontend at each run of tests
const response = await fetch("http://localhost:9999/contract-address");
const data = await response.json();
Expand All @@ -110,14 +109,17 @@ export async function bridgeTokens(
port = "4110";
}

if (destinyChain === "Fabric") {
if (destinationChain === "Fabric") {
toDLTNetworkID = "FabricSATPGateway";
receiverAddress = getFabricId(recipient);
destinyAsset = setFabricAsset(receiverAddress as string);
destinationAsset = setFabricAsset(receiverAddress as string);
} else {
toDLTNetworkID = "BesuSATPGateway";
receiverAddress = getEthAddress(recipient);
destinyAsset = setBesuAsset(receiverAddress as string, besuContractAddress);
destinationAsset = setBesuAsset(
receiverAddress as string,
besuContractAddress,
);
}
try {
await axios.post(
Expand All @@ -126,13 +128,13 @@ export async function bridgeTokens(
contextID: "MockID",
fromDLTNetworkID,
toDLTNetworkID,
fromAmount: originAmount,
toAmount: destinyAmount,
fromAmount: amount,
toAmount: amount,
receiver: receiverAddress,
originatorPubkey: senderAddress,
beneficiaryPubkey: receiverAddress,
sourceAsset,
destinyAsset,
destinyAsset: destinationAsset,
},
);
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,56 +1,61 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { useState, useEffect } from "react";
import { styled } from "@mui/material/styles";
import Button, { ButtonProps } from "@mui/material/Button";
import Paper from "@mui/material/Paper";
import Grid from "@mui/material/Grid";
import CircularProgress from "@mui/material/CircularProgress";
import MintDialog from "./dialogs/MintDialog";
import CrossChainTransferDialog from "./dialogs/CrossChainTransferDialog";
import TransferDialog from "./dialogs/TransferDialog";
import PermissionDialog from "./dialogs/PermissionDialog";
import { getFabricBalance } from "../api-calls/fabric-api";
import { getBesuBalance } from "../api-calls/besu-api";
import { SessionReference } from "../models/SessionReference";
import { NormalButton } from "./buttons/NormalButton";
import { CriticalButton } from "./buttons/CriticalButton";

const NormalButton = styled(Button)<ButtonProps>(({ theme }) => ({
margin: "auto",
width: "100%",
fontSize: "13px",
textTransform: "none",
background: "#2B9BF6",
color: "#FFFFFF",
border: "0.5px solid #000000",
"&:disabled": {
border: "0",
},
}));
// const NormalButton = styled(Button)<ButtonProps>(({ theme }) => ({
// margin: "auto",
// width: "100%",
// fontSize: "13px",
// textTransform: "none",
// background: "#2B9BF6",
// color: "#FFFFFF",
// border: "0.5px solid #000000",
// "&:disabled": {
// border: "0",
// },
// }));

const CriticalButton = styled(Button)<ButtonProps>(({ theme }) => ({
margin: "auto",
width: "100%",
fontSize: "13px",
textTransform: "none",
background: "#FF584B",
color: "#FFFFFF",
border: "0.5px solid #000000",
"&:hover": {
backgroundColor: "#444444",
color: "#FFFFFF",
},
"&:disabled": {
border: "0",
},
}));
// const CriticalButton = styled(Button)<ButtonProps>(({ theme }) => ({
// margin: "auto",
// width: "100%",
// fontSize: "13px",
// textTransform: "none",
// background: "#FF584B",
// color: "#FFFFFF",
// border: "0.5px solid #000000",
// "&:hover": {
// backgroundColor: "#444444",
// color: "#FFFFFF",
// },
// "&:disabled": {
// border: "0",
// },
// }));

export interface IActionsContainerOptions {
user: string;
ledger: string;
sessionRefs: Array<SessionReference>;
tokensApproved: number;
}

export default function ActionsContainer(props: IActionsContainerOptions) {
const [amount, setAmount] = useState(0);
const [mintDialog, setMintDialog] = useState(false);
const [transferDialog, setTransferDialog] = useState(false);
const [crossChainTransferDialog, setCrossChainTransferDialog] =
useState(false);
const [permissionDialog, setGivePermissionDialog] = useState(false);
const [loading, setLoading] = useState(false);

Expand All @@ -72,7 +77,13 @@ export default function ActionsContainer(props: IActionsContainerOptions) {
}, [props.user, props.ledger]);

return (
<div>
<Paper
elevation={0}
sx={{
background: "#EAEAEA",
padding: "0.5rem 1.1rem 1.1rem 1.1rem",
}}
>
{loading ? (
<center>
<CircularProgress
Expand Down Expand Up @@ -141,17 +152,27 @@ export default function ActionsContainer(props: IActionsContainerOptions) {
</Grid>
)}
{props.user !== "Bridge" && (
<Grid item xs={12} lg={12}>
<NormalButton
<Grid item xs={12} lg={6}>
<CriticalButton
variant="contained"
disabled={amount === 0}
onClick={() => setGivePermissionDialog(true)}
>
Give Permission
</NormalButton>
Approval
</CriticalButton>
</Grid>
)}
{props.user !== "Bridge" && (
<Grid item xs={12} lg={6}>
<CriticalButton
variant="contained"
disabled={amount === 0 || props.tokensApproved == 0}
onClick={() => setCrossChainTransferDialog(true)}
>
Bridge
</CriticalButton>
</Grid>
)}
{props.user !== "Bridge" && <Grid item xs={12} lg={6}></Grid>}
{props.ledger === "Fabric" && props.user !== "Bridge" && (
<Grid item xs={12} lg={6}></Grid>
)}
Expand All @@ -170,14 +191,23 @@ export default function ActionsContainer(props: IActionsContainerOptions) {
open={transferDialog}
user={props.user}
ledger={props.ledger}
balance={amount}
onClose={() => setTransferDialog(false)}
/>
<CrossChainTransferDialog
open={crossChainTransferDialog}
user={props.user}
ledger={props.ledger}
tokensApproved={props.tokensApproved}
onClose={() => setCrossChainTransferDialog(false)}
/>
<PermissionDialog
open={permissionDialog}
user={props.user}
ledger={props.ledger}
balance={amount}
onClose={() => setGivePermissionDialog(false)}
/>
</div>
</Paper>
);
}
Loading

0 comments on commit 270b9e5

Please sign in to comment.