-
Notifications
You must be signed in to change notification settings - Fork 18
/
evm_backend.go
117 lines (96 loc) · 3.45 KB
/
evm_backend.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
package contract
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/mapprotocol/atlas/accounts/abi"
"github.com/mapprotocol/atlas/core/vm/runtime"
)
// EVMBackend represents a contract interface that talks directly to an EVM
type EVMBackend struct {
abi *abi.ABI
runtimeConfigTemplate *runtime.Config
Address common.Address
defaultCallOpts CallOpts
}
type CallOpts struct {
Origin common.Address
Value *big.Int
}
func DeployEVMBackend(abi *abi.ABI, runtimeConfig *runtime.Config, code []byte, params ...interface{}) (*EVMBackend, error) {
constructorArgs, err := abi.Pack("", params...)
if err != nil {
return nil, err
}
_, address, _, err := runtime.Create(append(code, constructorArgs...), runtimeConfig)
if err != nil {
return nil, fmt.Errorf("error creating contract: %w", err)
}
contract := NewEVMBackend(abi, runtimeConfig, address)
return contract, nil
}
// NewEVMBackend creates a new EVM based contract
func NewEVMBackend(abi *abi.ABI, runtimeConfig *runtime.Config, receiver common.Address) *EVMBackend {
return &EVMBackend{
abi: abi,
runtimeConfigTemplate: runtimeConfig,
Address: receiver,
defaultCallOpts: CallOpts{
Origin: runtimeConfig.Origin,
Value: common.Big0,
},
}
}
// SimpleCallFrom makes an evm call with given sender address and just returns the error status
func (ecb *EVMBackend) SimpleCallFrom(origin common.Address, method string, args ...interface{}) error {
_, err := ecb.Call(CallOpts{Origin: origin}, method, args...)
return err
}
// SimpleCall makes an evm call and just returns the error status
func (ecb *EVMBackend) SimpleCall(method string, args ...interface{}) error {
_, err := ecb.Call(ecb.defaultCallOpts, method, args...)
return err
}
// Call makes an evm call and returns error and gasLeft
func (ecb *EVMBackend) Call(opts CallOpts, method string, args ...interface{}) (uint64, error) {
return ecb.call(opts, method, args...)
}
func (ecb *EVMBackend) call(opts CallOpts, method string, args ...interface{}) (uint64, error) {
log.Trace("SmartContract Call", "from", opts.Origin, "to", ecb.Address, "method", method, "arguments", args)
calldata, err := ecb.abi.Pack(method, args...)
if err != nil {
return 0, err
}
runtimeCfg := *ecb.runtimeConfigTemplate
runtimeCfg.Origin = opts.Origin
runtimeCfg.Value = opts.Value
ret, gasLeft, err := runtime.Call(ecb.Address, calldata, &runtimeCfg)
if err != nil {
// try unpacking the revert (if it is one)
revertReason, err2 := abi.UnpackRevert(ret)
if err2 == nil {
return gasLeft, fmt.Errorf("Revert: %s", revertReason)
}
}
return gasLeft, err
}
// Query makes an evm call, populates the result into returnValue and returns error and gasLeft
func (ecb *EVMBackend) Query(returnValue interface{}, method string, args ...interface{}) (uint64, error) {
calldata, err := ecb.abi.Pack(method, args...)
if err != nil {
return 0, err
}
runtimeCfg := *ecb.runtimeConfigTemplate
log.Debug("method query", "method", method, "to", ecb.Address, "data", common.Bytes2Hex(calldata))
ret, gasLeft, err := runtime.Call(ecb.Address, calldata, &runtimeCfg)
if err != nil {
// try unpacking the revert (if it is one)
revertReason, err2 := abi.UnpackRevert(ret)
if err2 == nil {
return gasLeft, fmt.Errorf("Revert: %s", revertReason)
}
}
err = ecb.abi.UnpackIntoInterface(returnValue, method, ret)
return gasLeft, err
}