feat: added script for registerToken#121
feat: added script for registerToken#121OWK50GA wants to merge 3 commits intoExplore-Beyond-Innovations:mainfrom
Conversation
WalkthroughAdds 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
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
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Suggested reviewers
Poem
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 unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
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.
⛔ Files ignored due to path filters (1)
contracts/L1/package-lock.jsonis 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 invokets-nodewithout--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?
| 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); | ||
| } | ||
|
|
There was a problem hiding this comment.
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.
| 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.
| 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); | ||
| } | ||
| }, |
There was a problem hiding this comment.
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.
| 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).
| 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; | ||
| } | ||
| }, |
There was a problem hiding this comment.
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.
| 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; | |
| }, | |
| }, |
| 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.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (7)
contracts/L1/.env.example (2)
1-3: Fix dotenv-linter warnings: reorder keys and add trailing newlineReorder 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 networkAdd 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_herecontracts/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 typoSmall UI nit.
- console.log(`TOken Address: ${answers.tokenAddress}`); + console.log(`Token Address: ${answers.tokenAddress}`);
94-100: Pre-flight: verify network and contract code at addressReduce 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 reasonsImproves 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 answersMinor 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.
📒 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 userThis complements the previous change; leaving as-is is fine once the decimals prompt is gated.
| 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); | ||
| } |
There was a problem hiding this comment.
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.
| 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.
| 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()) | ||
| }, |
There was a problem hiding this comment.
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.
| 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.
| 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; | ||
| } | ||
| }, |
There was a problem hiding this comment.
🛠️ Refactor suggestion
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.
| 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).
Add interactive TS script to register tokens
Changes Made
registerToken.tsin the Contracts/L1/scripts.env.example to serve as pointer for users laterAcceptance Criteria
Relevant Screenshots:

Fixes #114
Summary by CodeRabbit