Skip to content

Commit

Permalink
Merge pull request #4007 from osmosis-labs/stage
Browse files Browse the repository at this point in the history
Publish Stage
  • Loading branch information
JoseRFelix authored Dec 10, 2024
2 parents 7fb1c6f + 2ed6cc5 commit 24d18c2
Show file tree
Hide file tree
Showing 36 changed files with 463 additions and 203 deletions.
2 changes: 1 addition & 1 deletion packages/trpc/src/one-click-trading.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const oneClickTradingRouter = createTRPCRouter({
spendLimitTokenDecimals: usdcAsset.coinDecimals,
networkFeeLimit: OneClickTradingMaxGasLimit,
sessionPeriod: {
end: "1hour" as const,
end: "7days" as const,
},
};
}
Expand Down
8 changes: 3 additions & 5 deletions packages/types/src/one-click-trading-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,10 @@ export interface OneClickTradingTimeLimit {
}

export type OneClickTradingHumanizedSessionPeriod =
| "5min"
| "10min"
| "30min"
| "1hour"
| "3hours"
| "12hours";
| "1day"
| "7days"
| "30days";

export interface OneClickTradingTransactionParams {
isOneClickEnabled: boolean;
Expand Down
114 changes: 114 additions & 0 deletions packages/utils/src/__tests__/date.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { safeTimeout } from "../date";

describe("safeTimeout", () => {
jest.useFakeTimers(); // Use fake timers for controlled testing
jest.spyOn(global, "setTimeout"); // Spy on the setTimeout function

afterEach(() => {
jest.clearAllTimers(); // Clear timers after each test
jest.clearAllMocks();
});

test("calls setTimeout directly for time within safe range", () => {
const callback = jest.fn();
const timeInRange = 5000; // 5 seconds

safeTimeout(callback, timeInRange);

// Fast-forward time
jest.advanceTimersByTime(timeInRange);

// Ensure the callback is called once
expect(callback).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), timeInRange);
});

test("splits large timeout into smaller intervals", () => {
const callback = jest.fn();
const largeTimeout = 2591160000; // 29 days, 23 hours, 46 minutes
const MAX_TIMEOUT = 2 ** 31 - 1; // JavaScript's maximum timeout (~24.8 days)

safeTimeout(callback, largeTimeout);

// Fast-forward the first interval
jest.advanceTimersByTime(MAX_TIMEOUT);

// Ensure another setTimeout was scheduled for the remainder
expect(setTimeout).toHaveBeenCalledTimes(2);

// Fast-forward the remaining time
jest.advanceTimersByTime(largeTimeout - MAX_TIMEOUT);

// Ensure the callback is called once after both intervals
expect(callback).toHaveBeenCalledTimes(1);
});

test("handles exactly the maximum timeout limit", () => {
const callback = jest.fn();
const exactMaxTimeout = 2 ** 31 - 1;

safeTimeout(callback, exactMaxTimeout);

// Fast-forward time
jest.advanceTimersByTime(exactMaxTimeout);

// Ensure the callback is called once
expect(callback).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenCalledWith(
expect.any(Function),
exactMaxTimeout
);
});

test("does not call the callback prematurely", () => {
const callback = jest.fn();
const largeTimeout = 2591160000; // 29 days, 23 hours, 46 minutes

safeTimeout(callback, largeTimeout);

// Fast-forward time but do not complete all intervals
jest.advanceTimersByTime(2 ** 31 - 2); // Just shy of the max timeout

// Callback should not be called yet
expect(callback).not.toHaveBeenCalled();
});

test("clears the timeout before it completes", () => {
const callback = jest.fn(); // Mock callback function
const largeTimeout = 2591160000; // ~30 days

// Set up the safe timeout
const { clear } = safeTimeout(callback, largeTimeout);

// Fast-forward time by 5 seconds and then clear the timeout
jest.advanceTimersByTime(5000); // Advance 5 seconds
clear(); // Clear the timeout early

// Advance time further to ensure the callback does not execute
jest.advanceTimersByTime(largeTimeout);

// Verify that the callback was never called
expect(callback).not.toHaveBeenCalled();
});

test("clears the second timer in a split timeout chain", () => {
const callback = jest.fn(); // Mock callback function
const largeTimeout = 2591160000; // ~30 days
const MAX_TIMEOUT = 2 ** 31 - 1; // JavaScript's maximum timeout (~24.8 days)

// Set up the timeout
const { clear } = safeTimeout(callback, largeTimeout);

// Advance time to the end of the first timer but before the second timer finishes
jest.advanceTimersByTime(MAX_TIMEOUT);

// Clear the timeout during the second interval
clear();

// Advance time to simulate the remainder of the second timer
jest.advanceTimersByTime(largeTimeout - MAX_TIMEOUT);

// Verify that the callback was not called
expect(callback).not.toHaveBeenCalled();
});
});
35 changes: 35 additions & 0 deletions packages/utils/src/date.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,38 @@ export function unixNanoSecondsToSeconds(
): number {
return Number(unixNanoSeconds) / 1000000000;
}

/**
* A safe version of setTimeout that splits large timeouts into smaller intervals.
* This is necessary because the maximum timeout for setTimeout is 24.8 days.
* @param callback - The function to call after the timeout.
* @param milliseconds - The number of milliseconds to wait before calling the callback.
*/
export function safeTimeout(callback: () => void, milliseconds: number) {
const MAX_TIMEOUT = 2 ** 31 - 1; // Maximum safe timeout
let timeoutId: NodeJS.Timeout | null = null; // Track the current timeout
let cleared = false; // Track if the timeout has been cleared

const clear = () => {
cleared = true; // Mark as cleared
if (timeoutId) {
clearTimeout(timeoutId); // Clear the current timeout
}
};

const runTimeout = (remainingTime: number) => {
if (cleared) return; // Do nothing if cleared

if (remainingTime <= MAX_TIMEOUT) {
timeoutId = setTimeout(callback, remainingTime);
} else {
timeoutId = setTimeout(() => {
runTimeout(remainingTime - MAX_TIMEOUT); // Recurse with the remaining time
}, MAX_TIMEOUT);
}
};

runTimeout(milliseconds);

return { clear };
}
2 changes: 1 addition & 1 deletion packages/web/components/complex/add-conc-liquidity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export const AddConcLiquidity: FunctionComponent<
const { queryQuasarVaults } = queriesExternalStore;
const { vaults: quasarVaults } = queryQuasarVaults.get(poolId);

const { data: pool } = api.edge.pools.getPool.useQuery({
const { data: pool } = api.local.pools.getPool.useQuery({
poolId,
});

Expand Down
2 changes: 1 addition & 1 deletion packages/web/components/complex/pools-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export const PoolsTable = (props: PropsWithChildren<PoolsTableProps>) => {
isFetchingNextPage,
hasNextPage,
fetchNextPage,
} = api.edge.pools.getPools.useInfiniteQuery(
} = api.local.pools.getPools.useInfiniteQuery(
{
limit,
search: filters.searchQuery
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,26 @@ import {
} from "~/components/screen-manager";
import { useOneClickTradingSession, useTranslation } from "~/hooks";

export const oneClickTradingTimeMappings = {
"1hour": "oneClickTrading.sessionPeriods.1hour",
"1day": "oneClickTrading.sessionPeriods.1day",
"7days": "oneClickTrading.sessionPeriods.7days",
"30days": "oneClickTrading.sessionPeriods.30days",
};

export function getSessionPeriodTranslationKey(
input: OneClickTradingHumanizedSessionPeriod
) {
const timeMappings = {
"5min": "oneClickTrading.sessionPeriods.5min",
"10min": "oneClickTrading.sessionPeriods.10min",
"30min": "oneClickTrading.sessionPeriods.30min",
"1hour": "oneClickTrading.sessionPeriods.1hour",
"3hours": "oneClickTrading.sessionPeriods.3hours",
"12hours": "oneClickTrading.sessionPeriods.12hours",
};
const mapped = timeMappings[input];
const mapped = oneClickTradingTimeMappings[input];
if (!mapped) throw new Error(`No mapping for ${input}`);
return mapped;
}

const SessionPeriods: OneClickTradingHumanizedSessionPeriod[] = [
"5min",
"10min",
"30min",
"1hour",
"3hours",
"12hours",
"1day",
"7days",
"30days",
];

interface SessionPeriodScreenProps extends OneClickTradingBaseScreenProps {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,36 +295,27 @@ export async function makeCreate1CTSessionMessage({

let sessionPeriod: OneClickTradingTimeLimit;
switch (transaction1CTParams.sessionPeriod.end) {
case "5min":
sessionPeriod = {
end: unixSecondsToNanoSeconds(dayjs().add(5, "minute").unix()),
};
break;
case "10min":
sessionPeriod = {
end: unixSecondsToNanoSeconds(dayjs().add(10, "minute").unix()),
};
break;
case "30min":
case "1hour":
sessionPeriod = {
end: unixSecondsToNanoSeconds(dayjs().add(30, "minute").unix()),
end: unixSecondsToNanoSeconds(dayjs().add(1, "hour").unix()),
};
break;
case "1hour":
case "1day":
sessionPeriod = {
end: unixSecondsToNanoSeconds(dayjs().add(1, "hour").unix()),
end: unixSecondsToNanoSeconds(dayjs().add(1, "day").unix()),
};
break;
case "3hours":
case "7days":
sessionPeriod = {
end: unixSecondsToNanoSeconds(dayjs().add(3, "hours").unix()),
end: unixSecondsToNanoSeconds(dayjs().add(7, "day").unix()),
};
break;
case "12hours":
case "30days":
sessionPeriod = {
end: unixSecondsToNanoSeconds(dayjs().add(12, "hours").unix()),
end: unixSecondsToNanoSeconds(dayjs().add(30, "day").unix()),
};
break;

default:
throw new Error(
`Unsupported time limit: ${transaction1CTParams.sessionPeriod.end}`
Expand Down
Loading

0 comments on commit 24d18c2

Please sign in to comment.