Skip to content

Comments

feat: added script for registerToken#121

Open
OWK50GA wants to merge 3 commits intoExplore-Beyond-Innovations:mainfrom
OWK50GA:feat/script-registerToken
Open

feat: added script for registerToken#121
OWK50GA wants to merge 3 commits intoExplore-Beyond-Innovations:mainfrom
OWK50GA:feat/script-registerToken

Conversation

@OWK50GA
Copy link

@OWK50GA OWK50GA commented Aug 29, 2025

Add interactive TS script to register tokens

Changes Made

  • File Changed: added registerToken.ts in the Contracts/L1/scripts
  • Used ether.js to write a script within the file for the register token
  • Added a .env.example to serve as pointer for users later
  • Installed chalk and inquirer for interactive function

Acceptance Criteria

  • Running ts-node scripts/registertoken.ts (or pnpm ts-node …) launches an interactive flow
  • Successful registration logs tx details; failures provide actionable errors
  • Input validation prevents obvious misconfigs (bad address, decimals, etc.)
  • Script is documented with a short header comment and usage notes

Relevant Screenshots:
image

Fixes #114

Summary by CodeRabbit

  • New Features
    • Introduced an interactive CLI to register tokens on the L1 bridge with guided prompts and transaction preview/confirmation.
  • Chores
    • Added an example environment file for L1 to simplify local configuration (RPC URL, contract address, private key placeholder).
    • Pinned tool versions to ensure consistent local development.
    • Added dependencies to support CLI prompts and formatted output for the new script.

@coderabbitai
Copy link

coderabbitai bot commented Aug 29, 2025

Walkthrough

Adds tool version pins, an L1 environment example, new dependencies (chalk, inquirer), and a new interactive TypeScript script to register tokens via a Bridge contract on Sepolia using ethers. The script prompts for inputs, previews the transaction, and submits registerToken, handling success/failure.

Changes

Cohort / File(s) Summary
Tooling versions
./.tool-versions
Pins scarb 2.9.2 and starknet-foundry 0.36.0.
L1 env template
contracts/L1/.env.example
Adds example env with RPC_URL, CONTRACT_ADDRESS, PRIVATE_KEY.
L1 script deps
contracts/L1/package.json
Adds dependencies: chalk ^5.6.0, inquirer ^12.9.4.
Register token script
contracts/L1/scripts/registertoken.ts
New interactive script: validates env, prompts for asset type/token/price feed/decimals, previews, submits registerToken(assetType, tokenAddress, priceFeed, decimals), logs tx result. Includes minimal ABI and enums.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor Admin
    participant Script as Node TS Script
    participant Ethers as Ethers Provider/Wallet
    participant Contract as Bridge Contract
    participant Chain as Sepolia

    Admin->>Script: Start CLI (ts-node scripts/registertoken.ts)
    Script->>Script: Load env (RPC_URL, PRIVATE_KEY, CONTRACT_ADDRESS)
    Script->>Admin: Prompt: AssetType, Token Address, Price Feed, Decimals
    Admin-->>Script: Provide inputs / confirm preview
    Script->>Ethers: Create provider, wallet, contract
    alt Confirmed
        Script->>Contract: registerToken(assetType, tokenAddr, priceFeed, decimals)
        Contract->>Chain: Execute tx
        Chain-->>Script: Receipt (hash, block, gas)
        Script-->>Admin: Print tx details (success)
    else Canceled/Invalid
        Script-->>Admin: Print error/cancel and exit non-zero
    end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Assessment against linked issues

Objective Addressed Explanation
Interactive TS script at scripts/registertoken.ts with prompts and validation (#114) Script added with prompts; ERC20 address validation has assignment bug; some env error messages not printed.
Loads env: RPC_URL, PRIVATE_KEY, CONTRACT_ADDRESS (#114)
Transaction preview and confirmation; sends tx; logs hash/block/gas; clean exit codes (#114) Preview/confirmation and tx send implemented; success logs present; robustness of failures unclear due to logging bug.
Minimal ABI for registerToken and AssetType; header comment/usage notes (#114) Minimal ABI present; header comment/usage notes not evident.
Input validation (addresses, decimals range) prevents misconfig (#114) ERC20 token address validation bug and possible missing env error logging reduce effectiveness.

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Pinning tool versions (.tool-versions) Not requested by #114; unrelated to the interactive registration script.
Add env example template (contracts/L1/.env.example) Helpful but not specified in #114 requirements.

Suggested reviewers

  • JoE11-y
  • Ugo-X

Poem

I thump the keys with ears held high,
“Register!” I squeak to Sepolia’s sky.
With chalky hues and prompts that gleam,
I bridge a token—on-chain dream.
A hop, a hash, a block goes by—
Gas nibble spent—success! Oh my! 🐇✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ 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>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbit in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbit 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:
    • @coderabbit gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbit read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbit help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbit ignore or @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbit summary or @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbit or @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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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.

@OWK50GA OWK50GA marked this pull request as ready for review August 29, 2025 05:08
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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
contracts/L1/package.json (2)

1-11: Enable ESM for ts-node (ethers v6, chalk v5, inquirer v12 are ESM-only).
Without ESM, running the script will throw ERR_REQUIRE_ESM. Add type: module and use ts-node --esm.

Apply:

 {
   "name": "zeroxbridge-l1",
   "version": "1.0.0",
   "description": "ZeroXBridge L1 contracts",
+  "type": "module",
   "scripts": {
-    "deploy": "ts-node scripts/deploy_l1.ts deploy",
-    "deploy:testnet": "ts-node scripts/deploy_l1.ts deploy-testnet",
-    "deploy:anvil": "ts-node scripts/deploy_l1.ts deploy-anvil",
+    "deploy": "ts-node --esm scripts/deploy_l1.ts deploy",
+    "deploy:testnet": "ts-node --esm scripts/deploy_l1.ts deploy-testnet",
+    "deploy:anvil": "ts-node --esm scripts/deploy_l1.ts deploy-anvil",
+    "token:register": "ts-node --esm scripts/registerToken.ts",
     "test": "forge test",
     "build": "forge build"
   },

12-27: Deduplicate deps; fix version skew.
@types/node belongs in devDependencies; dotenv must appear once; ts-node should be dev-only.

   "dependencies": {
-    "@types/node": "^22.15.21",
     "chalk": "^5.6.0",
     "dotenv": "^16.5.0",
     "ethers": "^6.14.1",
     "inquirer": "^12.9.4",
     "starknet": "^6.24.1",
-    "ts-node": "^10.9.2"
+    "ts-node": "^10.9.2"
   },
   "devDependencies": {
-    "@types/node": "^24.0.7",
+    "@types/node": "^24.0.7",
     "commander": "^14.0.0",
-    "dotenv": "^17.0.0",
     "ora": "^8.2.0",
     "typescript": "^5.8.3"
   }

Optional: enforce runtime Node version for ESM:

   "devDependencies": {
     "typescript": "^5.8.3"
-  }
+  },
+  "engines": { "node": ">=18.17" }
🧹 Nitpick comments (6)
contracts/L1/.env.example (1)

1-3: Reorder keys and add trailing newline (dotenv-linter) + clarify placeholders.
Also ensure PRIVATE_KEY includes 0x-prefixed hex.

-SEPOLIA_RPC_URL=https://eth-sepolia.public.blastapi.io
-ZEROXBRIDGE_CONTRACT_ADDRESS=0x8F25bFe32269632dfd8D223D51FF145414d8107b
-PRIVATE_KEY=
+PRIVATE_KEY= # 0x... (DO NOT COMMIT REAL KEYS)
+SEPOLIA_RPC_URL=https://eth-sepolia.public.blastapi.io
+ZEROXBRIDGE_CONTRACT_ADDRESS=0x8F25bFe32269632dfd8D223D51FF145414d8107b
+
contracts/L1/scripts/registerToken.ts (5)

1-7: Add a short header with usage and env notes.
Matches the issue requirement.

+/**
+ * ZeroXBridge — registerToken CLI
+ * Usage: pnpm ts-node --esm scripts/registerToken.ts
+ * Env: SEPOLIA_RPC_URL, ZEROXBRIDGE_CONTRACT_ADDRESS, PRIVATE_KEY
+ */
 import { ethers } from 'ethers';
 import { configDotenv } from 'dotenv'
 import chalk from 'chalk'
 import inquirer from 'inquirer';
 
 configDotenv();

167-175: Typos and minor UX polish in the preview.
Fix “TOken”; align casing and add newlines for readability.

-    console.log('Ok, I will start now');
-    console.log(`\n` + chalk.yellow(`Transaction Preview: `));
+    console.log(chalk.yellow('\nTransaction Preview:'));
     console.log(`Asset type: ${AssetType[answers.assetType]}`);
-    console.log(`TOken Address: ${answers.tokenAddress}`);
+    console.log(`Token Address: ${answers.tokenAddress}`);
     console.log(`Price Feed: ${answers.priceFeed}`);
     console.log(`Decimals: ${answers.decimals}`);
     console.log(`From: ${wallet.address}`);
     console.log(`Contract: ${process.env.ZEROXBRIDGE_CONTRACT_ADDRESS}`);

194-204: Preflight simulation to surface revert reasons before sending.
Static call reduces failed txs and gives actionable errors.

     try {
         console.log(chalk.blue('Sending Transaction...'));
 
-        const tx = await contract.registerToken(
+        // Preflight
+        await contract.registerToken.staticCall(
+            answers.assetType, answers.tokenAddress, answers.priceFeed, answers.decimals
+        );
+
+        const tx = await contract.registerToken(
             answers.assetType, answers.tokenAddress, answers.priceFeed, answers.decimals
         );

203-220: Error reporting: include shortMessage/reason when available.
Helps triage permissions/param issues.

-    } catch (err) {
-        console.error(chalk.red('Transaction failed:'));
-        console.error(chalk.red((err as Error).message));
-        
-        process.exit(1);
-    }
+    } catch (err: any) {
+        console.error(chalk.red('Transaction failed:'));
+        console.error(chalk.red(err?.shortMessage ?? err?.reason ?? err?.message ?? String(err)));
+        process.exit(1);
+    }

90-96: Optional: log connected network to avoid mis-sends.
Prevents using Sepolia feeds on wrong chain.

     const provider = new ethers.JsonRpcProvider(process.env.SEPOLIA_RPC_URL);
     const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
     const contract = new ethers.Contract(process.env.ZEROXBRIDGE_CONTRACT_ADDRESS, BRIDGE_ABI, wallet);
 
-    console.log(chalk.green(`Connected at ${process.env.ZEROXBRIDGE_CONTRACT_ADDRESS}`));
-    console.log(chalk.green(`Using Account: ${wallet.address}\n`))
+    const net = await provider.getNetwork();
+    console.log(chalk.green(`Using Account: ${wallet.address}`));
+    console.log(chalk.green(`Network: ${net.name} (chainId=${Number(net.chainId)})`));
+    console.log(chalk.green(`Contract: ${process.env.ZEROXBRIDGE_CONTRACT_ADDRESS}\n`));
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 366215b and 8bd0bbe.

⛔ Files ignored due to path filters (1)
  • contracts/L1/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (4)
  • .tool-versions (1 hunks)
  • contracts/L1/.env.example (1 hunks)
  • contracts/L1/package.json (1 hunks)
  • contracts/L1/scripts/registerToken.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
contracts/L1/.env.example (2)
contracts/L1/scripts/deploy_l1.ts (3)
  • main (375-407)
  • deploymentTestnet (298-321)
  • deploymentMainnet (326-358)
contracts/L2/scripts/validate_setup.ts (1)
  • validateSetup (14-107)
contracts/L1/package.json (1)
contracts/L1/scripts/deploy_l1.ts (4)
  • deployAllContracts (250-293)
  • main (375-407)
  • deploymentMainnet (326-358)
  • deploymentAnvil (363-371)
contracts/L1/scripts/registerToken.ts (2)
contracts/L1/generateEthSignature.js (2)
  • ethers (41-41)
  • wallet (26-26)
contracts/L1/scripts/deploy_l1.ts (2)
  • main (375-407)
  • deploymentMainnet (326-358)
🪛 dotenv-linter (3.3.0)
contracts/L1/.env.example

[warning] 3-3: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)


[warning] 3-3: [UnorderedKey] The PRIVATE_KEY key should go before the SEPOLIA_RPC_URL key

(UnorderedKey)

🔇 Additional comments (3)
.tool-versions (1)

1-2: LGTM — benign tool pin updates.
No runtime impact on the new L1 script.

contracts/L1/package.json (1)

1-28: No ESM config changes required. tsconfig.json in both L1 and L2 defaults to CommonJS and your deploy scripts invoke ts-node without --esm, so adding "module": "NodeNext" isn’t necessary.

contracts/L1/.env.example (1)

1-3: Validate the sample contract address is current for Sepolia.
If it changes, users will get reverts.

Would you like me to add a tiny “validate_setup_l1.ts” that checks address format, chainId=11155111, and contract code at the address before running the script?

Comment on lines 69 to 83
if (!process.env.SEPOLIA_RPC_URL) {
chalk.red('RPC URL not found in environment variables');
process.exit(1);
}

if (!process.env.ZEROXBRIDGE_CONTRACT_ADDRESS) {
chalk.red('ZeroXBridge Contract Address not found in environment variables');
process.exit(1);
}

if (!process.env.PRIVATE_KEY) {
chalk.red('Admin Private key not found in environment variables');
process.exit(1);
}

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Errors are constructed but never printed (missing console.error).
Currently chalk.red(...) returns a string that’s discarded.

-    if (!process.env.SEPOLIA_RPC_URL) {
-        chalk.red('RPC URL not found in environment variables');
-        process.exit(1);
-    }
+    if (!process.env.SEPOLIA_RPC_URL) {
+        console.error(chalk.red('RPC URL not found (SEPOLIA_RPC_URL).'));
+        process.exit(1);
+    }
 
-    if (!process.env.ZEROXBRIDGE_CONTRACT_ADDRESS) {
-        chalk.red('ZeroXBridge Contract Address not found in environment variables');
-        process.exit(1);
-    }
+    if (!process.env.ZEROXBRIDGE_CONTRACT_ADDRESS) {
+        console.error(chalk.red('ZEROXBRIDGE_CONTRACT_ADDRESS not set.'));
+        process.exit(1);
+    }
+    if (!ethers.isAddress(process.env.ZEROXBRIDGE_CONTRACT_ADDRESS)) {
+        console.error(chalk.red('ZEROXBRIDGE_CONTRACT_ADDRESS is not a valid address.'));
+        process.exit(1);
+    }
 
-    if (!process.env.PRIVATE_KEY) {
-        chalk.red('Admin Private key not found in environment variables');
-        process.exit(1);
-    }
+    if (!process.env.PRIVATE_KEY) {
+        console.error(chalk.red('Admin PRIVATE_KEY not set.'));
+        process.exit(1);
+    }
📝 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
if (!process.env.SEPOLIA_RPC_URL) {
chalk.red('RPC URL not found in environment variables');
process.exit(1);
}
if (!process.env.ZEROXBRIDGE_CONTRACT_ADDRESS) {
chalk.red('ZeroXBridge Contract Address not found in environment variables');
process.exit(1);
}
if (!process.env.PRIVATE_KEY) {
chalk.red('Admin Private key not found in environment variables');
process.exit(1);
}
if (!process.env.SEPOLIA_RPC_URL) {
console.error(chalk.red('RPC URL not found (SEPOLIA_RPC_URL).'));
process.exit(1);
}
if (!process.env.ZEROXBRIDGE_CONTRACT_ADDRESS) {
console.error(chalk.red('ZEROXBRIDGE_CONTRACT_ADDRESS not set.'));
process.exit(1);
}
if (!ethers.isAddress(process.env.ZEROXBRIDGE_CONTRACT_ADDRESS)) {
console.error(chalk.red('ZEROXBRIDGE_CONTRACT_ADDRESS is not a valid address.'));
process.exit(1);
}
if (!process.env.PRIVATE_KEY) {
console.error(chalk.red('Admin PRIVATE_KEY not set.'));
process.exit(1);
}
🤖 Prompt for AI Agents
In contracts/L1/scripts/registerToken.ts around lines 69 to 83, the code
constructs error strings using chalk.red(...) but never prints them; replace the
discarded expressions with actual logging calls such as
console.error(chalk.red('...')) (or use console.error with plain text) before
calling process.exit(1) so the error messages appear in logs; ensure each
env-check uses console.error to output the message then exit.

Comment on lines 101 to 127
const answers = await inquirer.prompt([
{
type: 'list',
name: 'assetType',
message: 'select asset type',
choices: [
{ name: 'ETH', value: AssetType.ETH },
{ name: 'ERC20', value: AssetType.ERC20 }
]
},
{
type: 'input',
name: 'tokenAddress',
message: 'Token Address (leave empty for ETH):',
when: (answers) => answers.assetType === AssetType.ERC20,
validate: (input) => {
if (!input) return true //Empty is OK for ETH
if (!ethers.isAddress(input)) {
return 'Invalid Ethereum address format'
}
return true
},
filter: (input) => {
if (!input || input === '0x0') return ethers.ZeroAddress;
return ethers.getAddress(input);
}
},
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

ERC20 tokenAddress validation allows empty/zero, risking mis-registration.
This prompt only shows for ERC20, so empty should be rejected; don’t coerce to ZeroAddress here.

         {
             type: 'input',
             name: 'tokenAddress',
-            message: 'Token Address (leave empty for ETH):',
+            message: 'Token Address:',
             when: (answers) => answers.assetType === AssetType.ERC20,
-            validate: (input) => {
-                if (!input) return true //Empty is OK for ETH
-                if (!ethers.isAddress(input)) {
-                    return 'Invalid Ethereum address format'
-                }
-                return true
-            },
-            filter: (input) => {
-                if (!input || input === '0x0') return ethers.ZeroAddress;
-                return ethers.getAddress(input);
-            }
+            validate: (input) => {
+                if (!input) return 'Token address is required for ERC20';
+                if (!ethers.isAddress(input)) return 'Invalid Ethereum address format';
+                if (input === ethers.ZeroAddress) return 'Zero address is not allowed for ERC20';
+                return true;
+            },
+            filter: (input) => ethers.getAddress(input.trim())
         },
📝 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
const answers = await inquirer.prompt([
{
type: 'list',
name: 'assetType',
message: 'select asset type',
choices: [
{ name: 'ETH', value: AssetType.ETH },
{ name: 'ERC20', value: AssetType.ERC20 }
]
},
{
type: 'input',
name: 'tokenAddress',
message: 'Token Address (leave empty for ETH):',
when: (answers) => answers.assetType === AssetType.ERC20,
validate: (input) => {
if (!input) return true //Empty is OK for ETH
if (!ethers.isAddress(input)) {
return 'Invalid Ethereum address format'
}
return true
},
filter: (input) => {
if (!input || input === '0x0') return ethers.ZeroAddress;
return ethers.getAddress(input);
}
},
{
type: 'input',
name: 'tokenAddress',
message: 'Token Address:',
when: (answers) => answers.assetType === AssetType.ERC20,
validate: (input) => {
if (!input) return 'Token address is required for ERC20';
if (!ethers.isAddress(input)) return 'Invalid Ethereum address format';
if (input === ethers.ZeroAddress) return 'Zero address is not allowed for ERC20';
return true;
},
filter: (input) => ethers.getAddress(input.trim())
},
🤖 Prompt for AI Agents
In contracts/L1/scripts/registerToken.ts around lines 101 to 127, the ERC20
tokenAddress prompt currently accepts empty or '0x0' and coerces to
ethers.ZeroAddress which can cause mis-registration; update the prompt so that
for AssetType.ERC20 the validate function rejects empty or zero-like inputs
(returning an error string) and only accepts a valid address via
ethers.isAddress, and change the filter to stop coercing empty/'0x0' to
ZeroAddress and instead return ethers.getAddress(input) for valid inputs (or
leave invalid inputs untouched since validate will block them).

Comment on lines 149 to 160
type: 'number',
name: 'decimals',
message: 'Token Decimals',
default: 18,
validate: (input) => {
if (!input) return false;
if (input < 0 || input > 255 || !Number.isInteger(input)) {
return 'Decimals must integers be between 0 and 255'
}
return true;
}
},
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Prompt type 'number' isn’t built-in; 0 is currently rejected.
Switch to input + parse; accept 0–255; fix message.

-        {
-            type: 'number',
+        {
+            type: 'input',
             name: 'decimals',
             message: 'Token Decimals',
             default: 18,
-            validate: (input) => {
-                if (!input) return false;
-                if (input < 0 || input > 255 || !Number.isInteger(input)) {
-                    return 'Decimals must integers be between 0 and 255'
-                }
-                return true;
-            }
+            filter: (input) => Number.parseInt(String(input).trim(), 10),
+            validate: (input) => {
+                const n = Number(input);
+                if (!Number.isInteger(n)) return 'Decimals must be an integer';
+                if (n < 0 || n > 255) return 'Decimals must be between 0 and 255';
+                return true;
+            },
         },

Optional: force decimals=18 when assetType is ETH.

     if (answers.assetType === AssetType.ETH) {
         answers.tokenAddress = ethers.ZeroAddress;
+        answers.decimals = 18;
     }
📝 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
type: 'number',
name: 'decimals',
message: 'Token Decimals',
default: 18,
validate: (input) => {
if (!input) return false;
if (input < 0 || input > 255 || !Number.isInteger(input)) {
return 'Decimals must integers be between 0 and 255'
}
return true;
}
},
{
type: 'input',
name: 'decimals',
message: 'Token Decimals',
default: 18,
filter: (input) => Number.parseInt(String(input).trim(), 10),
validate: (input) => {
const n = Number(input);
if (!Number.isInteger(n)) return 'Decimals must be an integer';
if (n < 0 || n > 255) return 'Decimals must be between 0 and 255';
return true;
},
},
Suggested change
type: 'number',
name: 'decimals',
message: 'Token Decimals',
default: 18,
validate: (input) => {
if (!input) return false;
if (input < 0 || input > 255 || !Number.isInteger(input)) {
return 'Decimals must integers be between 0 and 255'
}
return true;
}
},
if (answers.assetType === AssetType.ETH) {
answers.tokenAddress = ethers.ZeroAddress;
answers.decimals = 18;
}
🤖 Prompt for AI Agents
In contracts/L1/scripts/registerToken.ts around lines 149 to 160, the prompt
currently uses type:'number' (not supported) and rejects 0; change the prompt to
type:'input' and parse the value to an integer (e.g., parseInt), update the
validate to accept 0–255 inclusive and ensure the parsed value is an integer,
and fix the validation message to "Decimals must be an integer between 0 and
255". Also, if assetType is ETH, bypass the prompt or force decimals = 18
automatically so ETH tokens get 18 decimals.

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

🧹 Nitpick comments (7)
contracts/L1/.env.example (2)

1-3: Fix dotenv-linter warnings: reorder keys and add trailing newline

Reorder keys alphabetically and end file with a newline. Also prefer placeholder RPC URLs to avoid vendor lock-in in examples.

-RPC_URL=https://eth-sepolia.public.blastapi.io
-CONTRACT_ADDRESS=0x8F25bFe32269632dfd8D223D51FF145414d8107b
-PRIVATE_KEY=
+CONTRACT_ADDRESS=0x8F25bFe32269632dfd8D223D51FF145414d8107b
+PRIVATE_KEY=
+RPC_URL=https://sepolia.infura.io/v3/your_api_key_here
+

1-3: Add cautionary comments for secrets and network

Add brief comments to prevent accidental commits and clarify the target network.

+# Sepolia settings for L1 registerToken script
+# Do NOT commit real secrets. Copy this file to .env and fill locally.
 CONTRACT_ADDRESS=0x8F25bFe32269632dfd8D223D51FF145414d8107b
 PRIVATE_KEY=
 RPC_URL=https://sepolia.infura.io/v3/your_api_key_here
contracts/L1/scripts/registertoken.ts (5)

129-138: Permit custom price feed address (not only predefined list)

Operationally you’ll need flexibility for new feeds. Offer a “Custom” option and an extra prompt with validation.

// after the priceFeed list question
{
  type: 'confirm',
  name: 'useCustomPriceFeed',
  message: 'Use a custom price feed address?',
  default: false
},
{
  type: 'input',
  name: 'priceFeed',
  when: (a) => a.useCustomPriceFeed,
  validate: (input) => ethers.isAddress(input) ? true : 'Invalid Ethereum address',
  filter: (input) => ethers.getAddress(input.trim())
}

159-167: Fix output typo

Small UI nit.

-    console.log(`TOken Address: ${answers.tokenAddress}`);
+    console.log(`Token Address: ${answers.tokenAddress}`);

94-100: Pre-flight: verify network and contract code at address

Reduce misconfig risk by confirming chain and that code exists at CONTRACT_ADDRESS before proceeding.

const network = await provider.getNetwork();
console.log(chalk.green(`Network: ${network.chainId} (${network.name})`));
const code = await provider.getCode(process.env.CONTRACT_ADDRESS);
if (code === '0x') {
  console.error(chalk.red('No contract code found at CONTRACT_ADDRESS'));
  process.exit(1);
}

186-206: Show gas/fee preview and handle revert reasons

Improves UX and diagnostics.

// Before sending
const gas = await contract.registerToken.estimateGas(
  answers.assetType, answers.tokenAddress, answers.priceFeed, answers.decimals
);
const feeData = await provider.getFeeData();
console.log(chalk.yellow(`Estimated Gas: ${gas.toString()}`));
if (feeData.maxFeePerGas) {
  console.log(chalk.yellow(`Max Fee Per Gas: ${feeData.maxFeePerGas.toString()}`));
}

// In catch block:
} catch (err) {
  const e = err as any;
  console.error(chalk.red('Transaction failed:'));
  console.error(chalk.red(e.shortMessage ?? e.reason ?? e.message));
  process.exit(1);
}

101-152: Type the prompt answers

Minor safety and editor support.

interface Answers {
  assetType: AssetType;
  tokenAddress?: string;
  priceFeed: string;
  decimals: number;
  useCustomPriceFeed?: boolean;
}
const answers = await inquirer.prompt<Answers>([ /* ... */ ]);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 8bd0bbe and 2250a3a.

📒 Files selected for processing (2)
  • contracts/L1/.env.example (1 hunks)
  • contracts/L1/scripts/registertoken.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
contracts/L1/scripts/registertoken.ts (2)
contracts/L1/scripts/deploy_l1.ts (4)
  • main (375-407)
  • deploymentMainnet (326-358)
  • deployAllContracts (250-293)
  • deploymentTestnet (298-321)
contracts/L1/scripts/generateSignature.ts (1)
  • main (26-64)
contracts/L1/.env.example (3)
contracts/L1/scripts/deploy_l1.ts (5)
  • deploymentTestnet (298-321)
  • main (375-407)
  • DeploymentConfig (34-38)
  • deploymentMainnet (326-358)
  • deploymentAnvil (363-371)
contracts/L2/scripts/deploy_l2.ts (1)
  • deploymentTestnet (590-602)
contracts/L2/scripts/validate_setup.ts (1)
  • validateSetup (14-107)
🪛 dotenv-linter (3.3.0)
contracts/L1/.env.example

[warning] 2-2: [UnorderedKey] The CONTRACT_ADDRESS key should go before the RPC_URL key

(UnorderedKey)


[warning] 3-3: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)


[warning] 3-3: [UnorderedKey] The PRIVATE_KEY key should go before the RPC_URL key

(UnorderedKey)

🔇 Additional comments (1)
contracts/L1/scripts/registertoken.ts (1)

154-157: ETH path: ensure decimals remain 18 and skip asking user

This complements the previous change; leaving as-is is fine once the decimals prompt is gated.

Comment on lines +69 to +86
if (!process.env.RPC_URL) {
chalk.red('RPC URL not found');
process.exit(1);
}

if (!process.env.CONTRACT_ADDRESS) {
chalk.red('Contract Address not set');
process.exit(1);
}

if (!ethers.isAddress(process.env.CONTRACT_ADDRESS)) {
chalk.red(`Contract address is not a valid address`);
}

if (!process.env.PRIVATE_KEY) {
chalk.red('Admin Private key not set');
process.exit(1);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Error messages not printed; invalid contract address doesn’t exit

You build colored strings but never log them, and an invalid contract address doesn’t stop execution.

-    if (!process.env.RPC_URL) {
-        chalk.red('RPC URL not found');
-        process.exit(1);
-    }
+    if (!process.env.RPC_URL) {
+        console.error(chalk.red('RPC URL not found'));
+        process.exit(1);
+    }

-    if (!process.env.CONTRACT_ADDRESS) {
-        chalk.red('Contract Address not set');
-        process.exit(1);
-    }
+    if (!process.env.CONTRACT_ADDRESS) {
+        console.error(chalk.red('Contract Address not set'));
+        process.exit(1);
+    }

-    if (!ethers.isAddress(process.env.CONTRACT_ADDRESS)) {
-        chalk.red(`Contract address is not a valid address`);
-    }
+    if (!ethers.isAddress(process.env.CONTRACT_ADDRESS)) {
+        console.error(chalk.red('Contract address is not a valid address'));
+        process.exit(1);
+    }

-    if (!process.env.PRIVATE_KEY) {
-        chalk.red('Admin Private key not set');
-        process.exit(1);
-    }
+    if (!process.env.PRIVATE_KEY) {
+        console.error(chalk.red('Admin private key not set'));
+        process.exit(1);
+    }
📝 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
if (!process.env.RPC_URL) {
chalk.red('RPC URL not found');
process.exit(1);
}
if (!process.env.CONTRACT_ADDRESS) {
chalk.red('Contract Address not set');
process.exit(1);
}
if (!ethers.isAddress(process.env.CONTRACT_ADDRESS)) {
chalk.red(`Contract address is not a valid address`);
}
if (!process.env.PRIVATE_KEY) {
chalk.red('Admin Private key not set');
process.exit(1);
}
if (!process.env.RPC_URL) {
console.error(chalk.red('RPC URL not found'));
process.exit(1);
}
if (!process.env.CONTRACT_ADDRESS) {
console.error(chalk.red('Contract Address not set'));
process.exit(1);
}
if (!ethers.isAddress(process.env.CONTRACT_ADDRESS)) {
console.error(chalk.red('Contract address is not a valid address'));
process.exit(1);
}
if (!process.env.PRIVATE_KEY) {
console.error(chalk.red('Admin private key not set'));
process.exit(1);
}
🤖 Prompt for AI Agents
In contracts/L1/scripts/registertoken.ts around lines 69 to 86, the code builds
red-colored error strings with chalk but never logs them to the console, and the
invalid contract address branch doesn't terminate execution; change each
chalk.red(...) call to an actual error output (e.g.,
console.error(chalk.red(...))) and for the invalid address check add a
process.exit(1) after logging the error so the script stops when
CONTRACT_ADDRESS is missing or malformed; keep existing checks for RPC_URL and
PRIVATE_KEY but ensure all error messages are printed and exit codes are
returned consistently.

Comment on lines +120 to +127
validate: (input) => {
if (!input) return 'Token adddress is required for ERC20';
if (!ethers.isAddress(input)) return 'Invalid Ethereum address format';
if (input = ethers.ZeroAddress) return 'Zero address is not allowed for ERC20';
return true
},
filter: (input) => ethers.getAddress(input.trim())
},
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

ERC20 address validation always fails (assignment instead of comparison) + typo

Assignment makes the condition always truthy; also fix “adddress”.

-                if (!input) return 'Token adddress is required for ERC20';
+                if (!input) return 'Token address is required for ERC20';
                 if (!ethers.isAddress(input)) return 'Invalid Ethereum address format';
-                if (input = ethers.ZeroAddress) return 'Zero address is not allowed for ERC20';
+                if (ethers.getAddress(input.trim()) === ethers.ZeroAddress) return 'Zero address is not allowed for ERC20';
                 return true
📝 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
validate: (input) => {
if (!input) return 'Token adddress is required for ERC20';
if (!ethers.isAddress(input)) return 'Invalid Ethereum address format';
if (input = ethers.ZeroAddress) return 'Zero address is not allowed for ERC20';
return true
},
filter: (input) => ethers.getAddress(input.trim())
},
validate: (input) => {
if (!input) return 'Token address is required for ERC20';
if (!ethers.isAddress(input)) return 'Invalid Ethereum address format';
if (ethers.getAddress(input.trim()) === ethers.ZeroAddress) return 'Zero address is not allowed for ERC20';
return true;
},
filter: (input) => ethers.getAddress(input.trim())
},
🤖 Prompt for AI Agents
In contracts/L1/scripts/registertoken.ts around lines 120 to 127: the validator
mistakenly uses assignment (input = ethers.ZeroAddress) which always succeeds
and causes validation to fail, and the error message contains a typo "adddress";
change the assignment to a comparison (e.g., input === ethers.ZeroAddress), trim
the input before validation, run ethers.isAddress on the trimmed value, and
correct the typo to "address" so the validation returns true only for a
non-empty, properly formatted, non-zero ERC20 address.

Comment on lines +140 to +151
type: 'number',
name: 'decimals',
message: 'Token Decimals',
default: 18,
validate: (input) => {
if (!input) return false;
if (input < 0 || input > 255 || !Number.isInteger(input)) {
return 'Decimals must integers be between 0 and 255'
}
return true;
}
},
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Decimals validation rejects 0; add robust parsing and show prompt only for ERC20

Zero is valid; current check treats 0 as falsy. Also tighten validation and grammar, and hide decimals prompt for ETH.

-        {
-            type: 'number',
+        {
+            type: 'input',
             name: 'decimals',
             message: 'Token Decimals',
             default: 18,
+            when: (answers) => answers.assetType === AssetType.ERC20,
             validate: (input) => {
-                if (!input) return false;
-                if (input < 0 || input > 255 || !Number.isInteger(input)) {
-                    return 'Decimals must integers be between 0 and 255'
+                const n = Number(input);
+                if (!Number.isFinite(n)) return 'Decimals is required';
+                if (n < 0 || n > 255 || !Number.isInteger(n)) {
+                    return 'Decimals must be an integer between 0 and 255';
                 }
                 return true;
-            }
+            },
+            filter: (input) => Number(input)
         },
📝 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
type: 'number',
name: 'decimals',
message: 'Token Decimals',
default: 18,
validate: (input) => {
if (!input) return false;
if (input < 0 || input > 255 || !Number.isInteger(input)) {
return 'Decimals must integers be between 0 and 255'
}
return true;
}
},
{
type: 'input',
name: 'decimals',
message: 'Token Decimals',
default: 18,
when: (answers) => answers.assetType === AssetType.ERC20,
validate: (input) => {
const n = Number(input);
if (!Number.isFinite(n)) return 'Decimals is required';
if (n < 0 || n > 255 || !Number.isInteger(n)) {
return 'Decimals must be an integer between 0 and 255';
}
return true;
},
filter: (input) => Number(input)
},
🤖 Prompt for AI Agents
In contracts/L1/scripts/registertoken.ts around lines 140 to 151, the decimals
prompt treats 0 as falsy and has loose validation/grammar and is shown for ETH;
change the validator to parse the input as an integer (e.g. parseInt), accept 0
as valid, ensure the parsed value is an integer and within 0–255 and return the
proper message "Decimals must be an integer between 0 and 255" on failure, and
add a when condition so the decimals prompt is only shown for ERC20 tokens (hide
it for ETH).

@OWK50GA
Copy link
Author

OWK50GA commented Sep 27, 2025

@JoE11-y

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.

Add interactive TS script to register tokens

1 participant