Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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 @@ -4,7 +4,6 @@ import { Box, Section } from '@metamask/snaps-sdk/jsx';
import {
periodAmountRule,
periodTypeRule,
periodDurationRule,
startTimeRule,
expiryRule,
} from './rules';
Expand Down Expand Up @@ -32,12 +31,7 @@ export async function createConfirmationContent({
<Box>
<Section>
{renderRules({
rules: [
startTimeRule,
periodAmountRule,
periodTypeRule,
periodDurationRule,
],
rules: [startTimeRule, periodAmountRule, periodTypeRule],
context,
metadata,
})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ 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 { getClosestTimePeriod, TIME_PERIOD_TO_SECONDS } from '../../utils/time';
import { parseUnits, formatUnitsFromHex } from '../../utils/value';
import {
validateAndParseAmount,
Expand Down Expand Up @@ -178,19 +177,8 @@ 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 periodType = getClosestTimePeriod(BigInt(data.periodDuration));
const periodDuration = TIME_PERIOD_TO_SECONDS[periodType].toString();

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,28 @@ export const periodTypeRule: RuleDefinition<
Erc20TokenPeriodicMetadata
> = {
name: PERIOD_TYPE_ELEMENT,
label: 'Period duration',
label: 'Transfer Window',
type: 'dropdown',
getRuleData: ({ context, metadata }) => ({
isAdjustmentAllowed: context.isAdjustmentAllowed,
value: context.permissionDetails.periodType,
isVisible: true,
tooltip: 'The duration of the period',
options: [TimePeriod.DAILY, TimePeriod.WEEKLY, 'Other'],
options: [
TimePeriod.HOURLY,
TimePeriod.DAILY,
TimePeriod.WEEKLY,
TimePeriod.BIWEEKLY,
TimePeriod.MONTHLY,
TimePeriod.YEARLY,
],
error: metadata.validationErrors.periodTypeError,
}),
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();
const periodType = value as TimePeriod;
const periodDuration = Number(
TIME_PERIOD_TO_SECONDS[periodType],
).toString();

return {
...context,
Expand All @@ -70,29 +76,6 @@ export const periodTypeRule: RuleDefinition<
},
};

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 @@ -162,7 +145,6 @@ export const expiryRule: RuleDefinition<
export const allRules = [
periodAmountRule,
periodTypeRule,
periodDurationRule,
startTimeRule,
expiryRule,
];
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export type Erc20TokenPeriodicMetadata = BaseMetadata & {
export type Erc20TokenPeriodicContext = BaseContext & {
permissionDetails: {
periodAmount: string;
periodType: TimePeriod | 'Other';
periodType: TimePeriod;
periodDuration: string;
startTime: number;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { Box, Section } from '@metamask/snaps-sdk/jsx';
import {
periodAmountRule,
periodTypeRule,
periodDurationRule,
startTimeRule,
expiryRule,
} from './rules';
Expand Down Expand Up @@ -32,12 +31,7 @@ export async function createConfirmationContent({
<Box>
<Section>
{renderRules({
rules: [
startTimeRule,
periodAmountRule,
periodTypeRule,
periodDurationRule,
],
rules: [startTimeRule, periodAmountRule, periodTypeRule],
context,
metadata,
})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ 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 { getClosestTimePeriod, TIME_PERIOD_TO_SECONDS } from '../../utils/time';
import { parseUnits, formatUnitsFromHex } from '../../utils/value';
import {
validateAndParseAmount,
Expand Down Expand Up @@ -178,19 +177,12 @@ 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';
}
console.log('zzzz', data);

const periodType = getClosestTimePeriod(BigInt(data.periodDuration));
const periodDuration = TIME_PERIOD_TO_SECONDS[periodType].toString();

console.log('yyyyy', { periodType, periodDuration });

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,28 @@ export const periodTypeRule: RuleDefinition<
NativeTokenPeriodicMetadata
> = {
name: PERIOD_TYPE_ELEMENT,
label: 'Period duration',
label: 'Transfer Window',
type: 'dropdown',
getRuleData: ({ context, metadata }) => ({
isAdjustmentAllowed: context.isAdjustmentAllowed,
value: context.permissionDetails.periodType,
isVisible: true,
tooltip: 'The duration of the period',
options: [TimePeriod.DAILY, TimePeriod.WEEKLY, 'Other'],
options: [
TimePeriod.HOURLY,
TimePeriod.DAILY,
TimePeriod.WEEKLY,
TimePeriod.BIWEEKLY,
TimePeriod.MONTHLY,
TimePeriod.YEARLY,
],
error: metadata.validationErrors.periodTypeError,
}),
updateContext: (context: NativeTokenPeriodicContext, value: string) => {
const periodType = value as TimePeriod | 'Other';
const periodDuration =
periodType === 'Other'
? context.permissionDetails.periodDuration
: Number(TIME_PERIOD_TO_SECONDS[periodType]).toString();
const periodType = value as TimePeriod;
const periodDuration = Number(
TIME_PERIOD_TO_SECONDS[periodType],
).toString();

return {
...context,
Expand All @@ -70,29 +76,6 @@ export const periodTypeRule: RuleDefinition<
},
};

export const periodDurationRule: RuleDefinition<
NativeTokenPeriodicContext,
NativeTokenPeriodicMetadata
> = {
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: NativeTokenPeriodicContext, value: string) => ({
...context,
permissionDetails: {
...context.permissionDetails,
periodDuration: value,
},
}),
};

export const startTimeRule: RuleDefinition<
NativeTokenPeriodicContext,
NativeTokenPeriodicMetadata
Expand Down Expand Up @@ -162,7 +145,6 @@ export const expiryRule: RuleDefinition<
export const allRules = [
periodAmountRule,
periodTypeRule,
periodDurationRule,
startTimeRule,
expiryRule,
];
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export type NativeTokenPeriodicMetadata = BaseMetadata & {
export type NativeTokenPeriodicContext = BaseContext & {
permissionDetails: {
periodAmount: string;
periodType: TimePeriod | 'Other';
periodType: TimePeriod;
periodDuration: string;
startTime: number;
};
Expand Down
41 changes: 38 additions & 3 deletions packages/gator-permissions-snap/src/utils/time.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,9 +253,44 @@ export const getStartOfNextDayUTC = (): number => {
* A mapping of time periods to their equivalent seconds.
*/
export const TIME_PERIOD_TO_SECONDS: Record<TimePeriod, bigint> = {
[TimePeriod.HOURLY]: 60n * 60n, // 3,600(seconds)
[TimePeriod.DAILY]: 60n * 60n * 24n, // 86,400(seconds)
[TimePeriod.WEEKLY]: 60n * 60n * 24n * 7n, // 604,800(seconds)
// Monthly is difficult because months are not consistent in length.
// We approximate by calculating the number of seconds in 1/12th of a year.
[TimePeriod.MONTHLY]: (60n * 60n * 24n * 365n) / 12n, // 2,629,760(seconds)
[TimePeriod.BIWEEKLY]: 60n * 60n * 24n * 14n, // 1,209,600(seconds)
[TimePeriod.MONTHLY]: 60n * 60n * 24n * 30n, // 2,592,000(seconds)
[TimePeriod.YEARLY]: 60n * 60n * 24n * 365n, // 31,536,000(seconds)
};

/**
* Finds the closest TimePeriod enum value for a given duration in seconds.
*
* @param seconds - The duration in seconds to match.
* @returns The TimePeriod that most closely matches the given duration.
* @throws InvalidInputError if no time periods are available.
*/
export const getClosestTimePeriod = (seconds: bigint): TimePeriod => {
const timePeriodEntries = Object.entries(TIME_PERIOD_TO_SECONDS) as [
TimePeriod,
bigint,
][];

const firstEntry = timePeriodEntries[0];
if (!firstEntry) {
throw new InvalidInputError('No time periods available');
}

let closestPeriod = firstEntry[0];
let minDifference =
seconds > firstEntry[1] ? seconds - firstEntry[1] : firstEntry[1] - seconds;

for (const [period, periodValue] of timePeriodEntries.slice(1)) {
const difference =
seconds > periodValue ? seconds - periodValue : periodValue - seconds;
if (difference < minDifference) {
minDifference = difference;
closestPeriod = period;
}
}

return closestPeriod;
};
Loading
Loading