{"version":3,"file":"dispenser-client.mjs","sources":["../../src/types/dispenser-client.ts"],"sourcesContent":["import { Address } from 'algosdk'\nimport { asJson } from '../util'\n\nconst DISPENSER_BASE_URL = 'https://api.dispenser.algorandfoundation.tools'\nconst DEFAULT_DISPENSER_REQUEST_TIMEOUT = 15\nconst DISPENSER_ACCESS_TOKEN_KEY = 'ALGOKIT_DISPENSER_ACCESS_TOKEN'\n\ninterface ErrorResponse {\n  code?: string\n}\n\nenum DispenserAssetName {\n  Algo = 0,\n}\n\nconst dispenserAssets = {\n  [DispenserAssetName.Algo]: {\n    assetId: 0,\n    decimals: 6,\n    description: 'Algo',\n  },\n}\n\n/** The TestNet Dispenser API response when funding. */\nexport interface DispenserFundResponse {\n  /** The ID of the transaction that was issued to fund the account. */\n  txId: string\n  /** The number of µAlgo that was funded. */\n  amount: number\n}\n\n/** The TestNet Dispenser API response when getting the current limit.  */\nexport interface DispenserLimitResponse {\n  /** The limit, in µAlgo, that you can currently fund. */\n  amount: number\n}\n\n/** The parameters to construct a TestNet Dispenser API client. */\nexport interface TestNetDispenserApiClientParams {\n  /** The API auth token */\n  authToken: string\n  /** The request timeout in seconds */\n  requestTimeout?: number\n}\n\n/**\n * `TestNetDispenserApiClient` is a class that provides methods to interact with the [Algorand TestNet Dispenser API](https://github.com/algorandfoundation/algokit/blob/main/docs/testnet_api.md).\n * It allows you to fund an address with Algo, refund a transaction, and get the funding limit for the Algo asset.\n *\n * The class requires an authentication token and a request timeout to be initialized. The authentication token can be provided\n * either directly as a parameter or through an `ALGOKIT_DISPENSER_ACCESS_TOKEN` environment variable. If neither is provided, an error is thrown.\n *\n * The request timeout can be provided as a parameter. If not provided, a default value is used.\n *\n * @property {string} authToken - The authentication token used for API requests.\n * @property {number} requestTimeout - The timeout for API requests, in seconds.\n *\n * @method fund - Sends a funding request to the dispenser API to fund the specified address with the given amount of Algo.\n * @method refund - Sends a refund request to the dispenser API for the specified refundTxnId.\n * @method limit - Sends a request to the dispenser API to get the funding limit for the Algo asset.\n *\n * @example\n * ```typescript\n * const client = new TestNetDispenserApiClient({ authToken: 'your_auth_token', requestTimeout: 30 });\n * const fundResponse = await client.fund('your_address', 100);\n * const limitResponse = await client.getLimit();\n * await client.refund('your_transaction_id');\n * ```\n *\n * @throws {Error} If neither the environment variable 'ALGOKIT_DISPENSER_ACCESS_TOKEN' nor the authToken parameter were provided.\n */\nexport class TestNetDispenserApiClient {\n  private _authToken: string\n  private _requestTimeout: number\n\n  constructor(params?: TestNetDispenserApiClientParams) {\n    const authTokenFromEnv = process?.env?.[DISPENSER_ACCESS_TOKEN_KEY]\n\n    if (params?.authToken) {\n      this._authToken = params.authToken\n    } else if (authTokenFromEnv) {\n      this._authToken = authTokenFromEnv\n    } else {\n      throw new Error(\n        `Can't init AlgoKit TestNet Dispenser API client because neither environment variable ${DISPENSER_ACCESS_TOKEN_KEY} or the authToken were provided.`,\n      )\n    }\n\n    this._requestTimeout = params?.requestTimeout || DEFAULT_DISPENSER_REQUEST_TIMEOUT\n  }\n\n  get authToken(): string {\n    return this._authToken\n  }\n\n  get requestTimeout(): number {\n    return this._requestTimeout\n  }\n\n  /**\n   * Processes a dispenser API request.\n   *\n   * @param authToken - The authentication token.\n   * @param urlSuffix - The URL suffix for the API request.\n   * @param body - The request body.\n   * @param method - The HTTP method.\n   *\n   * @returns The API response.\n   */\n  private async processDispenserRequest(\n    authToken: string,\n    urlSuffix: string,\n    body: Record<string, string | number> | null = null,\n    method = 'POST',\n  ): Promise<Response> {\n    const headers = { Authorization: `Bearer ${authToken}` }\n\n    const requestArgs: RequestInit = {\n      method: method,\n      headers: headers,\n      signal: AbortSignal.timeout(this.requestTimeout * 1000),\n    }\n\n    if (body) {\n      requestArgs.body = asJson(body)\n    }\n\n    const response = await fetch(`${DISPENSER_BASE_URL}/${urlSuffix}`, requestArgs)\n    if (!response.ok) {\n      let error_message = `Error processing dispenser API request: ${response.status}`\n      let error_response = null\n      try {\n        error_response = await response.json()\n      } catch {\n        // suppress exception\n      }\n\n      if (error_response && (error_response as ErrorResponse).code) {\n        error_message = (error_response as ErrorResponse).code!\n      } else if (response.status === 400) {\n        const errorResponse = (await response.json()) as { message: string }\n        error_message = errorResponse.message\n      }\n\n      throw new Error(error_message)\n    }\n    return response\n  }\n\n  /**\n   * Sends a funding request to the dispenser API to fund the specified address with the given amount of Algo.\n   *\n   * @param address - The address to fund.\n   * @param amount - The amount of µAlgo to fund.\n   *\n   * @returns DispenserFundResponse: An object containing the transaction ID and funded amount.\n   */\n  async fund(address: string | Address, amount: number | bigint): Promise<DispenserFundResponse> {\n    const response = await this.processDispenserRequest(\n      this.authToken,\n      `fund/${dispenserAssets[DispenserAssetName.Algo].assetId}`,\n      {\n        receiver: typeof address === 'string' ? address : address.toString(),\n        amount: Number(amount),\n        assetID: dispenserAssets[DispenserAssetName.Algo].assetId,\n      },\n      'POST',\n    )\n\n    const content = (await response.json()) as { txID: string; amount: number }\n    return { txId: content.txID, amount: content.amount }\n  }\n\n  /**\n   * Sends a refund request to the dispenser API for the specified refundTxnId.\n   *\n   * @param refundTxnId - The transaction ID to refund.\n   */\n  async refund(refundTxnId: string): Promise<void> {\n    await this.processDispenserRequest(this.authToken, 'refund', { refundTransactionID: refundTxnId }, 'POST')\n  }\n\n  /**\n   * Sends a request to the dispenser API to get the funding limit for the Algo asset.\n   *\n   * @returns DispenserLimitResponse: An object containing the funding limit amount.\n   */\n  async getLimit(): Promise<DispenserLimitResponse> {\n    const response = await this.processDispenserRequest(\n      this.authToken,\n      `fund/${dispenserAssets[DispenserAssetName.Algo].assetId}/limit`,\n      null,\n      'GET',\n    )\n    const content = (await response.json()) as { amount: number }\n\n    return { amount: content.amount }\n  }\n}\n"],"names":[],"mappings":"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"}