Skip to content

Commit

Permalink
Frontend for treasury transfer status (#137)
Browse files Browse the repository at this point in the history
* cancel transfer component

* mock data for status transfer

* added status transfer

* new treasury_abi

* render live transfers

* logical cancel transfer

* Fix 'There are no transfers' bug

* fix condition

* fix

* deleted unused file

* added handler click token_addr or  receiver

* Format

* Remove refetch

---------

Co-authored-by: Ondřej Sojka <[email protected]>
  • Loading branch information
Oleh-Zorin and tensojka authored Sep 21, 2024
1 parent b6317b9 commit 47ed5b0
Show file tree
Hide file tree
Showing 5 changed files with 1,059 additions and 2 deletions.
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

0 comments on commit 47ed5b0

Please sign in to comment.