Authorization
Use increased rate limits with our REST API
Last updated
Use increased rate limits with our REST API
Last updated
A system for obtaining and managing increased API rate limits through request authorization.
Default rate limit: 1000 requests per 10 seconds.
To request increased limits:
Visit
Open a ticket with:
sui-address: <your-sui-address>
rate-limit-requests: [<sdk-call>: <desired-rate-limit>]
import { decodeSuiPrivateKey, Keypair } from "@mysten/sui/cryptography";
import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";
import { Secp256k1Keypair } from "@mysten/sui/keypairs/secp256k1";
import { Secp256r1Keypair } from "@mysten/sui/keypairs/secp256r1";
const keypairFromPrivateKey = (privateKey: string): Keypair => {
const parsedKeypair = decodeSuiPrivateKey(privateKey);
switch (parsedKeypair.schema) {
case "ED25519":
return Ed25519Keypair.fromSecretKey(parsedKeypair.secretKey);
case "Secp256k1":
return Secp256k1Keypair.fromSecretKey(parsedKeypair.secretKey);
case "Secp256r1":
return Secp256r1Keypair.fromSecretKey(parsedKeypair.secretKey);
default:
throw new Error(`unsupported schema \`${parsedKeypair.schema}\``);
}
};
const createSerializedJson = <DataToSerialize extends Object>(
method: string,
value: DataToSerialize
) => {
const timestampSeconds = Math.floor(Date.now() / 1000);
const random = Math.floor(Math.random() * 1024 * 1024);
const data = {
date: timestampSeconds,
nonce: random,
method,
value,
};
return JSON.stringify(data);
};
type SignMessageCallback = (args: { message: Uint8Array }) => Promise<{
signature: string;
}>;
/**
* The response returned when a user obtains or refreshes an access token,
* containing the token string, the HTTP header name (usually "Authorization"),
* and the token's expiration timestamp in milliseconds.
*/
interface ApiGetAccessTokenResponse {
/**
* The newly issued access token to be used in `Authorization` headers.
*/
accessToken: string;
/**
* The header key that should contain `accessToken` (e.g., "Authorization").
*/
header: string;
/**
* The UNIX timestamp (milliseconds) after which the token is invalid.
*/
expirationTimestamp: number;
}
const getAccessToken = async (inputs: {
walletAddress: string;
signMessageCallback: SignMessageCallback;
}): Promise<ApiGetAccessTokenResponse> => {
const { walletAddress, signMessageCallback } = inputs;
// Prepare signable data
const serializedJson = createSerializedJson("GetAccessToken", {});
const message = new TextEncoder().encode(serializedJson);
const { signature } = await signMessageCallback({ message });
const res = await fetch("https://aftermath.finance/api/auth/access-token", {
method: "POST",
body: JSON.stringify({
signature,
serializedJson,
walletAddress,
}),
headers: {
"Content-Type": "application/json",
},
});
return (await res.json()) as unknown as ApiGetAccessTokenResponse;
};
let accessToken: string | undefined = undefined;
let isCanceled = false;
let refreshTimer: ReturnType<typeof setTimeout> | null = null;
const init = async (inputs: {
walletAddress: string;
signMessageCallback: SignMessageCallback;
}): Promise<() => void> => {
isCanceled = false; // Mark as active
const startRefresh = async () => {
if (isCanceled) return; // No-op if canceled
const { accessToken: newAccessToken, expirationTimestamp } =
await getAccessToken(inputs);
accessToken = newAccessToken;
if (isCanceled) return; // Double-check after token fetch
// Provide a margin by refreshing before actual expiration
const TIMEOUT_REDUCTION_RATIO = 0.9;
const interval =
(expirationTimestamp - Date.now()) * TIMEOUT_REDUCTION_RATIO;
// Schedule next refresh
refreshTimer = setTimeout(startRefresh, interval);
};
// Kick off first refresh
await startRefresh();
// Return cancellation function
return () => {
isCanceled = true;
if (refreshTimer) {
clearTimeout(refreshTimer);
}
};
};
const main = async () => {
// Initialize with private key
const keypair = keypairFromPrivateKey("your-private-key");
const stopAuth = await init({
signMessageCallback: async ({ message }) => {
const { signature } = await keypair.signPersonalMessage(message);
return { signature };
},
walletAddress: keypair.toSuiAddress(),
});
// Make authorized requests
const res = await fetch("https://aftermath.finance/api/pools", {
method: "POST",
body: JSON.stringify({
poolIds: [
"0x97aae7a80abb29c9feabbe7075028550230401ffe7fb745757d3c28a30437408",
],
}),
headers: {
"Content-Type": "application/json",
// Set auth header
...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}),
},
});
console.log(await res.json());
// Clean up
stopAuth();
};
main();