Skip to content

Commit fb02f46

Browse files
Fix server time issues
1 parent 1a69a49 commit fb02f46

File tree

2 files changed

+79
-0
lines changed

2 files changed

+79
-0
lines changed

apps/web/context/AptosCoreProvider.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ import { PropsWithChildren } from 'react';
22
import { AptosJSCoreProvider, useWalletAdapterCore } from '@aptos-labs/react';
33
import { useWallet } from '@aptos-labs/wallet-adapter-react';
44
import { Network } from '@aptos-labs/ts-sdk';
5+
import { getServerTime } from '@/lib/serverTime';
56

67
export default function AptosCoreProvider({ children }: PropsWithChildren) {
78
const wallet = useWallet();
89

910
const core = useWalletAdapterCore({
1011
wallet,
1112
config: {
13+
serverTime: getServerTime,
1214
apiKey: {
1315
...(process.env.NEXT_PUBLIC_APTOS_MAINNET_API_KEY && {
1416
[Network.MAINNET]: process.env.NEXT_PUBLIC_APTOS_MAINNET_API_KEY

apps/web/lib/serverTime.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright © Aptos
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
export const isProduction = process.env.NODE_ENV === 'production';
5+
6+
// URL for an API that responds with the server-side value of Date.now().
7+
const DATE_NOW_WORKER_URL = 'https://date-now.petra-wallet.workers.dev';
8+
9+
// Maximum allowed delta between server and client time, in milliseconds.
10+
const MAX_DELTA = 24 * 60 * 60 * 1000;
11+
12+
// The difference between the server time and the client time, in milliseconds.
13+
let serverTimeDelta: number | undefined;
14+
15+
async function getServerTimeDelta() {
16+
// The request cannot be cached (otherwise we would receive a stale timestamp).
17+
const requestTime = Date.now();
18+
const response = await fetch(`${DATE_NOW_WORKER_URL}?${requestTime}`);
19+
const responseTime = Date.now();
20+
21+
if (!response.ok) {
22+
throw new Error('Failed to fetch server time');
23+
}
24+
25+
const data = await response.json();
26+
27+
if (!/^\d{13}$/.test(data)) {
28+
throw new Error('Server did not respond with a timestamp');
29+
}
30+
31+
// Assume that the server generated its time halfway between request sent
32+
// and response received.
33+
const serverTime = Number(data);
34+
const clientTime = (requestTime + responseTime) / 2;
35+
const newDelta = serverTime - clientTime;
36+
37+
if (Number.isNaN(newDelta)) {
38+
throw new Error('newDelta is NaN');
39+
} else if (Math.abs(newDelta) > MAX_DELTA) {
40+
throw new Error('MAX_DELTA exceeded');
41+
}
42+
43+
return newDelta;
44+
}
45+
46+
/**
47+
* Synchronize local time with server time.
48+
*
49+
* The server time delta required to compute the current server time
50+
* is stored as a module variable.
51+
*/
52+
export async function synchronizeTime() {
53+
try {
54+
serverTimeDelta = await getServerTimeDelta();
55+
} catch (error) {
56+
if (!isProduction) {
57+
throw error;
58+
}
59+
}
60+
}
61+
62+
/**
63+
* Returns the current server time in milliseconds.
64+
*
65+
* A previous call to {@link synchronizeTime} will ensure the time is
66+
* synchronized with the server.
67+
*/
68+
export function getServerTime() {
69+
return Date.now() + (serverTimeDelta ?? 0);
70+
}
71+
72+
/**
73+
* Get a `Date` object for the current server time
74+
*/
75+
export function getServerDate() {
76+
return new Date(getServerTime());
77+
}

0 commit comments

Comments
 (0)