diff --git a/cmd/playground/tx/solidity/deployContract.go b/cmd/playground/tx/solidity/deployContract.go index 40b9928..b16b822 100644 --- a/cmd/playground/tx/solidity/deployContract.go +++ b/cmd/playground/tx/solidity/deployContract.go @@ -5,6 +5,7 @@ import ( "fmt" "os" + "github.com/hanchon/hanchond/lib/smartcontract" "github.com/hanchon/hanchond/playground/evmos" "github.com/hanchon/hanchond/playground/filesmanager" "github.com/hanchon/hanchond/playground/sql" @@ -49,6 +50,34 @@ var deployContractCmd = &cobra.Command{ os.Exit(1) } + abiPath, err := cmd.Flags().GetString("abi") + if err != nil { + fmt.Println("could not read abi path:", err.Error()) + os.Exit(1) + } + + if abiPath != "" { + // It requires a constructor + abiBytes, err := filesmanager.ReadFile(abiPath) + if err != nil { + fmt.Printf("error reading the abi file:%s\n", err.Error()) + os.Exit(1) + } + // Get Params + callArgs, err := smartcontract.StringsToABIArguments(params) + if err != nil { + fmt.Printf("error converting arguments: %s\n", err.Error()) + os.Exit(1) + } + + callData, err := smartcontract.ABIPackRaw(abiBytes, "", callArgs...) + if err != nil { + fmt.Println(err.Error()) + os.Exit(1) + } + bytecode = append(bytecode, callData...) + } + txHash, err := builder.DeployContract(0, bytecode, uint64(gasLimit)) if err != nil { fmt.Printf("error sending the transaction: %s\n", err.Error()) @@ -59,6 +88,14 @@ var deployContractCmd = &cobra.Command{ if err != nil { fmt.Printf("error getting the tx receipt:%s\n", err.Error()) } + trace, err := e.NewRequester().GetTransactionTrace(txHash) + if err != nil { + fmt.Printf("error getting the tx trace:%s\n", err.Error()) + } + if trace.Result.Error != "" { + fmt.Println("failed to execute the transaction:", trace.Result.Error) + os.Exit(1) + } fmt.Printf("{\"contract_address\":\"%s\", \"tx_hash\":\"%s\"}\n", receipt.Result.ContractAddress, txHash) os.Exit(0) @@ -68,4 +105,6 @@ var deployContractCmd = &cobra.Command{ func init() { SolidityCmd.AddCommand(deployContractCmd) deployContractCmd.Flags().Int("gas-limit", 2_000_000, "GasLimit to be used to deploy the transaction") + deployContractCmd.Flags().String("abi", "", "ABI file if the contract has a contronstructor that needs params") + deployContractCmd.Flags().StringSliceVarP(¶ms, "params", "p", []string{}, "A list of params. If the param is an address, prefix with `a:0x123...`") } diff --git a/cmd/playground/tx/solidity/deployUniswapV2Factory.go b/cmd/playground/tx/solidity/deployUniswapV2Factory.go new file mode 100644 index 0000000..60d7f1e --- /dev/null +++ b/cmd/playground/tx/solidity/deployUniswapV2Factory.go @@ -0,0 +1,136 @@ +package solidity + +import ( + "encoding/hex" + "fmt" + "os" + + "github.com/hanchon/hanchond/lib/smartcontract" + "github.com/hanchon/hanchond/playground/evmos" + "github.com/hanchon/hanchond/playground/filesmanager" + "github.com/hanchon/hanchond/playground/solidity" + "github.com/hanchon/hanchond/playground/sql" + "github.com/spf13/cobra" +) + +// deployUniswapV2FactoryCmd represents the deploy command +var deployUniswapV2FactoryCmd = &cobra.Command{ + Use: "deploy-uniswap-v2-factory [fee_wallet]", + Args: cobra.ExactArgs(1), + Short: "Deploy uniswap v2 factory", + Run: func(cmd *cobra.Command, args []string) { + queries := sql.InitDBFromCmd(cmd) + nodeID, err := cmd.Flags().GetString("node") + if err != nil { + fmt.Println("node not set") + os.Exit(1) + } + + gasLimit, err := cmd.Flags().GetInt("gas-limit") + if err != nil { + fmt.Println("incorrect gas limit") + os.Exit(1) + } + + feeWallet := args[0] + + // TODO: allow mainnet as a valid endpoint + e := evmos.NewEvmosFromDB(queries, nodeID) + builder := e.NewTxBuilder(uint64(gasLimit)) + + contractName := "/UniswapV2Factory" + // Clone uniswap-v2-core if needed + path, err := solidity.DownloadDep("https://github.com/Uniswap/uniswap-v2-core", "master", "uniswapv2") + if err != nil { + fmt.Println(err.Error()) + os.Exit(1) + } + path = path + "/contracts" + contractName + ".sol" + + // Set up temp folder + if err := filesmanager.CleanUpTempFolder(); err != nil { + fmt.Println("could not clean up the temp folder:", err.Error()) + os.Exit(1) + } + + folderName := "factoryBuilder" + if err := filesmanager.CreateTempFolder(folderName); err != nil { + fmt.Println("could not create the temp folder:", err.Error()) + os.Exit(1) + } + + // Compile the contract + err = solidity.CompileWithSolc("0.5.16", path, filesmanager.GetBranchFolder(folderName)) + if err != nil { + fmt.Println("could not compile the erc20 contract:", err.Error()) + os.Exit(1) + } + + bytecode, err := filesmanager.ReadFile(filesmanager.GetBranchFolder(folderName) + contractName + ".bin") + if err != nil { + fmt.Printf("error reading the bytecode file:%s\n", err.Error()) + os.Exit(1) + } + + bytecode, err = hex.DecodeString(string(bytecode)) + if err != nil { + fmt.Println("error converting bytecode to []byte:", err.Error()) + os.Exit(1) + } + + // Generate the constructor + abiBytes, err := filesmanager.ReadFile(filesmanager.GetBranchFolder(folderName) + contractName + ".abi") + if err != nil { + fmt.Printf("error reading the abi file:%s\n", err.Error()) + os.Exit(1) + } + + // Get Params + callArgs, err := smartcontract.StringsToABIArguments([]string{fmt.Sprintf("a:%s", feeWallet)}) + if err != nil { + fmt.Printf("error converting arguments: %s\n", err.Error()) + os.Exit(1) + } + + callData, err := smartcontract.ABIPackRaw(abiBytes, "", callArgs...) + if err != nil { + fmt.Println(err.Error()) + os.Exit(1) + } + bytecode = append(bytecode, callData...) + + txHash, err := builder.DeployContract(0, bytecode, uint64(gasLimit)) + if err != nil { + fmt.Printf("error sending the transaction: %s\n", err.Error()) + os.Exit(1) + } + + receipt, err := e.NewRequester().GetTransactionReceiptWithRetry(txHash, 15) + if err != nil { + fmt.Printf("error getting the tx receipt:%s\n", err.Error()) + } + + trace, err := e.NewRequester().GetTransactionTrace(txHash) + if err != nil { + fmt.Printf("error getting the tx trace:%s\n", err.Error()) + } + if trace.Result.Error != "" { + fmt.Println("failed to execute the transaction:", trace.Result.Error) + os.Exit(1) + } + + fmt.Printf("{\"contract_address\":\"%s\", \"tx_hash\":\"%s\"}\n", receipt.Result.ContractAddress, txHash) + + // Clean up files + if err := filesmanager.CleanUpTempFolder(); err != nil { + fmt.Println("could not clean up the temp folder:", err.Error()) + os.Exit(1) + } + os.Exit(0) + }, +} + +func init() { + SolidityCmd.AddCommand(deployUniswapV2FactoryCmd) + deployUniswapV2FactoryCmd.Flags().Int("gas-limit", 20_000_000, "GasLimit to be used to deploy the transaction") +} diff --git a/docs/pages/hanchond/playground/tx/solidity/deploy.mdx b/docs/pages/hanchond/playground/tx/solidity/deploy.mdx index dcd6839..0f3769a 100644 --- a/docs/pages/hanchond/playground/tx/solidity/deploy.mdx +++ b/docs/pages/hanchond/playground/tx/solidity/deploy.mdx @@ -18,3 +18,21 @@ The compiled`.bin` file must have the following format: hanchond playground tx solidity deploy-contract /tmp/CheckAddress.bin {"contract_address":"0xc2f633d1e91e1a2edeb55274753b44b204a4df91", "tx_hash":"0xa0a473953fb8ff88cbd7f63c956992d2621a7fbc47a6ba59427ef8482a0baf9f"} ``` + +## Deploy the Contract with Params + +Requirements: + +- ABI path +- Params + +```bash +hanchond playground tx solidity deploy-contract ./UniswapV2Factory.bin --gas-limit=1000000000 --abi=./UniswapV2Factory.abi --params=a:0x492bb9bb9a0b5a564307f65a75253e5d054669a9 +{"contract_address":"0xc2f633d1e91e1a2edeb55274753b44b204a4df91", "tx_hash":"0xa0a473953fb8ff88cbd7f63c956992d2621a7fbc47a6ba59427ef8482a0baf9f"} +``` + +The supported params list can be found [here](/lib/smartcontract/ABI) + +:::info +The params MUST be prefixed with the type followed by `:` +::: diff --git a/docs/pages/hanchond/playground/tx/solidity/deployUniswapV2.mdx b/docs/pages/hanchond/playground/tx/solidity/deployUniswapV2.mdx new file mode 100644 index 0000000..941d770 --- /dev/null +++ b/docs/pages/hanchond/playground/tx/solidity/deployUniswapV2.mdx @@ -0,0 +1,18 @@ +# Deploy UniswapV2 + +Before being able to deploy this contract, it requires `solc` `v0.5.16`. You can use the [Build Solc](/hanchond/playground/buildSolc) command to get the compiler. + +## Deploy the Factory + +Params: + +- Wallet that will get the fees + +```sh +hanchond playground tx solidity deploy-uniswap-v2-factory 0x492bb9bb9a0b5a564307f65a75253e5d054669a9 +{"contract_address":"0xe38de16950d3f1048f940268f80ef80bdd10d750", "tx_hash":"0x0ba8e69c71013345f28d7514125c7f323464b4179a68b2a8c28defcf2e96ef3a"} +``` + +:::info +The flag `gas-limit` can be set to use custom gas. It defaults to 2_000_000. +::: diff --git a/docs/pages/lib/smartcontract/ABI.mdx b/docs/pages/lib/smartcontract/ABI.mdx index f5297c1..105ef1b 100644 --- a/docs/pages/lib/smartcontract/ABI.mdx +++ b/docs/pages/lib/smartcontract/ABI.mdx @@ -10,6 +10,10 @@ callData, err := smartcontract.ABIPack(abiBytes, method, callArgs...) This is meant to be used after reading the `abi` file from disk. +### ABIPackRaw + +It is the same as the `ABIPack` function but it returns a byte array instead of an `hex` string. + ## Strings To ABIArguments The `StringsToABIArguments` function can be used to convert the strings used in the `cli` to the correct type used by solidity. diff --git a/lib/smartcontract/smartcontract.go b/lib/smartcontract/smartcontract.go index 381c430..8c36816 100644 --- a/lib/smartcontract/smartcontract.go +++ b/lib/smartcontract/smartcontract.go @@ -9,17 +9,26 @@ import ( "github.com/ethereum/go-ethereum/common" ) -func ABIPack(abiBytes []byte, method string, args ...interface{}) (string, error) { +// ABIPackRaw returns []byte instead of string +func ABIPackRaw(abiBytes []byte, method string, args ...interface{}) ([]byte, error) { parsedABI, err := abi.JSON(strings.NewReader(string(abiBytes))) if err != nil { - return "", fmt.Errorf("failed to parse the ABI: %s", err.Error()) + return []byte{}, fmt.Errorf("failed to parse the ABI: %s", err.Error()) } callData, err := parsedABI.Pack(method, args...) if err != nil { - return "", fmt.Errorf("failed to pack the ABI: %s", err.Error()) + return []byte{}, fmt.Errorf("failed to pack the ABI: %s", err.Error()) } + return callData, nil +} + +func ABIPack(abiBytes []byte, method string, args ...interface{}) (string, error) { + callData, err := ABIPackRaw(abiBytes, method, args...) + if err != nil { + return "", err + } return "0x" + hex.EncodeToString(callData), nil } diff --git a/lib/txbuilder/contract_deployment.go b/lib/txbuilder/contract_deployment.go index b3b1023..7300418 100644 --- a/lib/txbuilder/contract_deployment.go +++ b/lib/txbuilder/contract_deployment.go @@ -20,18 +20,5 @@ func (t *TxBuilder) DeployContract( return "", err } - // TODO: add support for constructors args - // parsedABI, err := abi.JSON(bytes.NewReader(abiFile)) - // if err != nil { - // log.Fatalf("Failed to parse ABI: %v", err) - // } - // input, err := parsedABI.Pack("", "Hello, Ethereum!") - // if err != nil { - // log.Fatalf("Failed to pack input parameters: %v", err) - // } - // - // // Combine bytecode + constructor arguments - // fullBytecode := append(bytecode, input...) - return t.SendTx(account.Address, nil, value, gasLimit, bytecode, privateKey) } diff --git a/playground/cosmosdaemon/daemon.go b/playground/cosmosdaemon/daemon.go index 968aeeb..ec8945d 100644 --- a/playground/cosmosdaemon/daemon.go +++ b/playground/cosmosdaemon/daemon.go @@ -93,7 +93,7 @@ func NewDameon( ValidatorInitialSupply: "100000000000000000000000000", // Maybe move this to just evmos - GasLimit: "10000000", + GasLimit: "1000000000", BaseFee: "1000000000", Ports: nil, diff --git a/vocs.config.ts b/vocs.config.ts index 14e0128..fca298b 100644 --- a/vocs.config.ts +++ b/vocs.config.ts @@ -172,6 +172,10 @@ export default defineConfig({ text: "Call Contract View", link: "/hanchond/playground/tx/solidity/contractView", }, + { + text: "Deploy UniswapV2", + link: "/hanchond/playground/tx/solidity/deployUniswapV2", + }, ], }, ],