Skip to content

Commit

Permalink
Merge pull request #35 from syscoin/admin-panel
Browse files Browse the repository at this point in the history
Admin panel
  • Loading branch information
osiastedian authored Dec 11, 2023
2 parents f19d45b + 5e4edf0 commit 4aa764a
Show file tree
Hide file tree
Showing 72 changed files with 2,862 additions and 462 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/aws.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
push:
branches:
- main
- sponsor-wallet-implementation
- admin-panel

env:
AWS_REGION: us-east-1 # set this to your preferred AWS region, e.g. us-west-1
Expand Down
18 changes: 18 additions & 0 deletions api/services/admin-transfer/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { BlockbookAPIURL } from "@contexts/Transfer/constants";
import { utils as syscoinUtils } from "syscoinjs-lib";

export const CONFIRM_UTXO_TRANSACTION = "Confirm UTXO Transaction";
export const BURN_SYS_TOKEN_TYPE = "SPTSyscoinBurnToAssetAllocation";
export const BURN_SYSX_TOKEN_TYPE = "SPTAssetAllocationBurnToNEVM";

export const verifyTxTokenTransfer = async (
txId: string,
tokenType: string
) => {
const rawTransaction = await syscoinUtils.fetchBackendRawTx(
BlockbookAPIURL,
txId
);

return rawTransaction.tokenType === tokenType ? rawTransaction : null;
};
80 changes: 80 additions & 0 deletions api/services/admin-transfer/handle-burn-sys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { AddBurnSysLogRequestPayload } from "api/types/admin/transfer/add-log";
import dbConnect from "lib/mongodb";
import { NextApiRequest, NextApiResponse } from "next";
import TransferModel from "models/transfer";
import { BlockbookAPIURL } from "@contexts/Transfer/constants";
import { utils as syscoinUtils } from "syscoinjs-lib";
import {
ITransferLog,
SYS_TO_ETH_TRANSFER_STATUS,
} from "@contexts/Transfer/types";
import { verifySignature } from "utils/api/verify-signature";
import { CONFIRM_UTXO_TRANSACTION, verifyTxTokenTransfer } from "./constants";

export const handleBurnSys = async (
transferId: string,
payload: AddBurnSysLogRequestPayload,
req: NextApiRequest,
res: NextApiResponse
) => {
await dbConnect();
const { address } = req.session.user!;
const transfer = await TransferModel.findOne({ id: transferId });
if (!transfer) {
return res.status(404).json({ message: "Transfer not found" });
}
const { txId, clearAll, operation, signedMessage } = payload;
const data = {
operation,
txId,
clearAll,
};
const message = JSON.stringify(data);

if (!verifySignature(message, signedMessage, address)) {
return res.status(401).json({ message: "Unauthorized" });
}

const verifiedTransaction = await verifyTxTokenTransfer(
txId,
"SPTSyscoinBurnToAssetAllocation"
);

if (!verifiedTransaction) {
return res.status(400).json({ message: "Invalid transaction type" });
}

if (clearAll) {
transfer.logs = transfer.logs.filter((log) => !(log.status === "burn-sys"));
}

const burnSysLog: ITransferLog = {
status: SYS_TO_ETH_TRANSFER_STATUS.BURN_SYS,
payload: {
data: {
tx: txId,
},
message: "Burning SYS to SYSX",
previousStatus: SYS_TO_ETH_TRANSFER_STATUS.BURN_SYS,
},
date: Date.now(),
};

transfer.logs.push(burnSysLog);

const confirmLog: ITransferLog = {
status: SYS_TO_ETH_TRANSFER_STATUS.BURN_SYS,
payload: {
data: verifiedTransaction,
message: CONFIRM_UTXO_TRANSACTION,
previousStatus: SYS_TO_ETH_TRANSFER_STATUS.BURN_SYS,
},
date: Date.now(),
};

transfer.logs.push(confirmLog);

await transfer.save();

res.status(200).json({ success: true });
};
84 changes: 84 additions & 0 deletions api/services/admin-transfer/handle-burn-sysx.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { AddBurnSysxLogRequestPayload } from "api/types/admin/transfer/add-log";
import dbConnect from "lib/mongodb";
import { NextApiRequest, NextApiResponse } from "next";
import TransferModel from "models/transfer";
import {
ITransferLog,
SYS_TO_ETH_TRANSFER_STATUS,
} from "@contexts/Transfer/types";
import { verifySignature } from "utils/api/verify-signature";
import {
BURN_SYSX_TOKEN_TYPE,
CONFIRM_UTXO_TRANSACTION,
verifyTxTokenTransfer,
} from "./constants";

export const handleBurnSysx = async (
transferId: string,
payload: AddBurnSysxLogRequestPayload,
req: NextApiRequest,
res: NextApiResponse
) => {
await dbConnect();
const { address } = req.session.user!;
const transfer = await TransferModel.findOne({ id: transferId });
if (!transfer) {
return res.status(404).json({ message: "Transfer not found" });
}
const { txId, clearAll, operation, signedMessage } = payload;
const data = {
operation,
txId,
clearAll,
};
const message = JSON.stringify(data);

if (!verifySignature(message, signedMessage, address)) {
return res.status(401).json({ message: "Unauthorized" });
}

const verifiedTransaction = await verifyTxTokenTransfer(
txId,
BURN_SYSX_TOKEN_TYPE
);

if (!verifiedTransaction) {
return res.status(400).json({ message: "Invalid transaction type" });
}

if (clearAll) {
transfer.logs = transfer.logs.filter(
(log) => !(log.status === SYS_TO_ETH_TRANSFER_STATUS.BURN_SYSX)
);
}

const burnSysxLog: ITransferLog = {
status: SYS_TO_ETH_TRANSFER_STATUS.BURN_SYSX,
payload: {
data: {
tx: txId,
},
message: "Burning SYSX to NEVM",
previousStatus: SYS_TO_ETH_TRANSFER_STATUS.BURN_SYS,
},
date: Date.now(),
};

transfer.logs.push(burnSysxLog);

const confirmLog: ITransferLog = {
status: SYS_TO_ETH_TRANSFER_STATUS.BURN_SYSX,
payload: {
data: verifiedTransaction,
message: CONFIRM_UTXO_TRANSACTION,
previousStatus: SYS_TO_ETH_TRANSFER_STATUS.BURN_SYS,
},
date: Date.now(),
};

transfer.logs.push(confirmLog);

await transfer.save();

res.status(200).json({ success: true });
};
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 });
};
30 changes: 30 additions & 0 deletions api/services/admin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Admin, { IAdmin } from "../../models/admin";
export class AdminService {
public async isAdmin(address: string): Promise<boolean> {
const admin = await Admin.exists({ address }).exec();

return admin !== null;
}

public async getAdmin(address: string): Promise<IAdmin | null> {
const admin = await Admin.findOne({ address }).exec();
if (!admin) {
return null;
}

return admin;
}

public async createAdmin(address: string, name: string): Promise<IAdmin> {
// Check if address is already an admin
const isAdmin = await this.isAdmin(address);

if (isAdmin) {
throw new Error("Address is already an admin");
}

const admin = new Admin({ address, name });

return await admin.save();
}
}
12 changes: 12 additions & 0 deletions api/types/admin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ITransfer } from "@contexts/Transfer/types";

export type Change = {
property: keyof ITransfer;
from: any;
to: any;
};

export interface OverrideTransferRequestBody {
changes: Change[];
signedMessage: string;
}
34 changes: 34 additions & 0 deletions api/types/admin/transfer/add-log.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
type BaseLog = {
clearAll: boolean;
signedMessage: string;
};

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

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

export type AddBurnSysLogRequestPayload = {
operation: "burn-sys";
} & BaseUtxoTransaction;

export type AddBurnSysxLogRequestPayload = {
operation: "burn-sysx";
} & BaseUtxoTransaction;

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

export type AddUTXOLogRequestPayload =
| AddBurnSysLogRequestPayload
| AddBurnSysxLogRequestPayload;

export type AddNEVMLogRequestPayload = AddSubmitProofsLogRequestPayload;

export type AddLogRequestPayload =
| AddUTXOLogRequestPayload
| AddNEVMLogRequestPayload;
Loading

1 comment on commit 4aa764a

@vercel
Copy link

@vercel vercel bot commented on 4aa764a Dec 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

syscoin-bridge – ./

syscoin-bridge-git-main-syslabs.vercel.app
syscoin-bridge-syslabs.vercel.app
syscoin-bridge-fawn.vercel.app

Please sign in to comment.