import { CdpClient } from "@coinbase/cdp-sdk";
    import "dotenv/config";
    import {
      Connection,
      PublicKey,
      SystemProgram,
      Transaction,
    } from "@solana/web3.js";
    async function main(sourceAddress?: string) {
      const cdp = new CdpClient();
      // Required: Destination address to send SOL to
      const destinationAddress = "3KzDtddx4i53FBkvCzuDmRbaMozTZoJBb1TToWhz3JfE";
      // Amount of lamports to send (default: 1000 = 0.000001 SOL)
      const lamportsToSend = 1000;
      try {
        const connection = new Connection("https://api.devnet.solana.com");
        // Set up a dedicated fee payer account.
        const feePayer = await cdp.solana.getOrCreateAccount({
          name: "test-sol-account-fee-payer",
        });
        console.log("Fee payer address: " + feePayer.address);
        // Request funds on the feePayer address to pay for the gas.
        await requestFaucetAndWaitForBalance(cdp, feePayer.address, connection);
        let fromAddress: string;
        if (sourceAddress) {
          fromAddress = sourceAddress;
          console.log("Using existing SOL account:", fromAddress);
        } else {
          // Set up a source account.
          const account = await cdp.solana.getOrCreateAccount({
            name: "test-sol-account",
          })
          fromAddress = account.address;
          console.log("Successfully created new SOL account:", fromAddress);
          // Request funds on the source account for transaction amount.
          await requestFaucetAndWaitForBalance(cdp, fromAddress, connection);
        }
        const balance = await connection.getBalance(new PublicKey(fromAddress));
        if (balance < lamportsToSend) {
          throw new Error(
            `Insufficient balance: ${balance} lamports, need at least ${lamportsToSend} lamports`
          );
        }
        const transaction = new Transaction();
        transaction.add(
          SystemProgram.transfer({
            fromPubkey: new PublicKey(fromAddress),
            toPubkey: new PublicKey(destinationAddress),
            lamports: lamportsToSend,
          })
        );
        const { blockhash } = await connection.getLatestBlockhash();
        transaction.recentBlockhash = blockhash;
        transaction.feePayer = new PublicKey(feePayer.address);
        const serializedTx = Buffer.from(
          transaction.serialize({ requireAllSignatures: false })
        ).toString("base64");
        // Sign with the funding account.
        const signedTxResponse = await cdp.solana.signTransaction({
          address: fromAddress,
          transaction: serializedTx,
        });
        const signedBase64Tx = signedTxResponse.signature;
        // Sign with the feePayer account.
        const finalSignedTxResponse = await cdp.solana.signTransaction({
          address: feePayer.address,
          transaction: signedBase64Tx,
        });
        // Send the signed transaction to the network.
        const signature = await connection.sendRawTransaction(Buffer.from(finalSignedTxResponse.signature, 'base64'));
        const latestBlockhash = await connection.getLatestBlockhash();
        const confirmation = await connection.confirmTransaction({
          signature,
          blockhash: latestBlockhash.blockhash,
          lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
        });
        if (confirmation.value.err) {
          throw new Error(
            `Transaction failed: ${confirmation.value.err.toString()}`
          );
        }
        console.log(
          "Transaction confirmed:",
          confirmation.value.err ? "failed" : "success"
        );
        console.log(
          `Transaction explorer link: https://explorer.solana.com/tx/${signature}?cluster=devnet`
        );
        return {
          fromAddress,
          destinationAddress,
          amount: lamportsToSend / 1e9,
          signature,
          success: !confirmation.value.err,
        };
      } catch (error) {
        console.error("Error processing SOL transaction:", error);
        throw error;
      }
    }
    /**
     * Sleeps for a given number of milliseconds
     *
     * @param {number} ms - The number of milliseconds to sleep
     * @returns {Promise<void>} A promise that resolves when the sleep is complete
     */
    function sleep(ms: number): Promise<void> {
      return new Promise((resolve) => setTimeout(resolve, ms));
    }
    /**
     * Requests funds from the faucet and waits for the balance to be available
     *
     * @param {CdpClient} cdp - The CDP client instance
     * @param {string} address - The address to fund
     * @param {Connection} connection - The Solana connection
     * @param {string} token - The token to request (default: "sol")
     * @returns {Promise<void>} A promise that resolves when the account is funded
     */
    async function requestFaucetAndWaitForBalance(
        cdp: CdpClient,
        address: string,
        connection: Connection,
    ): Promise<void> {
      // Request funds from faucet
      const faucetResp = await cdp.solana.requestFaucet({
        address: address,
        token: "sol",
      });
      console.log(
          `Successfully requested SOL from faucet:`,
          faucetResp.signature
      );
      // Wait until the address has balance
      let balance = 0;
      let attempts = 0;
      const maxAttempts = 30;
      while (balance === 0 && attempts < maxAttempts) {
        balance = await connection.getBalance(new PublicKey(address));
        if (balance === 0) {
          console.log("Waiting for funds...");
          await sleep(1000);
          attempts++;
        }
      }
      if (balance === 0) {
        throw new Error("Account not funded after multiple attempts");
      }
      console.log("Account funded with", balance / 1e9, "SOL");
      return;
    }
    const sourceAddress = process.argv.length > 2 ? process.argv[2] : undefined;
    main(sourceAddress).catch(console.error);