aftermath-ts-sdk
Version:
Aftermath TypeScript SDK
290 lines (289 loc) • 12.2 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Auth = void 0;
const caller_1 = require("../../general/utils/caller");
const utils_1 = require("../../general/utils");
/**
* The `Auth` class manages creation and refreshing of access tokens
* to obtain higher rate limits on the Aftermath API. It includes methods
* to initialize authorization either by a direct callback-based approach
* or by importing a local Sui keystore. Optionally, administrative functions
* are provided for creating specialized auth accounts.
*
* @example
* ```typescript
* const auth = new Auth();
* const stopAuth = await auth.init({
* walletAddress: "0x<address>",
* signMessageCallback: async ({ message }) => {
* // sign message
* },
* });
* // ... make authenticated requests ...
* stopAuth(); // stop auto refresh
* ```
*/
class Auth extends caller_1.Caller {
// =========================================================================
// Constructor
// =========================================================================
/**
* Creates a new `Auth` instance for token-based rate limit increases.
*
* @param config - Optional caller configuration, including network and access token.
*/
constructor(config) {
super(config, "auth");
// =========================================================================
// Private Class Members
// =========================================================================
/**
* Holds the timer reference for scheduled token refreshes.
*/
this.refreshTimer = null;
/**
* Indicates whether the user has canceled auto token refresh.
*/
this.isCanceled = false;
}
// =========================================================================
// User-Facing
// =========================================================================
/**
* Initializes the auth system by fetching an access token for the provided wallet address.
* After obtaining the token, it automatically schedules periodic refresh calls until stopped.
*
* @param inputs - An object containing the user's `walletAddress` and a `signMessageCallback` function
* for cryptographically signing messages.
*
* @returns A function that, when called, cancels further token refresh attempts.
*
* @example
* ```typescript
* const auth = new Auth();
* const stopAuth = await auth.init({
* walletAddress: "0x<address>",
* signMessageCallback: async ({ message }) => {
* // sign the message with your private key / keypair
* },
* });
*
* // ... make authorized calls ...
*
* stopAuth(); // Cancel further token refreshes
* ```
*/
init(inputs) {
return __awaiter(this, void 0, void 0, function* () {
this.isCanceled = false; // Mark as active
const startRefresh = () => __awaiter(this, void 0, void 0, function* () {
if (this.isCanceled)
return; // No-op if canceled
const { accessToken, expirationTimestamp } = yield this.getAccessToken(inputs);
this.setAccessToken(accessToken);
if (this.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
this.refreshTimer = setTimeout(startRefresh, interval);
});
// Kick off first refresh
yield startRefresh();
// Return cancellation function
return () => {
this.isCanceled = true;
if (this.refreshTimer) {
clearTimeout(this.refreshTimer);
}
};
});
}
/**
* Initializes the auth system by reading a local Sui keystore file (on the server side),
* using the private keys matching a provided address to sign messages for token creation.
* After the token is obtained, it automatically schedules periodic refresh calls until stopped.
*
* @param inputs - An object containing the target `walletAddress` and an optional path to the `.keystore`.
* If `path` is not provided, it defaults to `~/.sui/sui_config/sui.keystore`.
* @returns A function that, when called, cancels further token refresh attempts.
*
* @throws If this method is called in a browser environment (client-side).
*
* @example
* ```typescript
* // On server:
* const stopAuth = await auth.initFromSuiKeystore({
* walletAddress: "0x<address>",
* path: "/custom/path/to/keystore.json",
* });
* // authorized calls...
* stopAuth();
* ```
*/
// public async initFromSuiKeystore(inputs: {
// walletAddress: SuiAddress;
// path?: string;
// }): Promise<() => void> {
// const { walletAddress, path: pathStr } = inputs;
// if (typeof window === "undefined") {
// // Node environment, proceed with reading a keystore
// const fs = require("fs");
// const path = require("path");
// const os = require("os");
// const keystorePath = pathStr
// ? path.join(pathStr)
// : (() => {
// // Default to ~/.sui/sui_config/sui.keystore
// const homeDir = os.homedir();
// if (!homeDir) {
// throw new Error(
// "cannot obtain home directory path"
// );
// }
// return path.join(
// homeDir,
// ".sui",
// "sui_config",
// "sui.keystore"
// );
// })();
// // Read JSON with an array of private keys
// let privateKeys: string[];
// try {
// const fileContent = fs.readFileSync(keystorePath, "utf-8");
// privateKeys = JSON.parse(fileContent);
// if (!Array.isArray(privateKeys)) {
// throw new Error(
// "Invalid keystore format: Expected an array of private keys"
// );
// }
// } catch (error) {
// throw new Error(`Failed to read keystore file: ${error}`);
// }
// if (privateKeys.length <= 0) {
// throw new Error(`Empty keystore file`);
// }
// // Find the matching key for the requested walletAddress
// const foundKeypair = privateKeys
// .map((privateKey) => Helpers.keypairFromPrivateKey(privateKey))
// .find(
// (keypair) =>
// Helpers.addLeadingZeroesToType(
// keypair.toSuiAddress()
// ) === Helpers.addLeadingZeroesToType(walletAddress)
// );
// if (!foundKeypair) {
// throw new Error(
// `No private key found in keystore file for ${walletAddress}`
// );
// }
// // Initialize with sign callback
// return this.init({
// walletAddress,
// signMessageCallback: async ({ message }) =>
// foundKeypair.signPersonalMessage(message),
// });
// }
// throw new Error("`initFromSuiKeystore` must be called on server-side");
// }
// =========================================================================
// Admin
// =========================================================================
/**
* **Admin-only**: Creates a new auth account with specific rate limits for a given
* `accountWalletAddress`. The `walletAddress` performing this action must have
* admin privileges, or the call will fail. Use this to create custom sub-accounts
* with limited scope or usage rates.
*
* @param inputs - Contains:
* - `walletAddress`: The admin's wallet address
* - `signMessageCallback`: The admin's signing callback
* - `accountName`: A short name or identifier for the account
* - `accountWalletAddress`: The Sui address representing this sub-account
* - `rateLimits`: An array specifying the rate limits (method-based) for the sub-account
* @returns A promise resolving to `true` if successful, otherwise throws or returns `false`.
*/
adminCreateAuthAccount(inputs) {
return __awaiter(this, void 0, void 0, function* () {
const { walletAddress, signMessageCallback, accountName, accountWalletAddress, rateLimits, } = inputs;
// Prepare the data to sign
const serializedJson = Auth.createSerializedJson("AccountCreate", {
sub: accountName,
wallet_address: utils_1.Helpers.addLeadingZeroesToType(accountWalletAddress),
rate_limits: rateLimits,
});
const message = new TextEncoder().encode(serializedJson);
const { signature } = yield signMessageCallback({ message });
return this.fetchApi("create-account", {
signature,
serializedJson,
walletAddress: utils_1.Helpers.addLeadingZeroesToType(walletAddress),
});
});
}
// =========================================================================
// Private
// =========================================================================
/**
* Requests a new access token from the API by sending a signed message
* indicating the user wants a token.
*
* @param inputs - Contains the user's `walletAddress` and `signMessageCallback`.
* @returns A response object that includes the `accessToken` and an `expirationTimestamp`.
*/
getAccessToken(inputs) {
return __awaiter(this, void 0, void 0, function* () {
const { walletAddress, signMessageCallback } = inputs;
// Prepare signable data
const serializedJson = Auth.createSerializedJson("GetAccessToken", {});
const message = new TextEncoder().encode(serializedJson);
const { signature } = yield signMessageCallback({ message });
return this.fetchApi("access-token", {
signature,
serializedJson,
walletAddress: utils_1.Helpers.addLeadingZeroesToType(walletAddress),
});
});
}
// =========================================================================
// Private Static
// =========================================================================
/**
* Creates a JSON string with a standard format:
* ```json
* {
* "date": <epoch-seconds>,
* "nonce": <random_number>,
* "method": <method_string>,
* "value": <passed_value>
* }
* ```
*
* @param method - A short method name describing the action ("GetAccessToken", "AccountCreate", etc.).
* @param value - The data object to embed under the `value` field.
* @returns A JSON-serialized string for signing.
*/
static createSerializedJson(method, value) {
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);
}
}
exports.Auth = Auth;