feat: hop integration
This commit is contained in:
parent
60cb15739d
commit
b11643e66d
|
@ -8,6 +8,10 @@ import (
|
|||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/status-im/status-go/contracts/directory"
|
||||
"github.com/status-im/status-go/contracts/ethscan"
|
||||
"github.com/status-im/status-go/contracts/hop"
|
||||
hopBridge "github.com/status-im/status-go/contracts/hop/bridge"
|
||||
hopSwap "github.com/status-im/status-go/contracts/hop/swap"
|
||||
hopWrapper "github.com/status-im/status-go/contracts/hop/wrapper"
|
||||
"github.com/status-im/status-go/contracts/registrar"
|
||||
"github.com/status-im/status-go/contracts/resolver"
|
||||
"github.com/status-im/status-go/contracts/snt"
|
||||
|
@ -164,7 +168,6 @@ func (c *ContractMaker) NewEthScan(chainID uint64) (*ethscan.BalanceScanner, err
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bytecode, err := backend.CodeAt(context.Background(), contractAddr, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -179,3 +182,51 @@ func (c *ContractMaker) NewEthScan(chainID uint64) (*ethscan.BalanceScanner, err
|
|||
backend,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *ContractMaker) NewHopL2SaddlSwap(chainID uint64, symbol string) (*hopSwap.HopSwap, error) {
|
||||
contractAddr, err := hop.L2SaddleSwapContractAddress(chainID, symbol)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
backend, err := c.RPCClient.EthClient(chainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return hopSwap.NewHopSwap(
|
||||
contractAddr,
|
||||
backend,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *ContractMaker) NewHopL1Bridge(chainID uint64, symbol string) (*hopBridge.HopBridge, error) {
|
||||
contractAddr, err := hop.L1BridgeContractAddress(chainID, symbol)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
backend, err := c.RPCClient.EthClient(chainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return hopBridge.NewHopBridge(
|
||||
contractAddr,
|
||||
backend,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *ContractMaker) NewHopL2AmmWrapper(chainID uint64, symbol string) (*hopWrapper.HopWrapper, error) {
|
||||
contractAddr, err := hop.L2AmmWrapperContractAddress(chainID, symbol)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
backend, err := c.RPCClient.EthClient(chainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return hopWrapper.NewHopWrapper(
|
||||
contractAddr,
|
||||
backend,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
package hop
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
var errorNotAvailableOnChainID = errors.New("not available for chainID")
|
||||
|
||||
var l2SaddleSwapContractAddresses = map[uint64]map[string]common.Address{
|
||||
10: {
|
||||
"USDC": common.HexToAddress("0x3c0FFAca566fCcfD9Cc95139FEF6CBA143795963"),
|
||||
"USDT": common.HexToAddress("0xeC4B41Af04cF917b54AEb6Df58c0f8D78895b5Ef"),
|
||||
"DAI": common.HexToAddress("0xF181eD90D6CfaC84B8073FdEA6D34Aa744B41810"),
|
||||
"ETH": common.HexToAddress("0xaa30D6bba6285d0585722e2440Ff89E23EF68864"),
|
||||
"WBTC": common.HexToAddress("0x46fc3Af3A47792cA3ED06fdF3D657145A675a8D8"),
|
||||
},
|
||||
42161: {
|
||||
"USDC": common.HexToAddress("0x10541b07d8Ad2647Dc6cD67abd4c03575dade261"),
|
||||
"USDT": common.HexToAddress("0x18f7402B673Ba6Fb5EA4B95768aABb8aaD7ef18a"),
|
||||
"DAI": common.HexToAddress("0xa5A33aB9063395A90CCbEa2D86a62EcCf27B5742"),
|
||||
"ETH": common.HexToAddress("0x652d27c0F72771Ce5C76fd400edD61B406Ac6D97"),
|
||||
"WBTC": common.HexToAddress("0x7191061D5d4C60f598214cC6913502184BAddf18"),
|
||||
},
|
||||
420: {
|
||||
"USDC": common.HexToAddress("0xE4757dD81AFbecF61E51824AB9238df6691c3D0e"),
|
||||
"ETH": common.HexToAddress("0xa50395bdEaca7062255109fedE012eFE63d6D402"),
|
||||
},
|
||||
421613: {
|
||||
"USDC": common.HexToAddress("0x83f6244Bd87662118d96D9a6D44f09dffF14b30E"),
|
||||
"ETH": common.HexToAddress("0x69a71b7F6Ff088a0310b4f911b4f9eA11e2E9740"),
|
||||
},
|
||||
}
|
||||
|
||||
var l2AmmWrapperContractAddress = map[uint64]map[string]common.Address{
|
||||
10: {
|
||||
"USDC": common.HexToAddress("0x2ad09850b0CA4c7c1B33f5AcD6cBAbCaB5d6e796"),
|
||||
"USDT": common.HexToAddress("0x7D269D3E0d61A05a0bA976b7DBF8805bF844AF3F"),
|
||||
"DAI": common.HexToAddress("0xb3C68a491608952Cb1257FC9909a537a0173b63B"),
|
||||
"ETH": common.HexToAddress("0x86cA30bEF97fB651b8d866D45503684b90cb3312"),
|
||||
"WBTC": common.HexToAddress("0x2A11a98e2fCF4674F30934B5166645fE6CA35F56"),
|
||||
},
|
||||
42161: {
|
||||
"USDC": common.HexToAddress("0xe22D2beDb3Eca35E6397e0C6D62857094aA26F52"),
|
||||
"USDT": common.HexToAddress("0xCB0a4177E0A60247C0ad18Be87f8eDfF6DD30283"),
|
||||
"DAI": common.HexToAddress("0xe7F40BF16AB09f4a6906Ac2CAA4094aD2dA48Cc2"),
|
||||
"ETH": common.HexToAddress("0x33ceb27b39d2Bb7D2e61F7564d3Df29344020417"),
|
||||
"WBTC": common.HexToAddress("0xC08055b634D43F2176d721E26A3428D3b7E7DdB5"),
|
||||
},
|
||||
420: {
|
||||
"USDC": common.HexToAddress("0xfF21e82a4Bc305BCE591530A68628192b5b6B6FD"),
|
||||
"ETH": common.HexToAddress("0xC1985d7a3429cDC85E59E2E4Fcc805b857e6Ee2E"),
|
||||
},
|
||||
421613: {
|
||||
"USDC": common.HexToAddress("0x32219766597DFbb10297127238D921E7CCF5D920"),
|
||||
"ETH": common.HexToAddress("0xa832293f2DCe2f092182F17dd873ae06AD5fDbaF"),
|
||||
},
|
||||
}
|
||||
|
||||
var l1BridgeContractAddress = map[uint64]map[string]common.Address{
|
||||
1: {
|
||||
"USDC": common.HexToAddress("0x3666f603Cc164936C1b87e207F36BEBa4AC5f18a"),
|
||||
"USDT": common.HexToAddress("0x3666f603Cc164936C1b87e207F36BEBa4AC5f18a"),
|
||||
"DAI": common.HexToAddress("0x3d4Cc8A61c7528Fd86C55cfe061a78dCBA48EDd1"),
|
||||
"ETH": common.HexToAddress("0xb8901acB165ed027E32754E0FFe830802919727f"),
|
||||
"WBTC": common.HexToAddress("0xb98454270065A31D71Bf635F6F7Ee6A518dFb849"),
|
||||
},
|
||||
5: {
|
||||
"USDC": common.HexToAddress("0x7D269D3E0d61A05a0bA976b7DBF8805bF844AF3F"),
|
||||
"ETH": common.HexToAddress("0xC8A4FB931e8D77df8497790381CA7d228E68a41b"),
|
||||
},
|
||||
}
|
||||
|
||||
func L2SaddleSwapContractAddress(chainID uint64, symbol string) (common.Address, error) {
|
||||
tokens, exists := l2SaddleSwapContractAddresses[chainID]
|
||||
if !exists {
|
||||
return *new(common.Address), errorNotAvailableOnChainID
|
||||
}
|
||||
|
||||
addr, exists := tokens[symbol]
|
||||
if !exists {
|
||||
return *new(common.Address), errorNotAvailableOnChainID
|
||||
}
|
||||
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
func L2AmmWrapperContractAddress(chainID uint64, symbol string) (common.Address, error) {
|
||||
tokens, exists := l2AmmWrapperContractAddress[chainID]
|
||||
if !exists {
|
||||
return *new(common.Address), errorNotAvailableOnChainID
|
||||
}
|
||||
|
||||
addr, exists := tokens[symbol]
|
||||
if !exists {
|
||||
return *new(common.Address), errorNotAvailableOnChainID
|
||||
}
|
||||
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
func L1BridgeContractAddress(chainID uint64, symbol string) (common.Address, error) {
|
||||
tokens, exists := l1BridgeContractAddress[chainID]
|
||||
if !exists {
|
||||
return *new(common.Address), errorNotAvailableOnChainID
|
||||
}
|
||||
|
||||
addr, exists := tokens[symbol]
|
||||
if !exists {
|
||||
return *new(common.Address), errorNotAvailableOnChainID
|
||||
}
|
||||
|
||||
return addr, nil
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,5 @@
|
|||
package hop
|
||||
|
||||
//go:generate abigen --abi l2SaddleSwap.abi --pkg hopSwap --out swap/l2SaddleSwap.go
|
||||
//go:generate abigen --abi l1Bridge.abi --pkg hopBridge --out bridge/l1Bridge.go
|
||||
//go:generate abigen --abi l2AmmWrapper.abi --pkg hopWrapper --out wrapper/l2AmmWrapper.go
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
|||
[{"inputs":[{"internalType":"contract L2_Bridge","name":"_bridge","type":"address"},{"internalType":"contract IERC20","name":"_l2CanonicalToken","type":"address"},{"internalType":"bool","name":"_l2CanonicalTokenIsEth","type":"bool"},{"internalType":"contract IERC20","name":"_hToken","type":"address"},{"internalType":"contract Swap","name":"_exchangeAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"attemptSwap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"bridge","outputs":[{"internalType":"contract L2_Bridge","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"exchangeAddress","outputs":[{"internalType":"contract Swap","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"l2CanonicalToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"l2CanonicalTokenIsEth","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"bonderFee","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"destinationAmountOutMin","type":"uint256"},{"internalType":"uint256","name":"destinationDeadline","type":"uint256"}],"name":"swapAndSend","outputs":[],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,390 @@
|
|||
// Code generated - DO NOT EDIT.
|
||||
// This file is a generated binding and any manual changes will be lost.
|
||||
|
||||
package hopWrapper
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
ethereum "github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
"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/event"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var (
|
||||
_ = big.NewInt
|
||||
_ = strings.NewReader
|
||||
_ = ethereum.NotFound
|
||||
_ = bind.Bind
|
||||
_ = common.Big1
|
||||
_ = types.BloomLookup
|
||||
_ = event.NewSubscription
|
||||
)
|
||||
|
||||
// HopWrapperABI is the input ABI used to generate the binding from.
|
||||
const HopWrapperABI = "[{\"inputs\":[{\"internalType\":\"contractL2_Bridge\",\"name\":\"_bridge\",\"type\":\"address\"},{\"internalType\":\"contractIERC20\",\"name\":\"_l2CanonicalToken\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"_l2CanonicalTokenIsEth\",\"type\":\"bool\"},{\"internalType\":\"contractIERC20\",\"name\":\"_hToken\",\"type\":\"address\"},{\"internalType\":\"contractSwap\",\"name\":\"_exchangeAddress\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amountOutMin\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"deadline\",\"type\":\"uint256\"}],\"name\":\"attemptSwap\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"bridge\",\"outputs\":[{\"internalType\":\"contractL2_Bridge\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"exchangeAddress\",\"outputs\":[{\"internalType\":\"contractSwap\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"hToken\",\"outputs\":[{\"internalType\":\"contractIERC20\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"l2CanonicalToken\",\"outputs\":[{\"internalType\":\"contractIERC20\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"l2CanonicalTokenIsEth\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"bonderFee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amountOutMin\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"deadline\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"destinationAmountOutMin\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"destinationDeadline\",\"type\":\"uint256\"}],\"name\":\"swapAndSend\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]"
|
||||
|
||||
// HopWrapper is an auto generated Go binding around an Ethereum contract.
|
||||
type HopWrapper struct {
|
||||
HopWrapperCaller // Read-only binding to the contract
|
||||
HopWrapperTransactor // Write-only binding to the contract
|
||||
HopWrapperFilterer // Log filterer for contract events
|
||||
}
|
||||
|
||||
// HopWrapperCaller is an auto generated read-only Go binding around an Ethereum contract.
|
||||
type HopWrapperCaller struct {
|
||||
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||
}
|
||||
|
||||
// HopWrapperTransactor is an auto generated write-only Go binding around an Ethereum contract.
|
||||
type HopWrapperTransactor struct {
|
||||
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||
}
|
||||
|
||||
// HopWrapperFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
|
||||
type HopWrapperFilterer struct {
|
||||
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||
}
|
||||
|
||||
// HopWrapperSession is an auto generated Go binding around an Ethereum contract,
|
||||
// with pre-set call and transact options.
|
||||
type HopWrapperSession struct {
|
||||
Contract *HopWrapper // Generic contract binding to set the session for
|
||||
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||
}
|
||||
|
||||
// HopWrapperCallerSession is an auto generated read-only Go binding around an Ethereum contract,
|
||||
// with pre-set call options.
|
||||
type HopWrapperCallerSession struct {
|
||||
Contract *HopWrapperCaller // Generic contract caller binding to set the session for
|
||||
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||
}
|
||||
|
||||
// HopWrapperTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
|
||||
// with pre-set transact options.
|
||||
type HopWrapperTransactorSession struct {
|
||||
Contract *HopWrapperTransactor // Generic contract transactor binding to set the session for
|
||||
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||
}
|
||||
|
||||
// HopWrapperRaw is an auto generated low-level Go binding around an Ethereum contract.
|
||||
type HopWrapperRaw struct {
|
||||
Contract *HopWrapper // Generic contract binding to access the raw methods on
|
||||
}
|
||||
|
||||
// HopWrapperCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
|
||||
type HopWrapperCallerRaw struct {
|
||||
Contract *HopWrapperCaller // Generic read-only contract binding to access the raw methods on
|
||||
}
|
||||
|
||||
// HopWrapperTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
|
||||
type HopWrapperTransactorRaw struct {
|
||||
Contract *HopWrapperTransactor // Generic write-only contract binding to access the raw methods on
|
||||
}
|
||||
|
||||
// NewHopWrapper creates a new instance of HopWrapper, bound to a specific deployed contract.
|
||||
func NewHopWrapper(address common.Address, backend bind.ContractBackend) (*HopWrapper, error) {
|
||||
contract, err := bindHopWrapper(address, backend, backend, backend)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &HopWrapper{HopWrapperCaller: HopWrapperCaller{contract: contract}, HopWrapperTransactor: HopWrapperTransactor{contract: contract}, HopWrapperFilterer: HopWrapperFilterer{contract: contract}}, nil
|
||||
}
|
||||
|
||||
// NewHopWrapperCaller creates a new read-only instance of HopWrapper, bound to a specific deployed contract.
|
||||
func NewHopWrapperCaller(address common.Address, caller bind.ContractCaller) (*HopWrapperCaller, error) {
|
||||
contract, err := bindHopWrapper(address, caller, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &HopWrapperCaller{contract: contract}, nil
|
||||
}
|
||||
|
||||
// NewHopWrapperTransactor creates a new write-only instance of HopWrapper, bound to a specific deployed contract.
|
||||
func NewHopWrapperTransactor(address common.Address, transactor bind.ContractTransactor) (*HopWrapperTransactor, error) {
|
||||
contract, err := bindHopWrapper(address, nil, transactor, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &HopWrapperTransactor{contract: contract}, nil
|
||||
}
|
||||
|
||||
// NewHopWrapperFilterer creates a new log filterer instance of HopWrapper, bound to a specific deployed contract.
|
||||
func NewHopWrapperFilterer(address common.Address, filterer bind.ContractFilterer) (*HopWrapperFilterer, error) {
|
||||
contract, err := bindHopWrapper(address, nil, nil, filterer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &HopWrapperFilterer{contract: contract}, nil
|
||||
}
|
||||
|
||||
// bindHopWrapper binds a generic wrapper to an already deployed contract.
|
||||
func bindHopWrapper(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
|
||||
parsed, err := abi.JSON(strings.NewReader(HopWrapperABI))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
|
||||
}
|
||||
|
||||
// Call invokes the (constant) contract method with params as input values and
|
||||
// sets the output to result. The result type might be a single field for simple
|
||||
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||
// returns.
|
||||
func (_HopWrapper *HopWrapperRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
|
||||
return _HopWrapper.Contract.HopWrapperCaller.contract.Call(opts, result, method, params...)
|
||||
}
|
||||
|
||||
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||
// its default method if one is available.
|
||||
func (_HopWrapper *HopWrapperRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||
return _HopWrapper.Contract.HopWrapperTransactor.contract.Transfer(opts)
|
||||
}
|
||||
|
||||
// Transact invokes the (paid) contract method with params as input values.
|
||||
func (_HopWrapper *HopWrapperRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
|
||||
return _HopWrapper.Contract.HopWrapperTransactor.contract.Transact(opts, method, params...)
|
||||
}
|
||||
|
||||
// Call invokes the (constant) contract method with params as input values and
|
||||
// sets the output to result. The result type might be a single field for simple
|
||||
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||
// returns.
|
||||
func (_HopWrapper *HopWrapperCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
|
||||
return _HopWrapper.Contract.contract.Call(opts, result, method, params...)
|
||||
}
|
||||
|
||||
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||
// its default method if one is available.
|
||||
func (_HopWrapper *HopWrapperTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||
return _HopWrapper.Contract.contract.Transfer(opts)
|
||||
}
|
||||
|
||||
// Transact invokes the (paid) contract method with params as input values.
|
||||
func (_HopWrapper *HopWrapperTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
|
||||
return _HopWrapper.Contract.contract.Transact(opts, method, params...)
|
||||
}
|
||||
|
||||
// Bridge is a free data retrieval call binding the contract method 0xe78cea92.
|
||||
//
|
||||
// Solidity: function bridge() view returns(address)
|
||||
func (_HopWrapper *HopWrapperCaller) Bridge(opts *bind.CallOpts) (common.Address, error) {
|
||||
var out []interface{}
|
||||
err := _HopWrapper.contract.Call(opts, &out, "bridge")
|
||||
|
||||
if err != nil {
|
||||
return *new(common.Address), err
|
||||
}
|
||||
|
||||
out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)
|
||||
|
||||
return out0, err
|
||||
|
||||
}
|
||||
|
||||
// Bridge is a free data retrieval call binding the contract method 0xe78cea92.
|
||||
//
|
||||
// Solidity: function bridge() view returns(address)
|
||||
func (_HopWrapper *HopWrapperSession) Bridge() (common.Address, error) {
|
||||
return _HopWrapper.Contract.Bridge(&_HopWrapper.CallOpts)
|
||||
}
|
||||
|
||||
// Bridge is a free data retrieval call binding the contract method 0xe78cea92.
|
||||
//
|
||||
// Solidity: function bridge() view returns(address)
|
||||
func (_HopWrapper *HopWrapperCallerSession) Bridge() (common.Address, error) {
|
||||
return _HopWrapper.Contract.Bridge(&_HopWrapper.CallOpts)
|
||||
}
|
||||
|
||||
// ExchangeAddress is a free data retrieval call binding the contract method 0x9cd01605.
|
||||
//
|
||||
// Solidity: function exchangeAddress() view returns(address)
|
||||
func (_HopWrapper *HopWrapperCaller) ExchangeAddress(opts *bind.CallOpts) (common.Address, error) {
|
||||
var out []interface{}
|
||||
err := _HopWrapper.contract.Call(opts, &out, "exchangeAddress")
|
||||
|
||||
if err != nil {
|
||||
return *new(common.Address), err
|
||||
}
|
||||
|
||||
out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)
|
||||
|
||||
return out0, err
|
||||
|
||||
}
|
||||
|
||||
// ExchangeAddress is a free data retrieval call binding the contract method 0x9cd01605.
|
||||
//
|
||||
// Solidity: function exchangeAddress() view returns(address)
|
||||
func (_HopWrapper *HopWrapperSession) ExchangeAddress() (common.Address, error) {
|
||||
return _HopWrapper.Contract.ExchangeAddress(&_HopWrapper.CallOpts)
|
||||
}
|
||||
|
||||
// ExchangeAddress is a free data retrieval call binding the contract method 0x9cd01605.
|
||||
//
|
||||
// Solidity: function exchangeAddress() view returns(address)
|
||||
func (_HopWrapper *HopWrapperCallerSession) ExchangeAddress() (common.Address, error) {
|
||||
return _HopWrapper.Contract.ExchangeAddress(&_HopWrapper.CallOpts)
|
||||
}
|
||||
|
||||
// HToken is a free data retrieval call binding the contract method 0xfc6e3b3b.
|
||||
//
|
||||
// Solidity: function hToken() view returns(address)
|
||||
func (_HopWrapper *HopWrapperCaller) HToken(opts *bind.CallOpts) (common.Address, error) {
|
||||
var out []interface{}
|
||||
err := _HopWrapper.contract.Call(opts, &out, "hToken")
|
||||
|
||||
if err != nil {
|
||||
return *new(common.Address), err
|
||||
}
|
||||
|
||||
out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)
|
||||
|
||||
return out0, err
|
||||
|
||||
}
|
||||
|
||||
// HToken is a free data retrieval call binding the contract method 0xfc6e3b3b.
|
||||
//
|
||||
// Solidity: function hToken() view returns(address)
|
||||
func (_HopWrapper *HopWrapperSession) HToken() (common.Address, error) {
|
||||
return _HopWrapper.Contract.HToken(&_HopWrapper.CallOpts)
|
||||
}
|
||||
|
||||
// HToken is a free data retrieval call binding the contract method 0xfc6e3b3b.
|
||||
//
|
||||
// Solidity: function hToken() view returns(address)
|
||||
func (_HopWrapper *HopWrapperCallerSession) HToken() (common.Address, error) {
|
||||
return _HopWrapper.Contract.HToken(&_HopWrapper.CallOpts)
|
||||
}
|
||||
|
||||
// L2CanonicalToken is a free data retrieval call binding the contract method 0x1ee1bf67.
|
||||
//
|
||||
// Solidity: function l2CanonicalToken() view returns(address)
|
||||
func (_HopWrapper *HopWrapperCaller) L2CanonicalToken(opts *bind.CallOpts) (common.Address, error) {
|
||||
var out []interface{}
|
||||
err := _HopWrapper.contract.Call(opts, &out, "l2CanonicalToken")
|
||||
|
||||
if err != nil {
|
||||
return *new(common.Address), err
|
||||
}
|
||||
|
||||
out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)
|
||||
|
||||
return out0, err
|
||||
|
||||
}
|
||||
|
||||
// L2CanonicalToken is a free data retrieval call binding the contract method 0x1ee1bf67.
|
||||
//
|
||||
// Solidity: function l2CanonicalToken() view returns(address)
|
||||
func (_HopWrapper *HopWrapperSession) L2CanonicalToken() (common.Address, error) {
|
||||
return _HopWrapper.Contract.L2CanonicalToken(&_HopWrapper.CallOpts)
|
||||
}
|
||||
|
||||
// L2CanonicalToken is a free data retrieval call binding the contract method 0x1ee1bf67.
|
||||
//
|
||||
// Solidity: function l2CanonicalToken() view returns(address)
|
||||
func (_HopWrapper *HopWrapperCallerSession) L2CanonicalToken() (common.Address, error) {
|
||||
return _HopWrapper.Contract.L2CanonicalToken(&_HopWrapper.CallOpts)
|
||||
}
|
||||
|
||||
// L2CanonicalTokenIsEth is a free data retrieval call binding the contract method 0x28555125.
|
||||
//
|
||||
// Solidity: function l2CanonicalTokenIsEth() view returns(bool)
|
||||
func (_HopWrapper *HopWrapperCaller) L2CanonicalTokenIsEth(opts *bind.CallOpts) (bool, error) {
|
||||
var out []interface{}
|
||||
err := _HopWrapper.contract.Call(opts, &out, "l2CanonicalTokenIsEth")
|
||||
|
||||
if err != nil {
|
||||
return *new(bool), err
|
||||
}
|
||||
|
||||
out0 := *abi.ConvertType(out[0], new(bool)).(*bool)
|
||||
|
||||
return out0, err
|
||||
|
||||
}
|
||||
|
||||
// L2CanonicalTokenIsEth is a free data retrieval call binding the contract method 0x28555125.
|
||||
//
|
||||
// Solidity: function l2CanonicalTokenIsEth() view returns(bool)
|
||||
func (_HopWrapper *HopWrapperSession) L2CanonicalTokenIsEth() (bool, error) {
|
||||
return _HopWrapper.Contract.L2CanonicalTokenIsEth(&_HopWrapper.CallOpts)
|
||||
}
|
||||
|
||||
// L2CanonicalTokenIsEth is a free data retrieval call binding the contract method 0x28555125.
|
||||
//
|
||||
// Solidity: function l2CanonicalTokenIsEth() view returns(bool)
|
||||
func (_HopWrapper *HopWrapperCallerSession) L2CanonicalTokenIsEth() (bool, error) {
|
||||
return _HopWrapper.Contract.L2CanonicalTokenIsEth(&_HopWrapper.CallOpts)
|
||||
}
|
||||
|
||||
// AttemptSwap is a paid mutator transaction binding the contract method 0x676c5ef6.
|
||||
//
|
||||
// Solidity: function attemptSwap(address recipient, uint256 amount, uint256 amountOutMin, uint256 deadline) returns()
|
||||
func (_HopWrapper *HopWrapperTransactor) AttemptSwap(opts *bind.TransactOpts, recipient common.Address, amount *big.Int, amountOutMin *big.Int, deadline *big.Int) (*types.Transaction, error) {
|
||||
return _HopWrapper.contract.Transact(opts, "attemptSwap", recipient, amount, amountOutMin, deadline)
|
||||
}
|
||||
|
||||
// AttemptSwap is a paid mutator transaction binding the contract method 0x676c5ef6.
|
||||
//
|
||||
// Solidity: function attemptSwap(address recipient, uint256 amount, uint256 amountOutMin, uint256 deadline) returns()
|
||||
func (_HopWrapper *HopWrapperSession) AttemptSwap(recipient common.Address, amount *big.Int, amountOutMin *big.Int, deadline *big.Int) (*types.Transaction, error) {
|
||||
return _HopWrapper.Contract.AttemptSwap(&_HopWrapper.TransactOpts, recipient, amount, amountOutMin, deadline)
|
||||
}
|
||||
|
||||
// AttemptSwap is a paid mutator transaction binding the contract method 0x676c5ef6.
|
||||
//
|
||||
// Solidity: function attemptSwap(address recipient, uint256 amount, uint256 amountOutMin, uint256 deadline) returns()
|
||||
func (_HopWrapper *HopWrapperTransactorSession) AttemptSwap(recipient common.Address, amount *big.Int, amountOutMin *big.Int, deadline *big.Int) (*types.Transaction, error) {
|
||||
return _HopWrapper.Contract.AttemptSwap(&_HopWrapper.TransactOpts, recipient, amount, amountOutMin, deadline)
|
||||
}
|
||||
|
||||
// SwapAndSend is a paid mutator transaction binding the contract method 0xeea0d7b2.
|
||||
//
|
||||
// Solidity: function swapAndSend(uint256 chainId, address recipient, uint256 amount, uint256 bonderFee, uint256 amountOutMin, uint256 deadline, uint256 destinationAmountOutMin, uint256 destinationDeadline) payable returns()
|
||||
func (_HopWrapper *HopWrapperTransactor) SwapAndSend(opts *bind.TransactOpts, chainId *big.Int, recipient common.Address, amount *big.Int, bonderFee *big.Int, amountOutMin *big.Int, deadline *big.Int, destinationAmountOutMin *big.Int, destinationDeadline *big.Int) (*types.Transaction, error) {
|
||||
return _HopWrapper.contract.Transact(opts, "swapAndSend", chainId, recipient, amount, bonderFee, amountOutMin, deadline, destinationAmountOutMin, destinationDeadline)
|
||||
}
|
||||
|
||||
// SwapAndSend is a paid mutator transaction binding the contract method 0xeea0d7b2.
|
||||
//
|
||||
// Solidity: function swapAndSend(uint256 chainId, address recipient, uint256 amount, uint256 bonderFee, uint256 amountOutMin, uint256 deadline, uint256 destinationAmountOutMin, uint256 destinationDeadline) payable returns()
|
||||
func (_HopWrapper *HopWrapperSession) SwapAndSend(chainId *big.Int, recipient common.Address, amount *big.Int, bonderFee *big.Int, amountOutMin *big.Int, deadline *big.Int, destinationAmountOutMin *big.Int, destinationDeadline *big.Int) (*types.Transaction, error) {
|
||||
return _HopWrapper.Contract.SwapAndSend(&_HopWrapper.TransactOpts, chainId, recipient, amount, bonderFee, amountOutMin, deadline, destinationAmountOutMin, destinationDeadline)
|
||||
}
|
||||
|
||||
// SwapAndSend is a paid mutator transaction binding the contract method 0xeea0d7b2.
|
||||
//
|
||||
// Solidity: function swapAndSend(uint256 chainId, address recipient, uint256 amount, uint256 bonderFee, uint256 amountOutMin, uint256 deadline, uint256 destinationAmountOutMin, uint256 destinationDeadline) payable returns()
|
||||
func (_HopWrapper *HopWrapperTransactorSession) SwapAndSend(chainId *big.Int, recipient common.Address, amount *big.Int, bonderFee *big.Int, amountOutMin *big.Int, deadline *big.Int, destinationAmountOutMin *big.Int, destinationDeadline *big.Int) (*types.Transaction, error) {
|
||||
return _HopWrapper.Contract.SwapAndSend(&_HopWrapper.TransactOpts, chainId, recipient, amount, bonderFee, amountOutMin, deadline, destinationAmountOutMin, destinationDeadline)
|
||||
}
|
||||
|
||||
// Receive is a paid mutator transaction binding the contract receive function.
|
||||
//
|
||||
// Solidity: receive() payable returns()
|
||||
func (_HopWrapper *HopWrapperTransactor) Receive(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||
return _HopWrapper.contract.RawTransact(opts, nil) // calldata is disallowed for receive function
|
||||
}
|
||||
|
||||
// Receive is a paid mutator transaction binding the contract receive function.
|
||||
//
|
||||
// Solidity: receive() payable returns()
|
||||
func (_HopWrapper *HopWrapperSession) Receive() (*types.Transaction, error) {
|
||||
return _HopWrapper.Contract.Receive(&_HopWrapper.TransactOpts)
|
||||
}
|
||||
|
||||
// Receive is a paid mutator transaction binding the contract receive function.
|
||||
//
|
||||
// Solidity: receive() payable returns()
|
||||
func (_HopWrapper *HopWrapperTransactorSession) Receive() (*types.Transaction, error) {
|
||||
return _HopWrapper.Contract.Receive(&_HopWrapper.TransactOpts)
|
||||
}
|
2
go.mod
2
go.mod
|
@ -2,7 +2,7 @@ module github.com/status-im/status-go
|
|||
|
||||
go 1.18
|
||||
|
||||
replace github.com/ethereum/go-ethereum v1.10.21 => github.com/status-im/go-ethereum v1.10.4-status.4
|
||||
replace github.com/ethereum/go-ethereum v1.10.21 => github.com/status-im/go-ethereum v1.10.4-status.5
|
||||
|
||||
replace github.com/docker/docker => github.com/docker/engine v1.4.2-0.20190717161051-705d9623b7c1
|
||||
|
||||
|
|
|
@ -463,7 +463,11 @@ func (b *StatusNode) appmetricsService() common.StatusService {
|
|||
|
||||
func (b *StatusNode) walletService(accountsDB *accounts.Database, accountsFeed *event.Feed, openseaAPIKey string) common.StatusService {
|
||||
if b.walletSrvc == nil {
|
||||
b.walletSrvc = wallet.NewService(b.appDB, accountsDB, b.rpcClient, accountsFeed, openseaAPIKey, b.gethAccountManager, b.transactor, b.config)
|
||||
b.walletSrvc = wallet.NewService(
|
||||
b.appDB, accountsDB, b.rpcClient, accountsFeed, openseaAPIKey, b.gethAccountManager, b.transactor, b.config,
|
||||
b.ensService(),
|
||||
b.stickersService(accountsDB),
|
||||
)
|
||||
}
|
||||
return b.walletSrvc
|
||||
}
|
||||
|
|
|
@ -346,12 +346,16 @@ func (api *API) ReleaseEstimate(ctx context.Context, chainID uint64, txArgs tran
|
|||
return 0, err
|
||||
}
|
||||
|
||||
return ethClient.EstimateGas(ctx, ethereum.CallMsg{
|
||||
estimate, err := ethClient.EstimateGas(ctx, ethereum.CallMsg{
|
||||
From: common.Address(txArgs.From),
|
||||
To: ®istryAddr,
|
||||
Value: big.NewInt(0),
|
||||
Data: data,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return estimate + 1000, nil
|
||||
}
|
||||
|
||||
func (api *API) Register(ctx context.Context, chainID uint64, txArgs transactions.SendTxArgs, password string, username string, pubkey string) (string, error) {
|
||||
|
@ -465,7 +469,11 @@ func (api *API) RegisterEstimate(ctx context.Context, chainID uint64, txArgs tra
|
|||
return 0, err
|
||||
}
|
||||
|
||||
return ethClient.EstimateGas(ctx, callMsg)
|
||||
estimate, err := ethClient.EstimateGas(ctx, callMsg)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return estimate + 1000, nil
|
||||
}
|
||||
|
||||
func (api *API) SetPubKey(ctx context.Context, chainID uint64, txArgs transactions.SendTxArgs, password string, username string, pubkey string) (string, error) {
|
||||
|
@ -545,7 +553,11 @@ func (api *API) SetPubKeyEstimate(ctx context.Context, chainID uint64, txArgs tr
|
|||
return 0, err
|
||||
}
|
||||
|
||||
return ethClient.EstimateGas(ctx, callMsg)
|
||||
estimate, err := ethClient.EstimateGas(ctx, callMsg)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return estimate + 1000, nil
|
||||
}
|
||||
|
||||
func (api *API) ResourceURL(ctx context.Context, chainID uint64, username string) (*URI, error) {
|
||||
|
|
|
@ -28,6 +28,7 @@ func NewService(acc *accounts.Database, rpcClient *rpc.Client, accountsManager *
|
|||
httpServer: httpServer,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
api: NewAPI(ctx, acc, rpcClient, accountsManager, rpcFiltersSrvc, config.KeyStoreDir, downloader, httpServer),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,6 +43,7 @@ type Service struct {
|
|||
httpServer *server.MediaServer
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
api *API
|
||||
}
|
||||
|
||||
// Start a service.
|
||||
|
@ -55,13 +57,17 @@ func (s *Service) Stop() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) API() *API {
|
||||
return s.api
|
||||
}
|
||||
|
||||
// APIs returns list of available RPC APIs.
|
||||
func (s *Service) APIs() []ethRpc.API {
|
||||
return []ethRpc.API{
|
||||
{
|
||||
Namespace: "stickers",
|
||||
Version: "0.1.0",
|
||||
Service: NewAPI(s.ctx, s.accountsDB, s.rpcClient, s.accountsManager, s.rpcFiltersSrvc, s.keyStoreDir, s.downloader, s.httpServer),
|
||||
Service: s.api,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,9 +12,10 @@ import (
|
|||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/services/wallet/async"
|
||||
"github.com/status-im/status-go/services/wallet/bridge"
|
||||
"github.com/status-im/status-go/services/wallet/chain"
|
||||
"github.com/status-im/status-go/services/wallet/token"
|
||||
"github.com/status-im/status-go/services/wallet/transfer"
|
||||
"github.com/status-im/status-go/transactions"
|
||||
)
|
||||
|
||||
func NewAPI(s *Service) *API {
|
||||
|
@ -93,7 +94,7 @@ func (api *API) GetTokensBalances(ctx context.Context, accounts, addresses []com
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return api.s.tokenManager.getBalances(ctx, []*chain.Client{chainClient}, accounts, addresses)
|
||||
return api.s.tokenManager.GetBalances(ctx, []*chain.Client{chainClient}, accounts, addresses)
|
||||
}
|
||||
|
||||
func (api *API) GetTokensBalancesForChainIDs(ctx context.Context, chainIDs []uint64, accounts, addresses []common.Address) (map[common.Address]map[common.Address]*hexutil.Big, error) {
|
||||
|
@ -101,65 +102,65 @@ func (api *API) GetTokensBalancesForChainIDs(ctx context.Context, chainIDs []uin
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return api.s.tokenManager.getBalances(ctx, clients, accounts, addresses)
|
||||
return api.s.tokenManager.GetBalances(ctx, clients, accounts, addresses)
|
||||
}
|
||||
|
||||
func (api *API) GetTokens(ctx context.Context, chainID uint64) ([]*Token, error) {
|
||||
func (api *API) GetTokens(ctx context.Context, chainID uint64) ([]*token.Token, error) {
|
||||
log.Debug("call to get tokens")
|
||||
rst, err := api.s.tokenManager.getTokens(chainID)
|
||||
rst, err := api.s.tokenManager.GetTokens(chainID)
|
||||
log.Debug("result from token store", "len", len(rst))
|
||||
return rst, err
|
||||
}
|
||||
|
||||
func (api *API) GetCustomTokens(ctx context.Context) ([]*Token, error) {
|
||||
func (api *API) GetCustomTokens(ctx context.Context) ([]*token.Token, error) {
|
||||
log.Debug("call to get custom tokens")
|
||||
rst, err := api.s.tokenManager.getCustoms()
|
||||
rst, err := api.s.tokenManager.GetCustoms()
|
||||
log.Debug("result from database for custom tokens", "len", len(rst))
|
||||
return rst, err
|
||||
}
|
||||
|
||||
func (api *API) DiscoverToken(ctx context.Context, chainID uint64, address common.Address) (*Token, error) {
|
||||
func (api *API) DiscoverToken(ctx context.Context, chainID uint64, address common.Address) (*token.Token, error) {
|
||||
log.Debug("call to get discover token")
|
||||
token, err := api.s.tokenManager.discoverToken(ctx, chainID, address)
|
||||
token, err := api.s.tokenManager.DiscoverToken(ctx, chainID, address)
|
||||
return token, err
|
||||
}
|
||||
|
||||
func (api *API) GetVisibleTokens(chainIDs []uint64) (map[uint64][]*Token, error) {
|
||||
func (api *API) GetVisibleTokens(chainIDs []uint64) (map[uint64][]*token.Token, error) {
|
||||
log.Debug("call to get visible tokens")
|
||||
rst, err := api.s.tokenManager.getVisible(chainIDs)
|
||||
rst, err := api.s.tokenManager.GetVisible(chainIDs)
|
||||
log.Debug("result from database for visible tokens", "len", len(rst))
|
||||
return rst, err
|
||||
}
|
||||
|
||||
func (api *API) ToggleVisibleToken(ctx context.Context, chainID uint64, address common.Address) (bool, error) {
|
||||
log.Debug("call to toggle visible tokens")
|
||||
err := api.s.tokenManager.toggle(chainID, address)
|
||||
err := api.s.tokenManager.Toggle(chainID, address)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (api *API) AddCustomToken(ctx context.Context, token Token) error {
|
||||
func (api *API) AddCustomToken(ctx context.Context, token token.Token) error {
|
||||
log.Debug("call to create or edit custom token")
|
||||
if token.ChainID == 0 {
|
||||
token.ChainID = api.s.rpcClient.UpstreamChainID
|
||||
}
|
||||
err := api.s.tokenManager.upsertCustom(token)
|
||||
err := api.s.tokenManager.UpsertCustom(token)
|
||||
log.Debug("result from database for create or edit custom token", "err", err)
|
||||
return err
|
||||
}
|
||||
|
||||
func (api *API) DeleteCustomToken(ctx context.Context, address common.Address) error {
|
||||
log.Debug("call to remove custom token")
|
||||
err := api.s.tokenManager.deleteCustom(api.s.rpcClient.UpstreamChainID, address)
|
||||
err := api.s.tokenManager.DeleteCustom(api.s.rpcClient.UpstreamChainID, address)
|
||||
log.Debug("result from database for remove custom token", "err", err)
|
||||
return err
|
||||
}
|
||||
|
||||
func (api *API) DeleteCustomTokenByChainID(ctx context.Context, chainID uint64, address common.Address) error {
|
||||
log.Debug("call to remove custom token")
|
||||
err := api.s.tokenManager.deleteCustom(chainID, address)
|
||||
err := api.s.tokenManager.DeleteCustom(chainID, address)
|
||||
log.Debug("result from database for remove custom token", "err", err)
|
||||
return err
|
||||
}
|
||||
|
@ -325,14 +326,14 @@ func (api *API) GetSuggestedFees(ctx context.Context, chainID uint64) (*Suggeste
|
|||
return api.s.feesManager.suggestedFees(ctx, chainID)
|
||||
}
|
||||
|
||||
func (api *API) GetTransactionEstimatedTime(ctx context.Context, chainID uint64, maxFeePerGas float64) (TransactionEstimation, error) {
|
||||
func (api *API) GetTransactionEstimatedTime(ctx context.Context, chainID uint64, maxFeePerGas *big.Float) (TransactionEstimation, error) {
|
||||
log.Debug("call to getTransactionEstimatedTime")
|
||||
return api.s.feesManager.transactionEstimatedTime(ctx, chainID, maxFeePerGas), nil
|
||||
}
|
||||
|
||||
func (api *API) GetSuggestedRoutes(ctx context.Context, account common.Address, amount float64, tokenSymbol string, disabledChainIDs []uint64) (*SuggestedRoutes, error) {
|
||||
func (api *API) GetSuggestedRoutes(ctx context.Context, sendType SendType, account common.Address, amountIn *hexutil.Big, tokenSymbol string, disabledFromChainIDs, disabledToChaindIDs, preferedChainIDs []uint64, gasFeeMode GasFeeMode) (*SuggestedRoutes, error) {
|
||||
log.Debug("call to GetSuggestedRoutes")
|
||||
return api.router.suggestedRoutes(ctx, account, amount, tokenSymbol, disabledChainIDs)
|
||||
return api.router.suggestedRoutes(ctx, sendType, account, amountIn.ToInt(), tokenSymbol, disabledFromChainIDs, disabledToChaindIDs, preferedChainIDs, gasFeeMode)
|
||||
}
|
||||
|
||||
func (api *API) GetDerivedAddressesForPath(ctx context.Context, password string, derivedFrom string, path string, pageSize int, pageNumber int) ([]*DerivedAddress, error) {
|
||||
|
@ -476,7 +477,7 @@ func (api *API) getDerivedAddress(id string, derivedPath string) (*DerivedAddres
|
|||
return address, nil
|
||||
}
|
||||
|
||||
func (api *API) CreateMultiTransaction(ctx context.Context, multiTransaction *MultiTransaction, data map[uint64][]transactions.SendTxArgs, password string) (*MultiTransactionResult, error) {
|
||||
func (api *API) CreateMultiTransaction(ctx context.Context, multiTransaction *MultiTransaction, data []*bridge.TransactionBridge, password string) (*MultiTransactionResult, error) {
|
||||
log.Debug("[WalletAPI:: CreateMultiTransaction] create multi transaction")
|
||||
return api.s.transactionManager.createMultiTransaction(ctx, multiTransaction, data, password)
|
||||
return api.s.transactionManager.createMultiTransaction(ctx, multiTransaction, data, api.router.bridges, password)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
package bridge
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/status-im/status-go/account"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/services/wallet/token"
|
||||
"github.com/status-im/status-go/transactions"
|
||||
)
|
||||
|
||||
type TransactionBridge struct {
|
||||
BridgeName string
|
||||
ChainID uint64
|
||||
SimpleTx *transactions.SendTxArgs
|
||||
HopTx *HopTxArgs
|
||||
}
|
||||
|
||||
func (t *TransactionBridge) Value() *big.Int {
|
||||
if t.SimpleTx != nil && t.SimpleTx.To != nil {
|
||||
return t.SimpleTx.Value.ToInt()
|
||||
} else if t.HopTx != nil {
|
||||
return t.HopTx.Amount.ToInt()
|
||||
}
|
||||
|
||||
return big.NewInt(0)
|
||||
}
|
||||
|
||||
func (t *TransactionBridge) From() types.Address {
|
||||
if t.SimpleTx != nil && t.SimpleTx.To != nil {
|
||||
return t.SimpleTx.From
|
||||
} else if t.HopTx != nil {
|
||||
return t.HopTx.From
|
||||
}
|
||||
|
||||
return types.HexToAddress("0x0")
|
||||
}
|
||||
|
||||
func (t *TransactionBridge) To() types.Address {
|
||||
if t.SimpleTx != nil && t.SimpleTx.To != nil {
|
||||
return *t.SimpleTx.To
|
||||
} else if t.HopTx != nil {
|
||||
return types.Address(t.HopTx.Recipient)
|
||||
}
|
||||
|
||||
return types.HexToAddress("0x0")
|
||||
}
|
||||
|
||||
func (t *TransactionBridge) Data() types.HexBytes {
|
||||
if t.SimpleTx != nil && t.SimpleTx.To != nil {
|
||||
return t.SimpleTx.Data
|
||||
} else if t.HopTx != nil {
|
||||
return types.HexBytes("")
|
||||
}
|
||||
|
||||
return types.HexBytes("")
|
||||
}
|
||||
|
||||
type Bridge interface {
|
||||
Name() string
|
||||
Can(from *params.Network, to *params.Network, token *token.Token, balance *big.Int) (bool, error)
|
||||
CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int, nativeTokenPrice, tokenPrice float64, gasPrice *big.Float) (*big.Int, *big.Int, error)
|
||||
EstimateGas(from *params.Network, to *params.Network, token *token.Token, amountIn *big.Int) (uint64, error)
|
||||
CalculateAmountOut(from, to *params.Network, amountIn *big.Int, symbol string) (*big.Int, error)
|
||||
Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (types.Hash, error)
|
||||
}
|
|
@ -0,0 +1,380 @@
|
|||
package bridge
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
ethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/status-im/status-go/account"
|
||||
"github.com/status-im/status-go/contracts"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/rpc"
|
||||
"github.com/status-im/status-go/services/wallet/token"
|
||||
"github.com/status-im/status-go/transactions"
|
||||
)
|
||||
|
||||
const HopLpFeeBps = 4
|
||||
const HopCanonicalTokenIndex = 0
|
||||
const HophTokenIndex = 1
|
||||
const HopMinBonderFeeUsd = 0.25
|
||||
|
||||
var HopBondTransferGasLimit = map[uint64]int64{
|
||||
1: 165000,
|
||||
5: 165000,
|
||||
10: 100000000,
|
||||
42161: 2500000,
|
||||
420: 100000000,
|
||||
421613: 2500000,
|
||||
}
|
||||
var HopSettlementGasLimitPerTx = map[uint64]int64{
|
||||
1: 5141,
|
||||
5: 5141,
|
||||
10: 8545,
|
||||
42161: 19843,
|
||||
420: 8545,
|
||||
421613: 19843,
|
||||
}
|
||||
var HopBonderFeeBps = map[string]map[uint64]int64{
|
||||
"USDC": {
|
||||
1: 14,
|
||||
5: 14,
|
||||
10: 14,
|
||||
42161: 14,
|
||||
420: 14,
|
||||
421613: 14,
|
||||
},
|
||||
"USDT": {
|
||||
1: 26,
|
||||
10: 26,
|
||||
421613: 26,
|
||||
},
|
||||
"DAI": {
|
||||
1: 26,
|
||||
10: 26,
|
||||
42161: 26,
|
||||
},
|
||||
"ETH": {
|
||||
1: 5,
|
||||
5: 5,
|
||||
10: 5,
|
||||
42161: 5,
|
||||
420: 5,
|
||||
421613: 5,
|
||||
},
|
||||
"WBTC": {
|
||||
1: 23,
|
||||
10: 23,
|
||||
42161: 23,
|
||||
},
|
||||
}
|
||||
|
||||
type HopTxArgs struct {
|
||||
transactions.SendTxArgs
|
||||
ChainID uint64 `json:"chainId"`
|
||||
Symbol string `json:"symbol"`
|
||||
Recipient common.Address `json:"recipient"`
|
||||
Amount *hexutil.Big `json:"amount"`
|
||||
BonderFee *hexutil.Big `json:"bonderFee"`
|
||||
}
|
||||
|
||||
type HopBridge struct {
|
||||
contractMaker *contracts.ContractMaker
|
||||
}
|
||||
|
||||
func NewHopBridge(rpcClient *rpc.Client) *HopBridge {
|
||||
return &HopBridge{
|
||||
contractMaker: &contracts.ContractMaker{RPCClient: rpcClient},
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HopBridge) Name() string {
|
||||
return "Hop"
|
||||
}
|
||||
|
||||
func (h *HopBridge) Can(from, to *params.Network, token *token.Token, balance *big.Int) (bool, error) {
|
||||
if balance.Cmp(big.NewInt(0)) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if from.ChainID == to.ChainID {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
fees, ok := HopBonderFeeBps[token.Symbol]
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if _, ok := fees[from.ChainID]; !ok {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if _, ok := fees[to.ChainID]; !ok {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (s *HopBridge) EstimateGas(from, to *params.Network, token *token.Token, amountIn *big.Int) (uint64, error) {
|
||||
// TODO: find why this doesn't work
|
||||
// ethClient, err := s.contractMaker.RPCClient.EthClient(from.ChainID)
|
||||
// if err != nil {
|
||||
// return 0, err
|
||||
// }
|
||||
// zero := common.HexToAddress("0x0")
|
||||
// zeroInt := big.NewInt(0)
|
||||
// var data []byte
|
||||
// if from.Layer == 1 {
|
||||
// bridgeABI, err := abi.JSON(strings.NewReader(hopBridge.HopBridgeABI))
|
||||
// if err != nil {
|
||||
// return 0, err
|
||||
// }
|
||||
// data, err = bridgeABI.Pack("sendToL2", big.NewInt(int64(to.ChainID)), zero, amountIn, zeroInt, zeroInt, zero, zeroInt)
|
||||
// if err != nil {
|
||||
// return 0, err
|
||||
// }
|
||||
// } else {
|
||||
// wrapperABI, err := abi.JSON(strings.NewReader(hopWrapper.HopWrapperABI))
|
||||
// if err != nil {
|
||||
// return 0, err
|
||||
// }
|
||||
// data, err = wrapperABI.Pack("swapAndSend", big.NewInt(int64(to.ChainID)), zero, amountIn, zeroInt, zeroInt, zeroInt, zeroInt, zeroInt)
|
||||
// if err != nil {
|
||||
// return 0, err
|
||||
// }
|
||||
// }
|
||||
// estimate, err := ethClient.EstimateGas(context.Background(), ethereum.CallMsg{
|
||||
// From: zero,
|
||||
// To: &token.Address,
|
||||
// Value: big.NewInt(0),
|
||||
// Data: data,
|
||||
// })
|
||||
return 500000 + 1000, nil
|
||||
}
|
||||
|
||||
func (s *HopBridge) Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (hash types.Hash, err error) {
|
||||
networks, err := s.contractMaker.RPCClient.NetworkManager.Get(false)
|
||||
if err != nil {
|
||||
return hash, err
|
||||
}
|
||||
var fromNetwork *params.Network
|
||||
for _, network := range networks {
|
||||
if network.ChainID == sendArgs.ChainID {
|
||||
fromNetwork = network
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if fromNetwork.Layer == 1 {
|
||||
return s.sendToL2(sendArgs.ChainID, sendArgs.HopTx, verifiedAccount)
|
||||
}
|
||||
return s.swapAndSend(sendArgs.ChainID, sendArgs.HopTx, verifiedAccount)
|
||||
}
|
||||
|
||||
func getSigner(chainID uint64, from types.Address, verifiedAccount *account.SelectedExtKey) bind.SignerFn {
|
||||
return func(addr common.Address, tx *ethTypes.Transaction) (*ethTypes.Transaction, error) {
|
||||
s := ethTypes.NewLondonSigner(new(big.Int).SetUint64(chainID))
|
||||
return ethTypes.SignTx(tx, s, verifiedAccount.AccountKey.PrivateKey)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *HopBridge) sendToL2(chainID uint64, sendArgs *HopTxArgs, verifiedAccount *account.SelectedExtKey) (hash types.Hash, err error) {
|
||||
bridge, err := s.contractMaker.NewHopL1Bridge(chainID, sendArgs.Symbol)
|
||||
if err != nil {
|
||||
return hash, err
|
||||
}
|
||||
txOpts := sendArgs.ToTransactOpts(getSigner(chainID, sendArgs.From, verifiedAccount))
|
||||
txOpts.Value = (*big.Int)(sendArgs.Amount)
|
||||
now := time.Now()
|
||||
deadline := big.NewInt(now.Unix() + 604800)
|
||||
tx, err := bridge.SendToL2(
|
||||
txOpts,
|
||||
big.NewInt(int64(sendArgs.ChainID)),
|
||||
sendArgs.Recipient,
|
||||
sendArgs.Amount.ToInt(),
|
||||
big.NewInt(0),
|
||||
deadline,
|
||||
common.HexToAddress("0x0"),
|
||||
big.NewInt(0),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return hash, err
|
||||
}
|
||||
return types.Hash(tx.Hash()), nil
|
||||
}
|
||||
|
||||
func (s *HopBridge) swapAndSend(chainID uint64, sendArgs *HopTxArgs, verifiedAccount *account.SelectedExtKey) (hash types.Hash, err error) {
|
||||
ammWrapper, err := s.contractMaker.NewHopL2AmmWrapper(chainID, sendArgs.Symbol)
|
||||
if err != nil {
|
||||
return hash, err
|
||||
}
|
||||
|
||||
txOpts := sendArgs.ToTransactOpts(getSigner(chainID, sendArgs.From, verifiedAccount))
|
||||
txOpts.Value = (*big.Int)(sendArgs.Amount)
|
||||
now := time.Now()
|
||||
deadline := big.NewInt(now.Unix() + 604800)
|
||||
tx, err := ammWrapper.SwapAndSend(
|
||||
txOpts,
|
||||
big.NewInt(int64(sendArgs.ChainID)),
|
||||
sendArgs.Recipient,
|
||||
sendArgs.Amount.ToInt(),
|
||||
sendArgs.BonderFee.ToInt(),
|
||||
big.NewInt(0),
|
||||
deadline,
|
||||
big.NewInt(0),
|
||||
deadline,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return hash, err
|
||||
}
|
||||
|
||||
return types.Hash(tx.Hash()), nil
|
||||
}
|
||||
|
||||
// CalculateBonderFees logics come from: https://docs.hop.exchange/fee-calculation
|
||||
func (h *HopBridge) CalculateBonderFees(from, to *params.Network, token *token.Token, amountIn *big.Int, nativeTokenPrice, tokenPrice float64, gasPrice *big.Float) (*big.Int, error) {
|
||||
amount := new(big.Float).SetInt(amountIn)
|
||||
totalFee := big.NewFloat(0)
|
||||
destinationTxFee, err := h.getDestinationTxFee(from, to, nativeTokenPrice, tokenPrice, gasPrice)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bonderFeeRelative, err := h.getBonderFeeRelative(from, to, amount, token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if from.Layer != 1 {
|
||||
adjustedBonderFee, err := h.calcFromHTokenAmount(to, bonderFeeRelative, token.Symbol)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
adjustedDestinationTxFee, err := h.calcToHTokenAmount(to, destinationTxFee, token.Symbol)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bonderFeeAbsolute := h.getBonderFeeAbsolute(tokenPrice)
|
||||
if adjustedBonderFee.Cmp(bonderFeeAbsolute) == -1 {
|
||||
adjustedBonderFee = bonderFeeAbsolute
|
||||
}
|
||||
|
||||
totalFee.Add(adjustedBonderFee, adjustedDestinationTxFee)
|
||||
}
|
||||
res, _ := new(big.Float).Mul(totalFee, big.NewFloat(math.Pow(10, float64(token.Decimals)))).Int(nil)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (h *HopBridge) CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int, nativeTokenPrice, tokenPrice float64, gasPrice *big.Float) (*big.Int, *big.Int, error) {
|
||||
bonderFees, err := h.CalculateBonderFees(from, to, token, amountIn, nativeTokenPrice, tokenPrice, gasPrice)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
amountOut, err := h.amountOut(from, to, new(big.Float).SetInt(amountIn), token.Symbol)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
amountOutInt, _ := amountOut.Int(nil)
|
||||
|
||||
return bonderFees, new(big.Int).Add(
|
||||
bonderFees,
|
||||
new(big.Int).Sub(amountIn, amountOutInt),
|
||||
), nil
|
||||
}
|
||||
|
||||
func (h *HopBridge) calcToHTokenAmount(network *params.Network, amount *big.Float, symbol string) (*big.Float, error) {
|
||||
if network.Layer == 1 || amount.Cmp(big.NewFloat(0)) == 0 {
|
||||
return amount, nil
|
||||
}
|
||||
|
||||
contract, err := h.contractMaker.NewHopL2SaddlSwap(network.ChainID, symbol)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
amountInt, _ := amount.Int(nil)
|
||||
res, err := contract.CalculateSwap(&bind.CallOpts{Context: context.Background()}, HopCanonicalTokenIndex, HophTokenIndex, amountInt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return new(big.Float).SetInt(res), nil
|
||||
}
|
||||
|
||||
func (h *HopBridge) calcFromHTokenAmount(network *params.Network, amount *big.Float, symbol string) (*big.Float, error) {
|
||||
if network.Layer == 1 || amount.Cmp(big.NewFloat(0)) == 0 {
|
||||
return amount, nil
|
||||
}
|
||||
contract, err := h.contractMaker.NewHopL2SaddlSwap(network.ChainID, symbol)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
amountInt, _ := amount.Int(nil)
|
||||
res, err := contract.CalculateSwap(&bind.CallOpts{Context: context.Background()}, HophTokenIndex, HopCanonicalTokenIndex, amountInt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return new(big.Float).SetInt(res), nil
|
||||
}
|
||||
|
||||
func (h *HopBridge) CalculateAmountOut(from, to *params.Network, amountIn *big.Int, symbol string) (*big.Int, error) {
|
||||
amountOut, err := h.amountOut(from, to, new(big.Float).SetInt(amountIn), symbol)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
amountOutInt, _ := amountOut.Int(nil)
|
||||
return amountOutInt, nil
|
||||
}
|
||||
|
||||
func (h *HopBridge) amountOut(from, to *params.Network, amountIn *big.Float, symbol string) (*big.Float, error) {
|
||||
hTokenAmount, err := h.calcToHTokenAmount(from, amountIn, symbol)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return h.calcFromHTokenAmount(to, hTokenAmount, symbol)
|
||||
}
|
||||
|
||||
func (h *HopBridge) getBonderFeeRelative(from, to *params.Network, amount *big.Float, token *token.Token) (*big.Float, error) {
|
||||
if from.Layer != 1 {
|
||||
return big.NewFloat(0), nil
|
||||
}
|
||||
|
||||
hTokenAmount, err := h.calcToHTokenAmount(from, amount, token.Symbol)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
feeBps := HopBonderFeeBps[token.Symbol][to.ChainID]
|
||||
|
||||
factor := new(big.Float).Mul(hTokenAmount, big.NewFloat(float64(feeBps)))
|
||||
return new(big.Float).Quo(
|
||||
factor,
|
||||
big.NewFloat(10000),
|
||||
), nil
|
||||
}
|
||||
|
||||
func (h *HopBridge) getBonderFeeAbsolute(tokenPrice float64) *big.Float {
|
||||
return new(big.Float).Quo(big.NewFloat(HopMinBonderFeeUsd), big.NewFloat(tokenPrice))
|
||||
}
|
||||
|
||||
func (h *HopBridge) getDestinationTxFee(from, to *params.Network, nativeTokenPrice, tokenPrice float64, gasPrice *big.Float) (*big.Float, error) {
|
||||
if from.Layer != 1 {
|
||||
return big.NewFloat(0), nil
|
||||
}
|
||||
|
||||
bondTransferGasLimit := HopBondTransferGasLimit[to.ChainID]
|
||||
settlementGasLimit := HopSettlementGasLimitPerTx[to.ChainID]
|
||||
totalGasLimit := new(big.Int).Add(big.NewInt(bondTransferGasLimit), big.NewInt(settlementGasLimit))
|
||||
|
||||
rate := new(big.Float).Quo(big.NewFloat(nativeTokenPrice), big.NewFloat(tokenPrice))
|
||||
|
||||
txFeeEth := new(big.Float).Mul(gasPrice, new(big.Float).SetInt(totalGasLimit))
|
||||
return new(big.Float).Mul(txFeeEth, rate), nil
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package bridge
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/status-im/status-go/account"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/services/wallet/token"
|
||||
"github.com/status-im/status-go/transactions"
|
||||
)
|
||||
|
||||
type SimpleBridge struct {
|
||||
transactor *transactions.Transactor
|
||||
}
|
||||
|
||||
func NewSimpleBridge(transactor *transactions.Transactor) *SimpleBridge {
|
||||
return &SimpleBridge{transactor: transactor}
|
||||
}
|
||||
|
||||
func (s *SimpleBridge) Name() string {
|
||||
return "Simple"
|
||||
}
|
||||
|
||||
func (s *SimpleBridge) Can(from, to *params.Network, token *token.Token, balance *big.Int) (bool, error) {
|
||||
return from.ChainID == to.ChainID, nil
|
||||
}
|
||||
|
||||
func (s *SimpleBridge) CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int, nativeTokenPrice, tokenPrice float64, gasPrice *big.Float) (*big.Int, *big.Int, error) {
|
||||
return big.NewInt(0), big.NewInt(0), nil
|
||||
}
|
||||
|
||||
func (s *SimpleBridge) EstimateGas(from, to *params.Network, token *token.Token, amountIn *big.Int) (uint64, error) {
|
||||
// TODO: replace by estimate function
|
||||
if token.IsNative() {
|
||||
return 22000, nil // default gas limit for eth transaction
|
||||
}
|
||||
|
||||
return 200000, nil //default gas limit for erc20 transaction
|
||||
}
|
||||
|
||||
func (s *SimpleBridge) Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (types.Hash, error) {
|
||||
return s.transactor.SendTransactionWithChainID(sendArgs.ChainID, *sendArgs.SimpleTx, verifiedAccount)
|
||||
}
|
||||
|
||||
func (s *SimpleBridge) CalculateAmountOut(from, to *params.Network, amountIn *big.Int, symbol string) (*big.Int, error) {
|
||||
return amountIn, nil
|
||||
}
|
|
@ -12,6 +12,14 @@ import (
|
|||
"github.com/status-im/status-go/rpc"
|
||||
)
|
||||
|
||||
type GasFeeMode int
|
||||
|
||||
const (
|
||||
GasFeeLow GasFeeMode = iota
|
||||
GasFeeMedium
|
||||
GasFeeHigh
|
||||
)
|
||||
|
||||
type SuggestedFees struct {
|
||||
GasPrice *big.Float `json:"gasPrice"`
|
||||
BaseFee *big.Float `json:"baseFee"`
|
||||
|
@ -22,6 +30,22 @@ type SuggestedFees struct {
|
|||
EIP1559Enabled bool `json:"eip1559Enabled"`
|
||||
}
|
||||
|
||||
func (s *SuggestedFees) feeFor(mode GasFeeMode) *big.Float {
|
||||
if s.EIP1559Enabled {
|
||||
return s.GasPrice
|
||||
}
|
||||
|
||||
if mode == GasFeeLow {
|
||||
return s.MaxFeePerGasLow
|
||||
}
|
||||
|
||||
if mode == GasFeeHigh {
|
||||
return s.MaxFeePerGasHigh
|
||||
}
|
||||
|
||||
return s.MaxFeePerGasMedium
|
||||
}
|
||||
|
||||
const inclusionThreshold = 0.95
|
||||
|
||||
type TransactionEstimation int
|
||||
|
@ -52,9 +76,12 @@ func weiToGwei(val *big.Int) *big.Float {
|
|||
return result.Quo(result, new(big.Float).SetInt(unit))
|
||||
}
|
||||
|
||||
func gweiToWei(val float64) *big.Int {
|
||||
res := new(big.Int)
|
||||
res.SetUint64(uint64(val * 1000000000))
|
||||
func gweiToEth(val *big.Float) *big.Float {
|
||||
return new(big.Float).Quo(val, big.NewFloat(1000000000))
|
||||
}
|
||||
|
||||
func gweiToWei(val *big.Float) *big.Int {
|
||||
res, _ := new(big.Float).Mul(val, big.NewFloat(1000000000)).Int(nil)
|
||||
return res
|
||||
}
|
||||
|
||||
|
@ -67,12 +94,6 @@ func (f *FeeManager) suggestedFees(ctx context.Context, chainID uint64) (*Sugges
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
block, err := backend.BlockByNumber(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
maxPriorityFeePerGas, err := backend.SuggestGasTipCap(ctx)
|
||||
if err != nil {
|
||||
return &SuggestedFees{
|
||||
|
@ -86,12 +107,25 @@ func (f *FeeManager) suggestedFees(ctx context.Context, chainID uint64) (*Sugges
|
|||
}, nil
|
||||
}
|
||||
|
||||
block, err := backend.BlockByNumber(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config := params.MainnetChainConfig
|
||||
baseFee := misc.CalcBaseFee(config, block.Header())
|
||||
|
||||
fees, err := f.getFeeHistorySorted(chainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return &SuggestedFees{
|
||||
GasPrice: weiToGwei(gasPrice),
|
||||
BaseFee: weiToGwei(baseFee),
|
||||
MaxPriorityFeePerGas: weiToGwei(maxPriorityFeePerGas),
|
||||
MaxFeePerGasLow: weiToGwei(maxPriorityFeePerGas),
|
||||
MaxFeePerGasMedium: weiToGwei(maxPriorityFeePerGas),
|
||||
MaxFeePerGasHigh: weiToGwei(maxPriorityFeePerGas),
|
||||
EIP1559Enabled: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
perc10 := fees[int64(0.1*float64(len(fees)))-1]
|
||||
|
@ -125,7 +159,7 @@ func (f *FeeManager) suggestedFees(ctx context.Context, chainID uint64) (*Sugges
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (f *FeeManager) transactionEstimatedTime(ctx context.Context, chainID uint64, maxFeePerGas float64) TransactionEstimation {
|
||||
func (f *FeeManager) transactionEstimatedTime(ctx context.Context, chainID uint64, maxFeePerGas *big.Float) TransactionEstimation {
|
||||
fees, err := f.getFeeHistorySorted(chainID)
|
||||
if err != nil {
|
||||
return Unknown
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/status-im/status-go/multiaccounts/accounts"
|
||||
"github.com/status-im/status-go/services/wallet/chain"
|
||||
"github.com/status-im/status-go/services/wallet/token"
|
||||
"github.com/status-im/status-go/services/wallet/transfer"
|
||||
)
|
||||
|
||||
|
@ -20,7 +21,7 @@ type Reader struct {
|
|||
}
|
||||
|
||||
type ReaderToken struct {
|
||||
Token *Token `json:"token"`
|
||||
Token *token.Token `json:"token"`
|
||||
OraclePrice float64 `json:"oraclePrice"`
|
||||
CryptoBalance *hexutil.Big `json:"cryptoBalance"`
|
||||
FiatBalance *big.Float `json:"fiatBalance"`
|
||||
|
@ -39,8 +40,8 @@ type Wallet struct {
|
|||
Accounts []ReaderAccount `json:"accounts"`
|
||||
OnRamp []CryptoOnRamp `json:"onRamp"`
|
||||
SavedAddresses map[uint64][]SavedAddress `json:"savedAddresses"`
|
||||
Tokens map[uint64][]*Token `json:"tokens"`
|
||||
CustomTokens []*Token `json:"customTokens"`
|
||||
Tokens map[uint64][]*token.Token `json:"tokens"`
|
||||
CustomTokens []*token.Token `json:"customTokens"`
|
||||
PendingTransactions map[uint64][]*PendingTransaction `json:"pendingTransactions"`
|
||||
|
||||
FiatBalance *big.Float `json:"fiatBalance"`
|
||||
|
@ -59,7 +60,7 @@ func (r *Reader) buildReaderAccount(
|
|||
ctx context.Context,
|
||||
chainIDs []uint64,
|
||||
account *accounts.Account,
|
||||
visibleTokens map[uint64][]*Token,
|
||||
visibleTokens map[uint64][]*token.Token,
|
||||
prices map[string]float64,
|
||||
balances map[common.Address]*hexutil.Big,
|
||||
) (ReaderAccount, error) {
|
||||
|
@ -118,21 +119,21 @@ func (r *Reader) GetWallet(ctx context.Context, chainIDs []uint64) (*Wallet, err
|
|||
return nil, err
|
||||
}
|
||||
|
||||
tokensMap := make(map[uint64][]*Token)
|
||||
tokensMap := make(map[uint64][]*token.Token)
|
||||
for _, chainID := range chainIDs {
|
||||
tokens, err := r.s.tokenManager.getTokens(chainID)
|
||||
tokens, err := r.s.tokenManager.GetTokens(chainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tokensMap[chainID] = tokens
|
||||
}
|
||||
|
||||
customTokens, err := r.s.tokenManager.getCustoms()
|
||||
customTokens, err := r.s.tokenManager.GetCustoms()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
visibleTokens, err := r.s.tokenManager.getVisible(chainIDs)
|
||||
visibleTokens, err := r.s.tokenManager.GetVisible(chainIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -161,7 +162,7 @@ func (r *Reader) GetWallet(ctx context.Context, chainIDs []uint64) (*Wallet, err
|
|||
return nil, err
|
||||
}
|
||||
|
||||
balances, err := r.s.tokenManager.getBalances(ctx, clients, getAddresses(accounts), tokenAddresses)
|
||||
balances, err := r.s.tokenManager.GetBalances(ctx, clients, getAddresses(accounts), tokenAddresses)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -2,54 +2,276 @@ package wallet
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/services/wallet/async"
|
||||
"github.com/status-im/status-go/services/wallet/bigint"
|
||||
"github.com/status-im/status-go/services/wallet/bridge"
|
||||
"github.com/status-im/status-go/services/wallet/chain"
|
||||
"github.com/status-im/status-go/services/wallet/token"
|
||||
"github.com/status-im/status-go/transactions"
|
||||
)
|
||||
|
||||
type SendType int
|
||||
|
||||
const (
|
||||
Transfer SendType = iota
|
||||
ENSRegister
|
||||
ENSRelease
|
||||
ENSSetPubKey
|
||||
StickersBuy
|
||||
)
|
||||
const EstimateUsername = "RandomUsername"
|
||||
const EstimatePubKey = "0x04bb2024ce5d72e45d4a4f8589ae657ef9745855006996115a23a1af88d536cf02c0524a585fce7bfa79d6a9669af735eda6205d6c7e5b3cdc2b8ff7b2fa1f0b56"
|
||||
|
||||
func (s SendType) isTransfer() bool {
|
||||
return s == Transfer
|
||||
}
|
||||
|
||||
func (s SendType) isAvailableFor(network *params.Network) bool {
|
||||
if s == Transfer {
|
||||
return true
|
||||
}
|
||||
|
||||
if network.ChainID == 1 || network.ChainID == 5 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (s SendType) EstimateGas(service *Service, network *params.Network) uint64 {
|
||||
from := types.Address(common.HexToAddress("0x5ffa75ce51c3a7ebe23bde37b5e3a0143dfbcee0"))
|
||||
tx := transactions.SendTxArgs{
|
||||
From: from,
|
||||
Value: (*hexutil.Big)(big.NewInt(0)),
|
||||
}
|
||||
if s == ENSRegister {
|
||||
estimate, err := service.ens.API().RegisterEstimate(context.Background(), network.ChainID, tx, EstimateUsername, EstimatePubKey)
|
||||
if err != nil {
|
||||
return 400000
|
||||
}
|
||||
return estimate
|
||||
}
|
||||
|
||||
if s == ENSRelease {
|
||||
estimate, err := service.ens.API().ReleaseEstimate(context.Background(), network.ChainID, tx, EstimateUsername)
|
||||
if err != nil {
|
||||
return 200000
|
||||
}
|
||||
return estimate
|
||||
}
|
||||
|
||||
if s == ENSSetPubKey {
|
||||
estimate, err := service.ens.API().SetPubKeyEstimate(context.Background(), network.ChainID, tx, fmt.Sprint(EstimateUsername, ".stateofus.eth"), EstimatePubKey)
|
||||
if err != nil {
|
||||
return 400000
|
||||
}
|
||||
return estimate
|
||||
}
|
||||
|
||||
if s == StickersBuy {
|
||||
packId := &bigint.BigInt{Int: big.NewInt(2)}
|
||||
estimate, err := service.stickers.API().BuyEstimate(context.Background(), network.ChainID, from, packId)
|
||||
if err != nil {
|
||||
return 400000
|
||||
}
|
||||
return estimate
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
var zero = big.NewInt(0)
|
||||
|
||||
type Path struct {
|
||||
BridgeName string
|
||||
From *params.Network
|
||||
To *params.Network
|
||||
MaxAmountIn *hexutil.Big
|
||||
AmountIn *hexutil.Big
|
||||
AmountOut *hexutil.Big
|
||||
GasAmount uint64
|
||||
GasFees *SuggestedFees
|
||||
BonderFees *hexutil.Big
|
||||
TokenFees *big.Float
|
||||
Cost *big.Float
|
||||
Preferred bool
|
||||
EstimatedTime TransactionEstimation
|
||||
}
|
||||
|
||||
type Graph = []*Node
|
||||
|
||||
type Node struct {
|
||||
Path *Path
|
||||
Children Graph
|
||||
}
|
||||
|
||||
func newNode(path *Path) *Node {
|
||||
return &Node{Path: path, Children: make(Graph, 0)}
|
||||
}
|
||||
|
||||
func buildGraph(AmountIn *big.Int, routes []*Path, level int, sourceChainIDs []uint64) Graph {
|
||||
graph := make(Graph, 0)
|
||||
for _, route := range routes {
|
||||
found := false
|
||||
for _, chainID := range sourceChainIDs {
|
||||
if chainID == route.From.ChainID {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if found {
|
||||
continue
|
||||
}
|
||||
node := newNode(route)
|
||||
|
||||
newRoutes := make([]*Path, 0)
|
||||
for _, r := range routes {
|
||||
if r.From.ChainID == route.From.ChainID && r.To.ChainID == route.To.ChainID {
|
||||
continue
|
||||
}
|
||||
newRoutes = append(newRoutes, r)
|
||||
}
|
||||
|
||||
newAmountIn := new(big.Int).Sub(AmountIn, route.MaxAmountIn.ToInt())
|
||||
if newAmountIn.Sign() > 0 {
|
||||
newSourceChainIDs := make([]uint64, len(sourceChainIDs))
|
||||
copy(newSourceChainIDs, sourceChainIDs)
|
||||
newSourceChainIDs = append(newSourceChainIDs, route.From.ChainID)
|
||||
node.Children = buildGraph(newAmountIn, newRoutes, level+1, newSourceChainIDs)
|
||||
|
||||
if len(node.Children) == 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
graph = append(graph, node)
|
||||
}
|
||||
|
||||
return graph
|
||||
}
|
||||
|
||||
func (n Node) findBest(level int) ([]*Path, *big.Float) {
|
||||
if len(n.Children) == 0 {
|
||||
if n.Path == nil {
|
||||
return []*Path{}, big.NewFloat(0)
|
||||
}
|
||||
return []*Path{n.Path}, n.Path.Cost
|
||||
}
|
||||
|
||||
var best []*Path
|
||||
bestTotalCost := big.NewFloat(math.Inf(1))
|
||||
|
||||
for _, node := range n.Children {
|
||||
routes, totalCost := node.findBest(level + 1)
|
||||
if totalCost.Cmp(bestTotalCost) < 0 {
|
||||
best = routes
|
||||
bestTotalCost = totalCost
|
||||
}
|
||||
}
|
||||
|
||||
if n.Path == nil {
|
||||
return best, bestTotalCost
|
||||
}
|
||||
|
||||
return append([]*Path{n.Path}, best...), new(big.Float).Add(bestTotalCost, n.Path.Cost)
|
||||
}
|
||||
|
||||
type SuggestedRoutes struct {
|
||||
Networks []params.Network `json:"networks"`
|
||||
Best []*Path
|
||||
Candidates []*Path
|
||||
TokenPrice float64
|
||||
NativeChainTokenPrice float64
|
||||
}
|
||||
|
||||
func newSuggestedRoutes(amountIn *big.Int, candidates []*Path) *SuggestedRoutes {
|
||||
if len(candidates) == 0 {
|
||||
return &SuggestedRoutes{
|
||||
Candidates: candidates,
|
||||
Best: candidates,
|
||||
}
|
||||
}
|
||||
|
||||
node := &Node{
|
||||
Path: nil,
|
||||
Children: buildGraph(amountIn, candidates, 0, []uint64{}),
|
||||
}
|
||||
best, _ := node.findBest(0)
|
||||
|
||||
if len(best) > 0 {
|
||||
rest := new(big.Int).Set(amountIn)
|
||||
for _, path := range best {
|
||||
diff := new(big.Int).Sub(rest, path.MaxAmountIn.ToInt())
|
||||
if diff.Cmp(big.NewInt(0)) >= 0 {
|
||||
path.AmountIn = path.MaxAmountIn
|
||||
} else {
|
||||
path.AmountIn = (*hexutil.Big)(new(big.Int).Set(rest))
|
||||
}
|
||||
rest.Sub(rest, path.AmountIn.ToInt())
|
||||
}
|
||||
}
|
||||
|
||||
return &SuggestedRoutes{
|
||||
Candidates: candidates,
|
||||
Best: best,
|
||||
}
|
||||
}
|
||||
|
||||
func NewRouter(s *Service) *Router {
|
||||
return &Router{s}
|
||||
bridges := make(map[string]bridge.Bridge)
|
||||
simple := bridge.NewSimpleBridge(s.transactor)
|
||||
hop := bridge.NewHopBridge(s.rpcClient)
|
||||
bridges[simple.Name()] = simple
|
||||
bridges[hop.Name()] = hop
|
||||
|
||||
return &Router{s, bridges}
|
||||
}
|
||||
|
||||
func containsNetworkChainId(network *params.Network, chainIDs []uint64) bool {
|
||||
for _, chainID := range chainIDs {
|
||||
if chainID == network.ChainID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
type Router struct {
|
||||
s *Service
|
||||
bridges map[string]bridge.Bridge
|
||||
}
|
||||
|
||||
func (r *Router) suitableTokenExists(ctx context.Context, network *params.Network, tokens []*Token, account common.Address, amount float64, tokenSymbol string) (bool, error) {
|
||||
for _, token := range tokens {
|
||||
if token.Symbol != tokenSymbol {
|
||||
continue
|
||||
}
|
||||
|
||||
func (r *Router) getBalance(ctx context.Context, network *params.Network, token *token.Token, account common.Address) (*big.Int, error) {
|
||||
clients, err := chain.NewClients(r.s.rpcClient, []uint64{network.ChainID})
|
||||
if err != nil {
|
||||
return false, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
balance, err := r.s.tokenManager.getBalance(ctx, clients[0], account, token.Address)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return r.s.tokenManager.GetBalance(ctx, clients[0], account, token.Address)
|
||||
}
|
||||
|
||||
amountForToken, _ := new(big.Float).Mul(big.NewFloat(amount), big.NewFloat(math.Pow10(int(token.Decimals)))).Int(nil)
|
||||
if balance.Cmp(amountForToken) >= 0 {
|
||||
return true, nil
|
||||
}
|
||||
func (r *Router) estimateTimes(ctx context.Context, network *params.Network, gasFees *SuggestedFees, gasFeeMode GasFeeMode) TransactionEstimation {
|
||||
if gasFeeMode == GasFeeLow {
|
||||
return r.s.feesManager.transactionEstimatedTime(ctx, network.ChainID, gasFees.MaxFeePerGasLow)
|
||||
}
|
||||
|
||||
return false, nil
|
||||
if gasFeeMode == GasFeeMedium {
|
||||
return r.s.feesManager.transactionEstimatedTime(ctx, network.ChainID, gasFees.MaxFeePerGasMedium)
|
||||
}
|
||||
|
||||
func (r *Router) suggestedRoutes(ctx context.Context, account common.Address, amount float64, tokenSymbol string, disabledChainIDs []uint64) (*SuggestedRoutes, error) {
|
||||
return r.s.feesManager.transactionEstimatedTime(ctx, network.ChainID, gasFees.MaxFeePerGasHigh)
|
||||
}
|
||||
|
||||
func (r *Router) suggestedRoutes(ctx context.Context, sendType SendType, account common.Address, amountIn *big.Int, tokenSymbol string, disabledFromChainIDs, disabledToChaindIDs, preferedChainIDs []uint64, gasFeeMode GasFeeMode) (*SuggestedRoutes, error) {
|
||||
areTestNetworksEnabled, err := r.s.accountsDB.GetTestNetworksEnabled()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -60,10 +282,15 @@ func (r *Router) suggestedRoutes(ctx context.Context, account common.Address, am
|
|||
return nil, err
|
||||
}
|
||||
|
||||
prices, err := fetchCryptoComparePrices([]string{"ETH", tokenSymbol}, "USD")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
group = async.NewAtomicGroup(ctx)
|
||||
mu sync.Mutex
|
||||
candidates = make([]params.Network, 0)
|
||||
candidates = make([]*Path, 0)
|
||||
)
|
||||
for networkIdx := range networks {
|
||||
network := networks[networkIdx]
|
||||
|
@ -71,65 +298,136 @@ func (r *Router) suggestedRoutes(ctx context.Context, account common.Address, am
|
|||
continue
|
||||
}
|
||||
|
||||
networkFound := false
|
||||
for _, chainID := range disabledChainIDs {
|
||||
networkFound = false
|
||||
if chainID == network.ChainID {
|
||||
networkFound = true
|
||||
break
|
||||
if containsNetworkChainId(network, disabledFromChainIDs) {
|
||||
continue
|
||||
}
|
||||
|
||||
if !sendType.isAvailableFor(network) {
|
||||
continue
|
||||
}
|
||||
// This is network cannot be used as a suggestedRoute as the user has disabled it
|
||||
if networkFound {
|
||||
|
||||
token := r.s.tokenManager.FindToken(network, tokenSymbol)
|
||||
if token == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
nativeToken := r.s.tokenManager.FindToken(network, network.NativeCurrencySymbol)
|
||||
if nativeToken == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
group.Add(func(c context.Context) error {
|
||||
if tokenSymbol == network.NativeCurrencySymbol {
|
||||
tokens := []*Token{&Token{
|
||||
Address: common.HexToAddress("0x"),
|
||||
Symbol: network.NativeCurrencySymbol,
|
||||
Decimals: uint(network.NativeCurrencyDecimals),
|
||||
Name: network.NativeCurrencyName,
|
||||
}}
|
||||
ok, _ := r.suitableTokenExists(c, network, tokens, account, amount, tokenSymbol)
|
||||
if ok {
|
||||
gasFees, err := r.s.feesManager.suggestedFees(ctx, network.ChainID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
balance, err := r.getBalance(ctx, network, token, account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nativeBalance, err := r.getBalance(ctx, network, nativeToken, account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
maxFees := gasFees.feeFor(gasFeeMode)
|
||||
|
||||
estimatedTime := r.s.feesManager.transactionEstimatedTime(ctx, network.ChainID, maxFees)
|
||||
|
||||
for _, bridge := range r.bridges {
|
||||
for _, dest := range networks {
|
||||
if dest.IsTest != areTestNetworksEnabled {
|
||||
continue
|
||||
}
|
||||
|
||||
if !sendType.isAvailableFor(network) {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(preferedChainIDs) > 0 && !containsNetworkChainId(network, preferedChainIDs) {
|
||||
continue
|
||||
}
|
||||
|
||||
if containsNetworkChainId(dest, disabledToChaindIDs) {
|
||||
continue
|
||||
}
|
||||
|
||||
can, err := bridge.Can(network, dest, token, balance)
|
||||
if err != nil || !can {
|
||||
continue
|
||||
}
|
||||
|
||||
bonderFees, tokenFees, err := bridge.CalculateFees(network, dest, token, amountIn, prices["ETH"], prices[tokenSymbol], gasFees.GasPrice)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
gasLimit := uint64(0)
|
||||
if sendType.isTransfer() {
|
||||
gasLimit, err = bridge.EstimateGas(network, dest, token, amountIn)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
gasLimit = sendType.EstimateGas(r.s, network)
|
||||
}
|
||||
requiredNativeBalance := new(big.Int).Mul(gweiToWei(maxFees), big.NewInt(int64(gasLimit)))
|
||||
|
||||
if nativeBalance.Cmp(requiredNativeBalance) <= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
preferred := containsNetworkChainId(dest, preferedChainIDs)
|
||||
|
||||
gasCost := new(big.Float)
|
||||
gasCost.Mul(
|
||||
new(big.Float).Mul(gweiToEth(maxFees), big.NewFloat((float64(gasLimit)))),
|
||||
big.NewFloat(prices["ETH"]),
|
||||
)
|
||||
tokenFeesAsFloat := new(big.Float).Quo(
|
||||
new(big.Float).SetInt(tokenFees),
|
||||
big.NewFloat(math.Pow(10, float64(token.Decimals))),
|
||||
)
|
||||
tokenCost := new(big.Float)
|
||||
tokenCost.Mul(tokenFeesAsFloat, big.NewFloat(prices[tokenSymbol]))
|
||||
cost := new(big.Float)
|
||||
cost.Add(tokenCost, gasCost)
|
||||
|
||||
mu.Lock()
|
||||
candidates = append(candidates, *network)
|
||||
mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
tokens, err := r.s.tokenManager.getTokens(network.ChainID)
|
||||
if err == nil {
|
||||
ok, _ := r.suitableTokenExists(c, network, tokens, account, amount, tokenSymbol)
|
||||
|
||||
if ok {
|
||||
mu.Lock()
|
||||
candidates = append(candidates, *network)
|
||||
mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
customTokens, err := r.s.tokenManager.getCustomsByChainID(network.ChainID)
|
||||
if err == nil {
|
||||
ok, _ := r.suitableTokenExists(c, network, customTokens, account, amount, tokenSymbol)
|
||||
|
||||
if ok {
|
||||
mu.Lock()
|
||||
candidates = append(candidates, *network)
|
||||
candidates = append(candidates, &Path{
|
||||
BridgeName: bridge.Name(),
|
||||
From: network,
|
||||
To: dest,
|
||||
MaxAmountIn: (*hexutil.Big)(balance),
|
||||
AmountIn: (*hexutil.Big)(big.NewInt(0)),
|
||||
AmountOut: (*hexutil.Big)(big.NewInt(0)),
|
||||
GasAmount: gasLimit,
|
||||
GasFees: gasFees,
|
||||
BonderFees: (*hexutil.Big)(bonderFees),
|
||||
TokenFees: tokenFeesAsFloat,
|
||||
Preferred: preferred,
|
||||
Cost: cost,
|
||||
EstimatedTime: estimatedTime,
|
||||
})
|
||||
mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
group.Wait()
|
||||
return &SuggestedRoutes{
|
||||
Networks: candidates,
|
||||
}, nil
|
||||
|
||||
suggestedRoutes := newSuggestedRoutes(amountIn, candidates)
|
||||
suggestedRoutes.TokenPrice = prices[tokenSymbol]
|
||||
suggestedRoutes.NativeChainTokenPrice = prices["ETH"]
|
||||
for _, path := range suggestedRoutes.Best {
|
||||
amountOut, err := r.bridges[path.BridgeName].CalculateAmountOut(path.From, path.To, (*big.Int)(path.AmountIn), tokenSymbol)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
path.AmountOut = (*hexutil.Big)(amountOut)
|
||||
}
|
||||
return suggestedRoutes, nil
|
||||
}
|
||||
|
|
|
@ -12,6 +12,9 @@ import (
|
|||
"github.com/status-im/status-go/multiaccounts/accounts"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/rpc"
|
||||
"github.com/status-im/status-go/services/ens"
|
||||
"github.com/status-im/status-go/services/stickers"
|
||||
"github.com/status-im/status-go/services/wallet/token"
|
||||
"github.com/status-im/status-go/services/wallet/transfer"
|
||||
"github.com/status-im/status-go/transactions"
|
||||
)
|
||||
|
@ -26,11 +29,13 @@ func NewService(
|
|||
gethManager *account.GethManager,
|
||||
transactor *transactions.Transactor,
|
||||
config *params.NodeConfig,
|
||||
ens *ens.Service,
|
||||
stickers *stickers.Service,
|
||||
) *Service {
|
||||
cryptoOnRampManager := NewCryptoOnRampManager(&CryptoOnRampOptions{
|
||||
dataSourceType: DataSourceStatic,
|
||||
})
|
||||
tokenManager := NewTokenManager(db, rpcClient, rpcClient.NetworkManager)
|
||||
tokenManager := token.NewTokenManager(db, rpcClient, rpcClient.NetworkManager)
|
||||
savedAddressesManager := &SavedAddressesManager{db: db}
|
||||
transactionManager := &TransactionManager{db: db, transactor: transactor, gethManager: gethManager, config: config, accountsDB: accountsDB}
|
||||
transferController := transfer.NewTransferController(db, rpcClient, accountFeed)
|
||||
|
@ -47,6 +52,9 @@ func NewService(
|
|||
openseaAPIKey: openseaAPIKey,
|
||||
feesManager: &FeeManager{rpcClient},
|
||||
gethManager: gethManager,
|
||||
transactor: transactor,
|
||||
ens: ens,
|
||||
stickers: stickers,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,7 +64,7 @@ type Service struct {
|
|||
accountsDB *accounts.Database
|
||||
rpcClient *rpc.Client
|
||||
savedAddressesManager *SavedAddressesManager
|
||||
tokenManager *TokenManager
|
||||
tokenManager *token.TokenManager
|
||||
transactionManager *TransactionManager
|
||||
cryptoOnRampManager *CryptoOnRampManager
|
||||
transferController *transfer.Controller
|
||||
|
@ -64,6 +72,9 @@ type Service struct {
|
|||
started bool
|
||||
openseaAPIKey string
|
||||
gethManager *account.GethManager
|
||||
transactor *transactions.Transactor
|
||||
ens *ens.Service
|
||||
stickers *stickers.Service
|
||||
}
|
||||
|
||||
// Start signals transmitter.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package wallet
|
||||
package token
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/status-im/status-go/contracts"
|
||||
"github.com/status-im/status-go/contracts/ierc20"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/rpc"
|
||||
"github.com/status-im/status-go/rpc/network"
|
||||
"github.com/status-im/status-go/services/wallet/async"
|
||||
|
@ -35,6 +36,10 @@ type Token struct {
|
|||
ChainID uint64 `json:"chainId"`
|
||||
}
|
||||
|
||||
func (t *Token) IsNative() bool {
|
||||
return t.Address == nativeChainAddress
|
||||
}
|
||||
|
||||
type TokenManager struct {
|
||||
db *sql.DB
|
||||
RPCClient *rpc.Client
|
||||
|
@ -70,7 +75,34 @@ func NewTokenManager(
|
|||
return tokenManager
|
||||
}
|
||||
|
||||
func (tm *TokenManager) findSNT(chainID uint64) *Token {
|
||||
func (tm *TokenManager) FindToken(network *params.Network, tokenSymbol string) *Token {
|
||||
if tokenSymbol == network.NativeCurrencySymbol {
|
||||
return &Token{
|
||||
Address: common.HexToAddress("0x"),
|
||||
Symbol: network.NativeCurrencySymbol,
|
||||
Decimals: uint(network.NativeCurrencyDecimals),
|
||||
Name: network.NativeCurrencyName,
|
||||
}
|
||||
}
|
||||
|
||||
tokens, err := tm.GetTokens(network.ChainID)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
customTokens, err := tm.GetCustomsByChainID(network.ChainID)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
allTokens := append(tokens, customTokens...)
|
||||
for _, token := range allTokens {
|
||||
if token.Symbol == tokenSymbol {
|
||||
return token
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tm *TokenManager) FindSNT(chainID uint64) *Token {
|
||||
tokensMap, ok := tokenStore[chainID]
|
||||
if !ok {
|
||||
return nil
|
||||
|
@ -85,7 +117,7 @@ func (tm *TokenManager) findSNT(chainID uint64) *Token {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (tm *TokenManager) getTokens(chainID uint64) ([]*Token, error) {
|
||||
func (tm *TokenManager) GetTokens(chainID uint64) ([]*Token, error) {
|
||||
tokensMap, ok := tokenStore[chainID]
|
||||
if !ok {
|
||||
return nil, errors.New("no tokens for this network")
|
||||
|
@ -100,7 +132,7 @@ func (tm *TokenManager) getTokens(chainID uint64) ([]*Token, error) {
|
|||
return res, nil
|
||||
}
|
||||
|
||||
func (tm *TokenManager) discoverToken(ctx context.Context, chainID uint64, address common.Address) (*Token, error) {
|
||||
func (tm *TokenManager) DiscoverToken(ctx context.Context, chainID uint64, address common.Address) (*Token, error) {
|
||||
backend, err := tm.RPCClient.EthClient(chainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -139,7 +171,7 @@ func (tm *TokenManager) discoverToken(ctx context.Context, chainID uint64, addre
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (tm *TokenManager) getCustoms() ([]*Token, error) {
|
||||
func (tm *TokenManager) GetCustoms() ([]*Token, error) {
|
||||
rows, err := tm.db.Query("SELECT address, name, symbol, decimals, color, network_id FROM tokens")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -160,7 +192,7 @@ func (tm *TokenManager) getCustoms() ([]*Token, error) {
|
|||
return rst, nil
|
||||
}
|
||||
|
||||
func (tm *TokenManager) getCustomsByChainID(chainID uint64) ([]*Token, error) {
|
||||
func (tm *TokenManager) GetCustomsByChainID(chainID uint64) ([]*Token, error) {
|
||||
rows, err := tm.db.Query("SELECT address, name, symbol, decimals, color, network_id FROM tokens where network_id=?", chainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -181,7 +213,7 @@ func (tm *TokenManager) getCustomsByChainID(chainID uint64) ([]*Token, error) {
|
|||
return rst, nil
|
||||
}
|
||||
|
||||
func (tm *TokenManager) isTokenVisible(chainID uint64, address common.Address) (bool, error) {
|
||||
func (tm *TokenManager) IsTokenVisible(chainID uint64, address common.Address) (bool, error) {
|
||||
rows, err := tm.db.Query("SELECT chain_id, address FROM visible_tokens WHERE chain_id = ? AND address = ?", chainID, address)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
@ -191,8 +223,8 @@ func (tm *TokenManager) isTokenVisible(chainID uint64, address common.Address) (
|
|||
return rows.Next(), nil
|
||||
}
|
||||
|
||||
func (tm *TokenManager) toggle(chainID uint64, address common.Address) error {
|
||||
isVisible, err := tm.isTokenVisible(chainID, address)
|
||||
func (tm *TokenManager) Toggle(chainID uint64, address common.Address) error {
|
||||
isVisible, err := tm.IsTokenVisible(chainID, address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -212,8 +244,8 @@ func (tm *TokenManager) toggle(chainID uint64, address common.Address) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (tm *TokenManager) getVisible(chainIDs []uint64) (map[uint64][]*Token, error) {
|
||||
customTokens, err := tm.getCustoms()
|
||||
func (tm *TokenManager) GetVisible(chainIDs []uint64) (map[uint64][]*Token, error) {
|
||||
customTokens, err := tm.GetCustoms()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -271,7 +303,7 @@ func (tm *TokenManager) getVisible(chainIDs []uint64) (map[uint64][]*Token, erro
|
|||
|
||||
for _, chainID := range chainIDs {
|
||||
if len(rst[chainID]) == 1 {
|
||||
token := tm.findSNT(chainID)
|
||||
token := tm.FindSNT(chainID)
|
||||
if token != nil {
|
||||
rst[chainID] = append(rst[chainID], token)
|
||||
}
|
||||
|
@ -280,7 +312,7 @@ func (tm *TokenManager) getVisible(chainIDs []uint64) (map[uint64][]*Token, erro
|
|||
return rst, nil
|
||||
}
|
||||
|
||||
func (tm *TokenManager) upsertCustom(token Token) error {
|
||||
func (tm *TokenManager) UpsertCustom(token Token) error {
|
||||
insert, err := tm.db.Prepare("INSERT OR REPLACE INTO TOKENS (network_id, address, name, symbol, decimals, color) VALUES (?, ?, ?, ?, ?, ?)")
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -289,12 +321,12 @@ func (tm *TokenManager) upsertCustom(token Token) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (tm *TokenManager) deleteCustom(chainID uint64, address common.Address) error {
|
||||
func (tm *TokenManager) DeleteCustom(chainID uint64, address common.Address) error {
|
||||
_, err := tm.db.Exec(`DELETE FROM TOKENS WHERE address = ? and network_id = ?`, address, chainID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (tm *TokenManager) getTokenBalance(ctx context.Context, client *chain.Client, account common.Address, token common.Address) (*big.Int, error) {
|
||||
func (tm *TokenManager) GetTokenBalance(ctx context.Context, client *chain.Client, account common.Address, token common.Address) (*big.Int, error) {
|
||||
caller, err := ierc20.NewIERC20Caller(token, client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -305,19 +337,19 @@ func (tm *TokenManager) getTokenBalance(ctx context.Context, client *chain.Clien
|
|||
}, account)
|
||||
}
|
||||
|
||||
func (tm *TokenManager) getChainBalance(ctx context.Context, client *chain.Client, account common.Address) (*big.Int, error) {
|
||||
func (tm *TokenManager) GetChainBalance(ctx context.Context, client *chain.Client, account common.Address) (*big.Int, error) {
|
||||
return client.BalanceAt(ctx, account, nil)
|
||||
}
|
||||
|
||||
func (tm *TokenManager) getBalance(ctx context.Context, client *chain.Client, account common.Address, token common.Address) (*big.Int, error) {
|
||||
func (tm *TokenManager) GetBalance(ctx context.Context, client *chain.Client, account common.Address, token common.Address) (*big.Int, error) {
|
||||
if token == nativeChainAddress {
|
||||
return tm.getChainBalance(ctx, client, account)
|
||||
return tm.GetChainBalance(ctx, client, account)
|
||||
}
|
||||
|
||||
return tm.getTokenBalance(ctx, client, account, token)
|
||||
return tm.GetTokenBalance(ctx, client, account, token)
|
||||
}
|
||||
|
||||
func (tm *TokenManager) getBalances(parent context.Context, clients []*chain.Client, accounts, tokens []common.Address) (map[common.Address]map[common.Address]*hexutil.Big, error) {
|
||||
func (tm *TokenManager) GetBalances(parent context.Context, clients []*chain.Client, accounts, tokens []common.Address) (map[common.Address]map[common.Address]*hexutil.Big, error) {
|
||||
var (
|
||||
group = async.NewAtomicGroup(parent)
|
||||
mu sync.Mutex
|
||||
|
@ -421,7 +453,7 @@ func (tm *TokenManager) getBalances(parent context.Context, clients []*chain.Cli
|
|||
group.Add(func(parent context.Context) error {
|
||||
ctx, cancel := context.WithTimeout(parent, requestTimeout)
|
||||
defer cancel()
|
||||
balance, err := tm.getBalance(ctx, client, account, token)
|
||||
balance, err := tm.GetBalance(ctx, client, account, token)
|
||||
|
||||
if err != nil {
|
||||
log.Error("can't fetch erc20 token balance", "account", account, "token", token, "error", err)
|
|
@ -1,4 +1,4 @@
|
|||
package wallet
|
||||
package token
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
|
@ -1,4 +1,4 @@
|
|||
package wallet
|
||||
package token
|
||||
|
||||
import "github.com/ethereum/go-ethereum/common"
|
||||
|
||||
|
@ -1450,6 +1450,14 @@ var tokenStore = map[uint64]map[common.Address]*Token{
|
|||
Decimals: 18,
|
||||
ChainID: 5,
|
||||
},
|
||||
common.HexToAddress("0x98339d8c260052b7ad81c28c16c0b98420f2b46a"): &Token{
|
||||
Address: common.HexToAddress("0x98339d8c260052b7ad81c28c16c0b98420f2b46a"),
|
||||
Name: "USD Coin",
|
||||
Symbol: "USDC",
|
||||
Color: "#f8f8f8",
|
||||
Decimals: 6,
|
||||
ChainID: 5,
|
||||
},
|
||||
common.HexToAddress("0x022e292b44b5a146f2e8ee36ff44d3dd863c915c"): &Token{
|
||||
Address: common.HexToAddress("0x022e292b44b5a146f2e8ee36ff44d3dd863c915c"),
|
||||
Name: "Xeenus 💪",
|
||||
|
@ -1475,6 +1483,16 @@ var tokenStore = map[uint64]map[common.Address]*Token{
|
|||
ChainID: 5,
|
||||
},
|
||||
},
|
||||
10: {
|
||||
common.HexToAddress("0x7f5c764cbc14f9669b88837ca1490cca17c31607"): &Token{
|
||||
Address: common.HexToAddress("0x7f5c764cbc14f9669b88837ca1490cca17c31607"),
|
||||
Name: "USD Coin",
|
||||
Symbol: "USDC",
|
||||
Color: "#f8f8f8",
|
||||
Decimals: 6,
|
||||
ChainID: 10,
|
||||
},
|
||||
},
|
||||
100: {
|
||||
common.HexToAddress("0x3e50bf6703fc132a94e4baff068db2055655f11b"): &Token{
|
||||
Address: common.HexToAddress("0x3e50bf6703fc132a94e4baff068db2055655f11b"),
|
||||
|
@ -1485,6 +1503,16 @@ var tokenStore = map[uint64]map[common.Address]*Token{
|
|||
ChainID: 100,
|
||||
},
|
||||
},
|
||||
420: {
|
||||
common.HexToAddress("0xcb4ceefce514b2d910d3ac529076d18e3add3775"): &Token{
|
||||
Address: common.HexToAddress("0xcb4ceefce514b2d910d3ac529076d18e3add3775"),
|
||||
Name: "USD Coin",
|
||||
Symbol: "USDC",
|
||||
Color: "#f8f8f8",
|
||||
Decimals: 6,
|
||||
ChainID: 420,
|
||||
},
|
||||
},
|
||||
42161: {
|
||||
common.HexToAddress("0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9"): &Token{
|
||||
Address: common.HexToAddress("0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9"),
|
||||
|
@ -1499,7 +1527,7 @@ var tokenStore = map[uint64]map[common.Address]*Token{
|
|||
Name: "USD Coin",
|
||||
Symbol: "USDC",
|
||||
Color: "#f8f8f8",
|
||||
Decimals: 18,
|
||||
Decimals: 6,
|
||||
ChainID: 42161,
|
||||
},
|
||||
common.HexToAddress("0xda10009cbd5d07dd0cecc66161fc93d7c9000da1"): &Token{
|
||||
|
@ -1575,14 +1603,14 @@ var tokenStore = map[uint64]map[common.Address]*Token{
|
|||
ChainID: 42161,
|
||||
},
|
||||
},
|
||||
421611: {
|
||||
common.HexToAddress("0x615fbe6372676474d9e6933d310469c9b68e9726"): &Token{
|
||||
Address: common.HexToAddress("0x615fbe6372676474d9e6933d310469c9b68e9726"),
|
||||
Name: "ChainLink Token",
|
||||
Symbol: "Link",
|
||||
421613: {
|
||||
common.HexToAddress("0x17078F231AA8dc256557b49a8f2F72814A71f633"): &Token{
|
||||
Address: common.HexToAddress("0x17078F231AA8dc256557b49a8f2F72814A71f633"),
|
||||
Name: "USD Coin",
|
||||
Symbol: "USDC",
|
||||
Color: "#f8f8f8",
|
||||
Decimals: 18,
|
||||
ChainID: 421611,
|
||||
Decimals: 6,
|
||||
ChainID: 421613,
|
||||
},
|
||||
},
|
||||
}
|
|
@ -18,6 +18,7 @@ import (
|
|||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/services/wallet/async"
|
||||
"github.com/status-im/status-go/services/wallet/bigint"
|
||||
"github.com/status-im/status-go/services/wallet/bridge"
|
||||
"github.com/status-im/status-go/services/wallet/chain"
|
||||
"github.com/status-im/status-go/transactions"
|
||||
)
|
||||
|
@ -228,7 +229,7 @@ func (tm *TransactionManager) watch(ctx context.Context, transactionHash common.
|
|||
return watchTxCommand.Command()(commandContext)
|
||||
}
|
||||
|
||||
func (tm *TransactionManager) createMultiTransaction(ctx context.Context, multiTransaction *MultiTransaction, data map[uint64][]transactions.SendTxArgs, password string) (*MultiTransactionResult, error) {
|
||||
func (tm *TransactionManager) createMultiTransaction(ctx context.Context, multiTransaction *MultiTransaction, data []*bridge.TransactionBridge, bridges map[string]bridge.Bridge, password string) (*MultiTransactionResult, error) {
|
||||
selectedAccount, err := tm.getVerifiedWalletAccount(multiTransaction.FromAddress.Hex(), password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -260,29 +261,26 @@ func (tm *TransactionManager) createMultiTransaction(ctx context.Context, multiT
|
|||
}
|
||||
|
||||
hashes := make(map[uint64][]types.Hash)
|
||||
for chainID, txs := range data {
|
||||
for _, tx := range txs {
|
||||
hash, err := tm.transactor.SendTransactionWithChainID(chainID, tx, selectedAccount)
|
||||
for _, tx := range data {
|
||||
hash, err := bridges[tx.BridgeName].Send(tx, selectedAccount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = tm.addPending(PendingTransaction{
|
||||
Hash: common.Hash(hash),
|
||||
Timestamp: uint64(time.Now().Unix()),
|
||||
Value: bigint.BigInt{tx.Value.ToInt()},
|
||||
From: common.Address(tx.From),
|
||||
To: common.Address(*tx.To),
|
||||
Data: tx.Data.String(),
|
||||
Value: bigint.BigInt{tx.Value()},
|
||||
From: common.Address(tx.From()),
|
||||
To: common.Address(tx.To()),
|
||||
Data: tx.Data().String(),
|
||||
Type: WalletTransfer,
|
||||
ChainID: chainID,
|
||||
ChainID: tx.ChainID,
|
||||
MultiTransactionID: multiTransactionID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hashes[chainID] = append(hashes[chainID], hash)
|
||||
}
|
||||
hashes[tx.ChainID] = append(hashes[tx.ChainID], hash)
|
||||
}
|
||||
|
||||
return &MultiTransactionResult{
|
||||
|
|
|
@ -0,0 +1,462 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
type fallbackError struct {
|
||||
}
|
||||
|
||||
var fallbackErrorMsg = "missing trie node 0000000000000000000000000000000000000000000000000000000000000000 (path ) <nil>"
|
||||
var fallbackErrorCode = -32000
|
||||
|
||||
func SetFallbackError(msg string, code int) {
|
||||
fallbackErrorMsg = msg
|
||||
fallbackErrorCode = code
|
||||
log.Debug("setting fallback error", "msg", msg, "code", code)
|
||||
}
|
||||
|
||||
func (f fallbackError) ErrorCode() int { return fallbackErrorCode }
|
||||
func (f fallbackError) Error() string { return fallbackErrorMsg }
|
||||
|
||||
var ErrUseFallback = fallbackError{}
|
||||
|
||||
type FallbackClient interface {
|
||||
CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error
|
||||
}
|
||||
|
||||
var bigZero = big.NewInt(0)
|
||||
|
||||
func (tx *LegacyTx) isFake() bool { return false }
|
||||
func (tx *AccessListTx) isFake() bool { return false }
|
||||
func (tx *DynamicFeeTx) isFake() bool { return false }
|
||||
|
||||
type ArbitrumUnsignedTx struct {
|
||||
ChainId *big.Int
|
||||
From common.Address
|
||||
|
||||
Nonce uint64 // nonce of sender account
|
||||
GasFeeCap *big.Int // wei per gas
|
||||
Gas uint64 // gas limit
|
||||
To *common.Address `rlp:"nil"` // nil means contract creation
|
||||
Value *big.Int // wei amount
|
||||
Data []byte // contract invocation input data
|
||||
}
|
||||
|
||||
func (tx *ArbitrumUnsignedTx) txType() byte { return ArbitrumUnsignedTxType }
|
||||
|
||||
func (tx *ArbitrumUnsignedTx) copy() TxData {
|
||||
cpy := &ArbitrumUnsignedTx{
|
||||
ChainId: new(big.Int),
|
||||
Nonce: tx.Nonce,
|
||||
GasFeeCap: new(big.Int),
|
||||
Gas: tx.Gas,
|
||||
From: tx.From,
|
||||
To: nil,
|
||||
Value: new(big.Int),
|
||||
Data: common.CopyBytes(tx.Data),
|
||||
}
|
||||
if tx.ChainId != nil {
|
||||
cpy.ChainId.Set(tx.ChainId)
|
||||
}
|
||||
if tx.GasFeeCap != nil {
|
||||
cpy.GasFeeCap.Set(tx.GasFeeCap)
|
||||
}
|
||||
if tx.To != nil {
|
||||
tmp := *tx.To
|
||||
cpy.To = &tmp
|
||||
}
|
||||
if tx.Value != nil {
|
||||
cpy.Value.Set(tx.Value)
|
||||
}
|
||||
return cpy
|
||||
}
|
||||
|
||||
func (tx *ArbitrumUnsignedTx) chainID() *big.Int { return tx.ChainId }
|
||||
func (tx *ArbitrumUnsignedTx) accessList() AccessList { return nil }
|
||||
func (tx *ArbitrumUnsignedTx) data() []byte { return tx.Data }
|
||||
func (tx *ArbitrumUnsignedTx) gas() uint64 { return tx.Gas }
|
||||
func (tx *ArbitrumUnsignedTx) gasPrice() *big.Int { return tx.GasFeeCap }
|
||||
func (tx *ArbitrumUnsignedTx) gasTipCap() *big.Int { return bigZero }
|
||||
func (tx *ArbitrumUnsignedTx) gasFeeCap() *big.Int { return tx.GasFeeCap }
|
||||
func (tx *ArbitrumUnsignedTx) value() *big.Int { return tx.Value }
|
||||
func (tx *ArbitrumUnsignedTx) nonce() uint64 { return tx.Nonce }
|
||||
func (tx *ArbitrumUnsignedTx) to() *common.Address { return tx.To }
|
||||
func (tx *ArbitrumUnsignedTx) isFake() bool { return false }
|
||||
|
||||
func (tx *ArbitrumUnsignedTx) rawSignatureValues() (v, r, s *big.Int) {
|
||||
return bigZero, bigZero, bigZero
|
||||
}
|
||||
|
||||
func (tx *ArbitrumUnsignedTx) setSignatureValues(chainID, v, r, s *big.Int) {
|
||||
|
||||
}
|
||||
|
||||
type ArbitrumContractTx struct {
|
||||
ChainId *big.Int
|
||||
RequestId common.Hash
|
||||
From common.Address
|
||||
|
||||
GasFeeCap *big.Int // wei per gas
|
||||
Gas uint64 // gas limit
|
||||
To *common.Address `rlp:"nil"` // nil means contract creation
|
||||
Value *big.Int // wei amount
|
||||
Data []byte // contract invocation input data
|
||||
}
|
||||
|
||||
func (tx *ArbitrumContractTx) txType() byte { return ArbitrumContractTxType }
|
||||
|
||||
func (tx *ArbitrumContractTx) copy() TxData {
|
||||
cpy := &ArbitrumContractTx{
|
||||
ChainId: new(big.Int),
|
||||
RequestId: tx.RequestId,
|
||||
GasFeeCap: new(big.Int),
|
||||
Gas: tx.Gas,
|
||||
From: tx.From,
|
||||
To: nil,
|
||||
Value: new(big.Int),
|
||||
Data: common.CopyBytes(tx.Data),
|
||||
}
|
||||
if tx.ChainId != nil {
|
||||
cpy.ChainId.Set(tx.ChainId)
|
||||
}
|
||||
if tx.GasFeeCap != nil {
|
||||
cpy.GasFeeCap.Set(tx.GasFeeCap)
|
||||
}
|
||||
if tx.To != nil {
|
||||
tmp := *tx.To
|
||||
cpy.To = &tmp
|
||||
}
|
||||
if tx.Value != nil {
|
||||
cpy.Value.Set(tx.Value)
|
||||
}
|
||||
return cpy
|
||||
}
|
||||
|
||||
func (tx *ArbitrumContractTx) chainID() *big.Int { return tx.ChainId }
|
||||
func (tx *ArbitrumContractTx) accessList() AccessList { return nil }
|
||||
func (tx *ArbitrumContractTx) data() []byte { return tx.Data }
|
||||
func (tx *ArbitrumContractTx) gas() uint64 { return tx.Gas }
|
||||
func (tx *ArbitrumContractTx) gasPrice() *big.Int { return tx.GasFeeCap }
|
||||
func (tx *ArbitrumContractTx) gasTipCap() *big.Int { return bigZero }
|
||||
func (tx *ArbitrumContractTx) gasFeeCap() *big.Int { return tx.GasFeeCap }
|
||||
func (tx *ArbitrumContractTx) value() *big.Int { return tx.Value }
|
||||
func (tx *ArbitrumContractTx) nonce() uint64 { return 0 }
|
||||
func (tx *ArbitrumContractTx) to() *common.Address { return tx.To }
|
||||
func (tx *ArbitrumContractTx) rawSignatureValues() (v, r, s *big.Int) {
|
||||
return bigZero, bigZero, bigZero
|
||||
}
|
||||
func (tx *ArbitrumContractTx) setSignatureValues(chainID, v, r, s *big.Int) {}
|
||||
func (tx *ArbitrumContractTx) isFake() bool { return true }
|
||||
|
||||
type ArbitrumRetryTx struct {
|
||||
ChainId *big.Int
|
||||
Nonce uint64
|
||||
From common.Address
|
||||
|
||||
GasFeeCap *big.Int // wei per gas
|
||||
Gas uint64 // gas limit
|
||||
To *common.Address `rlp:"nil"` // nil means contract creation
|
||||
Value *big.Int // wei amount
|
||||
Data []byte // contract invocation input data
|
||||
TicketId common.Hash
|
||||
RefundTo common.Address
|
||||
MaxRefund *big.Int // the maximum refund sent to RefundTo (the rest goes to From)
|
||||
SubmissionFeeRefund *big.Int // the submission fee to refund if successful (capped by MaxRefund)
|
||||
}
|
||||
|
||||
func (tx *ArbitrumRetryTx) txType() byte { return ArbitrumRetryTxType }
|
||||
|
||||
func (tx *ArbitrumRetryTx) copy() TxData {
|
||||
cpy := &ArbitrumRetryTx{
|
||||
ChainId: new(big.Int),
|
||||
Nonce: tx.Nonce,
|
||||
GasFeeCap: new(big.Int),
|
||||
Gas: tx.Gas,
|
||||
From: tx.From,
|
||||
To: nil,
|
||||
Value: new(big.Int),
|
||||
Data: common.CopyBytes(tx.Data),
|
||||
TicketId: tx.TicketId,
|
||||
RefundTo: tx.RefundTo,
|
||||
MaxRefund: new(big.Int),
|
||||
SubmissionFeeRefund: new(big.Int),
|
||||
}
|
||||
if tx.ChainId != nil {
|
||||
cpy.ChainId.Set(tx.ChainId)
|
||||
}
|
||||
if tx.GasFeeCap != nil {
|
||||
cpy.GasFeeCap.Set(tx.GasFeeCap)
|
||||
}
|
||||
if tx.To != nil {
|
||||
tmp := *tx.To
|
||||
cpy.To = &tmp
|
||||
}
|
||||
if tx.Value != nil {
|
||||
cpy.Value.Set(tx.Value)
|
||||
}
|
||||
if tx.MaxRefund != nil {
|
||||
cpy.MaxRefund.Set(tx.MaxRefund)
|
||||
}
|
||||
if tx.SubmissionFeeRefund != nil {
|
||||
cpy.SubmissionFeeRefund.Set(tx.SubmissionFeeRefund)
|
||||
}
|
||||
return cpy
|
||||
}
|
||||
|
||||
func (tx *ArbitrumRetryTx) chainID() *big.Int { return tx.ChainId }
|
||||
func (tx *ArbitrumRetryTx) accessList() AccessList { return nil }
|
||||
func (tx *ArbitrumRetryTx) data() []byte { return tx.Data }
|
||||
func (tx *ArbitrumRetryTx) gas() uint64 { return tx.Gas }
|
||||
func (tx *ArbitrumRetryTx) gasPrice() *big.Int { return tx.GasFeeCap }
|
||||
func (tx *ArbitrumRetryTx) gasTipCap() *big.Int { return bigZero }
|
||||
func (tx *ArbitrumRetryTx) gasFeeCap() *big.Int { return tx.GasFeeCap }
|
||||
func (tx *ArbitrumRetryTx) value() *big.Int { return tx.Value }
|
||||
func (tx *ArbitrumRetryTx) nonce() uint64 { return tx.Nonce }
|
||||
func (tx *ArbitrumRetryTx) to() *common.Address { return tx.To }
|
||||
func (tx *ArbitrumRetryTx) rawSignatureValues() (v, r, s *big.Int) {
|
||||
return bigZero, bigZero, bigZero
|
||||
}
|
||||
func (tx *ArbitrumRetryTx) setSignatureValues(chainID, v, r, s *big.Int) {}
|
||||
func (tx *ArbitrumRetryTx) isFake() bool { return true }
|
||||
|
||||
type ArbitrumSubmitRetryableTx struct {
|
||||
ChainId *big.Int
|
||||
RequestId common.Hash
|
||||
From common.Address
|
||||
L1BaseFee *big.Int
|
||||
|
||||
DepositValue *big.Int
|
||||
GasFeeCap *big.Int // wei per gas
|
||||
Gas uint64 // gas limit
|
||||
RetryTo *common.Address `rlp:"nil"` // nil means contract creation
|
||||
RetryValue *big.Int // wei amount
|
||||
Beneficiary common.Address
|
||||
MaxSubmissionFee *big.Int
|
||||
FeeRefundAddr common.Address
|
||||
RetryData []byte // contract invocation input data
|
||||
}
|
||||
|
||||
func (tx *ArbitrumSubmitRetryableTx) txType() byte { return ArbitrumSubmitRetryableTxType }
|
||||
|
||||
func (tx *ArbitrumSubmitRetryableTx) copy() TxData {
|
||||
cpy := &ArbitrumSubmitRetryableTx{
|
||||
ChainId: new(big.Int),
|
||||
RequestId: tx.RequestId,
|
||||
DepositValue: new(big.Int),
|
||||
L1BaseFee: new(big.Int),
|
||||
GasFeeCap: new(big.Int),
|
||||
Gas: tx.Gas,
|
||||
From: tx.From,
|
||||
RetryTo: tx.RetryTo,
|
||||
RetryValue: new(big.Int),
|
||||
Beneficiary: tx.Beneficiary,
|
||||
MaxSubmissionFee: new(big.Int),
|
||||
FeeRefundAddr: tx.FeeRefundAddr,
|
||||
RetryData: common.CopyBytes(tx.RetryData),
|
||||
}
|
||||
if tx.ChainId != nil {
|
||||
cpy.ChainId.Set(tx.ChainId)
|
||||
}
|
||||
if tx.DepositValue != nil {
|
||||
cpy.DepositValue.Set(tx.DepositValue)
|
||||
}
|
||||
if tx.L1BaseFee != nil {
|
||||
cpy.L1BaseFee.Set(tx.L1BaseFee)
|
||||
}
|
||||
if tx.GasFeeCap != nil {
|
||||
cpy.GasFeeCap.Set(tx.GasFeeCap)
|
||||
}
|
||||
if tx.RetryTo != nil {
|
||||
tmp := *tx.RetryTo
|
||||
cpy.RetryTo = &tmp
|
||||
}
|
||||
if tx.RetryValue != nil {
|
||||
cpy.RetryValue.Set(tx.RetryValue)
|
||||
}
|
||||
if tx.MaxSubmissionFee != nil {
|
||||
cpy.MaxSubmissionFee.Set(tx.MaxSubmissionFee)
|
||||
}
|
||||
return cpy
|
||||
}
|
||||
|
||||
func (tx *ArbitrumSubmitRetryableTx) chainID() *big.Int { return tx.ChainId }
|
||||
func (tx *ArbitrumSubmitRetryableTx) accessList() AccessList { return nil }
|
||||
func (tx *ArbitrumSubmitRetryableTx) gas() uint64 { return tx.Gas }
|
||||
func (tx *ArbitrumSubmitRetryableTx) gasPrice() *big.Int { return tx.GasFeeCap }
|
||||
func (tx *ArbitrumSubmitRetryableTx) gasTipCap() *big.Int { return big.NewInt(0) }
|
||||
func (tx *ArbitrumSubmitRetryableTx) gasFeeCap() *big.Int { return tx.GasFeeCap }
|
||||
func (tx *ArbitrumSubmitRetryableTx) value() *big.Int { return common.Big0 }
|
||||
func (tx *ArbitrumSubmitRetryableTx) nonce() uint64 { return 0 }
|
||||
func (tx *ArbitrumSubmitRetryableTx) to() *common.Address { return &ArbRetryableTxAddress }
|
||||
func (tx *ArbitrumSubmitRetryableTx) rawSignatureValues() (v, r, s *big.Int) {
|
||||
return bigZero, bigZero, bigZero
|
||||
}
|
||||
func (tx *ArbitrumSubmitRetryableTx) setSignatureValues(chainID, v, r, s *big.Int) {}
|
||||
func (tx *ArbitrumSubmitRetryableTx) isFake() bool { return true }
|
||||
|
||||
func (tx *ArbitrumSubmitRetryableTx) data() []byte {
|
||||
var retryTo common.Address
|
||||
if tx.RetryTo != nil {
|
||||
retryTo = *tx.RetryTo
|
||||
}
|
||||
data := make([]byte, 0)
|
||||
data = append(data, tx.RequestId.Bytes()...)
|
||||
data = append(data, math.U256Bytes(tx.L1BaseFee)...)
|
||||
data = append(data, math.U256Bytes(tx.DepositValue)...)
|
||||
data = append(data, math.U256Bytes(tx.RetryValue)...)
|
||||
data = append(data, math.U256Bytes(tx.GasFeeCap)...)
|
||||
data = append(data, math.U256Bytes(new(big.Int).SetUint64(tx.Gas))...)
|
||||
data = append(data, math.U256Bytes(tx.MaxSubmissionFee)...)
|
||||
data = append(data, make([]byte, 12)...)
|
||||
data = append(data, tx.FeeRefundAddr.Bytes()...)
|
||||
data = append(data, make([]byte, 12)...)
|
||||
data = append(data, tx.Beneficiary.Bytes()...)
|
||||
data = append(data, make([]byte, 12)...)
|
||||
data = append(data, retryTo.Bytes()...)
|
||||
offset := len(data) + 32
|
||||
data = append(data, math.U256Bytes(big.NewInt(int64(offset)))...)
|
||||
data = append(data, math.U256Bytes(big.NewInt(int64(len(tx.RetryData))))...)
|
||||
data = append(data, tx.RetryData...)
|
||||
extra := len(tx.RetryData) % 32
|
||||
if extra > 0 {
|
||||
data = append(data, make([]byte, 32-extra)...)
|
||||
}
|
||||
data = append(hexutil.MustDecode("0xc9f95d32"), data...)
|
||||
return data
|
||||
}
|
||||
|
||||
type ArbitrumDepositTx struct {
|
||||
ChainId *big.Int
|
||||
L1RequestId common.Hash
|
||||
From common.Address
|
||||
To common.Address
|
||||
Value *big.Int
|
||||
}
|
||||
|
||||
func (d *ArbitrumDepositTx) txType() byte {
|
||||
return ArbitrumDepositTxType
|
||||
}
|
||||
|
||||
func (d *ArbitrumDepositTx) copy() TxData {
|
||||
tx := &ArbitrumDepositTx{
|
||||
ChainId: new(big.Int),
|
||||
L1RequestId: d.L1RequestId,
|
||||
From: d.From,
|
||||
To: d.To,
|
||||
Value: new(big.Int),
|
||||
}
|
||||
if d.ChainId != nil {
|
||||
tx.ChainId.Set(d.ChainId)
|
||||
}
|
||||
if d.Value != nil {
|
||||
tx.Value.Set(d.Value)
|
||||
}
|
||||
return tx
|
||||
}
|
||||
|
||||
func (d *ArbitrumDepositTx) chainID() *big.Int { return d.ChainId }
|
||||
func (d *ArbitrumDepositTx) accessList() AccessList { return nil }
|
||||
func (d *ArbitrumDepositTx) data() []byte { return nil }
|
||||
func (d *ArbitrumDepositTx) gas() uint64 { return 0 }
|
||||
func (d *ArbitrumDepositTx) gasPrice() *big.Int { return bigZero }
|
||||
func (d *ArbitrumDepositTx) gasTipCap() *big.Int { return bigZero }
|
||||
func (d *ArbitrumDepositTx) gasFeeCap() *big.Int { return bigZero }
|
||||
func (d *ArbitrumDepositTx) value() *big.Int { return d.Value }
|
||||
func (d *ArbitrumDepositTx) nonce() uint64 { return 0 }
|
||||
func (d *ArbitrumDepositTx) to() *common.Address { return &d.To }
|
||||
func (d *ArbitrumDepositTx) isFake() bool { return true }
|
||||
|
||||
func (d *ArbitrumDepositTx) rawSignatureValues() (v, r, s *big.Int) {
|
||||
return bigZero, bigZero, bigZero
|
||||
}
|
||||
|
||||
func (d *ArbitrumDepositTx) setSignatureValues(chainID, v, r, s *big.Int) {
|
||||
|
||||
}
|
||||
|
||||
type ArbitrumInternalTx struct {
|
||||
ChainId *big.Int
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func (t *ArbitrumInternalTx) txType() byte {
|
||||
return ArbitrumInternalTxType
|
||||
}
|
||||
|
||||
func (t *ArbitrumInternalTx) copy() TxData {
|
||||
return &ArbitrumInternalTx{
|
||||
new(big.Int).Set(t.ChainId),
|
||||
common.CopyBytes(t.Data),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *ArbitrumInternalTx) chainID() *big.Int { return t.ChainId }
|
||||
func (t *ArbitrumInternalTx) accessList() AccessList { return nil }
|
||||
func (t *ArbitrumInternalTx) data() []byte { return t.Data }
|
||||
func (t *ArbitrumInternalTx) gas() uint64 { return 0 }
|
||||
func (t *ArbitrumInternalTx) gasPrice() *big.Int { return bigZero }
|
||||
func (t *ArbitrumInternalTx) gasTipCap() *big.Int { return bigZero }
|
||||
func (t *ArbitrumInternalTx) gasFeeCap() *big.Int { return bigZero }
|
||||
func (t *ArbitrumInternalTx) value() *big.Int { return common.Big0 }
|
||||
func (t *ArbitrumInternalTx) nonce() uint64 { return 0 }
|
||||
func (t *ArbitrumInternalTx) to() *common.Address { return &ArbosAddress }
|
||||
func (t *ArbitrumInternalTx) isFake() bool { return true }
|
||||
|
||||
func (d *ArbitrumInternalTx) rawSignatureValues() (v, r, s *big.Int) {
|
||||
return bigZero, bigZero, bigZero
|
||||
}
|
||||
|
||||
func (d *ArbitrumInternalTx) setSignatureValues(chainID, v, r, s *big.Int) {
|
||||
|
||||
}
|
||||
|
||||
type HeaderInfo struct {
|
||||
SendRoot common.Hash
|
||||
SendCount uint64
|
||||
L1BlockNumber uint64
|
||||
ArbOSFormatVersion uint64
|
||||
}
|
||||
|
||||
func (info HeaderInfo) extra() []byte {
|
||||
return info.SendRoot[:]
|
||||
}
|
||||
|
||||
func (info HeaderInfo) mixDigest() [32]byte {
|
||||
mixDigest := common.Hash{}
|
||||
binary.BigEndian.PutUint64(mixDigest[:8], info.SendCount)
|
||||
binary.BigEndian.PutUint64(mixDigest[8:16], info.L1BlockNumber)
|
||||
binary.BigEndian.PutUint64(mixDigest[16:24], info.ArbOSFormatVersion)
|
||||
return mixDigest
|
||||
}
|
||||
|
||||
func (info HeaderInfo) UpdateHeaderWithInfo(header *Header) {
|
||||
header.MixDigest = info.mixDigest()
|
||||
header.Extra = info.extra()
|
||||
}
|
||||
|
||||
func DeserializeHeaderExtraInformation(header *Header) (HeaderInfo, error) {
|
||||
if header.BaseFee == nil || header.BaseFee.Sign() == 0 || len(header.Extra) == 0 {
|
||||
// imported blocks have no base fee
|
||||
// The genesis block doesn't have an ArbOS encoded extra field
|
||||
return HeaderInfo{}, nil
|
||||
}
|
||||
if len(header.Extra) != 32 {
|
||||
return HeaderInfo{}, fmt.Errorf("unexpected header extra field length %v", len(header.Extra))
|
||||
}
|
||||
extra := HeaderInfo{}
|
||||
copy(extra.SendRoot[:], header.Extra)
|
||||
extra.SendCount = binary.BigEndian.Uint64(header.MixDigest[:8])
|
||||
extra.L1BlockNumber = binary.BigEndian.Uint64(header.MixDigest[8:16])
|
||||
extra.ArbOSFormatVersion = binary.BigEndian.Uint64(header.MixDigest[16:24])
|
||||
return extra, nil
|
||||
}
|
54
vendor/github.com/ethereum/go-ethereum/core/types/arbitrum_legacy_tx.go
generated
vendored
Normal file
54
vendor/github.com/ethereum/go-ethereum/core/types/arbitrum_legacy_tx.go
generated
vendored
Normal file
|
@ -0,0 +1,54 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
type ArbitrumLegacyTxData struct {
|
||||
LegacyTx
|
||||
HashOverride common.Hash // Hash cannot be locally computed from other fields
|
||||
EffectiveGasPrice uint64
|
||||
L1BlockNumber uint64
|
||||
Sender *common.Address `rlp:"optional,nil"` // only used in unsigned Txs
|
||||
}
|
||||
|
||||
func NewArbitrumLegacyTx(origTx *Transaction, hashOverride common.Hash, effectiveGas uint64, l1Block uint64, senderOverride *common.Address) (*Transaction, error) {
|
||||
if origTx.Type() != LegacyTxType {
|
||||
return nil, errors.New("attempt to arbitrum-wrap non-legacy transaction")
|
||||
}
|
||||
legacyPtr := origTx.GetInner().(*LegacyTx)
|
||||
inner := ArbitrumLegacyTxData{
|
||||
LegacyTx: *legacyPtr,
|
||||
HashOverride: hashOverride,
|
||||
EffectiveGasPrice: effectiveGas,
|
||||
L1BlockNumber: l1Block,
|
||||
Sender: senderOverride,
|
||||
}
|
||||
return NewTx(&inner), nil
|
||||
}
|
||||
|
||||
func (tx *ArbitrumLegacyTxData) copy() TxData {
|
||||
legacyCopy := tx.LegacyTx.copy().(*LegacyTx)
|
||||
var sender *common.Address
|
||||
if tx.Sender != nil {
|
||||
sender = new(common.Address)
|
||||
*sender = *tx.Sender
|
||||
}
|
||||
return &ArbitrumLegacyTxData{
|
||||
LegacyTx: *legacyCopy,
|
||||
HashOverride: tx.HashOverride,
|
||||
EffectiveGasPrice: tx.EffectiveGasPrice,
|
||||
L1BlockNumber: tx.L1BlockNumber,
|
||||
Sender: sender,
|
||||
}
|
||||
}
|
||||
|
||||
func (tx *ArbitrumLegacyTxData) txType() byte { return ArbitrumLegacyTxType }
|
||||
|
||||
func (tx *ArbitrumLegacyTxData) EncodeOnlyLegacyInto(w *bytes.Buffer) {
|
||||
rlp.Encode(w, tx.LegacyTx)
|
||||
}
|
84
vendor/github.com/ethereum/go-ethereum/core/types/arbitrum_signer.go
generated
vendored
Normal file
84
vendor/github.com/ethereum/go-ethereum/core/types/arbitrum_signer.go
generated
vendored
Normal file
|
@ -0,0 +1,84 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
var ArbosAddress = common.HexToAddress("0xa4b05")
|
||||
var ArbSysAddress = common.HexToAddress("0x64")
|
||||
var ArbGasInfoAddress = common.HexToAddress("0x6c")
|
||||
var ArbRetryableTxAddress = common.HexToAddress("0x6e")
|
||||
var NodeInterfaceAddress = common.HexToAddress("0xc8")
|
||||
var NodeInterfaceDebugAddress = common.HexToAddress("0xc9")
|
||||
|
||||
type arbitrumSigner struct{ Signer }
|
||||
|
||||
func NewArbitrumSigner(signer Signer) Signer {
|
||||
return arbitrumSigner{Signer: signer}
|
||||
}
|
||||
|
||||
func (s arbitrumSigner) Sender(tx *Transaction) (common.Address, error) {
|
||||
switch inner := tx.inner.(type) {
|
||||
case *ArbitrumUnsignedTx:
|
||||
return inner.From, nil
|
||||
case *ArbitrumContractTx:
|
||||
return inner.From, nil
|
||||
case *ArbitrumDepositTx:
|
||||
return inner.From, nil
|
||||
case *ArbitrumInternalTx:
|
||||
return ArbosAddress, nil
|
||||
case *ArbitrumRetryTx:
|
||||
return inner.From, nil
|
||||
case *ArbitrumSubmitRetryableTx:
|
||||
return inner.From, nil
|
||||
case *ArbitrumLegacyTxData:
|
||||
legacyData := tx.inner.(*ArbitrumLegacyTxData)
|
||||
if legacyData.Sender != nil {
|
||||
return *legacyData.Sender, nil
|
||||
}
|
||||
fakeTx := NewTx(&legacyData.LegacyTx)
|
||||
return s.Signer.Sender(fakeTx)
|
||||
default:
|
||||
return s.Signer.Sender(tx)
|
||||
}
|
||||
}
|
||||
|
||||
func (s arbitrumSigner) Equal(s2 Signer) bool {
|
||||
x, ok := s2.(arbitrumSigner)
|
||||
return ok && x.Signer.Equal(s.Signer)
|
||||
}
|
||||
|
||||
func (s arbitrumSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
|
||||
switch tx.inner.(type) {
|
||||
case *ArbitrumUnsignedTx:
|
||||
return bigZero, bigZero, bigZero, nil
|
||||
case *ArbitrumContractTx:
|
||||
return bigZero, bigZero, bigZero, nil
|
||||
case *ArbitrumDepositTx:
|
||||
return bigZero, bigZero, bigZero, nil
|
||||
case *ArbitrumInternalTx:
|
||||
return bigZero, bigZero, bigZero, nil
|
||||
case *ArbitrumRetryTx:
|
||||
return bigZero, bigZero, bigZero, nil
|
||||
case *ArbitrumSubmitRetryableTx:
|
||||
return bigZero, bigZero, bigZero, nil
|
||||
case *ArbitrumLegacyTxData:
|
||||
legacyData := tx.inner.(*ArbitrumLegacyTxData)
|
||||
fakeTx := NewTx(&legacyData.LegacyTx)
|
||||
return s.Signer.SignatureValues(fakeTx, sig)
|
||||
default:
|
||||
return s.Signer.SignatureValues(tx, sig)
|
||||
}
|
||||
}
|
||||
|
||||
// Hash returns the hash to be signed by the sender.
|
||||
// It does not uniquely identify the transaction.
|
||||
func (s arbitrumSigner) Hash(tx *Transaction) common.Hash {
|
||||
if legacyData, isArbLegacy := tx.inner.(*ArbitrumLegacyTxData); isArbLegacy {
|
||||
fakeTx := NewTx(&legacyData.LegacyTx)
|
||||
return s.Signer.Hash(fakeTx)
|
||||
}
|
||||
return s.Signer.Hash(tx)
|
||||
}
|
|
@ -37,7 +37,7 @@ var (
|
|||
ErrInvalidTxType = errors.New("transaction type not valid in this context")
|
||||
ErrTxTypeNotSupported = errors.New("transaction type not supported")
|
||||
ErrGasFeeCapTooLow = errors.New("fee cap less than base fee")
|
||||
errEmptyTypedTx = errors.New("empty typed transaction bytes")
|
||||
errShortTypedTx = errors.New("typed transaction too short")
|
||||
)
|
||||
|
||||
// Transaction types.
|
||||
|
@ -45,6 +45,13 @@ const (
|
|||
LegacyTxType = iota
|
||||
AccessListTxType
|
||||
DynamicFeeTxType
|
||||
ArbitrumDepositTxType = 100
|
||||
ArbitrumUnsignedTxType = 101
|
||||
ArbitrumContractTxType = 102
|
||||
ArbitrumRetryTxType = 104
|
||||
ArbitrumSubmitRetryableTxType = 105
|
||||
ArbitrumInternalTxType = 106
|
||||
ArbitrumLegacyTxType = 120
|
||||
)
|
||||
|
||||
// Transaction is an Ethereum transaction.
|
||||
|
@ -52,6 +59,9 @@ type Transaction struct {
|
|||
inner TxData // Consensus contents of a transaction
|
||||
time time.Time // Time first seen locally (spam avoidance)
|
||||
|
||||
// Arbitrum cache: must be atomically accessed
|
||||
CalldataUnits uint64
|
||||
|
||||
// caches
|
||||
hash atomic.Value
|
||||
size atomic.Value
|
||||
|
@ -85,6 +95,8 @@ type TxData interface {
|
|||
|
||||
rawSignatureValues() (v, r, s *big.Int)
|
||||
setSignatureValues(chainID, v, r, s *big.Int)
|
||||
|
||||
isFake() bool
|
||||
}
|
||||
|
||||
// EncodeRLP implements rlp.Encoder
|
||||
|
@ -134,19 +146,17 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
|
|||
tx.setDecoded(&inner, int(rlp.ListSize(size)))
|
||||
}
|
||||
return err
|
||||
case kind == rlp.String:
|
||||
default:
|
||||
// It's an EIP-2718 typed TX envelope.
|
||||
var b []byte
|
||||
if b, err = s.Bytes(); err != nil {
|
||||
return err
|
||||
}
|
||||
inner, err := tx.decodeTyped(b)
|
||||
inner, err := tx.decodeTyped(b, true)
|
||||
if err == nil {
|
||||
tx.setDecoded(inner, len(b))
|
||||
}
|
||||
return err
|
||||
default:
|
||||
return rlp.ErrExpectedList
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -164,7 +174,7 @@ func (tx *Transaction) UnmarshalBinary(b []byte) error {
|
|||
return nil
|
||||
}
|
||||
// It's an EIP2718 typed transaction envelope.
|
||||
inner, err := tx.decodeTyped(b)
|
||||
inner, err := tx.decodeTyped(b, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -173,9 +183,37 @@ func (tx *Transaction) UnmarshalBinary(b []byte) error {
|
|||
}
|
||||
|
||||
// decodeTyped decodes a typed transaction from the canonical format.
|
||||
func (tx *Transaction) decodeTyped(b []byte) (TxData, error) {
|
||||
if len(b) == 0 {
|
||||
return nil, errEmptyTypedTx
|
||||
func (tx *Transaction) decodeTyped(b []byte, arbParsing bool) (TxData, error) {
|
||||
if len(b) <= 1 {
|
||||
return nil, errShortTypedTx
|
||||
}
|
||||
if arbParsing {
|
||||
switch b[0] {
|
||||
case ArbitrumDepositTxType:
|
||||
var inner ArbitrumDepositTx
|
||||
err := rlp.DecodeBytes(b[1:], &inner)
|
||||
return &inner, err
|
||||
case ArbitrumInternalTxType:
|
||||
var inner ArbitrumInternalTx
|
||||
err := rlp.DecodeBytes(b[1:], &inner)
|
||||
return &inner, err
|
||||
case ArbitrumUnsignedTxType:
|
||||
var inner ArbitrumUnsignedTx
|
||||
err := rlp.DecodeBytes(b[1:], &inner)
|
||||
return &inner, err
|
||||
case ArbitrumContractTxType:
|
||||
var inner ArbitrumContractTx
|
||||
err := rlp.DecodeBytes(b[1:], &inner)
|
||||
return &inner, err
|
||||
case ArbitrumRetryTxType:
|
||||
var inner ArbitrumRetryTx
|
||||
err := rlp.DecodeBytes(b[1:], &inner)
|
||||
return &inner, err
|
||||
case ArbitrumSubmitRetryableTxType:
|
||||
var inner ArbitrumSubmitRetryableTx
|
||||
err := rlp.DecodeBytes(b[1:], &inner)
|
||||
return &inner, err
|
||||
}
|
||||
}
|
||||
switch b[0] {
|
||||
case AccessListTxType:
|
||||
|
@ -186,6 +224,10 @@ func (tx *Transaction) decodeTyped(b []byte) (TxData, error) {
|
|||
var inner DynamicFeeTx
|
||||
err := rlp.DecodeBytes(b[1:], &inner)
|
||||
return &inner, err
|
||||
case ArbitrumLegacyTxType:
|
||||
var inner ArbitrumLegacyTxData
|
||||
err := rlp.DecodeBytes(b[1:], &inner)
|
||||
return &inner, err
|
||||
default:
|
||||
return nil, ErrTxTypeNotSupported
|
||||
}
|
||||
|
@ -250,6 +292,10 @@ func (tx *Transaction) Type() uint8 {
|
|||
return tx.inner.txType()
|
||||
}
|
||||
|
||||
func (tx *Transaction) GetInner() TxData {
|
||||
return tx.inner.copy()
|
||||
}
|
||||
|
||||
// ChainId returns the EIP155 chain ID of the transaction. The return value will always be
|
||||
// non-nil. For legacy transactions which are not replay-protected, the return value is
|
||||
// zero.
|
||||
|
@ -284,13 +330,7 @@ func (tx *Transaction) Nonce() uint64 { return tx.inner.nonce() }
|
|||
// To returns the recipient address of the transaction.
|
||||
// For contract-creation transactions, To returns nil.
|
||||
func (tx *Transaction) To() *common.Address {
|
||||
// Copy the pointed-to address.
|
||||
ito := tx.inner.to()
|
||||
if ito == nil {
|
||||
return nil
|
||||
}
|
||||
cpy := *ito
|
||||
return &cpy
|
||||
return copyAddressPtr(tx.inner.to())
|
||||
}
|
||||
|
||||
// Cost returns gas * gasPrice + value.
|
||||
|
@ -373,6 +413,8 @@ func (tx *Transaction) Hash() common.Hash {
|
|||
var h common.Hash
|
||||
if tx.Type() == LegacyTxType {
|
||||
h = rlpHash(tx.inner)
|
||||
} else if tx.Type() == ArbitrumLegacyTxType {
|
||||
h = tx.inner.(*ArbitrumLegacyTxData).HashOverride
|
||||
} else {
|
||||
h = prefixedRlpHash(tx.Type(), tx.inner)
|
||||
}
|
||||
|
@ -417,6 +459,9 @@ func (s Transactions) EncodeIndex(i int, w *bytes.Buffer) {
|
|||
tx := s[i]
|
||||
if tx.Type() == LegacyTxType {
|
||||
rlp.Encode(w, tx.inner)
|
||||
} else if tx.Type() == ArbitrumLegacyTxType {
|
||||
arbData := tx.inner.(*ArbitrumLegacyTxData)
|
||||
arbData.EncodeOnlyLegacyInto(w)
|
||||
} else {
|
||||
tx.encodeTyped(w)
|
||||
}
|
||||
|
@ -440,6 +485,24 @@ func TxDifference(a, b Transactions) Transactions {
|
|||
return keep
|
||||
}
|
||||
|
||||
// HashDifference returns a new set which is the difference between a and b.
|
||||
func HashDifference(a, b []common.Hash) []common.Hash {
|
||||
keep := make([]common.Hash, 0, len(a))
|
||||
|
||||
remove := make(map[common.Hash]struct{})
|
||||
for _, hash := range b {
|
||||
remove[hash] = struct{}{}
|
||||
}
|
||||
|
||||
for _, hash := range a {
|
||||
if _, ok := remove[hash]; !ok {
|
||||
keep = append(keep, hash)
|
||||
}
|
||||
}
|
||||
|
||||
return keep
|
||||
}
|
||||
|
||||
// TxByNonce implements the sort interface to allow sorting a list of transactions
|
||||
// by their nonces. This is usually only useful for sorting transactions from a
|
||||
// single account, otherwise a nonce comparison doesn't make much sense.
|
||||
|
@ -569,6 +632,9 @@ func (t *TransactionsByPriceAndNonce) Pop() {
|
|||
//
|
||||
// NOTE: In a future PR this will be removed.
|
||||
type Message struct {
|
||||
tx *Transaction
|
||||
TxRunMode MessageRunMode
|
||||
|
||||
to *common.Address
|
||||
from common.Address
|
||||
nonce uint64
|
||||
|
@ -580,8 +646,17 @@ type Message struct {
|
|||
data []byte
|
||||
accessList AccessList
|
||||
checkNonce bool
|
||||
isFake bool
|
||||
}
|
||||
|
||||
type MessageRunMode uint8
|
||||
|
||||
const (
|
||||
MessageCommitMode MessageRunMode = iota
|
||||
MessageGasEstimationMode
|
||||
MessageEthcallMode
|
||||
)
|
||||
|
||||
func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice, gasFeeCap, gasTipCap *big.Int, data []byte, accessList AccessList, checkNonce bool) Message {
|
||||
return Message{
|
||||
from: from,
|
||||
|
@ -595,12 +670,15 @@ func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *b
|
|||
data: data,
|
||||
accessList: accessList,
|
||||
checkNonce: checkNonce,
|
||||
isFake: false,
|
||||
}
|
||||
}
|
||||
|
||||
// AsMessage returns the transaction as a core.Message.
|
||||
func (tx *Transaction) AsMessage(s Signer, baseFee *big.Int) (Message, error) {
|
||||
msg := Message{
|
||||
tx: tx,
|
||||
|
||||
nonce: tx.Nonce(),
|
||||
gasLimit: tx.Gas(),
|
||||
gasPrice: new(big.Int).Set(tx.GasPrice()),
|
||||
|
@ -611,6 +689,7 @@ func (tx *Transaction) AsMessage(s Signer, baseFee *big.Int) (Message, error) {
|
|||
data: tx.Data(),
|
||||
accessList: tx.AccessList(),
|
||||
checkNonce: true,
|
||||
isFake: tx.inner.isFake(),
|
||||
}
|
||||
// If baseFee provided, set gasPrice to effectiveGasPrice.
|
||||
if baseFee != nil {
|
||||
|
@ -621,6 +700,9 @@ func (tx *Transaction) AsMessage(s Signer, baseFee *big.Int) (Message, error) {
|
|||
return msg, err
|
||||
}
|
||||
|
||||
func (m Message) UnderlyingTransaction() *Transaction { return m.tx }
|
||||
func (m Message) RunMode() MessageRunMode { return m.TxRunMode }
|
||||
|
||||
func (m Message) From() common.Address { return m.from }
|
||||
func (m Message) To() *common.Address { return m.to }
|
||||
func (m Message) GasPrice() *big.Int { return m.gasPrice }
|
||||
|
@ -632,3 +714,13 @@ func (m Message) Nonce() uint64 { return m.nonce }
|
|||
func (m Message) Data() []byte { return m.data }
|
||||
func (m Message) AccessList() AccessList { return m.accessList }
|
||||
func (m Message) CheckNonce() bool { return m.checkNonce }
|
||||
func (m Message) IsFake() bool { return m.isFake }
|
||||
|
||||
// copyAddressPtr copies an address.
|
||||
func copyAddressPtr(a *common.Address) *common.Address {
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
cpy := *a
|
||||
return &cpy
|
||||
}
|
||||
|
|
|
@ -46,7 +46,24 @@ type txJSON struct {
|
|||
ChainID *hexutil.Big `json:"chainId,omitempty"`
|
||||
AccessList *AccessList `json:"accessList,omitempty"`
|
||||
|
||||
// Only used for encoding:
|
||||
// Arbitrum fields:
|
||||
From *common.Address `json:"from,omitempty"` // Contract SubmitRetryable Unsigned Retry
|
||||
RequestId *common.Hash `json:"requestId,omitempty"` // Contract SubmitRetryable Deposit
|
||||
TicketId *common.Hash `json:"ticketId,omitempty"` // Retry
|
||||
MaxRefund *hexutil.Big `json:"maxRefund,omitempty"` // Retry
|
||||
SubmissionFeeRefund *hexutil.Big `json:"submissionFeeRefund,omitempty"` // Retry
|
||||
RefundTo *common.Address `json:"refundTo,omitempty"` // SubmitRetryable Retry
|
||||
L1BaseFee *hexutil.Big `json:"l1BaseFee,omitempty"` // SubmitRetryable
|
||||
DepositValue *hexutil.Big `json:"depositValue,omitempty"` // SubmitRetryable
|
||||
RetryTo *common.Address `json:"retryTo,omitempty"` // SubmitRetryable
|
||||
RetryValue *hexutil.Big `json:"retryValue,omitempty"` // SubmitRetryable
|
||||
RetryData *hexutil.Bytes `json:"retryData,omitempty"` // SubmitRetryable
|
||||
Beneficiary *common.Address `json:"beneficiary,omitempty"` // SubmitRetryable
|
||||
MaxSubmissionFee *hexutil.Big `json:"maxSubmissionFee,omitempty"` // SubmitRetryable
|
||||
EffectiveGasPrice *hexutil.Uint64 `json:"effectiveGasPrice,omitempty"` // ArbLegacy
|
||||
L1BlockNumber *hexutil.Uint64 `json:"l1BlockNumber,omitempty"` // ArbLegacy
|
||||
|
||||
// Only used for encoding - and for ArbLegacy
|
||||
Hash common.Hash `json:"hash"`
|
||||
}
|
||||
|
||||
|
@ -57,6 +74,17 @@ func (t *Transaction) MarshalJSON() ([]byte, error) {
|
|||
enc.Hash = t.Hash()
|
||||
enc.Type = hexutil.Uint64(t.Type())
|
||||
|
||||
// Arbitrum: set to 0 for compatibility
|
||||
var zero uint64
|
||||
enc.Nonce = (*hexutil.Uint64)(&zero)
|
||||
enc.Gas = (*hexutil.Uint64)(&zero)
|
||||
enc.GasPrice = (*hexutil.Big)(common.Big0)
|
||||
enc.Value = (*hexutil.Big)(common.Big0)
|
||||
enc.Data = (*hexutil.Bytes)(&[]byte{})
|
||||
enc.V = (*hexutil.Big)(common.Big0)
|
||||
enc.R = (*hexutil.Big)(common.Big0)
|
||||
enc.S = (*hexutil.Big)(common.Big0)
|
||||
|
||||
// Other fields are set conditionally depending on tx type.
|
||||
switch tx := t.inner.(type) {
|
||||
case *LegacyTx:
|
||||
|
@ -94,6 +122,76 @@ func (t *Transaction) MarshalJSON() ([]byte, error) {
|
|||
enc.V = (*hexutil.Big)(tx.V)
|
||||
enc.R = (*hexutil.Big)(tx.R)
|
||||
enc.S = (*hexutil.Big)(tx.S)
|
||||
case *ArbitrumLegacyTxData:
|
||||
enc.Nonce = (*hexutil.Uint64)(&tx.Nonce)
|
||||
enc.Gas = (*hexutil.Uint64)(&tx.Gas)
|
||||
enc.GasPrice = (*hexutil.Big)(tx.GasPrice)
|
||||
enc.Value = (*hexutil.Big)(tx.Value)
|
||||
enc.Data = (*hexutil.Bytes)(&tx.Data)
|
||||
enc.To = t.To()
|
||||
enc.V = (*hexutil.Big)(tx.V)
|
||||
enc.R = (*hexutil.Big)(tx.R)
|
||||
enc.S = (*hexutil.Big)(tx.S)
|
||||
enc.EffectiveGasPrice = (*hexutil.Uint64)(&tx.EffectiveGasPrice)
|
||||
enc.L1BlockNumber = (*hexutil.Uint64)(&tx.L1BlockNumber)
|
||||
enc.From = tx.Sender
|
||||
case *ArbitrumInternalTx:
|
||||
enc.ChainID = (*hexutil.Big)(tx.ChainId)
|
||||
enc.Data = (*hexutil.Bytes)(&tx.Data)
|
||||
case *ArbitrumDepositTx:
|
||||
enc.RequestId = &tx.L1RequestId
|
||||
enc.From = &tx.From
|
||||
enc.ChainID = (*hexutil.Big)(tx.ChainId)
|
||||
enc.Value = (*hexutil.Big)(tx.Value)
|
||||
enc.To = t.To()
|
||||
case *ArbitrumUnsignedTx:
|
||||
enc.From = (*common.Address)(&tx.From)
|
||||
enc.ChainID = (*hexutil.Big)(tx.ChainId)
|
||||
enc.Nonce = (*hexutil.Uint64)(&tx.Nonce)
|
||||
enc.Gas = (*hexutil.Uint64)(&tx.Gas)
|
||||
enc.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap)
|
||||
enc.Value = (*hexutil.Big)(tx.Value)
|
||||
enc.Data = (*hexutil.Bytes)(&tx.Data)
|
||||
enc.To = t.To()
|
||||
case *ArbitrumContractTx:
|
||||
enc.RequestId = &tx.RequestId
|
||||
enc.From = (*common.Address)(&tx.From)
|
||||
enc.ChainID = (*hexutil.Big)(tx.ChainId)
|
||||
enc.Gas = (*hexutil.Uint64)(&tx.Gas)
|
||||
enc.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap)
|
||||
enc.Value = (*hexutil.Big)(tx.Value)
|
||||
enc.Data = (*hexutil.Bytes)(&tx.Data)
|
||||
enc.To = t.To()
|
||||
case *ArbitrumRetryTx:
|
||||
enc.From = (*common.Address)(&tx.From)
|
||||
enc.TicketId = &tx.TicketId
|
||||
enc.RefundTo = &tx.RefundTo
|
||||
enc.ChainID = (*hexutil.Big)(tx.ChainId)
|
||||
enc.Nonce = (*hexutil.Uint64)(&tx.Nonce)
|
||||
enc.Gas = (*hexutil.Uint64)(&tx.Gas)
|
||||
enc.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap)
|
||||
enc.Value = (*hexutil.Big)(tx.Value)
|
||||
enc.Data = (*hexutil.Bytes)(&tx.Data)
|
||||
enc.MaxRefund = (*hexutil.Big)(tx.MaxRefund)
|
||||
enc.SubmissionFeeRefund = (*hexutil.Big)(tx.SubmissionFeeRefund)
|
||||
enc.To = t.To()
|
||||
case *ArbitrumSubmitRetryableTx:
|
||||
enc.RequestId = &tx.RequestId
|
||||
enc.From = &tx.From
|
||||
enc.L1BaseFee = (*hexutil.Big)(tx.L1BaseFee)
|
||||
enc.DepositValue = (*hexutil.Big)(tx.DepositValue)
|
||||
enc.Beneficiary = &tx.Beneficiary
|
||||
enc.RefundTo = &tx.FeeRefundAddr
|
||||
enc.MaxSubmissionFee = (*hexutil.Big)(tx.MaxSubmissionFee)
|
||||
enc.ChainID = (*hexutil.Big)(tx.ChainId)
|
||||
enc.Gas = (*hexutil.Uint64)(&tx.Gas)
|
||||
enc.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap)
|
||||
enc.RetryTo = tx.RetryTo
|
||||
enc.RetryValue = (*hexutil.Big)(tx.RetryValue)
|
||||
enc.RetryData = (*hexutil.Bytes)(&tx.RetryData)
|
||||
data := tx.data()
|
||||
enc.Data = (*hexutil.Bytes)(&data)
|
||||
enc.To = t.To()
|
||||
}
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
@ -263,6 +361,270 @@ func (t *Transaction) UnmarshalJSON(input []byte) error {
|
|||
}
|
||||
}
|
||||
|
||||
case ArbitrumLegacyTxType:
|
||||
var itx LegacyTx
|
||||
if dec.To != nil {
|
||||
itx.To = dec.To
|
||||
}
|
||||
if dec.Nonce == nil {
|
||||
return errors.New("missing required field 'nonce' in transaction")
|
||||
}
|
||||
itx.Nonce = uint64(*dec.Nonce)
|
||||
if dec.GasPrice == nil {
|
||||
return errors.New("missing required field 'gasPrice' in transaction")
|
||||
}
|
||||
itx.GasPrice = (*big.Int)(dec.GasPrice)
|
||||
if dec.Gas == nil {
|
||||
return errors.New("missing required field 'gas' in transaction")
|
||||
}
|
||||
itx.Gas = uint64(*dec.Gas)
|
||||
if dec.Value == nil {
|
||||
return errors.New("missing required field 'value' in transaction")
|
||||
}
|
||||
itx.Value = (*big.Int)(dec.Value)
|
||||
if dec.Data == nil {
|
||||
return errors.New("missing required field 'input' in transaction")
|
||||
}
|
||||
itx.Data = *dec.Data
|
||||
if dec.V == nil {
|
||||
return errors.New("missing required field 'v' in transaction")
|
||||
}
|
||||
itx.V = (*big.Int)(dec.V)
|
||||
if dec.R == nil {
|
||||
return errors.New("missing required field 'r' in transaction")
|
||||
}
|
||||
itx.R = (*big.Int)(dec.R)
|
||||
if dec.S == nil {
|
||||
return errors.New("missing required field 's' in transaction")
|
||||
}
|
||||
itx.S = (*big.Int)(dec.S)
|
||||
withSignature := itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0
|
||||
if withSignature {
|
||||
if err := sanityCheckSignature(itx.V, itx.R, itx.S, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if dec.EffectiveGasPrice == nil {
|
||||
return errors.New("missing required field 'EffectiveGasPrice' in transaction")
|
||||
}
|
||||
if dec.L1BlockNumber == nil {
|
||||
return errors.New("missing required field 'L1BlockNumber' in transaction")
|
||||
}
|
||||
inner = &ArbitrumLegacyTxData{
|
||||
LegacyTx: itx,
|
||||
HashOverride: dec.Hash,
|
||||
EffectiveGasPrice: uint64(*dec.EffectiveGasPrice),
|
||||
L1BlockNumber: uint64(*dec.L1BlockNumber),
|
||||
Sender: dec.From,
|
||||
}
|
||||
|
||||
case ArbitrumInternalTxType:
|
||||
if dec.ChainID == nil {
|
||||
return errors.New("missing required field 'chainId' in transaction")
|
||||
}
|
||||
if dec.Data == nil {
|
||||
return errors.New("missing required field 'input' in transaction")
|
||||
}
|
||||
inner = &ArbitrumInternalTx{
|
||||
ChainId: (*big.Int)(dec.ChainID),
|
||||
Data: *dec.Data,
|
||||
}
|
||||
|
||||
case ArbitrumDepositTxType:
|
||||
if dec.ChainID == nil {
|
||||
return errors.New("missing required field 'chainId' in transaction")
|
||||
}
|
||||
if dec.RequestId == nil {
|
||||
return errors.New("missing required field 'requestId' in transaction")
|
||||
}
|
||||
if dec.To == nil {
|
||||
return errors.New("missing required field 'to' in transaction")
|
||||
}
|
||||
if dec.From == nil {
|
||||
return errors.New("missing required field 'from' in transaction")
|
||||
}
|
||||
if dec.Value == nil {
|
||||
return errors.New("missing required field 'value' in transaction")
|
||||
}
|
||||
inner = &ArbitrumDepositTx{
|
||||
ChainId: (*big.Int)(dec.ChainID),
|
||||
L1RequestId: *dec.RequestId,
|
||||
To: *dec.To,
|
||||
From: *dec.From,
|
||||
Value: (*big.Int)(dec.Value),
|
||||
}
|
||||
|
||||
case ArbitrumUnsignedTxType:
|
||||
if dec.ChainID == nil {
|
||||
return errors.New("missing required field 'chainId' in transaction")
|
||||
}
|
||||
if dec.From == nil {
|
||||
return errors.New("missing required field 'from' in transaction")
|
||||
}
|
||||
if dec.Nonce == nil {
|
||||
return errors.New("missing required field 'nonce' in transaction")
|
||||
}
|
||||
if dec.MaxFeePerGas == nil {
|
||||
return errors.New("missing required field 'maxFeePerGas' for txdata")
|
||||
}
|
||||
if dec.Gas == nil {
|
||||
return errors.New("missing required field 'gas' in txdata")
|
||||
}
|
||||
if dec.Value == nil {
|
||||
return errors.New("missing required field 'value' in transaction")
|
||||
}
|
||||
if dec.Data == nil {
|
||||
return errors.New("missing required field 'input' in transaction")
|
||||
}
|
||||
inner = &ArbitrumUnsignedTx{
|
||||
ChainId: (*big.Int)(dec.ChainID),
|
||||
From: *dec.From,
|
||||
Nonce: uint64(*dec.Nonce),
|
||||
GasFeeCap: (*big.Int)(dec.MaxFeePerGas),
|
||||
Gas: uint64(*dec.Gas),
|
||||
To: dec.To,
|
||||
Value: (*big.Int)(dec.Value),
|
||||
Data: *dec.Data,
|
||||
}
|
||||
|
||||
case ArbitrumContractTxType:
|
||||
if dec.ChainID == nil {
|
||||
return errors.New("missing required field 'chainId' in transaction")
|
||||
}
|
||||
if dec.RequestId == nil {
|
||||
return errors.New("missing required field 'requestId' in transaction")
|
||||
}
|
||||
if dec.From == nil {
|
||||
return errors.New("missing required field 'from' in transaction")
|
||||
}
|
||||
if dec.MaxFeePerGas == nil {
|
||||
return errors.New("missing required field 'maxFeePerGas' for txdata")
|
||||
}
|
||||
if dec.Gas == nil {
|
||||
return errors.New("missing required field 'gas' in txdata")
|
||||
}
|
||||
if dec.Value == nil {
|
||||
return errors.New("missing required field 'value' in transaction")
|
||||
}
|
||||
if dec.Data == nil {
|
||||
return errors.New("missing required field 'input' in transaction")
|
||||
}
|
||||
inner = &ArbitrumContractTx{
|
||||
ChainId: (*big.Int)(dec.ChainID),
|
||||
RequestId: *dec.RequestId,
|
||||
From: *dec.From,
|
||||
GasFeeCap: (*big.Int)(dec.MaxFeePerGas),
|
||||
Gas: uint64(*dec.Gas),
|
||||
To: dec.To,
|
||||
Value: (*big.Int)(dec.Value),
|
||||
Data: *dec.Data,
|
||||
}
|
||||
|
||||
case ArbitrumRetryTxType:
|
||||
if dec.ChainID == nil {
|
||||
return errors.New("missing required field 'chainId' in transaction")
|
||||
}
|
||||
if dec.Nonce == nil {
|
||||
return errors.New("missing required field 'nonce' in transaction")
|
||||
}
|
||||
if dec.From == nil {
|
||||
return errors.New("missing required field 'from' in transaction")
|
||||
}
|
||||
if dec.MaxFeePerGas == nil {
|
||||
return errors.New("missing required field 'maxFeePerGas' for txdata")
|
||||
}
|
||||
if dec.Gas == nil {
|
||||
return errors.New("missing required field 'gas' in txdata")
|
||||
}
|
||||
if dec.Value == nil {
|
||||
return errors.New("missing required field 'value' in transaction")
|
||||
}
|
||||
if dec.Data == nil {
|
||||
return errors.New("missing required field 'input' in transaction")
|
||||
}
|
||||
if dec.TicketId == nil {
|
||||
return errors.New("missing required field 'ticketId' in transaction")
|
||||
}
|
||||
if dec.RefundTo == nil {
|
||||
return errors.New("missing required field 'refundTo' in transaction")
|
||||
}
|
||||
if dec.MaxRefund == nil {
|
||||
return errors.New("missing required field 'maxRefund' in transaction")
|
||||
}
|
||||
if dec.SubmissionFeeRefund == nil {
|
||||
return errors.New("missing required field 'submissionFeeRefund' in transaction")
|
||||
}
|
||||
inner = &ArbitrumRetryTx{
|
||||
ChainId: (*big.Int)(dec.ChainID),
|
||||
Nonce: uint64(*dec.Nonce),
|
||||
From: *dec.From,
|
||||
GasFeeCap: (*big.Int)(dec.MaxFeePerGas),
|
||||
Gas: uint64(*dec.Gas),
|
||||
To: dec.To,
|
||||
Value: (*big.Int)(dec.Value),
|
||||
Data: *dec.Data,
|
||||
TicketId: *dec.TicketId,
|
||||
RefundTo: *dec.RefundTo,
|
||||
MaxRefund: (*big.Int)(dec.MaxRefund),
|
||||
SubmissionFeeRefund: (*big.Int)(dec.SubmissionFeeRefund),
|
||||
}
|
||||
|
||||
case ArbitrumSubmitRetryableTxType:
|
||||
if dec.ChainID == nil {
|
||||
return errors.New("missing required field 'chainId' in transaction")
|
||||
}
|
||||
if dec.RequestId == nil {
|
||||
return errors.New("missing required field 'requestId' in transaction")
|
||||
}
|
||||
if dec.From == nil {
|
||||
return errors.New("missing required field 'from' in transaction")
|
||||
}
|
||||
if dec.L1BaseFee == nil {
|
||||
return errors.New("missing required field 'l1BaseFee' in transaction")
|
||||
}
|
||||
if dec.DepositValue == nil {
|
||||
return errors.New("missing required field 'depositValue' in transaction")
|
||||
}
|
||||
if dec.MaxFeePerGas == nil {
|
||||
return errors.New("missing required field 'maxFeePerGas' for txdata")
|
||||
}
|
||||
if dec.Gas == nil {
|
||||
return errors.New("missing required field 'gas' in txdata")
|
||||
}
|
||||
if dec.RetryTo == nil {
|
||||
return errors.New("missing required field 'retryTo' in txdata")
|
||||
}
|
||||
if dec.Beneficiary == nil {
|
||||
return errors.New("missing required field 'beneficiary' in transaction")
|
||||
}
|
||||
if dec.MaxSubmissionFee == nil {
|
||||
return errors.New("missing required field 'maxSubmissionFee' in transaction")
|
||||
}
|
||||
if dec.RefundTo == nil {
|
||||
return errors.New("missing required field 'refundTo' in transaction")
|
||||
}
|
||||
if dec.RetryValue == nil {
|
||||
return errors.New("missing required field 'retryValue' in transaction")
|
||||
}
|
||||
if dec.RetryData == nil {
|
||||
return errors.New("missing required field 'retryData' in transaction")
|
||||
}
|
||||
inner = &ArbitrumSubmitRetryableTx{
|
||||
ChainId: (*big.Int)(dec.ChainID),
|
||||
RequestId: *dec.RequestId,
|
||||
From: *dec.From,
|
||||
L1BaseFee: (*big.Int)(dec.L1BaseFee),
|
||||
DepositValue: (*big.Int)(dec.DepositValue),
|
||||
GasFeeCap: (*big.Int)(dec.MaxFeePerGas),
|
||||
Gas: uint64(*dec.Gas),
|
||||
RetryTo: dec.RetryTo,
|
||||
RetryValue: (*big.Int)(dec.RetryValue),
|
||||
Beneficiary: *dec.Beneficiary,
|
||||
MaxSubmissionFee: (*big.Int)(dec.MaxSubmissionFee),
|
||||
FeeRefundAddr: *dec.RefundTo,
|
||||
RetryData: *dec.RetryData,
|
||||
}
|
||||
|
||||
default:
|
||||
return ErrTxTypeNotSupported
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue