Guide to sending a specialized SPL token using the libraries '@solana/web3.js' and '@solana/sol-wallet-adapter'

Attempting to transfer a custom SPL token using the solana-wallet adapter is proving to be challenging due to difficulty in obtaining the wallet's secret key for signing the transaction.

Although I have reviewed resources on writing the transfer code, I am struggling to acquire the Signer with the solana-wallet adapter:

How can you transfer SOL using the web3.js sdk for Solana?

How to transfer custom token by '@solana/web3.js'

The examples available involve hardcoding the secret key which is not viable when utilizing a wallet extension.

Referring to the issue on the webadapter repository https://github.com/solana-labs/wallet-adapter/issues/120, it is advised to:

  1. Create a @solana/web3.js Transaction object and add instructions to it
  2. Sign the transaction with the wallet
  3. Send the transaction over a Connection

However, encountering challenges in finding relevant examples or documentation for carrying out step 1 and 2.

const SendTransaction: React.FC<Props> = ({ children }) => {
    const { connection } = useConnection()
    const { publicKey, sendTransaction } = useWallet()

    const onSendSPLTransaction = useCallback(
        async (toPubkey: string, amount: number) => {
            if (!toPubkey || !amount) return
            const toastId = toast.loading('Processing transaction...')

            try {
                if (!publicKey) throw new WalletNotConnectedError()
                const toPublicKey = new PublicKey(toPubkey)
                const mint = new PublicKey('Mint address')
                const payer = '????' // how to get this Signer
                const token = new Token(connection, mint, TOKEN_PROGRAM_ID, payer)
                const fromTokenAccount = await token.getOrCreateAssociatedAccountInfo(publicKey)
                const toTokenAccount = await token.getOrCreateAssociatedAccountInfo(toPublicKey)

                const transaction = new Transaction().add(
                    Token.createTransferInstruction(
                        TOKEN_PROGRAM_ID,
                        fromTokenAccount.address,
                        toTokenAccount.address,
                        publicKey,
                        [],
                        0
                    )
                )

                const signature = await sendTransaction(transaction, connection)

                const response = await connection.confirmTransaction(signature, 'processed')
                console.log('response', response)
                toast.success('Transaction sent', {
                    id: toastId,
                })
            } catch (error) {
                toast.error(`Transaction failed: ${error.message}`, {
                    id: toastId,
                })
            }
        },
        [publicKey, sendTransaction, connection]
    )

    return <>{children(onSendSPLTransaction)}</>
}

Answer №1

After thorough exploration, I've discovered a method to achieve this objective. It may require some tidying up and error management, but it enables a personalized token transaction through @solana/wallet-adapter.

// sendTransaction.tsx
import { WalletNotConnectedError } from '@solana/wallet-adapter-base'
import { useConnection, useWallet } from '@solana/wallet-adapter-react'
import { Transaction, PublicKey, LAMPORTS_PER_SOL } from '@solana/web3.js'
import React, { useCallback } from 'react'
import { toast } from 'react-hot-toast'
import { TOKEN_PROGRAM_ID } from '@solana/spl-token'
...
export default SendTransaction

// getOrCreateAssociatedTokenAccount.ts
import { TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID } from '@solana/spl-token'
import { SignerWalletAdapterProps } from '@solana/wallet-adapter-base'
import { Connection, PublicKey, Commitment, Transaction } from '@solana/web3.js'
...
export async function getOrCreateAssociatedTokenAccount(
    connection: Connection,
    payer: PublicKey,
    mint: PublicKey,
    owner: PublicKey,
    signTransaction: SignerWalletAdapterProps['signTransaction'],
    allowOwnerOffCurve = false,
    commitment?: Commitment,
    programId = TOKEN_PROGRAM_ID,
    associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID
) {
...

// createAssociatedTokenAccountInstruction.ts
import { TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID } from '@solana/spl-token'
import { PublicKey, TransactionInstruction, SystemProgram, SYSVAR_RENT_PUBKEY } from '@solana/web3.js'
...
export function createAssociatedTokenAccountInstruction(
    payer: PublicKey,
    associatedToken: PublicKey,
    owner: PublicKey,
    mint: PublicKey,
    programId = TOKEN_PROGRAM_ID,
    associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID
): TransactionInstruction {
...

// createTransferInstructions.ts
import { TOKEN_PROGRAM_ID } from '@solana/spl-token'
import { AccountMeta, PublicKey, Signer, TransactionInstruction } from '@solana/web3.js'
import BufferLayout from 'buffer-layout'
import BN from 'bn.js'
...
export function createTransferInstruction(
    source: PublicKey,
    destination: PublicKey,
    owner: PublicKey,
    amount: number,
    multiSigners: Signer[] = [],
    programId = TOKEN_PROGRAM_ID
): TransactionInstruction {
...

// getAccountInfo.ts
import { TOKEN_PROGRAM_ID, AccountLayout } from '@solana/spl-token'
import { Connection, PublicKey, Commitment } from '@solana/web3.js'
...
export enum AccountState {
...
export async function getAccountInfo(
    connection: Connection,
    address: PublicKey,
    commitment?: Commitment,
    programId = TOKEN_PROGRAM_ID
) {
...

// getAssociatedTokerAddress.ts
import { TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID } from '@solana/spl-token'
import { PublicKey } from '@solana/web3.js'
...
export async function getAssociatedTokenAddress(
    mint: PublicKey,
    owner: PublicKey,
    allowOwnerOffCurve = false,
    programId = TOKEN_PROGRAM_ID,
    associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID
): Promise<PublicKey> {
...

This solution aims to provide assistance to those in need. Any feedback or suggestions are greatly appreciated.

Answer №2

There are a couple of issues with the above answer

  1. Initially, there is a requirement for a significant amount of custom code creation when utilizing the built-in functions of '@solana/wallet-adapter', '@solana/spl-token', and '@solana/web3.js'.
  2. Secondly, if the receiving account does not exist, the user may be double charged by any connected wallet. The first charge would be to create the account, followed by a second charge to transfer the spl token. Additionally, it triggers the wallet approval modal twice, which is unfavorable for the user experience.

The code below facilitates the transfer of 1 USDC (USDC being one of the sol tokens on the Solana blockchain).

import React from 'react';
import {
  WalletNotConnectedError,
  SignerWalletAdapterProps
} from '@solana/wallet-adapter-base';
import { useConnection, useWallet } from '@solana/wallet-adapter-react';
import {
  createTransferInstruction,
  createAssociatedTokenAccountInstruction,
  getAssociatedTokenAddress,
  getAccount
} from '@solana/spl-token';
import {
  PublicKey,
  Transaction,
  Connection,
  TransactionInstruction
} from '@solana/web3.js';

export const configureAndSendCurrentTransaction = async (
  transaction: Transaction,
  connection: Connection,
  feePayer: PublicKey,
  signTransaction: SignerWalletAdapterProps['signTransaction']
) => {
  const blockHash = await connection.getLatestBlockhash();
  transaction.feePayer = feePayer;
  transaction.recentBlockhash = blockHash.blockhash;
  const signed = await signTransaction(transaction);
  const signature = await connection.sendRawTransaction(signed.serialize());
  await connection.confirmTransaction({
    blockhash: blockHash.blockhash,
    lastValidBlockHeight: blockHash.lastValidBlockHeight,
    signature
  });
  return signature;
};

const SendSolanaSplTokens: React.FC = () => {
  const { connection } = useConnection();
  const { publicKey, signTransaction } = useWallet();

  const handlePayment = async () => {
    try {
      if (!publicKey || !signTransaction) {
        throw new WalletNotConnectedError();
      }
      const mintToken = new PublicKey(
        '4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU'
      ); // 4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU is USDC token address on solana devnet
      const recipientAddress = new PublicKey(
        'token receiver solana account address'
      );

      const transactionInstructions: TransactionInstruction[] = [];
      const associatedTokenFrom = await getAssociatedTokenAddress(
        mintToken,
        publicKey
      );
      const fromAccount = await getAccount(connection, associatedTokenFrom);
      const associatedTokenTo = await getAssociatedTokenAddress(
        mintToken,
        recipientAddress
      );
      if (!(await connection.getAccountInfo(associatedTokenTo))) {
        transactionInstructions.push(
          createAssociatedTokenAccountInstruction(
            publicKey,
            associatedTokenTo,
            recipientAddress,
            mintToken
          )
        );
      }
      transactionInstructions.push(
        createTransferInstruction(
          fromAccount.address, // source
          associatedTokenTo, // dest
          publicKey,
          1000000 // transfer 1 USDC, USDC on solana devnet has 6 decimal
        )
      );
      const transaction = new Transaction().add(...transactionInstructions);
      const signature = await configureAndSendCurrentTransaction(
        transaction,
        connection,
        publicKey,
        signTransaction
      );
      // signature is transaction address, you can confirm your transaction on 'https://explorer.solana.com/?cluster=devnet'
    } catch (error) {}
  };

  return <button onClick={handlePayment}>Transfer spl token</button>;
};

export default SendSolanaSplTokens;

Answer №3

Learn how to generate keys, create tokens, establish token accounts for minters, mint tokens into token accounts, and transfer tokens between token accounts in this example

const splToken = require("@solana/spl-token");
const web3 = require("@solana/web3.js");

const example = async () => {
  console.log("Running Solana test");

  const cluster_url = "https://api.testnet.solana.com";

  //generate key pairs
  const mint_authority = web3.Keypair.generate();
  const freeze_authority = web3.Keypair.generate();

  const connection = new web3.Connection(cluster_url, "confirmed");

  //airdrop SOL to mint authority
  const airdropSignature = await connection.requestAirdrop(
    mint_authority.publicKey,
    web3.LAMPORTS_PER_SOL
  );

  await connection.confirmTransaction(airdropSignature);

  console.log("Mint authority public key", mint_authority.publicKey.toString());

  //check balance of mint authority
  console.log(
    "SOL balance of minter",
    await connection.getBalance(mint_authority.publicKey)
  );

  //create a new token without minting it
  const token = await splToken.Token.createMint(
    connection,
    mint_authority,
    mint_authority.publicKey,
    null,
    9,
    splToken.TOKEN_PROGRAM_ID
  );

  console.log(
    "New SOL balance of minter after token creation activity",
    await connection.getBalance(mint_authority.publicKey)
  );

  console.log("New spl token address", token.publicKey.toString());

  //mint some tokens into the mint authority wallet
  const minter_token_account = await token.getOrCreateAssociatedAccountInfo(
    mint_authority.publicKey
  );
  console.log(
    "Minter token account address",
    minter_token_account.address.toString()
  );

  console.log("Minting supply into token account created for the minter");
  console.log(
    "Minter_token_account.mint (same as token address)",
    minter_token_account.mint.toString()
  );
  console.log(
    "Minter_token_account.owner (same as pub key of token account)",
    minter_token_account.owner.toString()
  );

  // Mint the tokens - using mint_authority.publicKey as the authority
  await token.mintTo(
    minter_token_account.address,
    mint_authority.publicKey,
    [],
    10000000
  );

  //get balance of SOL for minter
  console.log(
    "New SOL balance of minter after token minting activity",
    await connection.getBalance(mint_authority.publicKey)
  );

  //retrieve token accounts by owner
  const tokenAccountsByOwner = await connection.getTokenAccountsByOwner(
    mint_authority.publicKey,
    {
      mint: token.publicKey,
    }
  );

  console.log(
    "TokenAccountsByOwner - token account address",
    tokenAccountsByOwner.value[0].pubkey.toString()
  );

  //get token account balance
  const tokenAccountBalance = await connection.getTokenAccountBalance(
    minter_token_account.address
  );
  console.log(
    "Token account balance:",
    tokenAccountBalance.value.uiAmountString
  );

  //create a new Solana address
  const bob = web3.Keypair.generate();

  console.log("Bob public address:", bob.publicKey.toString());

  console.log(
    "SOL balance of minter just before creating account for Bob",
    await connection.getBalance(mint_authority.publicKey)
  );

  //create token account for Bob's address
  const bob_token_account = await token.getOrCreateAssociatedAccountInfo(
    bob.publicKey
  );

  console.log(
    "SOL balance of minter just after creating account for Bob",
    await connection.getBalance(mint_authority.publicKey)
  ); 

  console.log(
    "Bob_token_account address",
    bob_token_account.address.toString()
  );

  console.log(
    "Bob_token_account.mint (same as token address)",
    bob_token_account.mint.toString()
  );

  console.log(
    "Bob_token_account.owner (same as pub key of token account)",
    bob_token_account.owner.toString()
  );

  //transfer from minter wallet to Bob
  var transferTrx = new web3.Transaction().add(
    splToken.Token.createTransferInstruction(
      splToken.TOKEN_PROGRAM_ID,
      minter_token_account.address,
      bob_token_account.address,
      mint_authority.publicKey,
      [],
      1
    )
  );

  let bobTokenAccountBalance = await connection.getTokenAccountBalance(
    bob_token_account.address
  );

  console.log(
    "Bob_token_account balance before transfer",
    bobTokenAccountBalance.value.uiAmountString
  );

  console.log(
    "SOL balance of minter just before transferring to Bob",
    await connection.getBalance(mint_authority.publicKey)
  );

  var signature = await web3.sendAndConfirmTransaction(
    connection,
    transferTrx,
    [mint_authority]
  );

  console.log(
    "SOL balance of minter just AFTER transferring to Bob",
    await connection.getBalance(mint_authority.publicKey)
  ); 

  bobTokenAccountBalance = await connection.getTokenAccountBalance(
    bob_token_account.address
  );

  console.log(
    "Bob_token_account balance after transfer",
    bobTokenAccountBalance.value.uiAmountString
  );
};

return example()
  .then()
  .catch((err) => {
    if (err.logs) {
      console.log(err.logs);
    } else {
      console.error(err.message);
    }
  });

Answer №4

This code snippet offers an alternative approach that eliminates the need to generate a custom transfer instruction. Instead, it leverages functions such as getOrCreateAssociatedTokenAccount, createAssociatedTokenAccountInstruction, getAccountInfo, and getAssociatedTokenAddress to achieve the desired functionality.

Importing spl-token is essential for creating the transfer instruction.

const fromTokenAccount = await getOrCreateAssociatedTokenAccount(
  connection,
  publicKey,
  mint,
  publicKey,
  signTransaction
)

const toTokenAccount = await getOrCreateAssociatedTokenAccount(
    connection,
    publicKey,
    mint,
    toPublicKey,
    signTransaction
)

const transaction = new web3.Transaction().add(
  Token.createTransferInstruction(
      TOKEN_PROGRAM_ID,
      fromTokenAccount.address,
      toTokenAccount.address,
      publicKey,
      [],
      1
  )
)

const signature = await sendTransaction(transaction, connection)

const response = await connection.confirmTransaction(signature, 'processed')
console.log('response', response)

Answer №5

Here is an alternative way to achieve the same functionality:

Function: getOrCreateAssociatedTokenAccount

import {
  ASSOCIATED_TOKEN_PROGRAM_ID,
  TOKEN_PROGRAM_ID,
  getAssociatedTokenAddress,
  getAccount,
  createAssociatedTokenAccountInstruction,
  TokenAccountNotFoundError,
  TokenInvalidAccountOwnerError,
  TokenInvalidMintError,
  TokenInvalidOwnerError,
} from '@solana/spl-token'
import { Transaction } from '@solana/web3.js'

/**
 * (rewrite)Retrieve the associated token account, or create it if it doesn't exist
 *
 * @param connection               Connection to use
 * @param payer                    Payer of the transaction and initialization fees
 * @param mint                     Mint associated with the account to set or verify
 * @param owner                    Owner of the account to set or verify
 * @param sendTransaction
 * @param allowOwnerOffCurve       Allow the owner account to be a PDA (Program Derived Address)
 * @param commitment               Desired level of commitment for querying the state
 * @param programId                SPL Token program account
 * @param associatedTokenProgramId SPL Associated Token program account
 *
 * @return Address of the new associated token account
 */
export async function getOrCreateAssociatedTokenAccount(
  connection,
  payer,
  mint,
  owner,
  sendTransaction,
  allowOwnerOffCurve = false,
  commitment,
  programId = TOKEN_PROGRAM_ID,
  associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID,
) {
  const associatedToken = await getAssociatedTokenAddress(
    mint,
    owner,
    allowOwnerOffCurve,
    programId,
    associatedTokenProgramId,
  )
  // This logic balances between fees, computations, roundtrips, and idempotency.
  let account
  try {
    account = await getAccount(connection, associatedToken, commitment, programId)
  } catch (error) {
    // Handle errors
    if (error instanceof TokenAccountNotFoundError || error instanceof TokenInvalidAccountOwnerError) {
      try {
        const transaction = new Transaction().add(
          createAssociatedTokenAccountInstruction(
            payer,
            associatedToken,
            owner,
            mint,
            programId,
            associatedTokenProgramId,
          ),
        )
        const signature = await sendTransaction(transaction, connection)

        await connection.confirmTransaction(signature)
      } catch (error) {}
     
      account = await getAccount(connection, associatedToken, commitment, programId)
    } else {
      throw error
    }
  }
  if (!account.mint.equals(mint)) throw new TokenInvalidMintError()
  if (!account.owner.equals(owner)) throw new TokenInvalidOwnerError()
  return account
}

SPL Token Transfer Function

import { Transaction, PublicKey } from '@solana/web3.js'
import { createTransferCheckedInstruction } from '@solana/spl-token'
import { useWallet, useConnection } from '@solana/wallet-adapter-react'

try {
    const { sendTransaction, publicKey } = useWallet()
    const { connection } = useConnection()

    // Step 1: Create transaction
    const toPublicKey = new PublicKey('')
    const mint = new PublicKey('token address')
    const transaction = new Transaction()
    
    // Use the rewritten function here
    const fromTokenAccount = await getOrCreateAssociatedTokenAccount(
        connection,
        publicKey,
        mint,
        publicKey,
        sendTransaction,
    )
    const toTokenAccount = await getOrCreateAssociatedTokenAccount(
        connection,
        publicKey,
        mint,
        toPublicKey,
        sendTransaction,
    )

    const instruction = createTransferCheckedInstruction(
        fromTokenAccount.address,
        mint,
        toTokenAccount.address,
        publicKey,
        1,
        0,
    )
    transaction.add(instruction)

    // Step 2: Sign & Send Transaction
    const result = await sendTransaction(transaction, connection)
} catch (err) {
    // Error handling
}

Answer №6

Transitioning OP's solution from the original question to an answer:

I inquired about this issue on the GitHub Repository of sol-wallet-adapter and received the following response: https://github.com/solana-labs/wallet-adapter/issues/189

According to the feedback, the issue lies not with wallet-adapter but with the limitations of the Token class -- especially regarding its functionality with a wallet where the Keypair/Signer is absent.

A TypeScript version of @spl-token is currently being reviewed, which should simplify some processes, although it has yet to be officially released on NPM: solana-labs/solana-program-library#2539

The recommendation given was to verify the associated token account and manually create it within an instruction for the transaction that is signed with the wallet. These provided links from the aforementioned open PR may aid in understanding how to proceed:

https://github.com/solana-labs/solana-program-library/blob/jsx/token-ts/token/ts/src/actions/getOrCreateAssociatedTokenAccount.ts

https://github.com/solana-labs/solana-program-library/blob/jsx/token-ts/token/ts/src/state/mint.ts#L114-L129

https://github.com/solana-labs/solana-program-library/blob/jsx/token-ts/token/ts/src/instructions/transfer.ts

The matter was closed as it falls outside of the library's scope, but it is hoped that these insights prove beneficial.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Using Vue.js to showcase Unicode, hexadecimal emojis, and octal literals in HTML

Received this response from the webserver: "\ud83d\ude48\ud83d\ude02\ud83d\ude30\ud83d\ude09\ud83d\udc4f\ud83c\udffd\ud83d\udc4c\ud83c\udffd\ud83d\udd1d\u2714&b ...

Tips for placing modal box in the exact desired position upon loading

I've been searching for a solution on how to dynamically calculate the position of each image loaded in a Twitter modal box and place the box underneath a custom toolbar element I have created. The situation is depicted in the image. The height of the ...

Mobile browser experiences video freezing when src is set through useState, yet it starts playing when triggered by a button click or when the video is set to M

Hey awesome folks at the StackOverflow Community, I'm currently encountering a perplexing issue with a video element in my web application. The video is supposed to play automatically on both desktop and mobile browsers. However, I've run into a ...

What is the appropriate utilization of the await keyword within concise if-else statements in JavaScript?

Along with transitioning away from jQuery, my main focus is to make my code more efficient. In large scale enterprise applications, the excessive use of jQuery and JavaScript can become problematic. I have decided to switch back to vanilla JavaScript for ...

"I'm encountering an issue with the discord.js module when I try to launch my bot using node. Any ideas on how

I encountered an unusual error with my Discord bot recently. It seems that discord.js crashes every time I try to run my bot: [nodemon] 2.0.12 [nodemon] to restart at any time, enter `rs` [nodemon] watching path(s): *.* [nodemon] watching extensions: js,mj ...

NgZone is no longer functioning properly

Seemingly out of the blue, my NgZone functionality has ceased to work. I'm currently in the process of developing an application using Ionic, Angular, and Firebase. An error is being thrown: Unhandled Promise rejection: Missing Command Error ; Zon ...

What is the reason for NextJS/React showing the message "You probably didn't export your component from the file where it was declared"?

What's the Issue The error code I am encountering Warning: React.jsx: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the ...

After upgrading to version 4.0.0 of typescript-eslint/parser, why is eslint having trouble recognizing JSX or certain react @types as undefined?"

In a large project built with ReactJs, the eslint rules are based on this specific eslint configuration: const DONT_WARN_CI = process.env.NODE_ENV === 'production' ? 0 : 1 module.exports = { ... After upgrading the library "@typescript-es ...

Jquery script that utilizes the Steam WebAPI to return data

I'm encountering an issue with a script I found on github. I added value.appid myself, thinking it was logical, but I believe that data.response.games contains values for all my games. How can I either print or view what is defined? I would like to ...

Connection error: API is down

The current issue I am facing with the application I'm developing is that it is not responding with the API address for weather data. Despite getting a response from Ajax, the specific weather API I am trying to call remains unresponsive. I have exha ...

`Month filter functionality optimized`

I have a flirty plugin installed on my website and it's working great except for one thing: I'd like the month category to be filtered in chronological order instead of alphabetically. Is there a way to achieve this? In simpler terms, how can I ...

Verifying the presence of a popover

Utilizing bootstrap popover in my project, I am encountering an issue where the target variable may not always exist on the page. Currently, I am displaying the popover using the following code snippet - target = $('#' + currentPopoverId.data(& ...

Deleting the initial line in a JSON reply

My current code is : $.getJSON("https://www.domain.com/someapi/callback=?", function(data){ $.each(data, function(i,item){ alert(item.x); }); }); I am currently facing an issue with my JSON response because there is ...

What is the best way to declare strings within a Typescript interface?

I have an array of Projects with multiple strings in the stack property const projects: IProject[] = [ {name: '', description: '', stack: {'php', 'sql'}} ] What is the best approach for defining the interface? ...

After successfully sending a GET request to the API, the Next.js 13.4.3 website still does not reflect new posts added on the hosting platform

I am currently using Next.js version 13.4.3 in my app directory to create a blog site. However, I am facing an issue. When I run npm run build locally on my computer and then start with npm run start, the new posts are displayed normally after adding them ...

Error encountered in the main thread: Fail to click on element at current position

An error occurred in thread "main" org.openqa.selenium.WebDriverException: The element located at point (126, 7.98333740234375) is not clickable. Another element is intercepting the click: <div class="_1H5F__" data-reactid="10"></div> The com ...

Learn how Angular 2 allows you to easily add multiple classes using the [class.className] binding

One way to add a single class is by using this syntax: [class.loading-state]="loading" But what if you want to add multiple classes? For example, if loading is true, you want to add the classes "loading-state" and "my-class". Is there a way to achieve t ...

Creating a custom pipe that converts seconds to hours and minutes retrieved from an API can be achieved by implementing a transformation function

Can someone please provide guidance on creating a custom pipe in Angular 8 that converts seconds to hours and minutes? Thank you. <div class="col-2" *ngFor="let movie of moviesList"> <div class="movie"> {{ movie.attributes.title }} ...

Unable to destructure the 'reservations' property from 'this.props.reservation' due to its undefined status, implementing a filter in ReactJs

I am having trouble retrieving reservations from my server when I try to access the rooms. I have followed a similar method but for some reason, I can't seem to access them. My goal is to retrieve reservations from the server and filter them based on ...

Export default does not actually yield a function; rather, it returns an object

I recently developed a function that is responsible for importing a script from a specified source, calling its exported function, and handling the returned result. To simplify things, I have streamlined the code to focus solely on returning the result. co ...