Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Frontend for treasury transfer status #137

Merged
merged 14 commits into from
Sep 21, 2024
3 changes: 2 additions & 1 deletion frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { CONTRACT_ADDR } from "./lib/config";
import SubmitProposalModal from "./components/SubmitProposalModal";
import TreasuryStatus from "./components/TreasuryStatus";
import VotingPower from "./components/staking/VotingPower";
import StatusTransfer from './components/StatusTransfer'

function App() {
const [isModalOpen, setIsModalOpen] = React.useState(false);
Expand Down Expand Up @@ -70,7 +71,7 @@ function App() {
</div>

<TreasuryStatus />

<StatusTransfer />
{address && <VotingPower />}
</main>
);
Expand Down
41 changes: 41 additions & 0 deletions frontend/src/components/CancelTransferBtn.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from 'react';
import { useContractWrite } from "@starknet-react/core";
import { TREASURY_ADDRESS } from "../lib/config";
import { Contract } from "starknet";
import TreasuryABI from "../lib/treasury_abi.json";

interface CancelTransferButtonProps {
transferId: number;
}

const CancelTransferBtn: React.FC<CancelTransferButtonProps> = ({ transferId }) => {

const contract = new Contract(TreasuryABI, TREASURY_ADDRESS)

const { writeAsync, error } = useContractWrite({
calls: [
contract.populateTransaction["cancel_transfer"](transferId)
],
});

const handleCancelTransfer = async () => {
try {
await writeAsync();
console.log(`Transfer ${transferId} cancelled successfully.`);
} catch (err) {
console.error(`Error cancelling transfer ${transferId}:`, err);
console.error(`Error write ${transferId}:`, error);
}
};

return (
<button
type="button"
className='bg-blue-500 rounded-md text-white disabled:opacity-50 p-1 w-full'
onClick={handleCancelTransfer}>
Сancel
</button>
);
};

export default CancelTransferBtn;
122 changes: 122 additions & 0 deletions frontend/src/components/StatusTransfer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import React from "react";
import {
useAccount,
useContractRead, useNetwork
} from "@starknet-react/core";
import TreasuryABI from "../lib/treasury_abi.json";
import CancelTransferBtn from "./CancelTransferBtn";
import { formatBalance } from "../lib/erc20";
import { TREASURY_ADDRESS } from "../lib/config";

const StatusTransfer = () => {
const { address } = useAccount()
const { data, isLoading } = useContractRead({
functionName: 'get_live_transfers',
address: TREASURY_ADDRESS,
args: [],
abi: TreasuryABI,
watch: false,
retry: false
})

const renderCancelBtn = (status, transfer_id) =>
getTransferStatus(status) == 'PENDING' && address &&
<CancelTransferBtn transferId={transfer_id.toString()} />

const { chain: { network } } = useNetwork()

const handleAddress = address => {
const sub_domain = network === 'sepolia' ? 'sepolia.' : ''
const url = `https://${sub_domain}starkscan.co/contract/${getAddressToHex(address)}`
window.open(`${url}`, '_blank')
}

const renderData = () => {
if (Array.isArray(data)) {
if (data.length === 0) {
return <div className='p-2'>There are no transfers</div>;
}

return data.map((transfer_item, index) => {

return (
<div key={index} className="flex flex-wrap flex-row">
<div className='flex flex-wrap flex-row w-full'>
<div className="flex-1 w-1/2 basis-1/2 p-2 bg-slate-200 text-left text-gray-700 uppercase tracking-wider border-r border-slate-400">token</div>
<div className="flex-1 w-1/2 basis-1/2 p-2 bg-slate-200 text-left text-gray-700 uppercase tracking-wider">receiver</div>
<div className="flex-1 w-1/2 basis-1/2 p-2 overflow-hidden cursor-pointer" onClick={() => handleAddress(transfer_item.token_addr)}>{getFormatAddress(transfer_item.token_addr.toString())}</div>
<div className="flex-1 w-1/2 basis-1/2 p-2 overflow-hidden cursor-pointer" onClick={() => handleAddress(transfer_item.receiver)}>{getFormatAddress(transfer_item.receiver.toString())}</div>
</div>
<div className='flex flex-wrap flex-row w-full'>
<div className="flex-1 w-1/4 basis-1/4 p-2 bg-slate-200 text-left text-gray-700 uppercase tracking-wider">amount</div>
<div className="flex-1 w-1/4 basis-1/4 p-2 bg-slate-200 text-left text-gray-700 uppercase tracking-wider">cooldown_end</div>
<div className="flex-1 w-1/4 basis-1/4 p-2 bg-slate-200 text-left text-gray-700 uppercase tracking-wider">status</div>
<div className="flex-1 w-1/4 basis-1/4 p-2 bg-slate-200 text-left text-gray-700 uppercase tracking-wider">actions</div>
<div className="flex-1 w-1/4 basis-1/4 p-2 ">{formatBalance(transfer_item.amount.toString())}</div>
<div className="flex-1 w-1/4 basis-1/4 p-2 ">{getHoursLeft(transfer_item.cooldown_end.toString())}</div>
<div className="flex-1 w-1/4 basis-1/4 p-2 ">{getTransferStatus(transfer_item.status)}</div>
<div className="flex-1 w-1/4 basis-1/4 p-2 ">
{renderCancelBtn(transfer_item?.status, transfer_item?.id)}
</div>
</div>

</div>
)
})
} else {
return null
}
}

return (
<div>
<div className="flex w-full flex-grow pb-4 text-2xl font-bold">Transfer status</div>
{isLoading ? (
<div>Loading...</div>
) : (
<div className="w-[50rem] max-w-[50rem] grid items-center pl-0 rounded-lg bg-slate-200">
<div className="min-w-full bg-white border border-gray-200 rounded-lg">

<div>
{renderData()}
</div>
</div>
</div>
)}
</div>
)
}

const getHoursLeft = (target_date: number): string => {
const targetTimestamp = Number(target_date) * 1000;
const currentTimestamp = Date.now();

const timeLeft = targetTimestamp - currentTimestamp;
if (timeLeft > 0) {
const secondsLeft = Math.floor(timeLeft / 1000);
const minutesLeft = Math.floor(secondsLeft / 60);
const hoursLeft = Math.floor(minutesLeft / 60);
return `hours ${hoursLeft} left`;
} else {
return 'expired';
}
};

const getTransferStatus = status => {
if (status?.variant?.PENDING) {
return 'PENDING'
}
if (status?.variant?.CANCELLED) {
return 'CANCELLED'
}
if (status?.variant?.FINISHED) {
return 'FINISHED'
}

}

const getFormatAddress = address => address.slice(0, 12) + "..." + address.slice(-4)
const getAddressToHex = address => "0x" + address.toString(16)


export default StatusTransfer
3 changes: 2 additions & 1 deletion frontend/src/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ export const CONTRACT_ADDR =
export const VOTING_TOKEN_CONTRACT =
"0x4ff1af47bb9659aa83bbd33e13c25e8fb1b5ecf8359320251f03e1440e8890a";
export const FLOATING_TOKEN_CONTRACT = "0x31868056874ad7629055ddd00eb0931cb92167851702abf6b441cb8ea02d02b";
export const TREASURY_ADDRESS = "0x072c5c218a1ecfdd8ba10ef9b1b55af4994f2cad2210d14270ff9824fadabd70";
// export const TREASURY_ADDRESS = "0x072c5c218a1ecfdd8ba10ef9b1b55af4994f2cad2210d14270ff9824fadabd70";
export const TREASURY_ADDRESS = "0x039f350d8a6b3adb8ade1ec083284c64f6d3cbaaba3dc27cbfc8ee39ffebc302"

export const formatAddress = (addr: string) =>
addr.length === 66
Expand Down
Loading