-
Notifications
You must be signed in to change notification settings - Fork 60
feat: Swap commands #278
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Swap commands #278
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| import path from "path"; | ||
| import fs from "fs"; | ||
|
|
||
| /** | ||
| * Load contract artifacts (ABI & bytecode) compiled by Foundry (out/** path) | ||
| */ | ||
| export const loadContractArtifacts = ( | ||
| contractName: string, | ||
| sourceName?: string | ||
| ) => { | ||
| const sourcePath = sourceName || `${contractName}.sol`; | ||
| const artifactPath = path.join( | ||
| __dirname, | ||
| `../out/${sourcePath}/${contractName}.json` | ||
| ); | ||
|
|
||
| try { | ||
| const artifact = JSON.parse(fs.readFileSync(artifactPath, "utf8")); | ||
| return { | ||
| abi: artifact.abi, | ||
| bytecode: artifact.bytecode, | ||
| } as { abi: any; bytecode: string }; | ||
| } catch (error) { | ||
| const message = error instanceof Error ? error.message : String(error); | ||
| throw new Error( | ||
| `Unable to load contract artifacts for ${contractName}: ${message}` | ||
| ); | ||
| } | ||
| }; | ||
|
Comment on lines
+7
to
+29
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Enhance type safety and add input validation. The function implementation is functional but could benefit from several improvements:
+interface ContractArtifact {
+ abi: any[]; // Could be further typed if ABI structure is known
+ bytecode: string;
+}
+
export const loadContractArtifacts = (
contractName: string,
sourceName?: string
-) => {
+): ContractArtifact => {
+ // Validate contractName to prevent path traversal
+ if (!contractName || contractName.includes('..') || contractName.includes('/')) {
+ throw new Error(`Invalid contract name: ${contractName}`);
+ }
+
const sourcePath = sourceName || `${contractName}.sol`;
const artifactPath = path.join(
__dirname,
`../out/${sourcePath}/${contractName}.json`
);
try {
const artifact = JSON.parse(fs.readFileSync(artifactPath, "utf8"));
return {
abi: artifact.abi,
bytecode: artifact.bytecode,
- } as { abi: any; bytecode: string };
+ };
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
throw new Error(
`Unable to load contract artifacts for ${contractName}: ${message}`
);
}
};🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,80 @@ | ||||||||||||||||||||||||||||||
| import { Command } from "commander"; | ||||||||||||||||||||||||||||||
| import { ethers } from "ethers"; | ||||||||||||||||||||||||||||||
| import { loadContractArtifacts } from "./common"; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| const main = async (opts: any) => { | ||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Replace The function parameter should use a strongly-typed interface instead of Define an interface for the options: +interface DeployOptions {
+ rpc: string;
+ privateKey: string;
+ name: string;
+ gateway: string;
+ uniswapRouter: string;
+ gasLimit: number;
+}
+
-const main = async (opts: any) => {
+const main = async (opts: DeployOptions) => {📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
| const provider = new ethers.providers.JsonRpcProvider(opts.rpc); | ||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Update to ethers v6 API. The code uses deprecated ethers v5 API. Migrate to ethers v6 for better performance and future support. Apply these changes: - const provider = new ethers.providers.JsonRpcProvider(opts.rpc);
+ const provider = new ethers.JsonRpcProvider(opts.rpc);- await implementation.deployed();
+ await implementation.waitForDeployment();- await proxy.deployed();
+ await proxy.waitForDeployment();Also applies to: 16-16, 34-34 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
| const signer = new ethers.Wallet(opts.privateKey, provider); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| const network = await provider.getNetwork(); | ||||||||||||||||||||||||||||||
| const networkInfo = network.name ?? network.chainId; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||
| const { abi, bytecode } = loadContractArtifacts(opts.name); | ||||||||||||||||||||||||||||||
| const factory = new ethers.ContractFactory(abi, bytecode, signer); | ||||||||||||||||||||||||||||||
| const implementation = await factory.deploy(); | ||||||||||||||||||||||||||||||
| await implementation.deployed(); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| const initData = new ethers.utils.Interface(abi).encodeFunctionData( | ||||||||||||||||||||||||||||||
| "initialize", | ||||||||||||||||||||||||||||||
| [opts.gateway, opts.uniswapRouter, opts.gasLimit, signer.address] | ||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||
|
Comment on lines
+18
to
+21
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Validate contract interface before encoding function data. The code assumes the contract has an Add validation: + // Validate that the contract has the initialize function
+ const contractInterface = new ethers.utils.Interface(abi);
+ if (!contractInterface.functions['initialize(address,address,uint256,address)']) {
+ throw new Error(`Contract ${opts.name} does not have the expected initialize function`);
+ }
+
- const initData = new ethers.utils.Interface(abi).encodeFunctionData(
+ const initData = contractInterface.encodeFunctionData(
"initialize",
[opts.gateway, opts.uniswapRouter, opts.gasLimit, signer.address]
);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| const { abi: proxyAbi, bytecode: proxyBytecode } = loadContractArtifacts( | ||||||||||||||||||||||||||||||
| "ERC1967Proxy", | ||||||||||||||||||||||||||||||
| "ERC1967Proxy.sol" | ||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| const proxyFactory = new ethers.ContractFactory( | ||||||||||||||||||||||||||||||
| proxyAbi, | ||||||||||||||||||||||||||||||
| proxyBytecode, | ||||||||||||||||||||||||||||||
| signer | ||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||
| const proxy = await proxyFactory.deploy(implementation.address, initData); | ||||||||||||||||||||||||||||||
| await proxy.deployed(); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| console.log( | ||||||||||||||||||||||||||||||
| JSON.stringify({ | ||||||||||||||||||||||||||||||
| contractAddress: proxy.address, | ||||||||||||||||||||||||||||||
| implementationAddress: implementation.address, | ||||||||||||||||||||||||||||||
| deployer: signer.address, | ||||||||||||||||||||||||||||||
| network: networkInfo, | ||||||||||||||||||||||||||||||
| transactionHash: proxy.deployTransaction?.hash, | ||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||||||||||||
| console.error( | ||||||||||||||||||||||||||||||
| "Deployment failed:", | ||||||||||||||||||||||||||||||
| err instanceof Error ? err.message : err | ||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||
| process.exit(1); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| export const deploy = new Command("deploy") | ||||||||||||||||||||||||||||||
| .description("Deploy a swap contract") | ||||||||||||||||||||||||||||||
| .requiredOption( | ||||||||||||||||||||||||||||||
| "-r, --rpc <url>", | ||||||||||||||||||||||||||||||
| "RPC URL (default: testnet)", | ||||||||||||||||||||||||||||||
| "https://zetachain-athens-evm.blockpi.network/v1/rpc/public" | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| .requiredOption("-k, --private-key <key>", "Private key") | ||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Security risk: Private key exposure in command line. Passing private keys as command-line arguments exposes them in process lists and shell history, creating a significant security vulnerability. Consider alternative approaches: - .requiredOption("-k, --private-key <key>", "Private key")
+ .option("-k, --private-key <key>", "Private key (consider using environment variable PRIVATE_KEY instead)")And in the main function: + const privateKey = opts.privateKey || process.env.PRIVATE_KEY;
+ if (!privateKey) {
+ throw new Error("Private key must be provided via --private-key option or PRIVATE_KEY environment variable");
+ }
- const signer = new ethers.Wallet(opts.privateKey, provider);
+ const signer = new ethers.Wallet(privateKey, provider);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
| .option("-n, --name <name>", "Contract name", "Swap") | ||||||||||||||||||||||||||||||
| .requiredOption( | ||||||||||||||||||||||||||||||
| "-u, --uniswap-router <address>", | ||||||||||||||||||||||||||||||
| "Uniswap V2 Router address (default: testnet)", | ||||||||||||||||||||||||||||||
| "0x2ca7d64A7EFE2D62A725E2B35Cf7230D6677FfEe" | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| .option( | ||||||||||||||||||||||||||||||
| "-g, --gateway <address>", | ||||||||||||||||||||||||||||||
| "Gateway address (default: testnet)", | ||||||||||||||||||||||||||||||
| "0x6c533f7fe93fae114d0954697069df33c9b74fd7" | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| .option("--gas-limit <number>", "Gas limit for the transaction", "1000000") | ||||||||||||||||||||||||||||||
| .action((opts) => { | ||||||||||||||||||||||||||||||
| opts.gasLimit = Number(opts.gasLimit); | ||||||||||||||||||||||||||||||
fadeev marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||
| main(opts).catch((err) => { | ||||||||||||||||||||||||||||||
| console.error("Unhandled error:", err); | ||||||||||||||||||||||||||||||
| process.exit(1); | ||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| #!/usr/bin/env npx tsx | ||
|
|
||
| import { Command } from "commander"; | ||
| import { deploy } from "./deploy"; | ||
|
|
||
| const program = new Command().addCommand(deploy); | ||
|
|
||
| program.parse(); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -60,4 +60,4 @@ | |
| "@zetachain/toolkit": "^16.0.0", | ||
| "zetachain": "6.0.1" | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.