第1节:编译部署合约

小白入门:https://github.com/dukedaily/solidity-expert ,欢迎star转发,文末加V入群。

职场进阶: https://dukeweb3.com

前置条件

安装编译器

solc会对合约进行编译,生成:abi和bytecode

# Ubuntu
sudo snap install solc --edge

# MacOS
brew update
brew tap ethereum/ethereum
brew install solidity

安装abigen

abigen可以对abi进行编译,生成配套的go代码,从而支持我们进行后续开发。

go get -u github.com/ethereum/go-ethereum
cd $GOPATH/src/github.com/ethereum/go-ethereum/
make
make devtools

编写合约

创建工程:go mod init code,并创建文件contracts/Store.sol

// SPDX-License-Identifier: GPL-3.0
pragma solidity =0.8.15;

contract Store {
  event ItemSet(bytes32 key, bytes32 value);

  string public version;
  mapping (bytes32 => bytes32) public items;

  constructor(string memory _version) {
    version = _version;
  }

  function setItem(bytes32 key, bytes32 value) external {
    items[key] = value;
    emit ItemSet(key, value);
  }
}

编译合约

https://geth.ethereum.org/docs/tools/abigen

指定合约,生成abi和bin(bytecode),存放在metadata目录下,同时生成配套代码Store.go

# 1.通过.sol编译出abi
 solc --abi --bin contracts/Store.sol -o metadata

# 2.通过abi编译出go文件
abigen --abi=metadata/Store.abi --bin=metadata/Store.bin --pkg=store --type Store --out=src/Store.go

# 4.安装go依赖包
go mod tidy

当前工程目录结构:

image-20221130114625242

部署合约

创建部署文件main/deploy.go,准备部署到bsc testnet,chainid:56

package main

import (
    "fmt"
    "log"

    utils "code/main/utils"
    store "code/src"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial(utils.BscTestnetRpc)
    if err != nil {
        log.Fatal(err)
    }

    auth := utils.Prepare(utils.HardhatPrivateKey, 0, client)

    input := "1.0"
    address, tx, instance, err := store.DeployStore(auth, client, input)
    if err != nil {
        log.Fatal(err)
    }

    // 0x587bf1bc96163e279d2ea1b27f3b41cc34b171c3
    fmt.Println("address:", address.Hex())

    // https://testnet.bscscan.com/tx/0x082186993e2786744366e2147827841dc02115439d9d3786ce39a1774209d38a
    fmt.Println("tx hash:", tx.Hash().Hex())
    _ = instance
}

main/utils/utils.go,为了方便开发,下面的私钥刻意暴露了出来,实际的代码中,应该使用环境变量,将私钥存放在.env文件中,请小心处理之,否则资产会被零元购

package utils

import (
    "context"
    "crypto/ecdsa"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/accounts/abi/bind"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

var (
    BscTestnetRpc = "https://data-seed-prebsc-2-s1.binance.org:8545"

    // address: 0xc783df8a850f42e7f7e57013759c285caa701eb6
    HardhatPrivateKey = "c5e8f61d1ab959b397eecc0a37a6517b8e67a0e7cf1f4bce5591f3ed80199122"
)

func Prepare(privKey string, value int64, client *ethclient.Client) *bind.TransactOpts {
    privateKey, err := crypto.HexToECDSA(privKey)
    if err != nil {
        log.Fatal(err)
    }

    publicKey := privateKey.Public()
    publicKeECDSA, ok := publicKey.(*ecdsa.PublicKey)
    if !ok {
        log.Fatal("invalid public key type")
    }

    fromAddress := crypto.PubkeyToAddress(*publicKeECDSA)
    nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
    if err != nil {
        log.Fatal(err)
    }

    gasPrice, err := client.SuggestGasPrice(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    // chainId := 56
    // auth, _ := bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(int64(56)))
    auth := bind.NewKeyedTransactor(privateKey)
    auth.Nonce = big.NewInt(int64(nonce))
    auth.Value = big.NewInt(value)
    // auth.GasLimit = uint64(300000)
    auth.GasPrice = gasPrice

    return auth
}

执行结果,点击查看

image-20221211104316143

总结

本文我们介绍了如何:创建与以太坊交互的客户端、编写合约、编译合约、与合约读写交互操作,有了这些基础准备后,下一节我们将介绍如何使用合约监听事件并与我们的世界杯项目进行结合,敬请期待!

相关链接