Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add Xverse to Connect Wallet component #8

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

fadeev
Copy link
Member

@fadeev fadeev commented Jul 29, 2024

Tasks

Summary by CodeRabbit

  • New Features

    • Enhanced the Page component to support blockchain interactions with new hooks and improved UI for swapping.
    • Introduced the "Xverse" wallet provider in the ConnectBitcoin component allowing users to connect seamlessly.
    • Added support for the "Xverse" wallet type in the BitcoinWalletProvider, enhancing wallet integration.
    • Implemented a new Bitcoin wallet provider for "Xverse Wallet," providing essential wallet functionalities.
  • Bug Fixes

    • Improved error handling in wallet connection and transaction processes across multiple components.
  • Documentation

    • Updated documentation related to new wallet functionalities and integration processes.

Copy link
Contributor

coderabbitai bot commented Jul 29, 2024

Walkthrough

Walkthrough

The recent changes introduce enhancements to the wallet integration in the application, particularly adding support for the "Xverse" wallet and streamlining wallet interaction methods. Several components have been updated to incorporate new hooks for blockchain functionalities, improving functionality and user experience. The updates also include modifications to existing wallet providers, simplifying method signatures and improving data retrieval processes, thereby enhancing the maintainability and readability of the codebase.

Changes

File Path Change Summary
src/app/page.tsx Enhanced Page component with new hooks for blockchain interaction and improved layout, allowing users to perform swaps more effectively.
src/components/ConnectBitcoin/index.tsx Added support for the "Xverse" wallet, including a new connection function and UI button for seamless user interaction.
src/providers/BitcoinWalletProvider/* Updated wallet provider interfaces and methods to support the new "Xverse" wallet, including changes to getAddress and sendTransaction methods for simplicity.
src/providers/BitcoinWalletProvider/xverse/* Introduced a new wallet provider for "Xverse," implementing methods for address retrieval and transaction sending, along with utility functions for transaction management.
src/providers/BitcoinWalletProvider/okx.ts Simplified getAddress and sendTransaction methods by removing redundant wallet parameters, enhancing code usability.
src/providers/BitcoinWalletProvider/unisat.ts Similar simplifications in method signatures for accessing the unisat wallet directly from the global context.
src/providers/BitcoinWalletProvider/xdefi.ts Streamlined wallet interaction methods to improve clarity and maintainability, focusing on direct access to the wallet instance.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ConnectButton
    participant XverseWallet
    participant BitcoinWalletProvider

    User->>ConnectButton: Click to connect wallet
    ConnectButton->>XverseWallet: Initiate connection
    XverseWallet->>BitcoinWalletProvider: Request user accounts
    BitcoinWalletProvider-->>XverseWallet: Return account information
    XverseWallet-->>ConnectButton: Connection successful
    ConnectButton-->>User: Display connected wallet
Loading
sequenceDiagram
    participant User
    participant SendTransactionButton
    participant BitcoinWalletProvider
    participant TransactionUtils

    User->>SendTransactionButton: Click to send transaction
    SendTransactionButton->>BitcoinWalletProvider: Prepare transaction
    BitcoinWalletProvider->>TransactionUtils: Create transaction
    TransactionUtils-->>BitcoinWalletProvider: Return PSBT
    BitcoinWalletProvider->>User: Transaction sent confirmation
Loading

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@fadeev
Copy link
Member Author

fadeev commented Jul 29, 2024

@coderabbitai review

Copy link
Contributor

coderabbitai bot commented Jul 29, 2024

Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Outside diff range, codebase verification and nitpick comments (2)
src/providers/BitcoinWalletProvider/okx.ts (1)

4-7: Add a check for the existence of window.okxwallet in the getAddress function.

The getAddress function in okx.ts directly accesses window.okxwallet without verifying its existence. To prevent potential runtime errors, add a check to handle cases where window.okxwallet might be undefined.

  • File: src/providers/BitcoinWalletProvider/okx.ts
  • Lines: 4-7
  getAddress: async () => {
+   const wallet = (window as any)?.okxwallet;
+   if (!wallet) {
+     throw new Error("OKX wallet not found");
+   }
    const address = (await wallet.bitcoinTestnet.connect()).address;
    return { address, publicKey: null };
  },
Analysis chain

Verify the function usage and handle potential issues with accessing the global window object.

The function now directly accesses the okxwallet from the global window object. Ensure that all function calls to getAddress match the new signature and handle cases where window.okxwallet might be undefined.

+  const wallet = (window as any)?.okxwallet;
+  if (!wallet) {
+    throw new Error("OKX wallet not found");
+  }
Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify all function calls to `getAddress` match the new signature.

# Test: Search for the function usage. Expect: Only occurrences of the new signature.
rg --type python -A 5 $'getAddress'

# Handle potential issues with accessing the global `window` object.
# Suggest adding a check for `window.okxwallet`.

Length of output: 65


Script:

#!/bin/bash
# Description: Verify all function calls to `getAddress` match the new signature.

# Test: Search for the function usage within TypeScript files. Expect: Only occurrences of the new signature.
rg --type ts -A 5 'getAddress'

Length of output: 5288

src/providers/BitcoinWalletProvider/xdefi.ts (1)

4-8: Ensure handling of potential issues with accessing the global window object in getAddress function

The getAddress function directly accesses the xfi wallet from the global window object. To ensure robustness, you should handle cases where window.xfi might be undefined. Additionally, the search results indicate that the function is used in src/providers/BitcoinWalletProvider/index.tsx.

Suggested code modification:

  getAddress: async () => {
+   const wallet = (window as any)?.xfi;
+   if (!wallet) {
+     throw new Error("XDEFI wallet not found");
+   }
    wallet.bitcoin.changeNetwork("testnet");
    const address = (await wallet?.bitcoin?.getAccounts())[0];
    return { address, publicKey: null };
  }

Files to update:

  • src/providers/BitcoinWalletProvider/xdefi.ts

Please ensure that all function calls to getAddress handle the new error scenario appropriately.

Analysis chain

Verify the function usage and handle potential issues with accessing the global window object.

The function now directly accesses the xfi wallet from the global window object. Ensure that all function calls to getAddress match the new signature and handle cases where window.xfi might be undefined.

+  const wallet = (window as any)?.xfi;
+  if (!wallet) {
+    throw new Error("XDEFI wallet not found");
+  }

+  const wallet = (window as any)?.xfi;
+  if (!wallet) {
+    throw new Error("XDEFI wallet not found");
+  }
Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify all function calls to `getAddress` match the new signature.

# Test: Search for the function usage. Expect: Only occurrences of the new signature.
rg --type python -A 5 $'getAddress'

# Handle potential issues with accessing the global `window` object.
# Suggest adding a check for `window.xfi`.

Length of output: 65


Script:

#!/bin/bash
# Description: Verify all function calls to `getAddress` match the new signature.

# Test: Search for the function usage. Expect: Only occurrences of the new signature.
rg --type js -A 5 'getAddress'

# Handle potential issues with accessing the global `window` object.
# Suggest adding a check for `window.xfi`.

Length of output: 30


Script:

#!/bin/bash
# Description: Verify all function calls to `getAddress` match the new signature.

# Test: Search for the function usage in JavaScript and TypeScript files. Expect: Only occurrences of the new signature.
rg --type js,ts -A 5 'getAddress'

# Handle potential issues with accessing the global `window` object.
# Suggest adding a check for `window.xfi`.

Length of output: 63


Script:

#!/bin/bash
# Description: Verify all function calls to `getAddress` match the new signature in JavaScript files.
rg --type js -A 5 'getAddress'

Length of output: 30


Script:

#!/bin/bash
# Description: Verify all function calls to `getAddress` match the new signature in TypeScript files.
rg --type ts -A 5 'getAddress'

Length of output: 5288

Tools
Biome

[error] 7-7: Unsafe usage of optional chaining.

If it short-circuits with 'undefined' the evaluation will throw TypeError here:

(lint/correctness/noUnsafeOptionalChaining)

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between a428313 and a102414.

Files ignored due to path filters (1)
  • src/components/ConnectBitcoin/xverse.jpeg is excluded by !**/*.jpeg
Files selected for processing (8)
  • src/app/page.tsx (1 hunks)
  • src/components/ConnectBitcoin/index.tsx (3 hunks)
  • src/providers/BitcoinWalletProvider/index.tsx (4 hunks)
  • src/providers/BitcoinWalletProvider/okx.ts (1 hunks)
  • src/providers/BitcoinWalletProvider/unisat.ts (1 hunks)
  • src/providers/BitcoinWalletProvider/xdefi.ts (1 hunks)
  • src/providers/BitcoinWalletProvider/xverse/index.ts (1 hunks)
  • src/providers/BitcoinWalletProvider/xverse/utils.ts (1 hunks)
Additional context used
Biome
src/providers/BitcoinWalletProvider/xdefi.ts

[error] 7-7: Unsafe usage of optional chaining.

If it short-circuits with 'undefined' the evaluation will throw TypeError here:

(lint/correctness/noUnsafeOptionalChaining)

Additional comments not posted (18)
src/providers/BitcoinWalletProvider/unisat.ts (2)

9-19: Verify the function usage and handle potential issues with accessing the global window object.

The function now directly accesses the unisat wallet from the global window object. Ensure that all function calls to sendTransaction match the new signature and handle cases where window.unisat might be undefined. Also, consider improving error handling.

+  const wallet = (window as any)?.unisat;
+  if (!wallet) {
+    throw new Error("Unisat wallet not found");
+  }
+  try {
+    await wallet.requestAccounts();
+    const memos = memo && [memo.toLowerCase()];
+    const tx = await wallet.sendBitcoin(to, value * 1e8, { memos });
+    return tx;
+  } catch (error) {
+    throw new Error("Transaction failed: " + error.message);
+  }

4-7: Verify the function usage and handle potential issues with accessing the global window object.

The function now directly accesses the unisat wallet from the global window object. Ensure that all function calls to getAddress match the new signature and handle cases where window.unisat might be undefined.

+  const wallet = (window as any)?.unisat;
+  if (!wallet) {
+    throw new Error("Unisat wallet not found");
+  }
src/providers/BitcoinWalletProvider/okx.ts (1)

Line range hint 9-29:
Verify the function usage and handle potential issues with accessing the global window object.

The function now directly accesses the okxwallet from the global window object. Ensure that all function calls to sendTransaction match the new signature and handle cases where window.okxwallet might be undefined. Also, consider improving error handling.

+  const wallet = (window as any)?.okxwallet;
+  if (!wallet) {
+    throw new Error("OKX wallet not found");
+  }
+  try {
+    const account = await wallet?.bitcoinTestnet?.connect();
+    if (!account) throw new Error("No account found");
+    const txHash = await wallet.bitcoinTestnet.send({
+      from: account.address,
+      to,
+      value: value * 1e8,
+      memo: `0x${memo}`,
+      memoPos: 1,
+    });
+    return txHash;
+  } catch (error) {
+    throw new Error("Transaction failed: " + error.message);
+  }
src/providers/BitcoinWalletProvider/xdefi.ts (1)

Line range hint 10-32:
Verify the function usage and handle potential issues with accessing the global window object.

The function now directly accesses the unisat wallet from the global window object. Ensure that all function calls to sendTransaction match the new signature and handle cases where window.unisat might be undefined. Also, consider improving error handling and addressing the unsafe usage of optional chaining.

+  const wallet = (window as any)?.unisat;
+  if (!wallet) {
+    throw new Error("Unisat wallet not found");
+  }
+  try {
+    wallet.bitcoin.changeNetwork("testnet");
+    const account = (await wallet?.bitcoin?.getAccounts())?.[0];
+    if (!account) throw new Error("No account found");
+    const tx = {
+      method: "transfer",
+      params: [
+        {
+          feeRate: 10,
+          from: account,
+          recipient: to,
+          amount: {
+            amount: value * 1e8,
+            decimals: 8,
+          },
+          memo: `hex::${memo}`,
+        },
+      ],
+    };
+    console.log(tx);
+    return new Promise((resolve, reject) => {
+      wallet.bitcoin.request(tx, (err: Error, res: Response) => {
+        if (err) {
+          reject(err);
+        } else {
+          resolve(res);
+        }
+      });
+    });
+  } catch (error) {
+    throw new Error("Transaction failed: " + error.message);
+  }
Tools
Biome

[error] 7-7: Unsafe usage of optional chaining.

If it short-circuits with 'undefined' the evaluation will throw TypeError here:

(lint/correctness/noUnsafeOptionalChaining)

src/app/page.tsx (4)

13-13: LGTM! Ensure the contract address is correct.

The contract constant is defined correctly. Verify that the contract address is accurate and intended for use.


16-21: LGTM! Verify hook functionality.

The hooks (useAccount, useChainId, useWalletClient, useEthersSigner, useZetaChainClient, useBitcoinWallet) are used correctly. Ensure that these hooks function as expected and provide the necessary data.


24-40: LGTM! Verify the conditional rendering logic.

The JSX structure is well-organized, and the Swap component is conditionally rendered based on the client availability. Ensure that the conditional rendering logic functions as intended.


4-10: Ensure all imports are used.

Verify that all imported modules (Swap, useEthersSigner, useZetaChainClient, ConnectBitcoin, useBitcoinWallet) are utilized within the component. Unused imports can lead to unnecessary bloat.

Verification successful

All imports are used correctly.

The verification confirms that each imported module (Swap, useEthersSigner, useZetaChainClient, ConnectBitcoin, useBitcoinWallet) is utilized within src/app/page.tsx. No unnecessary imports were found.

  • Swap is used in a JSX element.
  • useEthersSigner is used to get the signer.
  • useZetaChainClient is used to get the client.
  • ConnectBitcoin is used in a JSX element.
  • useBitcoinWallet is used to get the Bitcoin address.
Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify all imported modules are used within `src/app/page.tsx`.

# Test: Search for the usage of each imported module. Expect: Each module should be used at least once.
rg --type js --type ts --type tsx --type jsx -w 'Swap' src/app/page.tsx
rg --type js --type ts --type tsx --type jsx -w 'useEthersSigner' src/app/page.tsx
rg --type js --type ts --type tsx --type jsx -w 'useZetaChainClient' src/app/page.tsx
rg --type js --type ts --type tsx --type jsx -w 'ConnectBitcoin' src/app/page.tsx
rg --type js --type ts --type tsx --type jsx -w 'useBitcoinWallet' src/app/page.tsx

Length of output: 546


Script:

#!/bin/bash
# Description: Verify all imported modules are used within `src/app/page.tsx`.

# Test: Search for the usage of each imported module. Expect: Each module should be used at least once.
rg --type-add 'tsx:*.tsx' -w 'Swap' src/app/page.tsx
rg --type-add 'tsx:*.tsx' -w 'useEthersSigner' src/app/page.tsx
rg --type-add 'tsx:*.tsx' -w 'useZetaChainClient' src/app/page.tsx
rg --type-add 'tsx:*.tsx' -w 'ConnectBitcoin' src/app/page.tsx
rg --type-add 'tsx:*.tsx' -w 'useBitcoinWallet' src/app/page.tsx

Length of output: 622

src/providers/BitcoinWalletProvider/xverse/index.ts (1)

1-2: Ensure all imports are used.

Verify that all imported modules (AddressPurpose, BaseAdapter, createTransaction, signPsbt) are utilized within the file. Unused imports can lead to unnecessary bloat.

Verification successful

All imports are used.

The verification confirms that all imported modules (AddressPurpose, BaseAdapter, createTransaction, signPsbt) are utilized within the file src/providers/BitcoinWalletProvider/xverse/index.ts.

  • AddressPurpose is used in lines containing purposes: [AddressPurpose.Payment, AddressPurpose.Payment].
  • BaseAdapter is instantiated with new BaseAdapter("XverseProviders.BitcoinProvider").
  • createTransaction is called with createTransaction(publicKey, address, {...}).
  • signPsbt is invoked with signPsbt(result.psbtB64, result.utxoCnt, address).

No unused imports were found.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify all imported modules are used within `src/providers/BitcoinWalletProvider/xverse/index.ts`.

# Test: Search for the usage of each imported module. Expect: Each module should be used at least once.
rg --type js --type ts --type tsx --type jsx -w 'AddressPurpose' src/providers/BitcoinWalletProvider/xverse/index.ts
rg --type js --type ts --type tsx --type jsx -w 'BaseAdapter' src/providers/BitcoinWalletProvider/xverse/index.ts
rg --type js --type ts --type tsx --type jsx -w 'createTransaction' src/providers/BitcoinWalletProvider/xverse/index.ts
rg --type js --type ts --type tsx --type jsx -w 'signPsbt' src/providers/BitcoinWalletProvider/xverse/index.ts

Length of output: 573


Script:

#!/bin/bash
# Description: Verify all imported modules are used within `src/providers/BitcoinWalletProvider/xverse/index.ts`.

# Test: Search for the usage of each imported module. Expect: Each module should be used at least once.
rg --type js --type ts --type jsx -w 'AddressPurpose' src/providers/BitcoinWalletProvider/xverse/index.ts
rg --type js --type ts --type jsx -w 'BaseAdapter' src/providers/BitcoinWalletProvider/xverse/index.ts
rg --type js --type ts --type jsx -w 'createTransaction' src/providers/BitcoinWalletProvider/xverse/index.ts
rg --type js --type ts --type jsx -w 'signPsbt' src/providers/BitcoinWalletProvider/xverse/index.ts

Length of output: 529


Script:

#!/bin/bash
# Description: Verify all imported modules are used within `src/providers/BitcoinWalletProvider/xverse/index.ts`.

# Test: Search for the usage of each imported module. Expect: Each module should be used at least once.
rg --type js --type ts -w 'AddressPurpose' src/providers/BitcoinWalletProvider/xverse/index.ts
rg --type js --type ts -w 'BaseAdapter' src/providers/BitcoinWalletProvider/xverse/index.ts
rg --type js --type ts -w 'createTransaction' src/providers/BitcoinWalletProvider/xverse/index.ts
rg --type js --type ts -w 'signPsbt' src/providers/BitcoinWalletProvider/xverse/index.ts

Length of output: 1029

src/providers/BitcoinWalletProvider/index.tsx (5)

11-11: LGTM! Ensure the new wallet type is integrated correctly.

The xverse wallet type is added to the walletTypes object correctly. Verify that the new wallet type is used appropriately within the component.


18-18: LGTM! Ensure the new property is integrated correctly.

The publicKey property is added to the WalletContextProps interface correctly. Verify that the new property is used appropriately within the component.


40-40: LGTM! Ensure the new state is managed correctly.

The publicKey state is initialized correctly. Verify that the new state is managed appropriately within the component.


51-63: LGTM! Ensure error handling is sufficient.

The connectWallet method is updated correctly to retrieve both address and publicKey. Ensure that the error handling is sufficient and covers all edge cases.


5-5: Ensure the imported module is used.

Verify that the imported xverseWallet module is utilized within the file. Unused imports can lead to unnecessary bloat.

Verification successful

The imported xverseWallet module is used within the file.

The xverseWallet module is correctly imported and utilized in the src/providers/BitcoinWalletProvider/index.tsx file.

  • import xverseWallet from "./xverse";
  • xverse: xverseWallet,
Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the imported `xverseWallet` module is used within `src/providers/BitcoinWalletProvider/index.tsx`.

# Test: Search for the usage of `xverseWallet`. Expect: The module should be used at least once.
rg --type js --type ts --type tsx --type jsx -w 'xverseWallet' src/providers/BitcoinWalletProvider/index.tsx

Length of output: 136


Script:

#!/bin/bash
# Description: Verify the imported `xverseWallet` module is used within `src/providers/BitcoinWalletProvider/index.tsx`.

# Test: Search for the usage of `xverseWallet`. Expect: The module should be used at least once.
rg -w 'xverseWallet' src/providers/BitcoinWalletProvider/index.tsx

Length of output: 127

src/providers/BitcoinWalletProvider/xverse/utils.ts (1)

133-133: LGTM!

The export statement is correct.

src/components/ConnectBitcoin/index.tsx (3)

20-35: LGTM!

The import statements for xverseIcon and entities from @sats-connect/core module are correct and necessary for the new functionality.


193-199: LGTM!

The JSX structure for the new WalletButton for the Xverse wallet is correct and consistent with existing buttons.


Line range hint 1-199:
LGTM!

The ConnectBitcoin component correctly integrates the new connectXverse function and WalletButton for the Xverse wallet.

Comment on lines +13 to +31
async function fetchUtxo(address: string): Promise<any[]> {
try {
const response = await fetch(
`https://mempool.space/testnet/api/address/${address}/utxo`
);
if (!response.ok) {
throw new Error("Failed to fetch UTXO");
}
const utxos: any[] = await response.json();

if (utxos.length === 0) {
throw new Error("0 Balance");
}
return utxos;
} catch (error) {
console.error("Error fetching UTXO:", error);
throw error;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enhance error handling and type definitions.

  1. Consider using more specific error handling to differentiate between network errors and application errors.
  2. Define a type for the UTXO response to improve type safety.
type Utxo = {
  txid: string;
  vout: number;
  value: number;
  // Add other relevant fields
};

async function fetchUtxo(address: string): Promise<Utxo[]> {
  try {
    const response = await fetch(
      `https://mempool.space/testnet/api/address/${address}/utxo`
    );
    if (!response.ok) {
      throw new Error(`Failed to fetch UTXO: ${response.statusText}`);
    }
    const utxos: Utxo[] = await response.json();
    if (utxos.length === 0) {
      throw new Error("0 Balance");
    }
    return utxos;
  } catch (error) {
    console.error("Error fetching UTXO:", error);
    throw error;
  }
}

Comment on lines +33 to +90
async function createTransaction(
publickkey: string,
senderAddress: string,
params: any
): Promise<{ psbtB64: string; utxoCnt: number }> {
const publicKey = hex.decode(publickkey);

const p2wpkh = btc.p2wpkh(publicKey, bitcoinTestnet);
const p2sh = btc.p2sh(p2wpkh, bitcoinTestnet);

const recipientAddress = params.to;
if (!senderAddress) {
throw new Error("Error: no sender address");
}
if (!recipientAddress) {
throw new Error("Error: no recipient address in ENV");
}

const output = await fetchUtxo(senderAddress);

const tx = new btc.Transaction({
allowUnknowOutput: true,
});

let utxoCnt = 0;

output.forEach((utxo) => {
tx.addInput({
txid: utxo.txid,
index: utxo.vout,
witnessUtxo: {
script: p2sh.script,
amount: BigInt(utxo.value),
},
witnessScript: p2sh.witnessScript,
redeemScript: p2sh.redeemScript,
});
utxoCnt += 1;
});

const changeAddress = senderAddress;

const memo = params.memo.toLowerCase();

const opReturn = btc.Script.encode(["RETURN", Buffer.from(memo, "utf8")]);
tx.addOutputAddress(recipientAddress, BigInt(params.amount), bitcoinTestnet);
tx.addOutput({
script: opReturn,
amount: BigInt(0),
});
tx.addOutputAddress(changeAddress, BigInt(800), bitcoinTestnet);

const psbt = tx.toPSBT(0);

const psbtB64 = base64.encode(psbt);

return { psbtB64, utxoCnt };
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enhance error handling, type definitions, and code comments.

  1. Consider using more specific error handling to differentiate between different types of errors.
  2. Define types for the function parameters and return values to improve type safety.
  3. Add comments to explain the purpose of each code block for better readability.
type TransactionParams = {
  to: string;
  amount: number;
  memo: string;
};

async function createTransaction(
  publickkey: string,
  senderAddress: string,
  params: TransactionParams
): Promise<{ psbtB64: string; utxoCnt: number }> {
  const publicKey = hex.decode(publickkey);

  const p2wpkh = btc.p2wpkh(publicKey, bitcoinTestnet);
  const p2sh = btc.p2sh(p2wpkh, bitcoinTestnet);

  const recipientAddress = params.to;
  if (!senderAddress) {
    throw new Error("Error: no sender address");
  }
  if (!recipientAddress) {
    throw new Error("Error: no recipient address in ENV");
  }

  const output = await fetchUtxo(senderAddress);

  const tx = new btc.Transaction({
    allowUnknowOutput: true,
  });

  let utxoCnt = 0;

  output.forEach((utxo) => {
    tx.addInput({
      txid: utxo.txid,
      index: utxo.vout,
      witnessUtxo: {
        script: p2sh.script,
        amount: BigInt(utxo.value),
      },
      witnessScript: p2sh.witnessScript,
      redeemScript: p2sh.redeemScript,
    });
    utxoCnt += 1;
  });

  const changeAddress = senderAddress;

  const memo = params.memo.toLowerCase();

  const opReturn = btc.Script.encode(["RETURN", Buffer.from(memo, "utf8")]);
  tx.addOutputAddress(recipientAddress, BigInt(params.amount), bitcoinTestnet);
  tx.addOutput({
    script: opReturn,
    amount: BigInt(0),
  });
  tx.addOutputAddress(changeAddress, BigInt(800), bitcoinTestnet);

  const psbt = tx.toPSBT(0);

  const psbtB64 = base64.encode(psbt);

  return { psbtB64, utxoCnt };
}

Comment on lines +92 to +131
async function signPsbt(
psbtBase64: string,
utxoCnt: number,
senderAddress: string
) {
// Get the PSBT Base64 from the input

if (!psbtBase64) {
alert("Please enter a valid PSBT Base64 string.");
return;
}

const sigInputs = new Array(utxoCnt).fill(0, 0, utxoCnt).map((_, i) => i);

try {
const adapter = new BaseAdapter("XverseProviders.BitcoinProvider");
const response = await adapter.request("signPsbt", {
psbt: psbtBase64,
allowedSignHash: btc.SignatureHash.ALL,
broadcast: true,
signInputs: {
[senderAddress]: sigInputs,
},
});

if (response.status === "success") {
alert("PSBT signed successfully!");
} else {
if (response.error.code === RpcErrorCode.USER_REJECTION) {
alert("Request canceled by user");
} else {
console.error("Error signing PSBT:", response.error);
alert("Error signing PSBT: " + response.error.message);
}
}
} catch (err) {
console.error("Unexpected error:", err);
alert("Error while signing");
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enhance error handling, type definitions, and code comments.

  1. Consider using more specific error handling to differentiate between different types of errors.
  2. Define types for the function parameters to improve type safety.
  3. Add comments to explain the purpose of each code block for better readability.
type SignPsbtParams = {
  psbtBase64: string;
  utxoCnt: number;
  senderAddress: string;
};

async function signPsbt({ psbtBase64, utxoCnt, senderAddress }: SignPsbtParams) {
  if (!psbtBase64) {
    alert("Please enter a valid PSBT Base64 string.");
    return;
  }

  const sigInputs = new Array(utxoCnt).fill(0, 0, utxoCnt).map((_, i) => i);

  try {
    const adapter = new BaseAdapter("XverseProviders.BitcoinProvider");
    const response = await adapter.request("signPsbt", {
      psbt: psbtBase64,
      allowedSignHash: btc.SignatureHash.ALL,
      broadcast: true,
      signInputs: {
        [senderAddress]: sigInputs,
      },
    });

    if (response.status === "success") {
      alert("PSBT signed successfully!");
    } else {
      if (response.error.code === RpcErrorCode.USER_REJECTION) {
        alert("Request canceled by user");
      } else {
        console.error("Error signing PSBT:", response.error);
        alert("Error signing PSBT: " + response.error.message);
      }
    }
  } catch (err) {
    console.error("Unexpected error:", err);
    alert("Error while signing");
  }
}

Comment on lines +149 to +155
const connectXverse = async () => {
const adapter = new BaseAdapter("XverseProviders.BitcoinProvider");
const accounts = await adapter.request("getAccounts", {
purposes: [AddressPurpose.Stacks, AddressPurpose.Payment],
});
console.log(accounts);
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add error handling and logging.

Consider adding error handling and logging to the connectXverse function to handle potential issues gracefully.

const connectXverse = async () => {
  try {
    const adapter = new BaseAdapter("XverseProviders.BitcoinProvider");
    const accounts = await adapter.request("getAccounts", {
      purposes: [AddressPurpose.Stacks, AddressPurpose.Payment],
    });
    console.log(accounts);
  } catch (error) {
    console.error("Error connecting to Xverse wallet:", error);
  }
};

Comment on lines +18 to +42
sendTransaction: async ({
to,
value,
memo,
}: {
to: string;
value: number;
memo?: string;
}) => {
const adapter = new BaseAdapter("XverseProviders.BitcoinProvider");
const accounts: any = await adapter.request("getAccounts", {
purposes: [AddressPurpose.Payment, AddressPurpose.Payment],
});
const address = accounts.result[0].address;
const publicKey = accounts.result[0].publicKey;

const result = await createTransaction(publicKey, address, {
memo,
amount: value * 1e8,
to,
});

const res = await signPsbt(result.psbtB64, result.utxoCnt, address);
console.log(res);
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handle potential errors in sendTransaction.

The sendTransaction method creates and signs a transaction correctly. However, it lacks error handling, which could lead to unhandled exceptions.

  sendTransaction: async ({
    to,
    value,
    memo,
  }: {
    to: string;
    value: number;
    memo?: string;
  }) => {
    try {
      const adapter = new BaseAdapter("XverseProviders.BitcoinProvider");
      const accounts: any = await adapter.request("getAccounts", {
        purposes: [AddressPurpose.Payment, AddressPurpose.Payment],
      });
      const address = accounts.result[0].address;
      const publicKey = accounts.result[0].publicKey;

      const result = await createTransaction(publicKey, address, {
        memo,
        amount: value * 1e8,
        to,
      });

      const res = await signPsbt(result.psbtB64, result.utxoCnt, address);
      console.log(res);
    } catch (error) {
      console.error("Failed to send transaction:", error);
      throw error;
    }
  },
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
sendTransaction: async ({
to,
value,
memo,
}: {
to: string;
value: number;
memo?: string;
}) => {
const adapter = new BaseAdapter("XverseProviders.BitcoinProvider");
const accounts: any = await adapter.request("getAccounts", {
purposes: [AddressPurpose.Payment, AddressPurpose.Payment],
});
const address = accounts.result[0].address;
const publicKey = accounts.result[0].publicKey;
const result = await createTransaction(publicKey, address, {
memo,
amount: value * 1e8,
to,
});
const res = await signPsbt(result.psbtB64, result.utxoCnt, address);
console.log(res);
},
sendTransaction: async ({
to,
value,
memo,
}: {
to: string;
value: number;
memo?: string;
}) => {
try {
const adapter = new BaseAdapter("XverseProviders.BitcoinProvider");
const accounts: any = await adapter.request("getAccounts", {
purposes: [AddressPurpose.Payment, AddressPurpose.Payment],
});
const address = accounts.result[0].address;
const publicKey = accounts.result[0].publicKey;
const result = await createTransaction(publicKey, address, {
memo,
amount: value * 1e8,
to,
});
const res = await signPsbt(result.psbtB64, result.utxoCnt, address);
console.log(res);
} catch (error) {
console.error("Failed to send transaction:", error);
throw error;
}
},

Comment on lines +7 to +17
getAddress: async () => {
const adapter = new BaseAdapter("XverseProviders.BitcoinProvider");
const accounts: any = await adapter.request("getAccounts", {
purposes: [AddressPurpose.Payment, AddressPurpose.Payment],
});
console.log(accounts);
return {
address: accounts.result[0].address,
publicKey: accounts.result[0].publicKey,
};
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handle potential errors in getAddress.

The getAddress method retrieves account information correctly. However, it lacks error handling, which could lead to unhandled exceptions.

  getAddress: async () => {
    try {
      const adapter = new BaseAdapter("XverseProviders.BitcoinProvider");
      const accounts: any = await adapter.request("getAccounts", {
        purposes: [AddressPurpose.Payment, AddressPurpose.Payment],
      });
      console.log(accounts);
      return {
        address: accounts.result[0].address,
        publicKey: accounts.result[0].publicKey,
      };
    } catch (error) {
      console.error("Failed to get address:", error);
      throw error;
    }
  },
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
getAddress: async () => {
const adapter = new BaseAdapter("XverseProviders.BitcoinProvider");
const accounts: any = await adapter.request("getAccounts", {
purposes: [AddressPurpose.Payment, AddressPurpose.Payment],
});
console.log(accounts);
return {
address: accounts.result[0].address,
publicKey: accounts.result[0].publicKey,
};
},
getAddress: async () => {
try {
const adapter = new BaseAdapter("XverseProviders.BitcoinProvider");
const accounts: any = await adapter.request("getAccounts", {
purposes: [AddressPurpose.Payment, AddressPurpose.Payment],
});
console.log(accounts);
return {
address: accounts.result[0].address,
publicKey: accounts.result[0].publicKey,
};
} catch (error) {
console.error("Failed to get address:", error);
throw error;
}
},

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant