diff --git a/cmd/playground/tx/solidity/deployERC20.go b/cmd/playground/tx/solidity/deployERC20.go index eb404b7..c9f605c 100644 --- a/cmd/playground/tx/solidity/deployERC20.go +++ b/cmd/playground/tx/solidity/deployERC20.go @@ -14,8 +14,8 @@ import ( // deployERC20Cmd represents the deploy command var deployERC20Cmd = &cobra.Command{ - Use: "deploy-erc20 [name] [symbol] [initial_amount]", - Args: cobra.ExactArgs(3), + Use: "deploy-erc20 [name] [symbol]", + Args: cobra.ExactArgs(2), Short: "Deploy an erc20 contract", Run: func(cmd *cobra.Command, args []string) { queries := sql.InitDBFromCmd(cmd) @@ -31,9 +31,14 @@ var deployERC20Cmd = &cobra.Command{ os.Exit(1) } + initialAmount, err := cmd.Flags().GetString("initial-amount") + if err != nil { + fmt.Println("incorrect initial-amount") + os.Exit(1) + } + name := args[0] symbol := args[1] - initialAmount := args[2] // TODO: allow mainnet as a valid endpoint e := evmos.NewEvmosFromDB(queries, nodeID) @@ -58,8 +63,24 @@ var deployERC20Cmd = &cobra.Command{ os.Exit(1) } - // Create the contract - contract := solidity.GenerateERC20Contract(path, name, symbol, initialAmount) + isWrapped, err := cmd.Flags().GetBool("is-wrapped-coin") + if err != nil { + fmt.Println("incorrect wrapped flag") + os.Exit(1) + } + + contract := "" + solcVersion := "0.8.25" + switch isWrapped { + case false: + // Normal ERC20 + contract = solidity.GenerateERC20Contract(path, name, symbol, initialAmount) + case true: + // Wrapping base denom, use WETH9 + contract = solidity.GenerateWrappedCoinContract(name, symbol, "18") + solcVersion = "0.4.18" + } + contractPath := filesmanager.GetBranchFolder(folderName) + "/mycontract.sol" if err := filesmanager.SaveFile([]byte(contract), contractPath); err != nil { fmt.Println("could not save the contract file:", err.Error()) @@ -67,7 +88,7 @@ var deployERC20Cmd = &cobra.Command{ } // Compile the contract - err = solidity.CompileWithSolc("0.8.25", contractPath, filesmanager.GetBranchFolder(folderName)) + err = solidity.CompileWithSolc(solcVersion, contractPath, filesmanager.GetBranchFolder(folderName)) if err != nil { fmt.Println("could not compile the erc20 contract:", err.Error()) os.Exit(1) @@ -110,4 +131,7 @@ var deployERC20Cmd = &cobra.Command{ func init() { SolidityCmd.AddCommand(deployERC20Cmd) deployERC20Cmd.Flags().Int("gas-limit", 2_000_000, "GasLimit to be used to deploy the transaction") + deployERC20Cmd.Flags().String("initial-amount", "1000000", "Initial amout of coins sent to the deployer address") + deployERC20Cmd.Flags().Bool("is-wrapped-coin", false, "Flag used to indenfity if the contract is representing the base denom. It uses WETH9 instead of OpenZeppelin contracts") + } diff --git a/cmd/playground/tx/solidity/deployUniswapV2Factory.go b/cmd/playground/tx/solidity/deployUniswapV2Factory.go index 60d7f1e..6a81ec4 100644 --- a/cmd/playground/tx/solidity/deployUniswapV2Factory.go +++ b/cmd/playground/tx/solidity/deployUniswapV2Factory.go @@ -2,9 +2,12 @@ package solidity import ( "encoding/hex" + "encoding/json" "fmt" "os" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" "github.com/hanchon/hanchond/lib/smartcontract" "github.com/hanchon/hanchond/playground/evmos" "github.com/hanchon/hanchond/playground/filesmanager" @@ -119,7 +122,26 @@ var deployUniswapV2FactoryCmd = &cobra.Command{ os.Exit(1) } - fmt.Printf("{\"contract_address\":\"%s\", \"tx_hash\":\"%s\"}\n", receipt.Result.ContractAddress, txHash) + codeHash, err := e.NewRequester().EthCode(receipt.Result.ContractAddress, "latest") + if err != nil { + fmt.Println("failed to get the eth code:", err.Error()) + os.Exit(1) + } + type code struct { + Result string `json:"result"` + } + var c code + if err := json.Unmarshal(codeHash, &c); err != nil { + fmt.Println("failed to get the eth code:", err.Error()) + os.Exit(1) + } + + // Perform keccak256 hashing + hash := crypto.Keccak256(common.Hex2Bytes(c.Result[2:])) + // Convert hash to hexadecimal string for display + hashHex := hex.EncodeToString(hash) + + fmt.Printf("{\"contract_address\":\"%s\", \"code_hash\":\"%s\", \"tx_hash\":\"%s\"}\n", receipt.Result.ContractAddress, "0x"+hashHex, txHash) // Clean up files if err := filesmanager.CleanUpTempFolder(); err != nil { diff --git a/docs/pages/hanchond/playground/tx/solidity/deployERC20.mdx b/docs/pages/hanchond/playground/tx/solidity/deployERC20.mdx index bb303a8..13548e4 100644 --- a/docs/pages/hanchond/playground/tx/solidity/deployERC20.mdx +++ b/docs/pages/hanchond/playground/tx/solidity/deployERC20.mdx @@ -10,13 +10,26 @@ Params: - Name - Symbol -- Initial Amount (will be sent to the deployer) + +Optional: + +- Initial Amount (will be sent to the deployer) `--initial-amount` +- IsWrappedCoin (if it's wrapping the base denom) `--is-wrapped-coin`. This requires `solc v0.4.18` + +:::warning +If you are creating a network with Evmos v19+, the Wrapped Evmos will be automatically generated. +::: ```sh hanchond playground tx solidity deploy-erc20 Hanchon HCH 10000000 {"contract_address":"0xe38de16950d3f1048f940268f80ef80bdd10d750", "tx_hash":"0x0ba8e69c71013345f28d7514125c7f323464b4179a68b2a8c28defcf2e96ef3a"} ``` +```sh +hanchond playground tx solidity deploy-erc20 WrappedEvmos WEVMOS --is-wrapped-coin +{"contract_address":"0x0d5b94e15b5b6068e7cc4a9b6ee3a1cde89113ca", "tx_hash":"0x7eeda7ef271f64eb21aebfe64a9cac8e8631692d5201a1be63c4fbf001b5e84a"} +``` + :::info The flag `gas-limit` can be set to use custom gas. It defaults to 2_000_000. ::: diff --git a/playground/solidity/erc20builder.go b/playground/solidity/erc20builder.go index 1c7807b..139a475 100644 --- a/playground/solidity/erc20builder.go +++ b/playground/solidity/erc20builder.go @@ -29,3 +29,72 @@ contract %s is ERC20, ERC20Permit { } `, openzeppelinPath, openzeppelinPath, name, name, symbol, name, initialAmount) } + +func GenerateWrappedCoinContract(name, symbol, decimals string) string { + name = StringToTitle(name) + symbol = strings.ToUpper(symbol) + return fmt.Sprintf(`pragma solidity ^0.4.18; +contract %s { + string public name = "%s"; + string public symbol = "%s"; + uint8 public decimals = %s; + + event Approval(address indexed src, address indexed guy, uint wad); + event Transfer(address indexed src, address indexed dst, uint wad); + event Deposit(address indexed dst, uint wad); + event Withdrawal(address indexed src, uint wad); + + mapping(address => uint) public balanceOf; + mapping(address => mapping(address => uint)) public allowance; + + function() public payable { + deposit(); + } + + function deposit() public payable { + balanceOf[msg.sender] += msg.value; + Deposit(msg.sender, msg.value); + } + + function withdraw(uint wad) public { + require(balanceOf[msg.sender] >= wad); + balanceOf[msg.sender] -= wad; + msg.sender.transfer(wad); + Withdrawal(msg.sender, wad); + } + + function totalSupply() public view returns (uint) { + return this.balance; + } + + function approve(address guy, uint wad) public returns (bool) { + allowance[msg.sender][guy] = wad; + Approval(msg.sender, guy, wad); + return true; + } + + function transfer(address dst, uint wad) public returns (bool) { + return transferFrom(msg.sender, dst, wad); + } + + function transferFrom( + address src, + address dst, + uint wad + ) public returns (bool) { + require(balanceOf[src] >= wad); + + if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) { + require(allowance[src][msg.sender] >= wad); + allowance[src][msg.sender] -= wad; + } + + balanceOf[src] -= wad; + balanceOf[dst] += wad; + + Transfer(src, dst, wad); + + return true; + } +}`, name, name, symbol, decimals) +}