Skip to content

Base Sepolia #77

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

Merged
merged 5 commits into from
Mar 26, 2025
Merged

Base Sepolia #77

merged 5 commits into from
Mar 26, 2025

Conversation

spengrah
Copy link
Member

@spengrah spengrah commented Mar 16, 2025

Summary by CodeRabbit

  • New Features
    • Introduced a new RPC configuration for Base network testing.
    • Expanded blockchain support across various modules to include an additional network environment.
  • Updates
    • Upgraded a key dependency to a newer version for improved performance.
  • Tests
    • Added a new suite that validates module deployment and parameter configurations on the Base network.

Copy link

coderabbitai bot commented Mar 16, 2025

Walkthrough

This update adds a new environment variable (BASE_SEPOLIA_RPC) to both the GitHub Actions workflow and the example environment file. Multiple module JSON files have been modified to include new deployment entries for blockchain chain ID "84532" with respective block numbers. Additionally, the "viem" package version is upgraded in the project's package.json. A new test file is introduced to verify module deployment and parameter handling on the Base Sepolia network.

Changes

File(s) Change Summary
.github/workflows/test.yml Added environment variable BASE_SEPOLIA_RPC to the test-fork job (set to URL) and test-branch job (set via secrets).
example.env Added environment variable BASE_SEPOLIA_RPC.
modules/...*.json Added new deployment entries for blockchain chain ID "84532" with specific block numbers in multiple module JSON files.
package.json Upgraded the viem package version from 1.11.0 to 1.16.5 in the devDependencies.
test/baseSepoliaDeployments.test.ts Introduced a new test suite for Base Sepolia deployments, including module instance creation and parameter verification.

Sequence Diagram(s)

sequenceDiagram
    participant TR as Test Runner
    participant A as Anvil Instance
    participant VC as Viem Client
    participant HMC as HatsModulesClient

    TR->>A: Start Anvil instance for blockchain simulation
    TR->>VC: Initialize Base Sepolia client
    TR->>HMC: Retrieve modules for deployment
    loop For each module
        TR->>HMC: Invoke createNewInstance(module, args)
        HMC-->>TR: Return new module instance
        TR->>HMC: Verify hat ID and module parameters
        HMC-->>TR: Return parameter details
    end
Loading

Poem

I'm a rabbit with a codey twist,
New variables hop into the list.
Blockchains grow with "84532" in sight,
Tests and flows now dance in the light.
With every little change I cheer,
Carrots and commits make the future clear!
🥕🐇 Happy coding, my dear!


🪧 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 resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @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.

@scottrepreneur scottrepreneur changed the base branch from main to submissions March 17, 2025 12:23
@scottrepreneur
Copy link
Contributor

Also need to update the workflow action here

@spengrah spengrah marked this pull request as ready for review March 25, 2025 21:54
@spengrah spengrah requested review from a team as code owners March 25, 2025 21:54
Copy link

@YamineRL YamineRL left a comment

Choose a reason for hiding this comment

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

Looking good

import type { Module, Registry } from "@hatsprotocol/modules-sdk";
import "dotenv/config";

describe("Base deployments", () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

should be "Base Sepolia" 👀

Copy link
Member Author

Choose a reason for hiding this comment

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

nice catch! fixed in 9dcde05

Copy link

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

🧹 Nitpick comments (5)
test/baseSepoliaDeployments.test.ts (5)

35-37: Consider using an environment variable for the private key

Hardcoding private keys in test files can be a security risk, especially if the repository is public. Consider using an environment variable instead.

deployerAccount = privateKeyToAccount(
-  "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
+  process.env.TEST_PRIVATE_KEY || "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
);

74-79: Add more descriptive comment for skipped module

The current comment doesn't explain which module is being skipped or why it has dependencies on external contracts. A more descriptive comment would improve code clarity.

- // the unlock module has dependencies on other external contracts
+ // Skip the Unlock module as it has dependencies on other external contracts that are not available in the test environment

101-135: Refactor duplicated type conversion logic

The logic for converting types is duplicated between immutable and mutable arguments. Consider extracting this into a reusable function.

+ // Helper function to convert arguments based on their TypeScript type
+ function convertArgByType(exampleArg: unknown, tsType: string): unknown {
+   if (tsType === "bigint") {
+     return BigInt(exampleArg as string);
+   } else if (tsType === "bigint[]") {
+     return (exampleArg as Array<string>).map((val) => BigInt(val));
+   } else {
+     return exampleArg;
+   }
+ }

// prepare immutable args
for (let i = 0; i < module.creationArgs.immutable.length; i++) {
-  let arg: unknown;
  const exampleArg = module.creationArgs.immutable[i].example;
  const tsType = solidityToTypescriptType(
    module.creationArgs.immutable[i].type,
  );
-  if (tsType === "bigint") {
-    arg = BigInt(exampleArg as string);
-  } else if (tsType === "bigint[]") {
-    arg = (exampleArg as Array<string>).map((val) => BigInt(val));
-  } else {
-    arg = exampleArg;
-  }
+  const arg = convertArgByType(exampleArg, tsType);

  immutableArgs.push(arg);
}

// prepare mutable args
for (let i = 0; i < module.creationArgs.mutable.length; i++) {
-  let arg: unknown;
  const exampleArg = module.creationArgs.mutable[i].example;
  const tsType = solidityToTypescriptType(
    module.creationArgs.mutable[i].type,
  );
-  if (tsType === "bigint") {
-    arg = BigInt(exampleArg as string);
-  } else if (tsType === "bigint[]") {
-    arg = (exampleArg as Array<string>).map((val) => BigInt(val));
-  } else {
-    arg = exampleArg;
-  }
+  const arg = convertArgByType(exampleArg, tsType);

  mutableArgs.push(arg);
}

84-84: Extract chain ID as a constant

The Base Sepolia chain ID is hardcoded as a string. Consider extracting it as a constant for better maintainability.

+ const BASE_SEPOLIA_CHAIN_ID = "84532";
+
+ // Later in the code:
- if (module.deployments[i].chainId === "84532") {
+ if (module.deployments[i].chainId === BASE_SEPOLIA_CHAIN_ID) {

159-172: Enhance parameter testing with specific assertions

The current parameter testing only checks if parameters can be read, but doesn't verify their values. Consider enhancing the test to verify actual parameter values against expected values.

test("Test module parameters", async () => {
  for (let i = 0; i < instances.length; i++) {
    let instance = instances[i];

    const module = await hatsModulesClient.getModuleByInstance(instance);
    const res = await hatsModulesClient.getInstanceParameters(instance);

    if (res === undefined || res.length !== module?.parameters.length) {
      throw new Error(
        `Error: could not read all parameters from the instance of module ${module?.name}`,
      );
    }
+    
+    // Verify that parameters match expected values
+    expect(res).toBeDefined();
+    expect(res.length).toBe(module?.parameters.length);
+    
+    // Additional assertions can be added here to verify specific parameter values
+    // For example:
+    // if (module?.name === "SomeSpecificModule") {
+    //   expect(res[0]).toBe(expectedValue);
+    // }
  }
}, 30000);
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 23ba8ef and 9dcde05.

📒 Files selected for processing (1)
  • test/baseSepoliaDeployments.test.ts (1 hunks)
🔇 Additional comments (2)
test/baseSepoliaDeployments.test.ts (2)

66-66: Update comment to reflect correct network

The comment refers to "goerli" but the test is actually for Base Sepolia deployments.

- // create new module instance for each module which is deployed on goerli
+ // create new module instance for each module which is deployed on Base Sepolia

1-173: Overall assessment: Well-structured test file with minor improvements needed

The test file is well-structured and covers important test scenarios for Base Sepolia deployments. The code is generally well-written with clear variable names and logical organization. The suggestions above would enhance maintainability, error handling, and test robustness.

Comment on lines +63 to +157
test("Test create all modules", async () => {
const modules = hatsModulesClient.getModules();

// create new module instance for each module which is deployed on goerli
for (const [id, module] of Object.entries(modules)) {
console.log(`Testing module: ${module.name}`);
if (module.name === "JokeRace Eligibility") {
continue;
}

// the unlock module has dependencies on other external contracts
if (
module.implementationAddress ===
"0x4c7803041851f7a17Fc6b5Ff5c911FC748160637"
) {
continue;
}

// check if module is deployed on base sepolia. If not, then skip
let isOnBaseSepolia = false;
for (let i = 0; i < module.deployments.length; i++) {
if (module.deployments[i].chainId === "84532") {
isOnBaseSepolia = true;
break;
}
}
if (!isOnBaseSepolia) {
continue;
}

const hatId = module.creationArgs.useHatId
? BigInt(
"0x0000000100000000000000000000000000000000000000000000000000000000",
)
: BigInt("0");
const immutableArgs: unknown[] = [];
const mutableArgs: unknown[] = [];

// prepare immutable args
for (let i = 0; i < module.creationArgs.immutable.length; i++) {
let arg: unknown;
const exampleArg = module.creationArgs.immutable[i].example;
const tsType = solidityToTypescriptType(
module.creationArgs.immutable[i].type,
);
if (tsType === "bigint") {
arg = BigInt(exampleArg as string);
} else if (tsType === "bigint[]") {
arg = (exampleArg as Array<string>).map((val) => BigInt(val));
} else {
arg = exampleArg;
}

immutableArgs.push(arg);
}

// prepare mutable args
for (let i = 0; i < module.creationArgs.mutable.length; i++) {
let arg: unknown;
const exampleArg = module.creationArgs.mutable[i].example;
const tsType = solidityToTypescriptType(
module.creationArgs.mutable[i].type,
);
if (tsType === "bigint") {
arg = BigInt(exampleArg as string);
} else if (tsType === "bigint[]") {
arg = (exampleArg as Array<string>).map((val) => BigInt(val));
} else {
arg = exampleArg;
}

mutableArgs.push(arg);
}

// create new module instance
const res = await hatsModulesClient.createNewInstance({
account: deployerAccount,
moduleId: id,
hatId: hatId,
immutableArgs: immutableArgs,
mutableArgs: mutableArgs,
});

instances.push(res.newInstance);

// check correct hat Id in the new instance
const hatIdResult = await publicClient.readContract({
address: res.newInstance as Address,
abi: module.abi,
functionName: "hatId",
args: [],
});
expect(hatIdResult).toBe(hatId);
}
}, 30000);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling for module instance creation

The test doesn't handle errors that might occur during module instance creation. Consider adding try/catch blocks to provide more informative error messages.

test("Test create all modules", async () => {
  const modules = hatsModulesClient.getModules();

  // create new module instance for each module which is deployed on Base Sepolia
  for (const [id, module] of Object.entries(modules)) {
    console.log(`Testing module: ${module.name}`);
    if (module.name === "JokeRace Eligibility") {
      continue;
    }

    // Skip the Unlock module as it has dependencies on other external contracts
    if (
      module.implementationAddress ===
      "0x4c7803041851f7a17Fc6b5Ff5c911FC748160637"
    ) {
      continue;
    }

    // check if module is deployed on base sepolia. If not, then skip
    let isOnBaseSepolia = false;
    for (let i = 0; i < module.deployments.length; i++) {
      if (module.deployments[i].chainId === "84532") {
        isOnBaseSepolia = true;
        break;
      }
    }
    if (!isOnBaseSepolia) {
      continue;
    }

    const hatId = module.creationArgs.useHatId
      ? BigInt(
          "0x0000000100000000000000000000000000000000000000000000000000000000",
        )
      : BigInt("0");
    const immutableArgs: unknown[] = [];
    const mutableArgs: unknown[] = [];

    // prepare immutable args
    for (let i = 0; i < module.creationArgs.immutable.length; i++) {
      // ... [immutable args preparation]
    }

    // prepare mutable args
    for (let i = 0; i < module.creationArgs.mutable.length; i++) {
      // ... [mutable args preparation]
    }

    // create new module instance
+    try {
      const res = await hatsModulesClient.createNewInstance({
        account: deployerAccount,
        moduleId: id,
        hatId: hatId,
        immutableArgs: immutableArgs,
        mutableArgs: mutableArgs,
      });

      instances.push(res.newInstance);

      // check correct hat Id in the new instance
      const hatIdResult = await publicClient.readContract({
        address: res.newInstance as Address,
        abi: module.abi,
        functionName: "hatId",
        args: [],
      });
      expect(hatIdResult).toBe(hatId);
+    } catch (error) {
+      console.error(`Failed to create instance for module ${module.name}:`, error);
+      throw new Error(`Failed to create instance for module ${module.name}: ${error instanceof Error ? error.message : String(error)}`);
+    }
  }
}, 30000);
📝 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
test("Test create all modules", async () => {
const modules = hatsModulesClient.getModules();
// create new module instance for each module which is deployed on goerli
for (const [id, module] of Object.entries(modules)) {
console.log(`Testing module: ${module.name}`);
if (module.name === "JokeRace Eligibility") {
continue;
}
// the unlock module has dependencies on other external contracts
if (
module.implementationAddress ===
"0x4c7803041851f7a17Fc6b5Ff5c911FC748160637"
) {
continue;
}
// check if module is deployed on base sepolia. If not, then skip
let isOnBaseSepolia = false;
for (let i = 0; i < module.deployments.length; i++) {
if (module.deployments[i].chainId === "84532") {
isOnBaseSepolia = true;
break;
}
}
if (!isOnBaseSepolia) {
continue;
}
const hatId = module.creationArgs.useHatId
? BigInt(
"0x0000000100000000000000000000000000000000000000000000000000000000",
)
: BigInt("0");
const immutableArgs: unknown[] = [];
const mutableArgs: unknown[] = [];
// prepare immutable args
for (let i = 0; i < module.creationArgs.immutable.length; i++) {
let arg: unknown;
const exampleArg = module.creationArgs.immutable[i].example;
const tsType = solidityToTypescriptType(
module.creationArgs.immutable[i].type,
);
if (tsType === "bigint") {
arg = BigInt(exampleArg as string);
} else if (tsType === "bigint[]") {
arg = (exampleArg as Array<string>).map((val) => BigInt(val));
} else {
arg = exampleArg;
}
immutableArgs.push(arg);
}
// prepare mutable args
for (let i = 0; i < module.creationArgs.mutable.length; i++) {
let arg: unknown;
const exampleArg = module.creationArgs.mutable[i].example;
const tsType = solidityToTypescriptType(
module.creationArgs.mutable[i].type,
);
if (tsType === "bigint") {
arg = BigInt(exampleArg as string);
} else if (tsType === "bigint[]") {
arg = (exampleArg as Array<string>).map((val) => BigInt(val));
} else {
arg = exampleArg;
}
mutableArgs.push(arg);
}
// create new module instance
const res = await hatsModulesClient.createNewInstance({
account: deployerAccount,
moduleId: id,
hatId: hatId,
immutableArgs: immutableArgs,
mutableArgs: mutableArgs,
});
instances.push(res.newInstance);
// check correct hat Id in the new instance
const hatIdResult = await publicClient.readContract({
address: res.newInstance as Address,
abi: module.abi,
functionName: "hatId",
args: [],
});
expect(hatIdResult).toBe(hatId);
}
}, 30000);
test("Test create all modules", async () => {
const modules = hatsModulesClient.getModules();
// create new module instance for each module which is deployed on Base Sepolia
for (const [id, module] of Object.entries(modules)) {
console.log(`Testing module: ${module.name}`);
if (module.name === "JokeRace Eligibility") {
continue;
}
// Skip the Unlock module as it has dependencies on other external contracts
if (
module.implementationAddress ===
"0x4c7803041851f7a17Fc6b5Ff5c911FC748160637"
) {
continue;
}
// check if module is deployed on base sepolia. If not, then skip
let isOnBaseSepolia = false;
for (let i = 0; i < module.deployments.length; i++) {
if (module.deployments[i].chainId === "84532") {
isOnBaseSepolia = true;
break;
}
}
if (!isOnBaseSepolia) {
continue;
}
const hatId = module.creationArgs.useHatId
? BigInt(
"0x0000000100000000000000000000000000000000000000000000000000000000",
)
: BigInt("0");
const immutableArgs: unknown[] = [];
const mutableArgs: unknown[] = [];
// prepare immutable args
for (let i = 0; i < module.creationArgs.immutable.length; i++) {
let arg: unknown;
const exampleArg = module.creationArgs.immutable[i].example;
const tsType = solidityToTypescriptType(
module.creationArgs.immutable[i].type,
);
if (tsType === "bigint") {
arg = BigInt(exampleArg as string);
} else if (tsType === "bigint[]") {
arg = (exampleArg as Array<string>).map((val) => BigInt(val));
} else {
arg = exampleArg;
}
immutableArgs.push(arg);
}
// prepare mutable args
for (let i = 0; i < module.creationArgs.mutable.length; i++) {
let arg: unknown;
const exampleArg = module.creationArgs.mutable[i].example;
const tsType = solidityToTypescriptType(
module.creationArgs.mutable[i].type,
);
if (tsType === "bigint") {
arg = BigInt(exampleArg as string);
} else if (tsType === "bigint[]") {
arg = (exampleArg as Array<string>).map((val) => BigInt(val));
} else {
arg = exampleArg;
}
mutableArgs.push(arg);
}
// create new module instance
try {
const res = await hatsModulesClient.createNewInstance({
account: deployerAccount,
moduleId: id,
hatId: hatId,
immutableArgs: immutableArgs,
mutableArgs: mutableArgs,
});
instances.push(res.newInstance);
// check correct hat Id in the new instance
const hatIdResult = await publicClient.readContract({
address: res.newInstance as Address,
abi: module.abi,
functionName: "hatId",
args: [],
});
expect(hatIdResult).toBe(hatId);
} catch (error) {
console.error(`Failed to create instance for module ${module.name}:`, error);
throw new Error(
`Failed to create instance for module ${module.name}: ${
error instanceof Error ? error.message : String(error)
}`
);
}
}
}, 30000);

@spengrah spengrah merged commit 1f63d3a into submissions Mar 26, 2025
3 checks passed
@scottrepreneur scottrepreneur deleted the update/base-sepolia-deployments branch April 11, 2025 20:06
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