-
Notifications
You must be signed in to change notification settings - Fork 12
/
evm.go
160 lines (137 loc) · 5.07 KB
/
evm.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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package keeper
import (
"fmt"
"math/big"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
evmtypes "github.com/evmos/ethermint/x/evm/types"
fxtypes "github.com/functionx/fx-core/v3/types"
"github.com/functionx/fx-core/v3/x/erc20/types"
)
// QueryERC20 returns the data of a deployed ERC20 contract
func (k Keeper) QueryERC20(ctx sdk.Context, contract common.Address) (types.ERC20Data, error) {
erc20 := fxtypes.GetERC20().ABI
// Name
res, err := k.CallEVM(ctx, erc20, k.moduleAddress, contract, false, "name")
if err != nil {
return types.ERC20Data{}, err
}
var nameRes struct{ Value string }
if err := erc20.UnpackIntoInterface(&nameRes, "name", res.Ret); err != nil {
return types.ERC20Data{}, sdkerrors.Wrapf(types.ErrABIUnpack, "failed to unpack name: %s", err.Error())
}
// Symbol
res, err = k.CallEVM(ctx, erc20, k.moduleAddress, contract, false, "symbol")
if err != nil {
return types.ERC20Data{}, err
}
var symbolRes struct{ Value string }
if err := erc20.UnpackIntoInterface(&symbolRes, "symbol", res.Ret); err != nil {
return types.ERC20Data{}, sdkerrors.Wrapf(types.ErrABIUnpack, "failed to unpack symbol: %s", err.Error())
}
// Decimals
res, err = k.CallEVM(ctx, erc20, k.moduleAddress, contract, false, "decimals")
if err != nil {
return types.ERC20Data{}, err
}
var decimalRes struct{ Value uint8 }
if err := erc20.UnpackIntoInterface(&decimalRes, "decimals", res.Ret); err != nil {
return types.ERC20Data{}, sdkerrors.Wrapf(types.ErrABIUnpack, "failed to unpack decimals: %s", err.Error())
}
return types.NewERC20Data(nameRes.Value, symbolRes.Value, decimalRes.Value), nil
}
// BalanceOf returns the balance of an address for ERC20 contract
func (k Keeper) BalanceOf(ctx sdk.Context, contract, addr common.Address) (*big.Int, error) {
erc20 := fxtypes.GetERC20().ABI
res, err := k.CallEVM(ctx, erc20, k.moduleAddress, contract, false, "balanceOf", addr)
if err != nil {
return nil, err
}
var balanceRes struct{ Value *big.Int }
if err := erc20.UnpackIntoInterface(&balanceRes, "balanceOf", res.Ret); err != nil {
return nil, sdkerrors.Wrapf(types.ErrABIUnpack, "failed to unpack balanceOf: %s", err.Error())
}
return balanceRes.Value, nil
}
func (k Keeper) DeployUpgradableToken(ctx sdk.Context, from common.Address, name, symbol string, decimals uint8) (common.Address, error) {
var tokenContract fxtypes.Contract
if symbol == fxtypes.DefaultDenom {
tokenContract = fxtypes.GetWFX()
name = fmt.Sprintf("Wrapped %s", name)
symbol = fmt.Sprintf("W%s", symbol)
} else {
tokenContract = fxtypes.GetERC20()
}
k.Logger(ctx).Info("deploy token contract", "name", name, "symbol", symbol, "decimals", decimals)
// deploy proxy
erc1967Proxy := fxtypes.GetERC1967Proxy()
contract, err := k.DeployContract(ctx, from, erc1967Proxy.ABI, erc1967Proxy.Bin, tokenContract.Address, []byte{})
if err != nil {
return common.Address{}, err
}
_, err = k.CallEVM(ctx, tokenContract.ABI, from, contract, true, "initialize", name, symbol, decimals, k.moduleAddress)
if err != nil {
return common.Address{}, err
}
return contract, nil
}
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{}, sdkerrors.Wrap(err, "pack constructor data")
}
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.evmKeeper.CallEVMWithData(ctx, from, nil, data, true)
if err != nil {
return common.Address{}, err
}
contractAddr := crypto.CreateAddress(from, nonce)
return contractAddr, nil
}
// CallEVM performs a smart contract method call using given args
func (k Keeper) CallEVM(
ctx sdk.Context,
abi abi.ABI,
from, contract common.Address,
commit bool,
method string,
args ...interface{},
) (*evmtypes.MsgEthereumTxResponse, error) {
data, err := abi.Pack(method, args...)
if err != nil {
return nil, sdkerrors.Wrap(
types.ErrABIPack,
sdkerrors.Wrap(err, "failed to create transaction data").Error(),
)
}
resp, err := k.evmKeeper.CallEVMWithData(ctx, from, &contract, data, commit)
if err != nil {
return nil, sdkerrors.Wrapf(err, "contract call failed: method '%s', contract '%s'", method, contract)
}
return resp, nil
}
// monitorApprovalEvent returns an error if the given transactions logs include
// an unexpected `approve` event
func (k Keeper) monitorApprovalEvent(res *evmtypes.MsgEthereumTxResponse) error {
if res == nil || len(res.Logs) == 0 {
return nil
}
logApprovalSigHash := crypto.Keccak256Hash([]byte("Approval(address,address,uint256)"))
for _, log := range res.Logs {
if log.Topics[0] == logApprovalSigHash.Hex() {
return sdkerrors.Wrapf(
types.ErrUnexpectedEvent, "approval event",
)
}
}
return nil
}