From 70993180c2ef644d3bd172176e123b702f6d38e9 Mon Sep 17 00:00:00 2001 From: Patrick Bennett Date: Thu, 4 Apr 2024 00:49:34 -0400 Subject: [PATCH 01/14] feat(contracts): Add two more sets of validator/stake/rewards tests but checking validator commission of 0% and also 100% instead of the same 5% each time. --- contracts/__test__/contracts.test.ts | 371 ++++++++++++++++++++++++++- contracts/package.json | 2 +- 2 files changed, 369 insertions(+), 4 deletions(-) diff --git a/contracts/__test__/contracts.test.ts b/contracts/__test__/contracts.test.ts index b37726af..f1da985d 100644 --- a/contracts/__test__/contracts.test.ts +++ b/contracts/__test__/contracts.test.ts @@ -4,7 +4,6 @@ import { consoleLogger } from '@algorandfoundation/algokit-utils/types/logging'; import { AlgoAmount } from '@algorandfoundation/algokit-utils/types/amount'; import { Account, decodeAddress, encodeAddress, getApplicationAddress } from 'algosdk'; import { assetOptIn, transferAlgos, transferAsset } from '@algorandfoundation/algokit-utils'; -import * as constants from 'node:constants'; import { StakingPoolClient } from '../contracts/clients/StakingPoolClient'; import { ValidatorRegistryClient } from '../contracts/clients/ValidatorRegistryClient'; import { @@ -15,6 +14,8 @@ import { createAsset, createValidatorConfig, epochBalanceUpdate, + GATING_TYPE_ASSET_ID, + GATING_TYPE_ASSETS_CREATED_BY, gatingValueFromBigint, getCurMaxStatePerPool, getMbrAmountsFromValidatorClient, @@ -33,8 +34,6 @@ import { ValidatorConfig, ValidatorPoolKey, verifyRewardAmounts, - GATING_TYPE_ASSET_ID, - GATING_TYPE_ASSETS_CREATED_BY, } from './helpers'; const FEE_SINK_ADDR = 'Y76M3MSY6DKBRHBL7C3NNDXGS5IIMQVQVUAB6MP4XEMMGVF2QWNPL226CA'; @@ -1089,6 +1088,372 @@ describe('StakeWRewards', () => { }); }); +describe('StakeW0Commission', () => { + beforeEach(fixture.beforeEach); + beforeEach(logs.beforeEach); + afterEach(logs.afterEach); + + let validatorId: number; + let validatorOwnerAccount: Account; + const stakerAccounts: Account[] = []; + let poolAppId: bigint; + let firstPoolKey: ValidatorPoolKey; + let firstPoolClient: StakingPoolClient; + + const PctToValidator = 0; + + // add validator and 1 pool for subsequent stake tests + beforeAll(async () => { + // Fund a 'validator account' that will be the validator owner. + validatorOwnerAccount = await getTestAccount( + { initialFunds: AlgoAmount.Algos(500), suppressLog: true }, + fixture.context.algod, + fixture.context.kmd + ); + consoleLogger.info(`validator account ${validatorOwnerAccount.addr}`); + + const config = createValidatorConfig({ + Owner: validatorOwnerAccount.addr, + Manager: validatorOwnerAccount.addr, + MinEntryStake: BigInt(AlgoAmount.Algos(1000).microAlgos), + MaxAlgoPerPool: BigInt(MaxAlgoPerPool), // this comes into play in later tests !! + PercentToValidator: PctToValidator * 10000, + ValidatorCommissionAddress: validatorOwnerAccount.addr, + }); + validatorId = await addValidator( + fixture.context, + validatorMasterClient, + validatorOwnerAccount, + config, + validatorMbr + ); + firstPoolKey = await addStakingPool( + fixture.context, + validatorMasterClient, + validatorId, + 1, + validatorOwnerAccount, + poolMbr, + poolInitMbr + ); + expect(firstPoolKey.id).toEqual(BigInt(validatorId)); + expect(firstPoolKey.poolId).toEqual(1n); + + firstPoolClient = new StakingPoolClient( + { sender: validatorOwnerAccount, resolveBy: 'id', id: firstPoolKey.poolAppId }, + fixture.context.algod + ); + poolAppId = ( + await validatorMasterClient.getPoolAppId( + { validatorId: firstPoolKey.id, poolId: firstPoolKey.poolId }, + { sendParams: { populateAppCallResources: true } } + ) + ).return!; + expect(firstPoolKey.poolAppId).toEqual(poolAppId); + }); + + // boilerplate at this point.. just dd some stake - testing different commissions is all we care about + test('firstStaker', async () => { + // Fund a 'staker account' that will be the new 'staker' + const stakerAccount = await getTestAccount( + { initialFunds: AlgoAmount.Algos(5000), suppressLog: true }, + fixture.context.algod, + fixture.context.kmd + ); + stakerAccounts.push(stakerAccount); + + // now stake 1000(+mbr), min for this pool - for the first time - which means actual stake amount will be reduced + // by 'first time staker' fee to cover MBR (which goes to VALIDATOR contract account, not staker contract account!) + // we pay the extra here so the final staked amount should be exactly 1000 + const stakeAmount1 = AlgoAmount.MicroAlgos( + AlgoAmount.Algos(1000).microAlgos + AlgoAmount.MicroAlgos(Number(stakerMbr)).microAlgos + ); + const [stakedPoolKey] = await addStake( + fixture.context, + validatorMasterClient, + validatorId, + stakerAccount, + stakeAmount1, + 0n + ); + // should match info from first staking pool + expect(stakedPoolKey.id).toEqual(firstPoolKey.id); + expect(stakedPoolKey.poolId).toEqual(firstPoolKey.poolId); + expect(stakedPoolKey.poolAppId).toEqual(firstPoolKey.poolAppId); + + const poolInfo = await getPoolInfo(validatorMasterClient, firstPoolKey); + expect(poolInfo.totalStakers).toEqual(1); + expect(poolInfo.totalAlgoStaked).toEqual(BigInt(stakeAmount1.microAlgos - Number(stakerMbr))); + + expect((await getValidatorState(validatorMasterClient, validatorId)).totalStakers).toEqual(1n); + }); + + test('testFirstRewards', async () => { + // increment time a day(+) at a time per transaction + await fixture.context.algod.setBlockOffsetTimestamp(60 * 61 * 24).do(); + + const origValidatorState = await getValidatorState(validatorMasterClient, validatorId); + const ownerBalance = await fixture.context.algod.accountInformation(validatorOwnerAccount.addr).do(); + const stakersPriorToReward = await getStakeInfoFromBoxValue(firstPoolClient); + + const reward = AlgoAmount.Algos(200); + // put some test 'reward' algos into staking pool + await transferAlgos( + { + from: fixture.context.testAccount, + to: getApplicationAddress(firstPoolKey.poolAppId), + amount: reward, + }, + fixture.context.algod + ); + + const poolInfo = await getPoolInfo(validatorMasterClient, firstPoolKey); + consoleLogger.info(`pool stakers:${poolInfo.totalStakers}, staked:${poolInfo.totalAlgoStaked}`); + + const epochBefore = BigInt((await firstPoolClient.appClient.getGlobalState()).epochNumber.value as bigint); + + // Perform epoch payout calculation - we also get back how much it cost to issue the txn + const fees = await epochBalanceUpdate(firstPoolClient); + const expectedValidatorReward = reward.microAlgos * (PctToValidator / 100); + + expect(BigInt((await firstPoolClient.appClient.getGlobalState()).epochNumber.value as bigint)).toEqual( + epochBefore + 1n + ); + + const newValidatorState = await getValidatorState(validatorMasterClient, validatorId); + const newOwnerBalance = await fixture.context.algod.accountInformation(validatorOwnerAccount.addr).do(); + // validator owner should have gotten the expected reward (minus the fees they just paid ofc) + expect(newOwnerBalance.amount).toEqual(ownerBalance.amount - fees.microAlgos + expectedValidatorReward); + + // Verify all the stakers in the pool got what we think they should have + const stakersAfterReward = await getStakeInfoFromBoxValue(firstPoolClient); + + await verifyRewardAmounts( + fixture.context, + (BigInt(reward.microAlgos) - BigInt(expectedValidatorReward)) as bigint, + 0n, + stakersPriorToReward as StakedInfo[], + stakersAfterReward as StakedInfo[], + 1 as number + ); + + // the total staked should have grown as well - reward minus what the validator was paid in their commission + expect(Number(newValidatorState.totalAlgoStaked)).toEqual( + Number(origValidatorState.totalAlgoStaked) + (reward.microAlgos - expectedValidatorReward) + ); + + const poolBalance = await getPoolAvailBalance(fixture.context, firstPoolKey); + expect(poolBalance).toEqual(newValidatorState.totalAlgoStaked); + }); + + test('extractRewards', async () => { + const origStakerBalance = await fixture.context.algod.accountInformation(stakerAccounts[0].addr).do(); + + const expectedBalance = AlgoAmount.Algos(1000 + 200 - 200 * (PctToValidator / 100)); + // Remove it all + const fees = await removeStake(firstPoolClient, stakerAccounts[0], expectedBalance); + + const newStakerBalance = await fixture.context.algod.accountInformation(stakerAccounts[0].addr).do(); + // 1000 algos staked + 190 reward (- fees for removing stake) + expect(newStakerBalance.amount).toEqual(origStakerBalance.amount + expectedBalance.microAlgos - fees); + + // no one should be left and be 0 balance + const postRemovePoolInfo = await getPoolInfo(validatorMasterClient, firstPoolKey); + expect(postRemovePoolInfo.totalStakers).toEqual(0); + expect(postRemovePoolInfo.totalAlgoStaked).toEqual(0n); + + const newValidatorState = await getValidatorState(validatorMasterClient, validatorId); + expect(Number(newValidatorState.totalAlgoStaked)).toEqual(0); + expect(Number(newValidatorState.totalStakers)).toEqual(0); + + const poolBalance = await getPoolAvailBalance(fixture.context, firstPoolKey); + expect(poolBalance).toEqual(newValidatorState.totalAlgoStaked); + }); +}); + +describe('StakeW100Commission', () => { + beforeEach(fixture.beforeEach); + beforeEach(logs.beforeEach); + afterEach(logs.afterEach); + + let validatorId: number; + let validatorOwnerAccount: Account; + const stakerAccounts: Account[] = []; + let poolAppId: bigint; + let firstPoolKey: ValidatorPoolKey; + let firstPoolClient: StakingPoolClient; + + const PctToValidator = 100; + + // add validator and 1 pool for subsequent stake tests + beforeAll(async () => { + // Fund a 'validator account' that will be the validator owner. + validatorOwnerAccount = await getTestAccount( + { initialFunds: AlgoAmount.Algos(500), suppressLog: true }, + fixture.context.algod, + fixture.context.kmd + ); + consoleLogger.info(`validator account ${validatorOwnerAccount.addr}`); + + const config = createValidatorConfig({ + Owner: validatorOwnerAccount.addr, + Manager: validatorOwnerAccount.addr, + MinEntryStake: BigInt(AlgoAmount.Algos(1000).microAlgos), + MaxAlgoPerPool: BigInt(MaxAlgoPerPool), // this comes into play in later tests !! + PercentToValidator: PctToValidator * 10000, + ValidatorCommissionAddress: validatorOwnerAccount.addr, + }); + validatorId = await addValidator( + fixture.context, + validatorMasterClient, + validatorOwnerAccount, + config, + validatorMbr + ); + firstPoolKey = await addStakingPool( + fixture.context, + validatorMasterClient, + validatorId, + 1, + validatorOwnerAccount, + poolMbr, + poolInitMbr + ); + expect(firstPoolKey.id).toEqual(BigInt(validatorId)); + expect(firstPoolKey.poolId).toEqual(1n); + + firstPoolClient = new StakingPoolClient( + { sender: validatorOwnerAccount, resolveBy: 'id', id: firstPoolKey.poolAppId }, + fixture.context.algod + ); + poolAppId = ( + await validatorMasterClient.getPoolAppId( + { validatorId: firstPoolKey.id, poolId: firstPoolKey.poolId }, + { sendParams: { populateAppCallResources: true } } + ) + ).return!; + expect(firstPoolKey.poolAppId).toEqual(poolAppId); + }); + + // boilerplate at this point.. just dd some stake - testing different commissions is all we care about + test('firstStaker', async () => { + // Fund a 'staker account' that will be the new 'staker' + const stakerAccount = await getTestAccount( + { initialFunds: AlgoAmount.Algos(5000), suppressLog: true }, + fixture.context.algod, + fixture.context.kmd + ); + stakerAccounts.push(stakerAccount); + + // now stake 1000(+mbr), min for this pool - for the first time - which means actual stake amount will be reduced + // by 'first time staker' fee to cover MBR (which goes to VALIDATOR contract account, not staker contract account!) + // we pay the extra here so the final staked amount should be exactly 1000 + const stakeAmount1 = AlgoAmount.MicroAlgos( + AlgoAmount.Algos(1000).microAlgos + AlgoAmount.MicroAlgos(Number(stakerMbr)).microAlgos + ); + const [stakedPoolKey] = await addStake( + fixture.context, + validatorMasterClient, + validatorId, + stakerAccount, + stakeAmount1, + 0n + ); + // should match info from first staking pool + expect(stakedPoolKey.id).toEqual(firstPoolKey.id); + expect(stakedPoolKey.poolId).toEqual(firstPoolKey.poolId); + expect(stakedPoolKey.poolAppId).toEqual(firstPoolKey.poolAppId); + + const poolInfo = await getPoolInfo(validatorMasterClient, firstPoolKey); + expect(poolInfo.totalStakers).toEqual(1); + expect(poolInfo.totalAlgoStaked).toEqual(BigInt(stakeAmount1.microAlgos - Number(stakerMbr))); + + expect((await getValidatorState(validatorMasterClient, validatorId)).totalStakers).toEqual(1n); + }); + + test('testFirstRewards', async () => { + // increment time a day(+) at a time per transaction + await fixture.context.algod.setBlockOffsetTimestamp(60 * 61 * 24).do(); + + const origValidatorState = await getValidatorState(validatorMasterClient, validatorId); + const ownerBalance = await fixture.context.algod.accountInformation(validatorOwnerAccount.addr).do(); + const stakersPriorToReward = await getStakeInfoFromBoxValue(firstPoolClient); + + const reward = AlgoAmount.Algos(200); + // put some test 'reward' algos into staking pool + await transferAlgos( + { + from: fixture.context.testAccount, + to: getApplicationAddress(firstPoolKey.poolAppId), + amount: reward, + }, + fixture.context.algod + ); + + const poolInfo = await getPoolInfo(validatorMasterClient, firstPoolKey); + consoleLogger.info(`pool stakers:${poolInfo.totalStakers}, staked:${poolInfo.totalAlgoStaked}`); + + const epochBefore = BigInt((await firstPoolClient.appClient.getGlobalState()).epochNumber.value as bigint); + + // Perform epoch payout calculation - we also get back how much it cost to issue the txn + const fees = await epochBalanceUpdate(firstPoolClient); + const expectedValidatorReward = reward.microAlgos * (PctToValidator / 100); + + expect(BigInt((await firstPoolClient.appClient.getGlobalState()).epochNumber.value as bigint)).toEqual( + epochBefore + 1n + ); + + const newValidatorState = await getValidatorState(validatorMasterClient, validatorId); + const newOwnerBalance = await fixture.context.algod.accountInformation(validatorOwnerAccount.addr).do(); + // validator owner should have gotten the expected reward (minus the fees they just paid ofc) + expect(newOwnerBalance.amount).toEqual(ownerBalance.amount - fees.microAlgos + expectedValidatorReward); + + // Verify all the stakers in the pool got what we think they should have + const stakersAfterReward = await getStakeInfoFromBoxValue(firstPoolClient); + + await verifyRewardAmounts( + fixture.context, + (BigInt(reward.microAlgos) - BigInt(expectedValidatorReward)) as bigint, + 0n, + stakersPriorToReward as StakedInfo[], + stakersAfterReward as StakedInfo[], + 1 as number + ); + + // the total staked should have grown as well - reward minus what the validator was paid in their commission + expect(Number(newValidatorState.totalAlgoStaked)).toEqual( + Number(origValidatorState.totalAlgoStaked) + (reward.microAlgos - expectedValidatorReward) + ); + + const poolBalance = await getPoolAvailBalance(fixture.context, firstPoolKey); + expect(poolBalance).toEqual(newValidatorState.totalAlgoStaked); + }); + + test('extractRewards', async () => { + const origStakerBalance = await fixture.context.algod.accountInformation(stakerAccounts[0].addr).do(); + + const expectedBalance = AlgoAmount.Algos(1000 + 200 - 200 * (PctToValidator / 100)); + // Remove it all + const fees = await removeStake(firstPoolClient, stakerAccounts[0], expectedBalance); + + const newStakerBalance = await fixture.context.algod.accountInformation(stakerAccounts[0].addr).do(); + // 1000 algos staked + 190 reward (- fees for removing stake) + expect(newStakerBalance.amount).toEqual(origStakerBalance.amount + expectedBalance.microAlgos - fees); + + // no one should be left and be 0 balance + const postRemovePoolInfo = await getPoolInfo(validatorMasterClient, firstPoolKey); + expect(postRemovePoolInfo.totalStakers).toEqual(0); + expect(postRemovePoolInfo.totalAlgoStaked).toEqual(0n); + + const newValidatorState = await getValidatorState(validatorMasterClient, validatorId); + expect(Number(newValidatorState.totalAlgoStaked)).toEqual(0); + expect(Number(newValidatorState.totalStakers)).toEqual(0); + + const poolBalance = await getPoolAvailBalance(fixture.context, firstPoolKey); + expect(poolBalance).toEqual(newValidatorState.totalAlgoStaked); + }); +}); + describe('StakeWTokenWRewards', () => { beforeEach(fixture.beforeEach); beforeEach(logs.beforeEach); diff --git a/contracts/package.json b/contracts/package.json index 58e44603..326fbc85 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -1,5 +1,5 @@ { - "name": "ValidatorRegistry", + "name": "reti-contracts", "version": "0.2.0", "license": "MIT", "scripts": { From cf0e66413338e550560568cd856bee7728282f70 Mon Sep 17 00:00:00 2001 From: Patrick Bennett Date: Thu, 4 Apr 2024 02:15:40 -0400 Subject: [PATCH 02/14] fix(ui): Update 'max stake' allowed per validator calculation Enhanced the max stake per-pool calculation function to use the 'protocol constraints' and taking lesser of (max per validator / num pools) or protocol max per pool. The constraints will also be updated at least once every 30 minutes. --- ui/src/api/queries.ts | 2 +- ui/src/components/ValidatorTable.tsx | 2 +- ui/src/utils/contracts.ts | 15 +++++++++++++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/ui/src/api/queries.ts b/ui/src/api/queries.ts index aebc2ebd..d893b9e6 100644 --- a/ui/src/api/queries.ts +++ b/ui/src/api/queries.ts @@ -41,7 +41,7 @@ export const mbrQueryOptions = queryOptions({ export const constraintsQueryOptions = queryOptions({ queryKey: ['constraints'], queryFn: () => fetchProtocolConstraints(), - staleTime: Infinity, + staleTime: 1000 * 60 * 30, // every 30 mins }) export const balanceQueryOptions = (address: string | null) => diff --git a/ui/src/components/ValidatorTable.tsx b/ui/src/components/ValidatorTable.tsx index 6a4ac352..08270f3b 100644 --- a/ui/src/components/ValidatorTable.tsx +++ b/ui/src/components/ValidatorTable.tsx @@ -116,7 +116,7 @@ export function ValidatorTable({ validators, stakesByValidator }: ValidatorTable notation: 'compact', }).format(currentStake) - const maxStake = calculateMaxStake(validator, true) + const maxStake = calculateMaxStake(validator, constraints!, true) const maxStakeCompact = new Intl.NumberFormat(undefined, { notation: 'compact', }).format(maxStake) diff --git a/ui/src/utils/contracts.ts b/ui/src/utils/contracts.ts index 99f51d51..71843961 100644 --- a/ui/src/utils/contracts.ts +++ b/ui/src/utils/contracts.ts @@ -378,9 +378,20 @@ export function getAddValidatorFormSchema(constraints: Constraints) { }) } -export function calculateMaxStake(validator: Validator, algos = false): number { +export function calculateMaxStake( + validator: Validator, + constraints: Constraints, + algos = false, +): number { const { numPools } = validator.state - const { maxAlgoPerPool } = validator.config + const hardMaxDividedBetweenPools = constraints.maxAlgoPerValidator / BigInt(numPools) + let { maxAlgoPerPool } = validator.config + if (maxAlgoPerPool === 0n) { + maxAlgoPerPool = constraints.maxAlgoPerPool + } + if (hardMaxDividedBetweenPools < maxAlgoPerPool) { + maxAlgoPerPool = hardMaxDividedBetweenPools + } const maxStake = Number(maxAlgoPerPool) * numPools From d786bcd6d873e040a4ed6c4c18f41435435219a9 Mon Sep 17 00:00:00 2001 From: Doug Richar Date: Thu, 4 Apr 2024 03:25:48 -0400 Subject: [PATCH 03/14] chore(ui): run prettier/eslint --- ui/.prettierignore | 7 +- ui/src/api/contracts.ts | 1 - ui/src/components/ui/avatar.tsx | 17 ++--- ui/src/components/ui/card.tsx | 116 +++++++++++++------------------ ui/src/components/ui/form.tsx | 69 ++++++++---------- ui/src/components/ui/label.tsx | 19 ++--- ui/src/components/ui/sonner.tsx | 18 +++-- ui/src/components/ui/tooltip.tsx | 10 +-- ui/src/main.tsx | 12 +++- ui/src/styles/main.css | 4 +- 10 files changed, 120 insertions(+), 153 deletions(-) diff --git a/ui/.prettierignore b/ui/.prettierignore index dbda6ae9..3ab7444f 100644 --- a/ui/.prettierignore +++ b/ui/.prettierignore @@ -1,12 +1,9 @@ -# don't ever format node_modules node_modules -# don't lint format output (make sure it's set to your correct build folder name) dist build -# don't format nyc coverage output coverage -# don't format generated types **/generated/types.d.ts **/generated/types.ts -# don't format ide files .idea + +src/contracts diff --git a/ui/src/api/contracts.ts b/ui/src/api/contracts.ts index f6ee97df..4e4ef12d 100644 --- a/ui/src/api/contracts.ts +++ b/ui/src/api/contracts.ts @@ -26,7 +26,6 @@ import { } from '@/utils/contracts' import { getAlgodConfigFromViteEnvironment } from '@/utils/network/getAlgoClientConfigs' import { getRetiAppIdFromViteEnvironment } from '@/utils/env' -import { getActiveWalletAddress } from '@/utils/wallets' const algodConfig = getAlgodConfigFromViteEnvironment() const algodClient = algokit.getAlgoClient({ diff --git a/ui/src/components/ui/avatar.tsx b/ui/src/components/ui/avatar.tsx index 33ae3c6e..26e303f1 100644 --- a/ui/src/components/ui/avatar.tsx +++ b/ui/src/components/ui/avatar.tsx @@ -1,7 +1,7 @@ -import * as React from "react" -import * as AvatarPrimitive from "@radix-ui/react-avatar" +import * as React from 'react' +import * as AvatarPrimitive from '@radix-ui/react-avatar' -import { cn } from "@/utils/ui" +import { cn } from '@/utils/ui' const Avatar = React.forwardRef< React.ElementRef, @@ -9,10 +9,7 @@ const Avatar = React.forwardRef< >(({ className, ...props }, ref) => ( )) @@ -24,7 +21,7 @@ const AvatarImage = React.forwardRef< >(({ className, ...props }, ref) => ( )) @@ -37,8 +34,8 @@ const AvatarFallback = React.forwardRef< diff --git a/ui/src/components/ui/card.tsx b/ui/src/components/ui/card.tsx index d67b9e57..d009c087 100644 --- a/ui/src/components/ui/card.tsx +++ b/ui/src/components/ui/card.tsx @@ -1,76 +1,56 @@ -import * as React from "react" - -import { cn } from "@/utils/ui" - -const Card = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -Card.displayName = "Card" - -const CardHeader = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -CardHeader.displayName = "CardHeader" - -const CardTitle = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -

-)) -CardTitle.displayName = "CardTitle" +import * as React from 'react' + +import { cn } from '@/utils/ui' + +const Card = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ), +) +Card.displayName = 'Card' + +const CardHeader = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ), +) +CardHeader.displayName = 'CardHeader' + +const CardTitle = React.forwardRef>( + ({ className, ...props }, ref) => ( +

+ ), +) +CardTitle.displayName = 'CardTitle' const CardDescription = React.forwardRef< HTMLParagraphElement, React.HTMLAttributes >(({ className, ...props }, ref) => ( -

-)) -CardDescription.displayName = "CardDescription" - -const CardContent = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -

-)) -CardContent.displayName = "CardContent" - -const CardFooter = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
+

)) -CardFooter.displayName = "CardFooter" +CardDescription.displayName = 'CardDescription' + +const CardContent = React.forwardRef>( + ({ className, ...props }, ref) => ( +

+ ), +) +CardContent.displayName = 'CardContent' + +const CardFooter = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ), +) +CardFooter.displayName = 'CardFooter' export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/ui/src/components/ui/form.tsx b/ui/src/components/ui/form.tsx index 76125903..cbe3c7aa 100644 --- a/ui/src/components/ui/form.tsx +++ b/ui/src/components/ui/form.tsx @@ -1,6 +1,6 @@ -import * as React from "react" -import * as LabelPrimitive from "@radix-ui/react-label" -import { Slot } from "@radix-ui/react-slot" +import * as React from 'react' +import * as LabelPrimitive from '@radix-ui/react-label' +import { Slot } from '@radix-ui/react-slot' import { Controller, ControllerProps, @@ -8,27 +8,25 @@ import { FieldValues, FormProvider, useFormContext, -} from "react-hook-form" +} from 'react-hook-form' -import { cn } from "@/utils/ui" -import { Label } from "@/components/ui/label" +import { cn } from '@/utils/ui' +import { Label } from '@/components/ui/label' const Form = FormProvider type FormFieldContextValue< TFieldValues extends FieldValues = FieldValues, - TName extends FieldPath = FieldPath + TName extends FieldPath = FieldPath, > = { name: TName } -const FormFieldContext = React.createContext( - {} as FormFieldContextValue -) +const FormFieldContext = React.createContext({} as FormFieldContextValue) const FormField = < TFieldValues extends FieldValues = FieldValues, - TName extends FieldPath = FieldPath + TName extends FieldPath = FieldPath, >({ ...props }: ControllerProps) => { @@ -47,7 +45,7 @@ const useFormField = () => { const fieldState = getFieldState(fieldContext.name, formState) if (!fieldContext) { - throw new Error("useFormField should be used within ") + throw new Error('useFormField should be used within ') } const { id } = itemContext @@ -66,23 +64,20 @@ type FormItemContextValue = { id: string } -const FormItemContext = React.createContext( - {} as FormItemContextValue -) +const FormItemContext = React.createContext({} as FormItemContextValue) -const FormItem = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => { - const id = React.useId() +const FormItem = React.forwardRef>( + ({ className, ...props }, ref) => { + const id = React.useId() - return ( - -
- - ) -}) -FormItem.displayName = "FormItem" + return ( + +
+ + ) + }, +) +FormItem.displayName = 'FormItem' const FormLabel = React.forwardRef< React.ElementRef, @@ -93,13 +88,13 @@ const FormLabel = React.forwardRef< return (
From b65277ef15115729a00fea15a7a81f03b04e469a Mon Sep 17 00:00:00 2001 From: Doug Richar Date: Thu, 4 Apr 2024 14:17:35 -0400 Subject: [PATCH 14/14] chore: release v0.2.1 --- contracts/bootstrap/package.json | 2 +- contracts/package.json | 2 +- ui/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/bootstrap/package.json b/contracts/bootstrap/package.json index 207bdeab..bffd8ee8 100644 --- a/contracts/bootstrap/package.json +++ b/contracts/bootstrap/package.json @@ -1,6 +1,6 @@ { "name": "bootstrap", - "version": "0.2.0", + "version": "0.2.1", "description": "", "main": "index.ts", "scripts": { diff --git a/contracts/package.json b/contracts/package.json index 326fbc85..457bab8a 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -1,6 +1,6 @@ { "name": "reti-contracts", - "version": "0.2.0", + "version": "0.2.1", "license": "MIT", "scripts": { "generate-client": "algokit generate client contracts/artifacts/ --language typescript --output contracts/clients/{contract_name}Client.ts && ./update_contract_artifacts.sh``", diff --git a/ui/package.json b/ui/package.json index fa9e23e7..4653343e 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,6 +1,6 @@ { "name": "reti-ui", - "version": "0.2.0", + "version": "0.2.1", "author": { "name": "Doug Richar", "email": "drichar@gmail.com"