Services

We set a unique and generic service in order to interact with Trustless Work API.

Overview

In order to have a good quality and clean code, we've generated a generic service which interacts with Trustless Work.

Generic Escrow Service

Every response and payload are completely typed with the entities that we had defined before.

import http from "@/config/axios";
import { AxiosError } from "axios";
import { signTransaction } from "../../auth/helpers/stellar-wallet-kit.helper";
import { handleError } from "@/errors/utils/handle-errors";
import { WalletError } from "@/@types/errors.entity";
import { kit } from "@/config/wallet-kit";
import { EscrowRequestResponse } from "@/@types/escrows/escrow-response.entity";
import { EscrowPayloadService } from "@/@types/escrows/escrow-payload.entity";
import { Escrow } from "@/@types/escrows/escrow.entity";
import { HttpMethod } from "@/@types/http.entity";

// Interface defining the required properties for escrow service operations
interface EscrowServiceProps<T extends EscrowPayloadService> {
  payload: T;
  endpoint: string;
  method: HttpMethod;
  requiresSignature?: boolean;
  returnEscrowDataIsRequired?: boolean;
}

/**
 * EscrowService class handles all escrow-related operations
 * including transaction signing and sending to the Stellar network
 */
export class EscrowService {
  private static instance: EscrowService;

  private constructor() {}

  /**
   * Get singleton instance of EscrowService
   */
  public static getInstance(): EscrowService {
    if (!EscrowService.instance) {
      EscrowService.instance = new EscrowService();
    }
    return EscrowService.instance;
  }

  /**
   * Handle GET requests that don't require signature
   */
  private async handleGetRequest<T extends EscrowPayloadService>(
    endpoint: string,
    payload: T
  ): Promise<EscrowRequestResponse> {
    const { data } = await http.get<EscrowRequestResponse>(endpoint, {
      params: payload,
    });
    return data;
  }

  /**
   * Get wallet address for transaction signing
   */
  private async getWalletAddress(): Promise<string> {
    const { address } = await kit.getAddress();
    return address;
  }

  /**
   * Sign transaction using wallet
   */
  private async signTransactionWithWallet(
    unsignedTransaction: string,
    address: string
  ): Promise<string> {
    // Sign the transaction using the Stellar Wallet Kit
    return await signTransaction({ unsignedTransaction, address });
  }

  /**
   * Send signed transaction to the network
   */
  private async sendSignedTransaction(
    signedXdr: string,
    returnEscrowDataIsRequired: boolean
  ): Promise<EscrowRequestResponse> {
    const tx = await http.post("/helper/send-transaction", {
      signedXdr,
      returnEscrowDataIsRequired,
    });
    return tx.data;
  }

  /**
   *
   * Main method to handle escrow operations
   *
   * @Reference URL: https://surli.cc/rlyqso
   *
   * @Flow:
   * 1. Get wallet address
   * 2. Make initial request to get [unsigned transaction]
   * 3. [Sign transaction] with wallet
   * 4. Send [signed transaction] to the network
   * 5. Return [escrow data]
   *
   * @Note:
   * - This method handles both GET and POST requests
   * - It also handles the signing of transactions
   * - It returns the escrow data if required
   * - It handles both HTTP and Wallet errors
   *
   */
  public async execute<T extends EscrowPayloadService>({
    payload,
    endpoint,
    method,
    requiresSignature = true,
    returnEscrowDataIsRequired = true,
  }: EscrowServiceProps<T>): Promise<EscrowRequestResponse | Escrow> {
    try {
      // Handle GET requests that don't require signature
      if (!requiresSignature && method === "get") {
        return await this.handleGetRequest(endpoint, payload);
      }

      // Get wallet address and make initial request
      const address = await this.getWalletAddress();
      const response = await http[method]<EscrowRequestResponse>(
        endpoint,
        payload
      );

      const { unsignedTransaction } = response.data;

      // Validate that we received an unsigned transaction
      if (!unsignedTransaction) {
        throw new Error("No unsigned transaction received from the server");
      }

      // Sign and send transaction
      const signedTxXdr = await this.signTransactionWithWallet(
        unsignedTransaction,
        address
      );

      // Send the signed transaction to the network
      return await this.sendSignedTransaction(
        signedTxXdr,
        returnEscrowDataIsRequired
      );
    } catch (error: unknown) {
      const mappedError = handleError(error as AxiosError | WalletError);
      console.error("Error:", mappedError.message);
      throw new Error(mappedError.message);
    }
  }
}

// Export a singleton instance for easy access
export const escrowService = EscrowService.getInstance();

This service will be used in the custom hooks where we send submits with the forms adapted to each Trustless Work endpoint.

Last updated

Was this helpful?