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 plugin-bnb to support BNB chain #2278

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from

Conversation

pythonberg1997
Copy link
Contributor

@pythonberg1997 pythonberg1997 commented Jan 14, 2025

Relates to

N/A (No specific issue or ticket linked)

Risks

Low

  • This change only adds support for BNB chain(BSC and opBNB).
  • Other features and functionalities of the Eliza platform remain unaffected.

Background

We are developers from NodeReal, a leading blockchain infrastructure provider delivering comprehensive one-stop blockchain solutions. As a key contributor to BNB Chain and enthusiastic adopters of AI technology, we are excited to contribute the BNB plugin to Eliza.

What does this PR do?

This PR adds a new plugin to Eliza for BNB Chain support. With this plugin, users can perform various operations on BNB Chain, including getBalance, transfer, swap, stake, faucet, bridge, and deploy ERC20/ERC721/ERC1155 tokens.

What kind of change is this?

Features (non-breaking change which adds functionality)

Testing

Where should a reviewer start?

From packages/plugin-bnb folder.

Detailed testing steps

  • Configure the plugin-bnb related entries (BNB_PRIVATE_KEY and BNB_PUBLIC_KEY) in the .env file to enable plugin-bnb and start the client.
  • As a user, ask Eliza to do any action that plugin-bnb supported(getBalance/transfer/staking/faucet/swap/bridge/deploy ERC20/ERC721/ERC1155 tokens).
  • Verify that the return result is correct and the transaction completes successfully on chain.

Discord username

pythonberg

Summary by CodeRabbit

Here are the release notes for this pull request:

  • New Features

    • Added support for BNB Smart Chain (BSC) and opBNB blockchain interactions
    • Introduced new plugin with actions for token transfers, swaps, balance checks, bridging, staking, and faucet requests
    • Enabled deployment of ERC20, ERC721, and ERC1155 token contracts
  • Configuration Updates

    • Added new environment variables for BNB chain configuration
    • Updated .env.example with BNB-related keys and provider URLs
  • Version Update

    • Incremented client version from 0.1.8+build.1 to 0.1.9-alpha.1
  • Development Improvements

    • Added TypeScript and build configurations for the new BNB plugin
    • Implemented comprehensive test suites for wallet and balance retrieval

@pythonberg1997 pythonberg1997 changed the title feat: plugin-bnb feat: add plugin-bnb to support BNB chain Jan 14, 2025
@unclezoro
Copy link

@shakkernerd could you take a look of this PR before the author moving forward?

@shakkernerd
Copy link
Member

@shakkernerd could you take a look of this PR before the author moving forward?

Okay, will do.

@shakkernerd
Copy link
Member

shakkernerd commented Jan 15, 2025

We have plugin-evm that supports pretty much all evm based blockchains. If there are actions / features you'll like to see or use, feel free to extend the plugin-evm and add your actions or features to it.
But currently, this will be re-inventing the wheel.

I can you see you've got a deploy action, do you mind adding that action to plugin-evm?

The exception to continuing this will be if this implementation is targeting something completely different and if that will be a pain to implement in plugin-evm.

@shakkernerd
Copy link
Member

UPDATE:
I took another look at it and I think there are custom features being built that may not be suitable in the plugin-evm.
On that note, it is very much okay to continue and move forward.

Thanks, looking forward to this.

@unclezoro
Copy link

unclezoro commented Jan 15, 2025

We have plugin-evm that supports pretty much all evm based blockchains. If there are actions / features you'll like to see or use, feel free to extend the plugin-evm and add your actions or features to it. But currently, this will be re-inventing the wheel.

I can you see you've got a deploy action, do you mind adding that action to plugin-evm?

The exception to continuing this will be if this implementation is targeting something completely different and if that will be a pain to implement in plugin-evm.

Thanks for the quick response, we will make it ready ASAP

@pythonberg1997 pythonberg1997 marked this pull request as ready for review January 20, 2025 03:02
Copy link
Contributor

coderabbitai bot commented Jan 20, 2025

📝 Walkthrough

Walkthrough

This pull request introduces a comprehensive BNB chain plugin for the agent ecosystem, adding support for blockchain interactions on the Binance Smart Chain (BSC) and opBNB. The changes include new configuration variables, smart contract templates, action handlers for token transfers, swaps, staking, and bridging, along with a robust wallet provider and extensive type definitions.

Changes

File Change Summary
.env.example Added BNB chain configuration variables
.gitignore Added eliza.sig to ignored files
agent/package.json Added @elizaos/plugin-bnb workspace dependency
agent/src/index.ts Imported and conditionally included BNB plugin
client/src/lib/info.json Version bumped to 0.1.9-alpha.1
packages/plugin-bnb/* Comprehensive new plugin implementation with actions, contracts, providers, and types

Possibly related PRs

Finishing Touches

  • 📝 Generate Docstrings (Beta)

🪧 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>, please review it.
    • 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 gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @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 using 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 generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

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.

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: 7

🧹 Nitpick comments (22)
packages/plugin-bnb/src/utils/contracts.ts (1)

74-74: Use consistent logging methods

Replace console.warn and console.error with elizaLogger.warn and elizaLogger.error for consistent logging throughout the application.

Apply this diff:

-                console.warn("Compilation warnings:", output.errors);
+                elizaLogger.warn("Compilation warnings:", output.errors);
-            console.error("Compilation failed:", error);
+            elizaLogger.error("Compilation failed:", error);

Also applies to: 90-90

packages/plugin-bnb/src/actions/swap.ts (3)

73-75: Remove unnecessary try-catch block

The catch block simply rethrows the error without adding any handling. Removing it will simplify the code while maintaining the same functionality.

Apply this diff:

             return resp;
-        } catch (error) {
-            throw error;
-        }
🧰 Tools
🪛 Biome (1.9.4)

[error] 74-74: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)


78-82: Enhance parameter validation

Currently, only the chain parameter is validated. Consider adding checks for amount, fromToken, and toToken to ensure they are valid and prevent unexpected errors.

Update the validation method:

     validateAndNormalizeParams(params: SwapParams): void {
         if (params.chain != "bsc") {
             throw new Error("Only BSC mainnet is supported");
         }
+        if (!params.amount || Number(params.amount) <= 0) {
+            throw new Error("Amount must be a positive number");
+        }
+        if (!params.fromToken || !params.toToken) {
+            throw new Error("Token addresses must be specified");
+        }
     }

53-54: Set default slippage value if undefined

If params.slippage is undefined, the swap may fail due to missing slippage configuration. Set a default slippage value to ensure smooth execution.

Update the code to set a default slippage of 0.5%:

             options: {
-                slippage: params.slippage,
+                slippage: params.slippage ?? 0.005, // Default to 0.5% slippage
                 order: "RECOMMENDED",
             },

And ensure slippage is handled in swapOptions:

     const swapOptions: SwapParams = {
         chain: content.chain,
         fromToken: content.inputToken,
         toToken: content.outputToken,
         amount: content.amount,
-        slippage: content.slippage,
+        slippage: content.slippage ?? 0.005, // Default to 0.5% if undefined
     };

Also applies to: 119-124

packages/plugin-bnb/src/actions/faucet.ts (2)

38-104: Simplify the faucet method by avoiding manual Promise construction.

The use of new Promise in the faucet method can be avoided. Refactoring to use async/await with event listeners will make the code cleaner and more maintainable.


98-102: Consider making the timeout duration configurable.

A fixed 15-second timeout may not suffice under poor network conditions. Allowing the timeout duration to be configurable can improve the user experience.

packages/plugin-bnb/src/actions/deploy.ts (1)

137-139: Eliminate redundant catch block in deployERC721.

The catch block merely rethrows the error without any additional handling. Removing it will streamline the method.

Apply this diff:

        try {
            const args = [name, symbol, baseURI];
            const contractAddress = await this.deployContract(
                chain,
                "ERC721Contract",
                args
            );

            return {
                address: contractAddress,
            };
-       } catch (error) {
-           throw error;
-       }
🧰 Tools
🪛 Biome (1.9.4)

[error] 138-138: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)

packages/plugin-bnb/src/actions/stake.ts (1)

44-55: Remove unnecessary try/catch block in stake method

The try/catch block in the stake method rethrows the caught error without adding any additional handling. It's redundant and can be removed to simplify the code.

Apply this diff to remove the redundant try/catch block:

-        try {
             const actions = {
                 deposit: async () => await this.doDeposit(params.amount!),
                 withdraw: async () => await this.doWithdraw(params.amount),
                 claim: async () => await this.doClaim(),
             };
             const resp = await actions[params.action]();
             return { response: resp };
-        } catch (error) {
-            throw error;
-        }
🧰 Tools
🪛 Biome (1.9.4)

[error] 53-53: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)

packages/plugin-bnb/src/actions/bridge.ts (1)

55-316: Eliminate redundant try/catch block in bridge method

The try/catch block in the bridge method simply rethrows the error without additional processing. Removing it will streamline the code.

Apply this diff to remove the unnecessary try/catch block:

-        try {
             // ... existing code ...
             return resp;
-        } catch (error) {
-            throw error;
-        }
🧰 Tools
🪛 Biome (1.9.4)

[error] 315-315: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)

packages/plugin-bnb/src/types/index.ts (3)

13-19: Make token and amount required in TransferParams

In TransferParams, both token and amount are optional. Typically, a transfer action requires specifying the token and amount to transfer. Making these parameters mandatory can prevent errors due to missing values.


21-27: Set a default value for slippage in SwapParams

The slippage parameter is optional in SwapParams. Setting a reasonable default value can enhance user experience and prevent failed swaps due to unspecified slippage tolerance.


94-1222: Externalize large ABI definitions

The ABI definitions are hardcoded in the TypeScript file. To improve maintainability and reduce clutter, consider loading these ABIs from external JSON files.

packages/plugin-bnb/src/index.ts (2)

26-26: Move implementation notes to documentation.

The comment about bridge limitations should be moved to the README.md or API documentation for better visibility and maintenance.


17-18: Enhance plugin metadata.

Consider adding more descriptive metadata about supported features and chains.

-    name: "bnb",
-    description: "BNB Smart Chain integration plugin",
+    name: "bnb",
+    description: "BNB Smart Chain (BSC) and opBNB integration plugin supporting transfers, swaps, staking, bridging, and token deployments",
packages/plugin-bnb/src/templates/index.ts (2)

8-8: Apply DRY principle to chain validation.

Chain validation is repeated across templates. Consider extracting it to a shared constant:

+const SUPPORTED_CHAINS = ["bsc", "bscTestnet", "opBNB", "opBNBTestnet"] as const;
+const SUPPORTED_CHAINS_TEXT = `Must be one of ${JSON.stringify(SUPPORTED_CHAINS)}. Default is "bsc".`;

-Chain to execute on. Must be one of ["bsc", "bscTestnet", "opBNB", "opBNBTestnet"]. Default is "bsc".
+Chain to execute on. ${SUPPORTED_CHAINS_TEXT}

Also applies to: 31-31, 58-58, 85-86, 113-113


174-182: Improve type definitions in contract template.

The JSON response types should be more precise:

     "chain": SUPPORTED_CHAINS,
     "contractType": "ERC20" | "ERC721" | "ERC1155",
     "name": string,
-    "symbol": string,
-    "decimals": number,
-    "totalSupply": string,
-    "baseURI": string
+    "symbol": string | null,  // null for ERC1155
+    "decimals": number | null,  // null for ERC721/ERC1155
+    "totalSupply": string | null,  // null for ERC721/ERC1155
+    "baseURI": string | null  // null for ERC20
agent/src/index.ts (1)

944-948: Simplify the conditional check using optional chaining.

The condition for including the BNB plugin can be simplified.

-            getSecret(character, "BNB_PRIVATE_KEY") ||
-            (getSecret(character, "BNB_PUBLIC_KEY") &&
-                getSecret(character, "BNB_PUBLIC_KEY")?.startsWith("0x"))
-                ? bnbPlugin
-                : null,
+            getSecret(character, "BNB_PRIVATE_KEY") ||
+            getSecret(character, "BNB_PUBLIC_KEY")?.startsWith("0x")
+                ? bnbPlugin
+                : null,
🧰 Tools
🪛 Biome (1.9.4)

[error] 945-946: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/plugin-bnb/README.md (2)

13-16: Enhance security guidance in configuration section.

Consider adding:

  • Warning about secure storage of private keys
  • Recommendation to use environment variables over direct values
  • Note about not committing .env file

121-121: Add rate limit values to faucet documentation.

Specify the exact rate limit values to improve clarity.

packages/plugin-bnb/src/actions/getBalance.ts (2)

82-84: Remove unnecessary try-catch block.

The try-catch block that only rethrows the error adds no value and can be removed.

-        try {
             let resp: GetBalanceResponse = {
                 chain,
                 address: address!,
             };
             // ... rest of the code ...
             return resp;
-        } catch (error) {
-            throw error;
-        }
🧰 Tools
🪛 Biome (1.9.4)

[error] 83-83: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)


31-85: Consider caching token decimals.

The implementation looks good but could benefit from caching token decimals to reduce RPC calls for frequently queried tokens.

🧰 Tools
🪛 Biome (1.9.4)

[error] 83-83: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)

.env.example (1)

498-503: Add validation hints for environment variables.

Consider adding format hints for the variables:

  • BNB_PRIVATE_KEY should be a hex string
  • BNB_PUBLIC_KEY should be a 0x-prefixed address
  • Provider URLs should be valid HTTP(S) URLs
-BNB_PRIVATE_KEY=            # BNB chain private key
-BNB_PUBLIC_KEY=             # BNB-smart-chain public key (address)
-BSC_PROVIDER_URL=           # BNB-smart-chain rpc url
-OPBNB_PROVIDER_URL=         # OPBNB rpc url
+BNB_PRIVATE_KEY=            # BNB chain private key (hex string without 0x prefix)
+BNB_PUBLIC_KEY=             # BNB-smart-chain public key (0x-prefixed address)
+BSC_PROVIDER_URL=           # BNB-smart-chain RPC URL (e.g., https://bsc-dataseed.binance.org)
+OPBNB_PROVIDER_URL=         # opBNB RPC URL (e.g., https://opbnb-mainnet-rpc.bnbchain.org)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f8b9e3e and 8aebe1c.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (26)
  • .env.example (1 hunks)
  • .gitignore (1 hunks)
  • agent/package.json (1 hunks)
  • agent/src/index.ts (2 hunks)
  • client/src/lib/info.json (1 hunks)
  • packages/plugin-bnb/README.md (1 hunks)
  • packages/plugin-bnb/package.json (1 hunks)
  • packages/plugin-bnb/src/actions/bridge.ts (1 hunks)
  • packages/plugin-bnb/src/actions/deploy.ts (1 hunks)
  • packages/plugin-bnb/src/actions/faucet.ts (1 hunks)
  • packages/plugin-bnb/src/actions/getBalance.ts (1 hunks)
  • packages/plugin-bnb/src/actions/stake.ts (1 hunks)
  • packages/plugin-bnb/src/actions/swap.ts (1 hunks)
  • packages/plugin-bnb/src/actions/transfer.ts (1 hunks)
  • packages/plugin-bnb/src/contracts/Erc1155Contract.sol (1 hunks)
  • packages/plugin-bnb/src/contracts/Erc20Contract.sol (1 hunks)
  • packages/plugin-bnb/src/contracts/Erc721Contract.sol (1 hunks)
  • packages/plugin-bnb/src/index.ts (1 hunks)
  • packages/plugin-bnb/src/providers/wallet.ts (1 hunks)
  • packages/plugin-bnb/src/templates/index.ts (1 hunks)
  • packages/plugin-bnb/src/tests/getBalance.test.ts (1 hunks)
  • packages/plugin-bnb/src/tests/wallet.test.ts (1 hunks)
  • packages/plugin-bnb/src/types/index.ts (1 hunks)
  • packages/plugin-bnb/src/utils/contracts.ts (1 hunks)
  • packages/plugin-bnb/tsconfig.json (1 hunks)
  • packages/plugin-bnb/tsup.config.ts (1 hunks)
✅ Files skipped from review due to trivial changes (4)
  • .gitignore
  • client/src/lib/info.json
  • packages/plugin-bnb/tsconfig.json
  • packages/plugin-bnb/tsup.config.ts
🧰 Additional context used
📓 Learnings (1)
packages/plugin-bnb/src/actions/swap.ts (3)
Learnt from: B1boid
PR: elizaOS/eliza#2332
File: packages/plugin-evm/src/actions/swap.ts:0-0
Timestamp: 2025-01-17T19:36:49.663Z
Learning: The Bebop API URL (https://api.bebop.xyz) in the SwapAction is intentionally hardcoded as the system only operates in the production environment.
Learnt from: B1boid
PR: elizaOS/eliza#2332
File: packages/plugin-evm/src/actions/swap.ts:0-0
Timestamp: 2025-01-17T19:29:52.172Z
Learning: When comparing token amounts from different DEX aggregators in the SwapAction, use BigInt for direct comparison as the amounts are already normalized to the same decimal precision.
Learnt from: B1boid
PR: elizaOS/eliza#2332
File: packages/plugin-evm/src/actions/swap.ts:230-242
Timestamp: 2025-01-17T19:27:59.723Z
Learning: The transaction configuration in EVM plugins uses unimplemented KZG functions and undefined chain parameter as a type compatibility pattern with viem's transaction type. This is an accepted pattern across the codebase.
🪛 Biome (1.9.4)
agent/src/index.ts

[error] 945-946: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/plugin-bnb/src/actions/swap.ts

[error] 74-74: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)

packages/plugin-bnb/src/actions/deploy.ts

[error] 138-138: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)

packages/plugin-bnb/src/actions/bridge.ts

[error] 315-315: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)

packages/plugin-bnb/src/actions/transfer.ts

[error] 140-140: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)

packages/plugin-bnb/src/actions/stake.ts

[error] 53-53: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)

packages/plugin-bnb/src/actions/getBalance.ts

[error] 83-83: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)

🔇 Additional comments (7)
packages/plugin-bnb/src/contracts/Erc721Contract.sol (2)

16-16: ⚠️ Potential issue

Correct the Ownable constructor call

The Ownable contract constructor does not accept parameters. Remove msg.sender to properly initialize the Ownable contract.

Apply this diff to fix the constructor:

-        ) ERC721(name, symbol) Ownable(msg.sender) {
+        ) ERC721(name, symbol) Ownable() {

Likely invalid or redundant comment.


55-68: ⚠️ Potential issue

Remove overrides of non-existent functions

The functions _update and _increaseBalance do not exist in ERC721 or ERC721Enumerable from OpenZeppelin. Overriding these functions may cause compilation errors.

Apply this diff to remove the incorrect overrides:

-    function _update(
-        address to,
-        uint256 tokenId,
-        address auth
-    ) internal override(ERC721, ERC721Enumerable) returns (address) {
-        return super._update(to, tokenId, auth);
-    }

-    function _increaseBalance(
-        address account,
-        uint128 value
-    ) internal override(ERC721, ERC721Enumerable) {
-        super._increaseBalance(account, value);
-    }

Likely invalid or redundant comment.

packages/plugin-bnb/src/providers/wallet.ts (1)

1-355: Well-structured implementation of WalletProvider

The WalletProvider class and its methods are comprehensive, correctly implementing the wallet functionalities for BNB chain interactions. The code is clean and follows best practices.

agent/src/index.ts (1)

56-56: LGTM!

The import follows the established pattern for plugin imports.

packages/plugin-bnb/package.json (1)

13-15: Review version constraints for security.

The following dependencies use caret ranges which may introduce breaking changes:

  • @openzeppelin/contracts
  • @types/node
✅ Verification successful

Dependencies and version constraints are appropriate

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for known vulnerabilities in dependencies
npm audit --package-lock-only

# Check latest versions
npm view @openzeppelin/contracts version
npm view @types/node version

Length of output: 468

agent/package.json (1)

44-44: LGTM!

The dependency addition follows the established pattern for workspace plugins.

packages/plugin-bnb/src/actions/getBalance.ts (1)

134-283: Well-structured action definition with comprehensive examples!

The action definition includes clear examples covering various use cases: checking native token balance, ERC20 token balance by symbol, and by contract address.

Comment on lines +55 to +68
function _update(
address to,
uint256 tokenId,
address auth
) internal override(ERC721, ERC721Enumerable) returns (address) {
return super._update(to, tokenId, auth);
}

function _increaseBalance(
address account,
uint128 value
) internal override(ERC721, ERC721Enumerable) {
super._increaseBalance(account, value);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Override required functions due to multiple inheritance

When inheriting from multiple contracts that implement the same functions, you need to override them to specify how to combine their behaviors. Specifically, you should override _beforeTokenTransfer and _burn to ensure compatibility with ERC721, ERC721Enumerable, and ERC721URIStorage.

Add the following overrides:

+    function _beforeTokenTransfer(
+        address from,
+        address to,
+        uint256 tokenId,
+        uint256 batchSize
+    ) internal override(ERC721, ERC721Enumerable) {
+        super._beforeTokenTransfer(from, to, tokenId, batchSize);
+    }

+    function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
+        super._burn(tokenId);
+    }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 45 to 141
try {
const nativeToken =
this.walletProvider.chains[params.chain].nativeCurrency.symbol;

let resp: TransferResponse = {
chain: params.chain,
txHash: "0x",
recipient: params.toAddress,
amount: "",
token: params.token ?? nativeToken,
};

if (!params.token || params.token == nativeToken) {
// Native token transfer
let options: { gas?: bigint; gasPrice?: bigint; data?: Hex } = {
data: params.data,
};
let value: bigint;
if (!params.amount) {
// Transfer all balance minus gas
const publicClient = this.walletProvider.getPublicClient(
params.chain
);
const balance = await publicClient.getBalance({
address: fromAddress,
});

value = balance - this.DEFAULT_GAS_PRICE * 21000n;
options.gas = this.TRANSFER_GAS;
options.gasPrice = this.DEFAULT_GAS_PRICE;
} else {
value = parseEther(params.amount);
}

resp.amount = formatEther(value);
resp.txHash = await this.walletProvider.transfer(
params.chain,
params.toAddress,
value,
options
);
} else {
// ERC20 token transfer
let tokenAddress = params.token;
if (!params.token.startsWith("0x")) {
tokenAddress = await this.walletProvider.getTokenAddress(
params.chain,
params.token
);
}

const publicClient = this.walletProvider.getPublicClient(
params.chain
);
const decimals = await publicClient.readContract({
address: tokenAddress as `0x${string}`,
abi: ERC20Abi,
functionName: "decimals",
});

let value: bigint;
if (!params.amount) {
value = await publicClient.readContract({
address: tokenAddress as `0x${string}`,
abi: ERC20Abi,
functionName: "balanceOf",
args: [fromAddress],
});
} else {
value = parseUnits(params.amount, decimals);
}

resp.amount = formatUnits(value, decimals);
resp.txHash = await this.walletProvider.transferERC20(
params.chain,
tokenAddress as `0x${string}`,
params.toAddress,
value
);
}

if (!resp.txHash || resp.txHash == "0x") {
throw new Error("Get transaction hash failed");
}

// wait for the transaction to be confirmed
const publicClient = this.walletProvider.getPublicClient(
params.chain
);
await publicClient.waitForTransactionReceipt({
hash: resp.txHash,
});

return resp;
} catch (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.

🛠️ Refactor suggestion

Remove unnecessary try/catch block in the transfer method.

The try/catch block that only rethrows the error without additional handling is unnecessary and can be removed to simplify the code.

Apply this diff to remove the redundant block:

-        try {
             // existing code
             ...
-        } catch (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
try {
const nativeToken =
this.walletProvider.chains[params.chain].nativeCurrency.symbol;
let resp: TransferResponse = {
chain: params.chain,
txHash: "0x",
recipient: params.toAddress,
amount: "",
token: params.token ?? nativeToken,
};
if (!params.token || params.token == nativeToken) {
// Native token transfer
let options: { gas?: bigint; gasPrice?: bigint; data?: Hex } = {
data: params.data,
};
let value: bigint;
if (!params.amount) {
// Transfer all balance minus gas
const publicClient = this.walletProvider.getPublicClient(
params.chain
);
const balance = await publicClient.getBalance({
address: fromAddress,
});
value = balance - this.DEFAULT_GAS_PRICE * 21000n;
options.gas = this.TRANSFER_GAS;
options.gasPrice = this.DEFAULT_GAS_PRICE;
} else {
value = parseEther(params.amount);
}
resp.amount = formatEther(value);
resp.txHash = await this.walletProvider.transfer(
params.chain,
params.toAddress,
value,
options
);
} else {
// ERC20 token transfer
let tokenAddress = params.token;
if (!params.token.startsWith("0x")) {
tokenAddress = await this.walletProvider.getTokenAddress(
params.chain,
params.token
);
}
const publicClient = this.walletProvider.getPublicClient(
params.chain
);
const decimals = await publicClient.readContract({
address: tokenAddress as `0x${string}`,
abi: ERC20Abi,
functionName: "decimals",
});
let value: bigint;
if (!params.amount) {
value = await publicClient.readContract({
address: tokenAddress as `0x${string}`,
abi: ERC20Abi,
functionName: "balanceOf",
args: [fromAddress],
});
} else {
value = parseUnits(params.amount, decimals);
}
resp.amount = formatUnits(value, decimals);
resp.txHash = await this.walletProvider.transferERC20(
params.chain,
tokenAddress as `0x${string}`,
params.toAddress,
value
);
}
if (!resp.txHash || resp.txHash == "0x") {
throw new Error("Get transaction hash failed");
}
// wait for the transaction to be confirmed
const publicClient = this.walletProvider.getPublicClient(
params.chain
);
await publicClient.waitForTransactionReceipt({
hash: resp.txHash,
});
return resp;
} catch (error) {
throw error;
}
const nativeToken =
this.walletProvider.chains[params.chain].nativeCurrency.symbol;
let resp: TransferResponse = {
chain: params.chain,
txHash: "0x",
recipient: params.toAddress,
amount: "",
token: params.token ?? nativeToken,
};
if (!params.token || params.token == nativeToken) {
// Native token transfer
let options: { gas?: bigint; gasPrice?: bigint; data?: Hex } = {
data: params.data,
};
let value: bigint;
if (!params.amount) {
// Transfer all balance minus gas
const publicClient = this.walletProvider.getPublicClient(
params.chain
);
const balance = await publicClient.getBalance({
address: fromAddress,
});
value = balance - this.DEFAULT_GAS_PRICE * 21000n;
options.gas = this.TRANSFER_GAS;
options.gasPrice = this.DEFAULT_GAS_PRICE;
} else {
value = parseEther(params.amount);
}
resp.amount = formatEther(value);
resp.txHash = await this.walletProvider.transfer(
params.chain,
params.toAddress,
value,
options
);
} else {
// ERC20 token transfer
let tokenAddress = params.token;
if (!params.token.startsWith("0x")) {
tokenAddress = await this.walletProvider.getTokenAddress(
params.chain,
params.token
);
}
const publicClient = this.walletProvider.getPublicClient(
params.chain
);
const decimals = await publicClient.readContract({
address: tokenAddress as `0x${string}`,
abi: ERC20Abi,
functionName: "decimals",
});
let value: bigint;
if (!params.amount) {
value = await publicClient.readContract({
address: tokenAddress as `0x${string}`,
abi: ERC20Abi,
functionName: "balanceOf",
args: [fromAddress],
});
} else {
value = parseUnits(params.amount, decimals);
}
resp.amount = formatUnits(value, decimals);
resp.txHash = await this.walletProvider.transferERC20(
params.chain,
tokenAddress as `0x${string}`,
params.toAddress,
value
);
}
if (!resp.txHash || resp.txHash == "0x") {
throw new Error("Get transaction hash failed");
}
// wait for the transaction to be confirmed
const publicClient = this.walletProvider.getPublicClient(
params.chain
);
await publicClient.waitForTransactionReceipt({
hash: resp.txHash,
});
return resp;
🧰 Tools
🪛 Biome (1.9.4)

[error] 140-140: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)

string memory symbol,
uint8 decimalsValue,
uint256 initialSupply
) ERC20(name, symbol) Ownable(msg.sender) {
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Incorrect usage of Ownable constructor

The Ownable contract's constructor does not accept parameters. The call Ownable(msg.sender) is invalid. Ownable automatically sets the owner to msg.sender.

Apply this diff to fix the issue:

 ) ERC20(name, symbol) Ownable(msg.sender) {
     _decimals = decimalsValue;
     _mint(msg.sender, initialSupply);
 }
+ ) ERC20(name, symbol) {
+    _decimals = decimalsValue;
+    _mint(msg.sender, initialSupply);
+ }

Committable suggestion skipped: line range outside the PR's diff.


import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155URIStorage.sol";
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Incorrect inheritance and Ownable constructor usage

The contract imports ERC1155URIStorage but doesn't inherit from it. Since _setURI is used, you should inherit from ERC1155URIStorage. Also, Ownable does not take parameters in its constructor.

Apply this diff to fix the issues:

 import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
 import "@openzeppelin/contracts/access/Ownable.sol";
 import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155URIStorage.sol";

-contract ERC1155Contract is ERC1155, Ownable {
+contract ERC1155Contract is ERC1155URIStorage, Ownable {

     constructor(
         string memory _name,
         string memory _baseURI
-    ) ERC1155(_baseURI) Ownable(msg.sender) {
+    ) ERC1155URIStorage(_baseURI) {
         name = _name;
     }

+    function uri(uint256 tokenId) public view override returns (string memory) {
+        return super.uri(tokenId);
+    }
 }

Also applies to: 8-8

token: "BNB",
};
const resp = await ga.getBalance(input);
console.log("BNB balance", resp.balance);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve test assertions and remove console.logs.

The tests should:

  1. Remove console.log statements
  2. Add proper assertions for balance values
  3. Include error cases (e.g., invalid tokens, addresses)
-            console.log("BNB balance", resp.balance);
+            expect(resp.balance).toBeDefined();
+            expect(typeof resp.balance).toBe("string");

-            console.log("USDC balance", resp.balance);
+            expect(resp.balance).toBeDefined();
+            expect(typeof resp.balance).toBe("string");

-            console.log(
-                "0x55d398326f99059ff775485246999027b3197955 balance",
-                resp.balance
-            );
+            expect(resp.balance).toBeDefined();
+            expect(typeof resp.balance).toBe("string");

Also applies to: 42-42, 52-55

Comment on lines 97 to 100
it("should fetch balance", async () => {
let bal = await walletProvider.getWalletBalance();
expect(bal).toEqual("0");
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance balance test coverage.

The balance test only verifies a "0" balance. Add tests for:

  1. Non-zero balances
  2. Error cases (e.g., network issues)
  3. Different token types

Comment on lines 110 to 131
async validateAndNormalizeParams(params: GetBalanceParams): Promise<void> {
if (!params.address) {
params.address = this.walletProvider.getAddress();
} else {
params.address = await this.walletProvider.formatAddress(
params.address
);
}

if (!params.token || params.token === "") {
params.token = "BNB";
}

if (params.chain != "bsc") {
// if token contract address is not provided, only BSC mainnet is supported
if (!params.token.startsWith("0x")) {
throw new Error(
"Only BSC mainnet is supported for querying balance by token symbol"
);
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Remove BSC-only restriction for token symbols

The validation should allow token symbols on opBNB since it's a fully supported chain in the plugin. Consider updating the condition to:

if (params.chain !== "bsc" && params.chain !== "opBNB") {
🔗 Analysis chain

Consider supporting opBNB chain.

The chain validation currently restricts token symbol queries to BSC mainnet only. Consider extending support to opBNB since it's mentioned in the PR objectives.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if opBNB support is implemented elsewhere
rg -g '!*.{md,json}' -i 'opbnb|op.*bnb'

Length of output: 6070

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.

3 participants