Skip to content

Commit

Permalink
feat(admin): add submit-proofs log
Browse files Browse the repository at this point in the history
  • Loading branch information
tedtribe committed Dec 7, 2023
1 parent 156f708 commit a26af52
Show file tree
Hide file tree
Showing 10 changed files with 387 additions and 95 deletions.
105 changes: 105 additions & 0 deletions api/services/admin-transfer/handle-submit-proofs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { AddSubmitProofsLogRequestPayload } from "api/types/admin/transfer/add-log";
import dbConnect from "lib/mongodb";
import { NextApiRequest, NextApiResponse } from "next";
import { verifySignature } from "utils/api/verify-signature";
import TransferModel from "models/transfer";
import Web3 from "web3";
import relayAbi from "@contexts/Transfer/relay-abi";
import { RELAY_CONTRACT_ADDRESS } from "@constants";
import {
COMMON_STATUS,
ITransferLog,
SYS_TO_ETH_TRANSFER_STATUS,
} from "@contexts/Transfer/types";

export const handleSubmitProofs = async (
transferId: string,
payload: AddSubmitProofsLogRequestPayload,
req: NextApiRequest,
res: NextApiResponse
) => {
await dbConnect();
const web3 = new Web3("https://rpc.syscoin.org");

const { address } = req.session.user!;

const transfer = await TransferModel.findOne({ id: transferId });
if (!transfer) {
return res.status(404).json({ message: "Transfer not found" });
}

const { clearAll, signedMessage, txHash, operation } = payload;

const data = {
operation,
txHash,
clearAll,
};
const message = JSON.stringify(data);
if (!verifySignature(message, signedMessage, address)) {
return res.status(401).json({ message: "Unauthorized" });
}

const receipt = await web3.eth.getTransactionReceipt(txHash);

if (!receipt) {
return res.status(400).json({
message: "Invalid transaction hash: Transaction not found",
});
}

if (
!receipt.to ||
receipt.to.toLowerCase() !== RELAY_CONTRACT_ADDRESS.toLowerCase()
) {
return res.status(400).json({
message: "Invalid transaction: To is not the relay contract address",
});
}

if (receipt.logs.length === 0) {
return res.status(400).json({
message: "Invalid transaction: No logs found",
});
}

if (clearAll) {
transfer.logs = transfer.logs.filter(
(log) =>
!(
log.status === SYS_TO_ETH_TRANSFER_STATUS.SUBMIT_PROOFS ||
log.status === COMMON_STATUS.FINALIZING
)
);
}

const burnSysxLog: ITransferLog = {
payload: {
data: {
hash: receipt.transactionHash,
},
message: "submit-proofs",
previousStatus: SYS_TO_ETH_TRANSFER_STATUS.GENERATE_PROOFS,
},
status: SYS_TO_ETH_TRANSFER_STATUS.SUBMIT_PROOFS,
date: Date.now(),
};

transfer.logs.push(burnSysxLog);

const confirmLog: ITransferLog = {
status: COMMON_STATUS.FINALIZING,
payload: {
data: receipt,
message: "Confirm NEVM Transaction",
previousStatus: SYS_TO_ETH_TRANSFER_STATUS.SUBMIT_PROOFS,
},
date: Date.now(),
};

transfer.logs.push(confirmLog);

await transfer.save();

res.status(200).json({ success: true });
};
23 changes: 20 additions & 3 deletions api/types/admin/transfer/add-log.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
type BaseUtxoTransaction = {
txId: string;
type BaseLog = {
clearAll: boolean;
signedMessage: string;
};

type BaseUtxoTransaction = {
txId: string;
} & BaseLog;

type BaseEVMTransaction = {
txHash: string;
} & BaseLog;

export type AddBurnSysLogRequestPayload = {
operation: "burn-sys";
} & BaseUtxoTransaction;
Expand All @@ -12,6 +19,16 @@ export type AddBurnSysxLogRequestPayload = {
operation: "burn-sysx";
} & BaseUtxoTransaction;

export type AddLogRequestPayload =
export type AddSubmitProofsLogRequestPayload = {
operation: "submit-proofs";
} & BaseEVMTransaction;

export type AddUTXOLogRequestPayload =
| AddBurnSysLogRequestPayload
| AddBurnSysxLogRequestPayload;

export type AddNEVMLogRequestPayload = AddSubmitProofsLogRequestPayload;

export type AddLogRequestPayload =
| AddUTXOLogRequestPayload
| AddNEVMLogRequestPayload;
3 changes: 3 additions & 0 deletions components/Admin/Transfer/AddLog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ const AddLogMenu: React.FC<Props> = ({ onSelect }) => {
<MenuItem onClick={() => handleClose("burn-sysx")}>
Add Burn Sysx Transaction
</MenuItem>
<MenuItem onClick={() => handleClose("submit-proofs")}>
Add Submit Proofs Transaction
</MenuItem>
</Menu>
</Box>
);
Expand Down
133 changes: 133 additions & 0 deletions components/Admin/Transfer/AddLogModals/AddNEVMTransactionModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import {
Box,
Button,
FormControlLabel,
TextField,
Typography,
Checkbox,
Alert,
} from "@mui/material";
import AddLogModalContainer from "./ModalContainer";
import { useForm } from "react-hook-form";
import { useUtxoTransaction } from "components/Bridge/v3/hooks/useUtxoTransaction";
import { useNEVM } from "@contexts/ConnectedWallet/NEVMProvider";
import {
AddNEVMLogRequestPayload,
AddUTXOLogRequestPayload,
} from "api/types/admin/transfer/add-log";
import { useState } from "react";
import { useNevmTransaction } from "components/Bridge/v3/hooks/useNevmTransaction";

type Props = {
onClose: (refetch?: boolean) => void;
transferId: string;
operation: AddNEVMLogRequestPayload["operation"];
};

type FormValues = {
txHash: string;
clearAll: boolean;
};

const AddNEVMTransactionModal: React.FC<Props> = ({
onClose,
transferId,
operation,
}) => {
const { handleSubmit, register, watch } = useForm<FormValues>({
defaultValues: {
txHash: "",
clearAll: true,
},
});

const [submitError, setSubmitError] = useState<string>();

const { signMessage } = useNEVM();

const txHash = watch("txHash");

const { isFetching, isFetched, isSuccess, data, isError } =
useNevmTransaction(txHash, { refetch: false });

const isValidTx = isFetched;

let helperText =
isFetched && !isValidTx ? `Not a valid ${operation} transaction` : "";

if (isError) {
helperText = "Error fetching transaction";
}

const onSubmit = (values: FormValues) => {
const data = {
operation,
...values,
};
const message = `0x${Buffer.from(JSON.stringify(data), "utf8").toString(
"hex"
)}`;
signMessage(message)
.then((signedMessage) => {
const payload: AddNEVMLogRequestPayload = {
operation,
clearAll: values.clearAll,
txHash: values.txHash,
signedMessage,
};
setSubmitError(undefined);
return fetch(`/api/admin/transfer/${transferId}/add-log`, {
body: JSON.stringify(payload),
headers: {
"Content-Type": "application/json",
},
method: "POST",
});
})
.then((res) => {
if (res.ok) {
onClose(true);
return;
}
return res.json().then(({ message }) => setSubmitError(message));
});
};

return (
<AddLogModalContainer component="form" onSubmit={handleSubmit(onSubmit)}>
<Typography variant="body1" sx={{ textTransform: "capitalize" }}>
Add {operation.split("_")} Transaction Log
</Typography>
<Box sx={{ my: 2 }} width="100%">
<TextField
label="Transaction Hash"
fullWidth
sx={{ mb: 2 }}
{...register("txHash")}
error={Boolean(helperText)}
helperText={helperText}
/>
<FormControlLabel
control={<Checkbox defaultChecked {...register("clearAll")} />}
label={`Clear all other ${operation} logs`}
/>
</Box>
{submitError && <Alert severity="error">{submitError}</Alert>}
<Box display="flex">
<Button color="secondary" onClick={() => onClose()}>
Cancel
</Button>
<Button
variant="contained"
sx={{ ml: "auto" }}
type="submit"
disabled={!(isSuccess && isValidTx) || isFetching}
>
{isFetching ? "Checking" : "Add"}
</Button>
</Box>
</AddLogModalContainer>
);
};

export default AddNEVMTransactionModal;
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import AddNEVMTransactionModal from "./AddNEVMTransactionModal";

type Props = {
onClose: (refetch?: boolean) => void;
transferId: string;
};

const AddSubmitProofsTransaction: React.FC<Props> = ({
onClose,
transferId,
}) => {
return (
<AddNEVMTransactionModal
onClose={onClose}
operation="submit-proofs"
transferId={transferId}
/>
);
};

export default AddSubmitProofsTransaction;
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ import AddLogModalContainer from "./ModalContainer";
import { useForm } from "react-hook-form";
import { useUtxoTransaction } from "components/Bridge/v3/hooks/useUtxoTransaction";
import { useNEVM } from "@contexts/ConnectedWallet/NEVMProvider";
import { AddLogRequestPayload } from "api/types/admin/transfer/add-log";
import { AddUTXOLogRequestPayload } from "api/types/admin/transfer/add-log";
import { useState } from "react";

type Props = {
onClose: (refetch?: boolean) => void;
transferId: string;
tokenType: string;
operation: AddLogRequestPayload["operation"];
operation: AddUTXOLogRequestPayload["operation"];
};

type FormValues = {
Expand Down Expand Up @@ -67,7 +67,7 @@ const AddUTXOTransactionModal: React.FC<Props> = ({
)}`;
signMessage(message)
.then((signedMessage) => {
const payload: AddLogRequestPayload = {
const payload: AddUTXOLogRequestPayload = {
operation,
clearAll: values.clearAll,
txId: values.txId,
Expand Down
2 changes: 1 addition & 1 deletion components/Bridge/v3/Steps/ConfirmNEVMTransaction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const BridgeV3ConfirmNEVMTransaction: React.FC<Props> = ({

const sourceTxHash = sourceLog?.payload.data.hash;

const { data, isFetched } = useNevmTransaction(sourceTxHash);
const { data, isFetched } = useNevmTransaction(sourceTxHash, { refetch: true });

useEffect(() => {
if (!isFetched || !data) {
Expand Down
15 changes: 13 additions & 2 deletions components/Bridge/v3/hooks/useNevmTransaction.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import { useQuery } from "react-query";
import { useWeb3 } from "../context/Web";

export const useNevmTransaction = (transactionHash?: string) => {
type Options = {
refetch?: boolean;
};

export const useNevmTransaction = (
transactionHash?: string,
options: Options = {}
) => {
const web3 = useWeb3();
return useQuery(["nevm", "transaction", transactionHash], {
queryFn: async () => {
Expand All @@ -12,7 +19,11 @@ export const useNevmTransaction = (transactionHash?: string) => {
}
throw new Error("Transaction not confirmed");
},
refetchInterval: 1000,
refetchInterval: options.refetch ? 1000 : undefined,
refetchOnMount: options.refetch,
refetchOnReconnect: options.refetch,
refetchOnWindowFocus: options.refetch,
refetchIntervalInBackground: options.refetch,
retry: true,
enabled: Boolean(transactionHash),
});
Expand Down
Loading

0 comments on commit a26af52

Please sign in to comment.