Skip to content
Merged
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
3 changes: 3 additions & 0 deletions packages/gator-permissions-snap/src/core/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,12 @@ export type DeepRequired<TParent> = TParent extends (infer U)[]
* An enum representing the time periods for which the stream rate can be calculated.
*/
export enum TimePeriod {
HOURLY = 'Hourly',
DAILY = 'Daily',
WEEKLY = 'Weekly',
BIWEEKLY = 'Biweekly',
MONTHLY = 'Monthly',
YEARLY = 'Yearly',
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,12 @@ export function calculateAmountPerSecond(
* @param periodDuration - The period duration string to validate.
* @returns Object containing parsed duration and any validation error.
*/
export function validatePeriodDuration(periodDuration: string): {
export function validatePeriodDuration(periodDuration: number): {
duration: number | undefined;
error: string | undefined;
} {
try {
const duration = parseInt(periodDuration, 10);
const duration = periodDuration;
if (isNaN(duration) || duration <= 0) {
return {
duration: undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Box, Section } from '@metamask/snaps-sdk/jsx';

import {
periodAmountRule,
periodTypeRule,
periodDurationRule,
startTimeRule,
expiryRule,
Expand Down Expand Up @@ -32,12 +31,7 @@ export async function createConfirmationContent({
<Box>
<Section>
{renderRules({
rules: [
startTimeRule,
periodAmountRule,
periodTypeRule,
periodDurationRule,
],
rules: [startTimeRule, periodAmountRule, periodDurationRule],
context,
metadata,
})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ import {
type Hex,
} from '@metamask/utils';

import { TimePeriod } from '../../core/types';
import type { TokenMetadataService } from '../../services/tokenMetadataService';
import { TIME_PERIOD_TO_SECONDS } from '../../utils/time';
import { parseUnits, formatUnitsFromHex } from '../../utils/value';
import {
validateAndParseAmount,
Expand Down Expand Up @@ -76,7 +74,7 @@ export async function applyContext({
periodAmount: bigIntToHex(
parseUnits({ formatted: permissionDetails.periodAmount, decimals }),
),
periodDuration: parseInt(permissionDetails.periodDuration, 10),
periodDuration: permissionDetails.periodDuration,
startTime: permissionDetails.startTime,
justification: originalRequest.permission.data.justification,
tokenAddress: originalRequest.permission.data.tokenAddress,
Expand Down Expand Up @@ -178,19 +176,7 @@ export async function buildContext({
decimals,
});

const periodDuration = data.periodDuration.toString();

// Determine the period type based on the duration
let periodType: TimePeriod | 'Other';
if (periodDuration === TIME_PERIOD_TO_SECONDS[TimePeriod.DAILY].toString()) {
periodType = TimePeriod.DAILY;
} else if (
periodDuration === TIME_PERIOD_TO_SECONDS[TimePeriod.WEEKLY].toString()
) {
periodType = TimePeriod.WEEKLY;
} else {
periodType = 'Other';
}
const { periodDuration } = data;

const startTime = data.startTime ?? Math.floor(Date.now() / 1000);

Expand Down Expand Up @@ -220,7 +206,6 @@ export async function buildContext({
},
permissionDetails: {
periodAmount,
periodType,
periodDuration,
startTime,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { InvalidInputError } from '@metamask/snaps-sdk';

import { TimePeriod } from '../../core/types';
import type { RuleDefinition } from '../../core/types';
import { TIME_PERIOD_TO_SECONDS } from '../../utils/time';
import { getClosestTimePeriod, TIME_PERIOD_TO_SECONDS } from '../../utils/time';
import { getIconData } from '../iconUtil';
import type {
Erc20TokenPeriodicContext,
Expand All @@ -9,7 +11,6 @@ import type {

export const PERIOD_AMOUNT_ELEMENT = 'erc20-token-periodic-period-amount';
export const PERIOD_TYPE_ELEMENT = 'erc20-token-periodic-period-type';
export const PERIOD_DURATION_ELEMENT = 'erc20-token-periodic-period-duration';
export const START_TIME_ELEMENT = 'erc20-token-periodic-start-date';
export const EXPIRY_ELEMENT = 'erc20-token-periodic-expiry';

Expand Down Expand Up @@ -37,62 +38,51 @@ export const periodAmountRule: RuleDefinition<
}),
};

export const periodTypeRule: RuleDefinition<
export const periodDurationRule: RuleDefinition<
Erc20TokenPeriodicContext,
Erc20TokenPeriodicMetadata
> = {
name: PERIOD_TYPE_ELEMENT,
label: 'Period duration',
label: 'Frequency',
type: 'dropdown',
getRuleData: ({ context, metadata }) => ({
isAdjustmentAllowed: context.isAdjustmentAllowed,
value: context.permissionDetails.periodType,
value: getClosestTimePeriod(context.permissionDetails.periodDuration),
isVisible: true,
tooltip: 'The duration of the period',
options: [TimePeriod.DAILY, TimePeriod.WEEKLY, 'Other'],
error: metadata.validationErrors.periodTypeError,
options: Object.values(TimePeriod),
error: metadata.validationErrors.periodDurationError,
}),
updateContext: (context: Erc20TokenPeriodicContext, value: string) => {
const periodType = value as TimePeriod | 'Other';
const periodDuration =
periodType === 'Other'
? context.permissionDetails.periodDuration
: Number(TIME_PERIOD_TO_SECONDS[periodType]).toString();
// Validate that value is a valid TimePeriod
if (!Object.values(TimePeriod).includes(value as TimePeriod)) {
throw new InvalidInputError(
`Invalid period type: "${value}". Valid options are: ${Object.values(TimePeriod).join(', ')}`,
);
}

const periodType = value as TimePeriod;
const periodSeconds = TIME_PERIOD_TO_SECONDS[periodType];

// This should never happen if the above check passed, but be defensive
if (periodSeconds === undefined) {
throw new InvalidInputError(
`Period type "${periodType}" is not mapped to a duration. This indicates a system error.`,
);
}

const periodDuration = Number(periodSeconds);

return {
...context,
permissionDetails: {
...context.permissionDetails,
periodType,
periodDuration,
},
};
},
};

export const periodDurationRule: RuleDefinition<
Erc20TokenPeriodicContext,
Erc20TokenPeriodicMetadata
> = {
name: PERIOD_DURATION_ELEMENT,
label: 'Duration (seconds)',
type: 'number',
getRuleData: ({ context, metadata }) => ({
value: context.permissionDetails.periodDuration,
isAdjustmentAllowed: context.isAdjustmentAllowed,
isVisible: context.permissionDetails.periodType === 'Other',
tooltip: 'The length of each period in seconds',
error: metadata.validationErrors.periodDurationError,
}),
updateContext: (context: Erc20TokenPeriodicContext, value: string) => ({
...context,
permissionDetails: {
...context.permissionDetails,
periodDuration: value,
},
}),
};

export const startTimeRule: RuleDefinition<
Erc20TokenPeriodicContext,
Erc20TokenPeriodicMetadata
Expand Down Expand Up @@ -161,7 +151,6 @@ export const expiryRule: RuleDefinition<

export const allRules = [
periodAmountRule,
periodTypeRule,
periodDurationRule,
startTimeRule,
expiryRule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
zPermission,
zMetaMaskPermissionData,
zAddress,
zTimestamp,
zStartTime,
} from '@metamask/7715-permissions-shared/types';
import { z } from 'zod';
Expand All @@ -12,15 +11,14 @@ import type {
DeepRequired,
TypedPermissionRequest,
BaseContext,
TimePeriod,
BaseMetadata,
} from '../../core/types';
import { zPeriodDuration } from '../../utils/time';

export type Erc20TokenPeriodicMetadata = BaseMetadata & {
validationErrors: {
periodAmountError?: string;
periodDurationError?: string;
periodTypeError?: string;
startTimeError?: string;
expiryError?: string;
};
Expand All @@ -29,8 +27,7 @@ export type Erc20TokenPeriodicMetadata = BaseMetadata & {
export type Erc20TokenPeriodicContext = BaseContext & {
permissionDetails: {
periodAmount: string;
periodType: TimePeriod | 'Other';
periodDuration: string;
periodDuration: number;
startTime: number;
};
};
Expand All @@ -41,7 +38,7 @@ export const zErc20TokenPeriodicPermission = zPermission.extend({
zMetaMaskPermissionData,
z.object({
periodAmount: zHexStr,
periodDuration: zTimestamp,
periodDuration: zPeriodDuration,
startTime: zStartTime,
tokenAddress: zAddress,
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Box, Section } from '@metamask/snaps-sdk/jsx';

import {
periodAmountRule,
periodTypeRule,
periodDurationRule,
startTimeRule,
expiryRule,
Expand Down Expand Up @@ -32,12 +31,7 @@ export async function createConfirmationContent({
<Box>
<Section>
{renderRules({
rules: [
startTimeRule,
periodAmountRule,
periodTypeRule,
periodDurationRule,
],
rules: [startTimeRule, periodAmountRule, periodDurationRule],
context,
metadata,
})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ import {
type Hex,
} from '@metamask/utils';

import { TimePeriod } from '../../core/types';
import type { TokenMetadataService } from '../../services/tokenMetadataService';
import { TIME_PERIOD_TO_SECONDS } from '../../utils/time';
import { parseUnits, formatUnitsFromHex } from '../../utils/value';
import {
validateAndParseAmount,
Expand Down Expand Up @@ -77,7 +75,7 @@ export async function applyContext({
periodAmount: bigIntToHex(
parseUnits({ formatted: permissionDetails.periodAmount, decimals }),
),
periodDuration: parseInt(permissionDetails.periodDuration, 10),
periodDuration: permissionDetails.periodDuration,
startTime: permissionDetails.startTime,
justification: originalRequest.permission.data.justification,
};
Expand Down Expand Up @@ -178,19 +176,7 @@ export async function buildContext({
decimals,
});

const periodDuration = data.periodDuration.toString();

// Determine the period type based on the duration
let periodType: TimePeriod | 'Other';
if (periodDuration === TIME_PERIOD_TO_SECONDS[TimePeriod.DAILY].toString()) {
periodType = TimePeriod.DAILY;
} else if (
periodDuration === TIME_PERIOD_TO_SECONDS[TimePeriod.WEEKLY].toString()
) {
periodType = TimePeriod.WEEKLY;
} else {
periodType = 'Other';
}
const { periodDuration } = data;

const startTime = data.startTime ?? Math.floor(Date.now() / 1000);

Expand Down Expand Up @@ -220,7 +206,6 @@ export async function buildContext({
},
permissionDetails: {
periodAmount,
periodType,
periodDuration,
startTime,
},
Expand Down
Loading
Loading