# Authorization

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 [Aftermath Discord](https://discord.com/invite/KvVCAauXk5)
2. Open a ticket with:

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

### Example Usage

```typescript
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();

```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.aftermath.finance/for-developers/api/rest-api/authorization.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
