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

cancelling, formatting form token registry and more #7

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion frontend/.env
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
NEXT_PUBLIC_BLOCKFROST_URL="https://cardano-preprod.blockfrost.io/api/v0"
NEXT_PUBLIC_BLOCKFROST_API_KEY="preprod39RvncWPPCXSZxldJwsO33qAvez1v3mB"
NEXT_PUBLIC_BLOCKFROST_API_KEY="preprod1ua0Gr9lDz3zClFAFDUP7MXdbOo6Rkf4"
NEXT_PUBLIC_BLOCKFROST_NETWORK="Preprod"
9 changes: 7 additions & 2 deletions frontend/components/charts/stacked-bar-chart.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useEffect, useRef } from 'react';
import * as d3 from 'd3';
import { tokenNameFromHex } from '@/utils/utils';

export type VestedAmount = {
tokenName: string;
Expand Down Expand Up @@ -42,6 +43,8 @@ const OrganizationVestingChart: React.FC<Props> = ({ data }) => {
useEffect(() => {
if (svgRef.current) {
const svg = d3.select(svgRef.current);
svg
.attr("class", "text-primary") // Add this line

// Dimensions
const width = 800;
Expand Down Expand Up @@ -98,7 +101,7 @@ const OrganizationVestingChart: React.FC<Props> = ({ data }) => {
// Display totals in the top-right corner
const legend = svg.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("font-size", 13)
.attr("text-anchor", "start")
.selectAll("g")
.data(tokenTotals)
Expand All @@ -112,9 +115,11 @@ const OrganizationVestingChart: React.FC<Props> = ({ data }) => {
.attr("fill", d => colorScale(d.tokenName));

legend.append("text")
.attr("class", " fill-primary") // Add this line
// .style("fill", "white")
.attr("x", 20)
.attr("y", 10)
.text(d => `${d.tokenName}: ${d.total}`);
.text(d => `${tokenNameFromHex(d.tokenName.slice(56))}: ${d.total}`);
}

}, [data]);
Expand Down
1 change: 1 addition & 0 deletions frontend/components/charts/timeline-chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ const TokenUnlockChart: React.FC<TokenUnlockChartProps> = ({ data }) => {
.attr("x", width - 24)
.attr("y", 9.5)
.attr("dy", "0.32em")
.attr("class", " fill-primary") // Add this line
.text(d => d);
}
}, [data]);
Expand Down
6 changes: 3 additions & 3 deletions frontend/components/charts/token-pie-chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const PieChart: React.FC<PieChartProps> = ({ data }) => {
.data(color.domain())
.enter().append("g")
// .attr("class", "legend")
.attr("class", "text-primary-foreground") // Add this line

.attr("transform", (d, i) => `translate(0,${i * 20})`);

Expand All @@ -66,9 +67,8 @@ const PieChart: React.FC<PieChartProps> = ({ data }) => {
.attr("x", width - 65)
.attr("y", 25)
.attr("dy", ".35em")
.style("text-anchor", "start")
.classed("text-xs text-accent font-medium", true)

//.style("text-anchor", "start")
.attr("class", "fill-primary") // Add this line
.text(d => d);

}, [data]);
Expand Down
155 changes: 112 additions & 43 deletions frontend/components/dashboard/org-dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ import Overview from "./Overview"
import { useContext, useEffect, useState } from "react"
import { UserContext } from "@/pages/_app"
import { useQuery } from "@tanstack/react-query"
import { calculateWithdrawablePeriods, getOrgDatumsAndAmount, getOrgStats, getUserAddressesAndPkhs, getUtxosForAddresses, tokenNameFromHex } from "@/utils/utils"
import { DatumsAndAmounts, calculateWithdrawablePeriods, formatFromTokenRegistry, getOrgDatumsAndAmount, getOrgStats, getTokenHoldersAndPkhs, getUserAddressesAndPkhs, getUtxosForAddresses, tokenNameFromHex } from "@/utils/utils"
import { useRouter } from "next/router"
import { BeaconBeaconToken, VestingVesting } from "@/validators/plutus"
import { Data, toHex, UTxO } from "lucid-cardano"
import { Constr, Data, toHex, UTxO } from "lucid-cardano"
import BeneficiariesList from "../org/beneficiaries-list"
import TokenUnlockChart, { TokenUnlock } from "../charts/timeline-chart"
import StackedBarChart, { OrganizationVesting } from "../charts/stacked-bar-chart"
Expand All @@ -34,6 +34,7 @@ import {
SelectValue,
} from "@/components/ui/select"
import Link from "next/link"
import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from "../ui/table"


export const metadata: Metadata = {
Expand Down Expand Up @@ -115,30 +116,9 @@ const stackedBarChartData: OrganizationVesting = {
],
};

/* const beneficiariesData = [
{
address: '0x1234567890abcdef1234567890abcdef12345678',
amount: 1200,
},
{
address: '0xabcdef1234567890abcdef1234567890abcdef12',
amount: 800,
},
{
address: '0x7890abcdef1234567890abcdef1234567890abcd',
amount: 500,
},
{
address: '0x567890abcdef1234567890abcdef1234567890ab',
amount: 2500,
},
{
address: '0x234567890abcdef1234567890abcdef1234567890',
amount: 1000,
},
]; */

export default function OrgDashboard() {
const [unlockData, setUnlockData] = useState<TokenUnlock[]>([])
const [stackedBarChartData, setStackedBarChartData] = useState<OrganizationVesting>({} as OrganizationVesting)
const [tokenList, setTokenList] = useState<string[]>([])
const [beneficiariesData, setBeneficiariesData] = useState<{ address: string, amount: number }[]>([])
Expand All @@ -162,8 +142,8 @@ export default function OrgDashboard() {
console.log('orgStats rejected. reason:', orgStats.reason)
return null
}
setTokenList(orgDatums.value.map(datum => datum.datum.tokenPolicyId + datum.datum.tokenName))
return { orgDatums: orgDatums.value, orgStats: orgStats.value }
setTokenList(Array.from(new Set(orgDatums.value.map(datum => datum.datum.tokenPolicyId + datum.datum.tokenName))))
return { orgDatums: orgDatums.value as DatumsAndAmounts, orgStats: orgStats.value }
}
return null
})
Expand All @@ -174,30 +154,92 @@ export default function OrgDashboard() {
}
}, [lucid])

const onTokenSelect = (tokenName: string) => {

const createUnlockData = (data: DatumsAndAmounts) => {
const unlockData: TokenUnlock[] = []
for (let datum of data) {
const d = datum.datum
const start = new Date(Number(d.date))
const end = new Date(Number(d.date) + Number(d.periodLength) * Number(d.numPeriods))
for (let i = 0; i < Number(d.numPeriods); i++) {
unlockData.push({
date: new Date(start.getTime() + i * Number(d.periodLength)),
amount: Number(d.amountPerPeriod),
tokenName: tokenNameFromHex(d.tokenName)
})
}
}
return unlockData

}

const onCancel = async (d: any) => {
const datum = Data.to(d, VestingVesting.datum)
const myAddress = "addr_test1qrsaj9wppjzqq9aa8yyg4qjs0vn32zjr36ysw7zzy9y3xztl9fadz30naflhmq653up3tkz275gh5npdejwjj23l0rdquxfsdj"
const myAddressDetails = lucid?.utils.getAddressDetails(myAddress)
const stakeCredential = myAddressDetails?.stakeCredential
const validatorAddress = lucid!.utils.validatorToAddress(vestingValidator, stakeCredential)
const orgToken = d.orgToken + toHex(Buffer.from("orgToken", "utf8"))
const beaconToken = d.beaconToken + d.orgToken
const holders = await getTokenHoldersAndPkhs(lucid!, orgToken)
console.log({ holders })
console.log({ datum })
const utxo = (await lucid?.utxosAtWithUnit(validatorAddress, beaconToken))?.filter(u => u.datum === datum)
console.log({ utxo })
const redeemer = Data.to(new Constr(1, []))
let tx = lucid?.newTx()
.collectFrom(utxo as UTxO[], redeemer)
.attachSpendingValidator(vestingValidator)

for (let holder of holders) {
tx = tx?.payToAddress(holder.address, { [orgToken]: BigInt(1) })
.addSigner(holder.address)
console.log({ holder })
}
const txComplete = await tx?.complete()
const txHex = toHex(txComplete!.txComplete.to_bytes())
router.push(`https://roundtable.adaodapp.xyz/hex/${txHex}`)
}

const onTokenSelect = async (tokenName: string) => {
console.log(tokenName)
setBeneficiariesData(data?.orgDatums.filter(datum => datum.datum.tokenPolicyId + datum.datum.tokenName === tokenName).map(datum => {
return {
const benefData = []
for (let datum of data?.orgDatums.filter(datum => datum.datum.tokenPolicyId + datum.datum.tokenName === tokenName) || []) {
const unit = datum.datum.tokenPolicyId + datum.datum.tokenName
const amount = BigInt(datum.datum.amountPerPeriod) * BigInt(calculateWithdrawablePeriods(datum.datum.periodLength, datum.datum.date))
const formattedData = await formatFromTokenRegistry(unit, amount)
const formatedAmount = formattedData.quantity
const d = {
address: datum.datum.beneficiary,
amount: Number(datum.datum.amountPerPeriod) * calculateWithdrawablePeriods(datum.datum.periodLength, datum.datum.date)
amount: Number(formatedAmount)
}
benefData.push(d)
}
setBeneficiariesData(benefData)

const barChartBeneficiaries = []

for (let beneficiary of Object.keys(data?.orgStats.beneficiaries || [])) {
const vestedAmounts = []
for (let tokenName of Object.keys(data?.orgStats.beneficiaries[beneficiary] || [])) {
const formattedData = await formatFromTokenRegistry(tokenName, BigInt(data?.orgStats.beneficiaries[beneficiary][tokenName].totalVested || 0))
const formattedAmount = formattedData.quantity
vestedAmounts.push({
tokenName,
amount: Number(formattedAmount) || 0
})
}
}) || [])
barChartBeneficiaries.push({
beneficiaryName: beneficiary,
vestedAmounts
})

}
setStackedBarChartData({
orgName: orgPolicy as string,
beneficiaries: Object.keys(data?.orgStats.beneficiaries || []).map(beneficiary => {
return {
beneficiaryName: beneficiary,
vestedAmounts: Object.keys(data?.orgStats.beneficiaries[beneficiary] || []).map(tokenName => {
return {
tokenName,
amount: Number(data?.orgStats.beneficiaries[beneficiary][tokenName].totalVested) || 0
}
}).filter(vestedAmount => vestedAmount.tokenName === tokenName)
}
})
beneficiaries: barChartBeneficiaries
})

setUnlockData(createUnlockData(data!.orgDatums))
}

return (
Expand Down Expand Up @@ -352,6 +394,33 @@ export default function OrgDashboard() {
<StackedBarChart data={stackedBarChartData} />
</CardContent>
</Card>}

{data?.orgDatums && <Card className="col-span-4">
<CardHeader>
<CardTitle>UTxOs</CardTitle>
</CardHeader>
<CardContent className="pl-2 justify-center flex flex-col space-y-2">
<div>
{data?.orgDatums.map((item, index) => (
<Card key={index} className="m-4">
<CardHeader>
<CardTitle>Datum {index + 1}</CardTitle>
<CardDescription>Token Amount: {item.tokenAmount.toString()}</CardDescription>
</CardHeader>
<CardContent>
{Object.entries(item.datum).map(([key, value], i) => (
<div key={i} className="flex justify-between">
<span className="font-medium">{key}:</span>
<span>{value.toString()}</span>
</div>
))}
<Button onClick={() => onCancel(item.datum)} variant={"default"}>Cancel</Button>
</CardContent>
</Card>
))}
</div>
</CardContent>
</Card>}
</div>
</div>
</div>
Expand Down
31 changes: 18 additions & 13 deletions frontend/components/form/create-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { BeneficiarySchedule } from "./schedule-form"
import { Card, CardContent, CardTitle } from "../ui/card"
import { useContext, useEffect, useState } from "react"
import { UserContext } from "@/pages/_app"
import { evenDigits, getAssetsFromStakeAddress } from "@/utils/utils"
import { applyTokenDecimals, evenDigits, getAssetsFromStakeAddress } from "@/utils/utils"
import { Constr, Data, SpendingValidator, fromHex, toHex } from "lucid-cardano"
import { BeaconBeaconToken, VestingVesting } from "@/validators/plutus"
import { useRouter } from "next/router"
Expand Down Expand Up @@ -58,6 +58,7 @@ const vestingScheduleSchema = z.object(
}),
periodical: z.boolean(),
periods: z.number().min(1, { message: "Periods must be greater than 0" }).optional(),
tokensRequired: z.number().min(1, { message: "Required tokens must be greater than 0" }).optional(),
periodLength: z.number().min(0, { message: "Period length must be greater than 0" }).optional(),
amount: z.number().min(0.1, { message: "Amount must be greater than 0" })
})
Expand Down Expand Up @@ -97,6 +98,7 @@ const defaultValues: Partial<VestingFormValues> = {
periods: 1,
periodical: true,
periodLength: 0,
tokensRequired: 1,
token: ""
}
]
Expand Down Expand Up @@ -175,7 +177,7 @@ export function VestingForm() {
date: BigInt(schedule.freeDate.getTime()),
token_name: schedule.token.slice(56),
amount: schedule.amount,
tokens_required: 1, //org tokens required to unlock
tokens_required: schedule.tokensRequired || 1, //org tokens required to unlock
token_policy_id: schedule.token.slice(0, 56),
period_length: 0,
periods: 1
Expand All @@ -197,44 +199,46 @@ export function VestingForm() {
const stakeCredential = myAddressDetails?.stakeCredential
const contractAddress = lucid!.utils.validatorToAddress(validator, stakeCredential)
const validatorHash = lucid!.utils.validatorToScriptHash(validator)
const beaconPolicy = new BeaconBeaconToken(validatorHash, stakeCredential!.hash )
const beaconPolicy = new BeaconBeaconToken(validatorHash, stakeCredential!.hash)
const beaconPolicyId = lucid!.utils.mintingPolicyToId(beaconPolicy)
const mintRedeemer = Data.to(new Constr(0, []));
const formatted = formatValues(values)
const utxos = await lucid?.utxosAt(await lucid.wallet.address())
const utxo = utxos![0]
const beaconName = orgPolicy//Buffer.from(orgPolicy as string, "hex").toString("hex") //toHex(Buffer.from("caca", "utf8"))//toHex(Buffer.from(beaconPolicyId, "utf8"))
console.log({beaconName})
console.log({ beaconName })
console.log({ formatted })
let tx = lucid?.newTx()
.collectFrom([utxo])
.attachMintingPolicy(beaconPolicy)
.collectFrom([utxo])
.attachMintingPolicy(beaconPolicy)
for (let receiver of formatted) {
// const {amount, token, ...rest} = receiver // remove the "amount" property from the receiver object
const datum = Data.to(d, VestingVesting.datum)
const unit = receiver.token_policy_id + receiver.token_name
const tokenData = await applyTokenDecimals(unit, receiver.amount)
const formattedAmount = tokenData.quantity

const d: VestingVesting["datum"] = {
datumId: utxo.txHash+evenDigits(utxo.outputIndex),
datumId: utxo.txHash + evenDigits(utxo.outputIndex),
beneficiary: receiver.beneficiary,
date: BigInt(receiver.date),
tokensRequired: BigInt(receiver.tokens_required),
orgToken: orgPolicy as string,
beaconToken: beaconPolicyId,
numPeriods: BigInt(receiver.periods),
periodLength: BigInt(receiver.period_length),
amountPerPeriod: BigInt(receiver.amount),
amountPerPeriod: formattedAmount,
tokenPolicyId: receiver.token_policy_id,
tokenName: receiver.token_name,
}
/* console.log({ d })
const datumVals = Object.values(d)
console.log({datumVals})
const datum = Data.to(new Constr(0,datumVals)) */

const datum = Data.to(d, VestingVesting.datum)


tx = tx!.payToContract(contractAddress, { inline: datum }, { [receiver.token_policy_id+receiver.token_name]: BigInt(receiver.amount!*receiver.periods), [beaconPolicyId+orgPolicy]: BigInt(1) })
tx = tx!.payToContract(contractAddress, { inline: datum }, { [unit]: formattedAmount *BigInt(receiver.periods), [beaconPolicyId + orgPolicy]: BigInt(1) })
}
tx= tx?.mintAssets({[beaconPolicyId+orgPolicy]: BigInt(formatted.length)}, mintRedeemer)
tx = tx?.mintAssets({ [beaconPolicyId + orgPolicy]: BigInt(formatted.length) }, mintRedeemer)
console.log(await tx?.toString())
const txComplete = await tx?.complete()
const signedTx = await txComplete?.sign().complete()
Expand All @@ -256,6 +260,7 @@ export function VestingForm() {
</CardTitle>
</div>
<CardContent>

<FormField
control={form.control}
name={`items.${index}.beneficiary`}
Expand Down
Loading