-
Notifications
You must be signed in to change notification settings - Fork 7
/
EthProvider.go
137 lines (122 loc) · 5.04 KB
/
EthProvider.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
package zksync
import (
"context"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/pkg/errors"
"github.com/zksync-sdk/zksync-go/contracts/ERC20"
"github.com/zksync-sdk/zksync-go/contracts/ZkSync"
"math/big"
)
//go:generate mockery -name=EthProvider -output=./ -outpkg=zksync -filename=EthProviderMock_test.go -structname=EthProviderMock -inpkg
type EthProvider interface {
ApproveDeposits(token *Token, limit *big.Int, options *GasOptions) (*types.Transaction, error)
IsDepositApproved(token *Token, userAddress common.Address, threshold *big.Int) (bool, error)
Deposit(token *Token, amount *big.Int, userAddress common.Address, options *GasOptions) (*types.Transaction, error)
SetAuthPubkeyHash(pubKeyHash string, zkNonce uint32, options *GasOptions) (*types.Transaction, error)
IsOnChainAuthPubkeyHashSet(nonce uint32) (bool, error)
GetBalance(token *Token) (*big.Int, error)
GetNonce() (uint64, error)
FullExit(token *Token, accountId uint32, options *GasOptions) (*types.Transaction, error)
FullExitNFT(token *NFT, accountId uint32, options *GasOptions) (*types.Transaction, error)
}
type DefaultEthProvider struct {
client *ethclient.Client
contract *ZkSync.ZkSync
address common.Address
auth *bind.TransactOpts
}
type GasOptions struct {
GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle)
GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate)
}
func (p *DefaultEthProvider) ApproveDeposits(token *Token, limit *big.Int, options *GasOptions) (*types.Transaction, error) {
tokenContract, err := ERC20.NewERC20(token.GetAddress(), p.client)
if err != nil {
return nil, errors.Wrap(err, "failed to load token contract")
}
if limit == nil {
// max approve amount 2^256 - 1
limit = big.NewInt(0).Sub(big.NewInt(0).Exp(big.NewInt(2), big.NewInt(256), nil), big.NewInt(1))
}
auth := p.getAuth(options)
return tokenContract.Approve(auth, p.address, limit)
}
func (p *DefaultEthProvider) IsDepositApproved(token *Token, userAddress common.Address, threshold *big.Int) (bool, error) {
tokenContract, err := ERC20.NewERC20(token.GetAddress(), p.client)
if err != nil {
return false, errors.Wrap(err, "failed to load token contract")
}
auth := &bind.CallOpts{}
allowed, err := tokenContract.Allowance(auth, userAddress, p.address)
if err != nil {
return false, errors.Wrap(err, "failed to call Allowance view of token contract")
}
if threshold == nil {
// default threshold 2^255
threshold = big.NewInt(0).Exp(big.NewInt(2), big.NewInt(255), nil)
}
return allowed.Cmp(threshold) >= 0, nil
}
func (p *DefaultEthProvider) Deposit(token *Token, amount *big.Int, userAddress common.Address, options *GasOptions) (*types.Transaction, error) {
auth := p.getAuth(options)
if token.IsETH() {
auth.Value = amount
return p.contract.DepositETH(auth, userAddress)
} else {
auth.Value = nil
return p.contract.DepositERC20(auth, token.GetAddress(), amount, userAddress)
}
}
func (p *DefaultEthProvider) SetAuthPubkeyHash(pubKeyHash string, zkNonce uint32, options *GasOptions) (*types.Transaction, error) {
auth := p.getAuth(options)
pkh, err := pkhToBytes(pubKeyHash)
if err != nil {
return nil, errors.Wrap(err, "invalid pubKeyHash value")
}
return p.contract.SetAuthPubkeyHash(auth, pkh, zkNonce)
}
func (p *DefaultEthProvider) IsOnChainAuthPubkeyHashSet(nonce uint32) (bool, error) {
opts := &bind.CallOpts{}
publicKeyHash, err := p.contract.AuthFacts(opts, p.auth.From, nonce)
if err != nil {
return false, errors.Wrap(err, "failed to call AuthFacts")
}
return publicKeyHash != [32]byte{}, nil
}
func (p *DefaultEthProvider) GetBalance(token *Token) (*big.Int, error) {
if token == nil || token.IsETH() {
return p.client.BalanceAt(context.Background(), p.auth.From, nil) // latest
}
tokenContract, err := ERC20.NewERC20(token.GetAddress(), p.client)
if err != nil {
return nil, errors.Wrap(err, "failed to load token contract")
}
opts := &bind.CallOpts{}
return tokenContract.BalanceOf(opts, p.auth.From)
}
func (p *DefaultEthProvider) GetNonce() (uint64, error) {
return p.client.PendingNonceAt(context.Background(), p.auth.From) // pending
}
func (p *DefaultEthProvider) FullExit(token *Token, accountId uint32, options *GasOptions) (*types.Transaction, error) {
auth := p.getAuth(options)
return p.contract.RequestFullExit(auth, accountId, token.GetAddress())
}
func (p *DefaultEthProvider) FullExitNFT(token *NFT, accountId uint32, options *GasOptions) (*types.Transaction, error) {
auth := p.getAuth(options)
return p.contract.RequestFullExitNFT(auth, accountId, token.Id)
}
// getAuth make a new copy of origin TransactOpts to be used safely for each call
func (p *DefaultEthProvider) getAuth(options *GasOptions) *bind.TransactOpts {
newAuth := &bind.TransactOpts{
From: p.auth.From,
Signer: p.auth.Signer,
}
if options != nil {
newAuth.GasPrice = options.GasPrice
newAuth.GasLimit = options.GasLimit
}
return newAuth
}