-
Notifications
You must be signed in to change notification settings - Fork 12
/
contract_code.go
142 lines (126 loc) · 4.71 KB
/
contract_code.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package keeper
import (
"bytes"
"encoding/hex"
"fmt"
"math/big"
errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/evmos/ethermint/x/evm/statedb"
evmtypes "github.com/evmos/ethermint/x/evm/types"
"github.com/functionx/fx-core/v7/contract"
"github.com/functionx/fx-core/v7/x/evm/types"
)
// CreateContractWithCode create contract account and set code
func (k *Keeper) CreateContractWithCode(ctx sdk.Context, address common.Address, code []byte) error {
codeHash := crypto.Keccak256Hash(code)
k.Logger(ctx).Debug("create contract with code", "address", address.String(), "code-hash", codeHash)
acc := k.GetAccount(ctx, address)
if acc == nil {
acc = statedb.NewEmptyAccount()
}
acc.CodeHash = codeHash.Bytes()
k.SetCode(ctx, acc.CodeHash, code)
if err := k.SetAccount(ctx, address, *acc); err != nil {
return err
}
ctx.EventManager().EmitEvent(sdk.NewEvent(
types.EventContractCode,
sdk.NewAttribute(types.AttributeKeyContract, address.String()),
sdk.NewAttribute(types.AttributeKeyCodeHash, hex.EncodeToString(acc.CodeHash)),
))
return nil
}
// UpdateContractCode update contract code and code-hash
func (k *Keeper) UpdateContractCode(ctx sdk.Context, address common.Address, contractCode []byte) error {
acc := k.GetAccount(ctx, address)
if acc == nil {
return errorsmod.Wrap(evmtypes.ErrInvalidAccount, address.String())
}
codeHash := crypto.Keccak256Hash(contractCode).Bytes()
if bytes.Equal(codeHash, acc.CodeHash) {
return fmt.Errorf("update the same code hash: %s", address.String())
}
acc.CodeHash = codeHash
k.SetCode(ctx, acc.CodeHash, contractCode)
if err := k.SetAccount(ctx, address, *acc); err != nil {
return err
}
k.Logger(ctx).Info("update contract code", "address", address.String(), "code-hash", hex.EncodeToString(acc.CodeHash))
ctx.EventManager().EmitEvent(sdk.NewEvent(
types.EventContractCode,
sdk.NewAttribute(types.AttributeKeyContract, address.String()),
sdk.NewAttribute(types.AttributeKeyCodeHash, hex.EncodeToString(acc.CodeHash)),
))
return nil
}
// DeployContract deploy contract with args
func (k *Keeper) DeployContract(ctx sdk.Context, from common.Address, abi abi.ABI, bin []byte, constructorData ...interface{}) (common.Address, error) {
args, err := abi.Pack("", constructorData...)
if err != nil {
return common.Address{}, errorsmod.Wrap(types.ErrABIPack, err.Error())
}
data := make([]byte, len(bin)+len(args))
copy(data[:len(bin)], bin)
copy(data[len(bin):], args)
nonce, err := k.accountKeeper.GetSequence(ctx, from.Bytes())
if err != nil {
return common.Address{}, err
}
_, err = k.CallEVMWithoutGas(ctx, from, nil, nil, data, true)
if err != nil {
return common.Address{}, err
}
contractAddr := crypto.CreateAddress(from, nonce)
return contractAddr, nil
}
// DeployUpgradableContract deploy upgrade contract and initialize it
func (k *Keeper) DeployUpgradableContract(ctx sdk.Context, from, logic common.Address, logicData []byte, initializeAbi *abi.ABI, initializeArgs ...interface{}) (common.Address, error) {
// deploy proxy
erc1967Proxy := contract.GetERC1967Proxy()
if logicData == nil {
logicData = []byte{}
}
proxyContract, err := k.DeployContract(ctx, from, erc1967Proxy.ABI, erc1967Proxy.Bin, logic, logicData)
if err != nil {
return common.Address{}, err
}
// initialize contract
if initializeAbi != nil {
_, err = k.ApplyContract(ctx, from, proxyContract, nil, *initializeAbi, "initialize", initializeArgs...)
if err != nil {
return common.Address{}, err
}
}
return proxyContract, nil
}
// QueryContract query contract with args and res
func (k *Keeper) QueryContract(ctx sdk.Context, from, contract common.Address, abi abi.ABI, method string, res interface{}, constructorData ...interface{}) error {
args, err := abi.Pack(method, constructorData...)
if err != nil {
return errorsmod.Wrap(types.ErrABIPack, err.Error())
}
resp, err := k.CallEVMWithoutGas(ctx, from, &contract, nil, args, false)
if err != nil {
return err
}
if err = abi.UnpackIntoInterface(res, method, resp.Ret); err != nil {
return errorsmod.Wrap(types.ErrABIUnpack, err.Error())
}
return nil
}
// ApplyContract apply contract with args
func (k *Keeper) ApplyContract(ctx sdk.Context, from, contract common.Address, value *big.Int, abi abi.ABI, method string, constructorData ...interface{}) (*evmtypes.MsgEthereumTxResponse, error) {
args, err := abi.Pack(method, constructorData...)
if err != nil {
return nil, errorsmod.Wrap(types.ErrABIPack, err.Error())
}
resp, err := k.CallEVMWithoutGas(ctx, from, &contract, value, args, true)
if err != nil {
return nil, err
}
return resp, nil
}