Aftermath
  • Aftermath
    • About Aftermath Finance
      • What are we building?
  • Getting Started
    • Creating an account
      • zkLogin
        • Removing a zkLogin account
      • Sui Metamask Snap
      • Native Sui wallets
    • Dynamic Gas
    • Navigating Aftermath
      • Interacting with your Wallet
      • Viewing your Portfolio
      • Changing your Settings
      • Bridge
      • Referrals
  • Trade
    • Smart-Order Router
      • Agg of Aggs
      • Making a trade
      • Exact Out
      • Fees
    • DCA
      • Why should I use DCA
      • How does DCA work
      • Tutorials
        • Creating a DCA order
        • Monitoring DCA progress
        • Advanced Features
      • Fees
      • Contracts
  • Limit Orders
    • Contracts
    • Fees
  • Pools
    • Constant Function Market Maker
      • Tutorials
        • Depositing
        • Withdrawing
        • Creating a Pool
      • Fees
      • Contracts
      • Audit
  • Farms
    • Afterburner Vaults
      • Tutorials
        • Staking into a Farm
        • Claiming Rewards
        • Unstaking
        • Creating a Farm
      • Architecture
        • Vault
        • Stake Position
      • Fees
      • FAQs
  • Liquid Staking
    • afSUI
      • Tutorials
        • Staking
        • Unstaking
      • Architecture
        • Packages & Modules
        • Entry Points
      • Fees
      • FAQs
      • Contracts
      • Audit
  • Perpetuals
    • Aftermath Perpetuals
      • Tutorials
        • Creating an Account
        • Selecting a Market
        • Creating a Market Order
        • Creating a Limit Order
        • Maintaining your Positions
      • Architecture
        • Oracle Prices
        • Margin
        • Account
        • Trading
        • Funding
        • Liquidations
        • Fees
  • GameFi
    • NFT AMM
      • Architecture
        • Fission Vaults
        • AMM Pools
      • Tutorials
        • Buy
        • Sell
        • Deposit
        • Withdraw
      • Sui Overflow
  • Our validator
    • About us
  • Developers
    • Aftermath TS SDK
      • Utils
        • Coin
        • Users Data
        • Authorization
      • Products
        • Prices
        • Router
        • DCA
        • Limit Orders
        • Liquid Staking
        • Pools
        • Farms
    • Aftermath REST API
      • Authorization
  • Egg
    • About Egg
  • Legal
    • Terms of Service
    • Privacy Policy
  • Languages
    • 中文
    • 한국어
  • Links
    • X
    • Telegram
    • Discord
    • Github
    • Medium
    • Aftermath Validator
Powered by GitBook
On this page
  • Rate Limits
  • Example Usage
  1. Developers
  2. Aftermath REST API

Authorization

Use increased rate limits with our REST API

PreviousAftermath REST APINextAbout Egg

Last updated 1 day ago

A system for obtaining and managing increased API rate limits through request authorization.

Rate Limits

Default rate limit: 1000 requests per 10 seconds.

To request increased limits:

  1. Visit

  2. Open a ticket with:

sui-address: <your-sui-address>
rate-limit-requests: [<sdk-call>: <desired-rate-limit>]

Example Usage

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();
Aftermath Discord