Skip to content

Commit

Permalink
Merge pull request #104 from valory-xyz/tanya/validation-fix
Browse files Browse the repository at this point in the history
(launch) fix: replace hardcode with form value
  • Loading branch information
Tanya-atatakai authored Sep 30, 2024
2 parents fa36c37 + e5ea62d commit d2edecd
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,12 @@ describe('<CreateStakingContract />', () => {
await userEvent.type(rewardsPerSecondInput, '100');
expect(rewardsPerSecondInput).toHaveValue('100');

const minStakingDepositInput = screen.getByLabelText('Minimum service staking deposit, OLAS');
// clear first, because it has default value
await userEvent.clear(minStakingDepositInput);
await userEvent.type(minStakingDepositInput, '1000');
expect(minStakingDepositInput).toHaveValue('1000');

await clickCreateContractButton();
expect(
screen.getByText('The rewards per second must be below the allowed limit'),
Expand Down
6 changes: 5 additions & 1 deletion apps/launch/components/MyStakingContracts/Create/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,11 @@ const TemplateInfoContent = ({ id }: { id: number }) => {
* Create staking contract
*/
export const CreateStakingContract = () => {
const [form] = Form.useForm();

const router = useRouter();
const dispatch = useAppDispatch();
const rulesConfig = useFieldRules();
const { rulesConfig, onValuesChange } = useFieldRules(form);

const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<ErrorType>(null);
Expand Down Expand Up @@ -210,6 +212,8 @@ export const CreateStakingContract = () => {
{alertMessage}

<Form<FormValues>
form={form}
onValuesChange={onValuesChange}
layout="vertical"
onFinish={handleCreate}
requiredMark={false}
Expand Down
170 changes: 88 additions & 82 deletions apps/launch/components/MyStakingContracts/Create/useFieldRules.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Rule } from 'antd/es/form';
import { FormInstance, Rule } from 'antd/es/form';

import { FORM_VALIDATION } from 'libs/util-functions/src';

Expand All @@ -21,96 +21,102 @@ const getGenericFieldRules = (label: string) => [
];

type StakingDepositRules = { [K in keyof FormValues]: { rules: Rule[] | undefined } };
export const useFieldRules = (): StakingDepositRules => {
export const useFieldRules = (
form: FormInstance,
): {
onValuesChange: (changedValues: Record<string, unknown>) => void;
rulesConfig: StakingDepositRules;
} => {
const { data: numServicesLimit } = useNumServicesLimit();
const { data: minStakingDepositLimit } = useMinStakingDepositLimit();
const { data: timeForEmissionsLimit } = useTimeForEmissionsLimit();
const { data: apyLimit } = useApyLimit();

const onValuesChange = (changedValues: Record<string, unknown>) => {
// The rewardsPerSecond validation depends on minStakingDeposit value
// Manually trigger it's validation when minStakingDeposit changes
if ('minStakingDeposit' in changedValues) {
form.validateFields(['rewardsPerSecond']);
}
};

return {
contractName: { rules: getGenericFieldRules(FieldConfig.contractName.name) },
description: { rules: getGenericFieldRules(FieldConfig.description.name) },
maxNumServices: {
rules: [
...getGenericFieldRules(FieldConfig.maxNumServices.name),
{
type: 'number',
min: 1,
max: numServicesLimit,
message: `Maximum number of staked agents must be at least 1 and at most ${numServicesLimit}`,
},
],
},
rewardsPerSecond: {
rules: [
...getGenericFieldRules(FieldConfig.rewardsPerSecond.name),
{
// BE validation from https://github.com/valory-xyz/autonolas-registries/blob/main/contracts/staking/StakingVerifier
validator: async (_, value) => {
if (!minStakingDepositLimit) return Promise.resolve();
if (!apyLimit) return Promise.resolve();
onValuesChange,
rulesConfig: {
contractName: { rules: getGenericFieldRules(FieldConfig.contractName.name) },
description: { rules: getGenericFieldRules(FieldConfig.description.name) },
maxNumServices: {
rules: [
...getGenericFieldRules(FieldConfig.maxNumServices.name),
{
type: 'number',
min: 1,
max: numServicesLimit,
message: `Maximum number of staked agents must be at least 1 and at most ${numServicesLimit}`,
},
],
},
rewardsPerSecond: {
rules: [
...getGenericFieldRules(FieldConfig.rewardsPerSecond.name),
{
// BE validation from https://github.com/valory-xyz/autonolas-registries/blob/main/contracts/staking/StakingVerifier
validator: async (_, value) => {
if (!minStakingDepositLimit) return Promise.resolve();
if (!apyLimit) return Promise.resolve();

const rewardsPerYear = value * ONE_YEAR;
const apy = (rewardsPerYear * 1e18) / 1000;
const minStakingDeposit = form.getFieldValue('minStakingDeposit');
const rewardsPerYear = value * ONE_YEAR;
const apy = (rewardsPerYear * 1e18) / minStakingDeposit;

if (apy > apyLimit) {
return Promise.reject('The rewards per second must be below the allowed limit');
}
if (apy > apyLimit) {
return Promise.reject('The rewards per second must be below the allowed limit');
}
},
},
},
],
},
minStakingDeposit: {
rules: [
...getGenericFieldRules(FieldConfig.minStakingDeposit.name),
{
type: 'number',
min: 1,
max: minStakingDepositLimit,
message: `Minimum service staking deposit, OLAS must be at least 1 and at most ${minStakingDepositLimit}`,
},
],
},
minNumStakingPeriods: { rules: getGenericFieldRules(FieldConfig.minNumStakingPeriods.name) },
maxNumInactivityPeriods: {
rules: getGenericFieldRules(FieldConfig.maxNumInactivityPeriods.name),
},
livenessPeriod: {
rules: [
...getGenericFieldRules(FieldConfig.livenessPeriod.name),
// TODO: any comments on this validation?
// {
// type: 'number',
// min: 86400,
// max: 86400 * 30,
// message: `Liveness period must be between 24 hours and 30 days`,
// },
],
},
timeForEmissions: {
rules: [
...getGenericFieldRules(FieldConfig.timeForEmissions.name),
{
type: 'number',
min: 1,
max: timeForEmissionsLimit,
message: `Time for emissions must be between 1 and ${timeForEmissionsLimit} seconds`,
},
],
},
numAgentInstances: { rules: getGenericFieldRules(FieldConfig.numAgentInstances.name) },
agentIds: {
rules: [
{ ...FORM_VALIDATION.validateCommaSeparatedList },
],
},
threshold: { rules: undefined },
configHash: { rules: undefined },
activityChecker: {
rules: [
...getGenericFieldRules(FieldConfig.activityChecker.name),
{ ...FORM_VALIDATION.validateAddress },
],
],
},
minStakingDeposit: {
rules: [
...getGenericFieldRules(FieldConfig.minStakingDeposit.name),
{
type: 'number',
min: 1,
max: minStakingDepositLimit,
message: `Minimum service staking deposit, OLAS must be at least 1 and at most ${minStakingDepositLimit}`,
},
],
},
minNumStakingPeriods: { rules: getGenericFieldRules(FieldConfig.minNumStakingPeriods.name) },
maxNumInactivityPeriods: {
rules: getGenericFieldRules(FieldConfig.maxNumInactivityPeriods.name),
},
livenessPeriod: {
rules: [...getGenericFieldRules(FieldConfig.livenessPeriod.name)],
},
timeForEmissions: {
rules: [
...getGenericFieldRules(FieldConfig.timeForEmissions.name),
{
type: 'number',
min: 1,
max: timeForEmissionsLimit,
message: `Time for emissions must be between 1 and ${timeForEmissionsLimit} seconds`,
},
],
},
numAgentInstances: { rules: getGenericFieldRules(FieldConfig.numAgentInstances.name) },
agentIds: {
rules: [{ ...FORM_VALIDATION.validateCommaSeparatedList }],
},
threshold: { rules: undefined },
configHash: { rules: undefined },
activityChecker: {
rules: [
...getGenericFieldRules(FieldConfig.activityChecker.name),
{ ...FORM_VALIDATION.validateAddress },
],
},
},
};
};

0 comments on commit d2edecd

Please sign in to comment.