github.com/cwntr/go-defi@v0.0.0-20210629134751-07f9ec2f7e66/client/client.go (about)

     1  package client
     2  
     3  import (
     4  	"context"
     5  	"encoding/hex"
     6  	"fmt"
     7  	"math/big"
     8  	"strings"
     9  
    10  	"github.com/524119574/go-defi/binding/haave"
    11  	"github.com/524119574/go-defi/binding/hbalancer_exchange"
    12  	"github.com/524119574/go-defi/binding/hcether"
    13  	"github.com/524119574/go-defi/binding/hctoken"
    14  	"github.com/524119574/go-defi/binding/hcurve"
    15  	"github.com/524119574/go-defi/binding/hkyber"
    16  	"github.com/524119574/go-defi/binding/hmaker"
    17  	"github.com/524119574/go-defi/binding/huniswap"
    18  	"github.com/524119574/go-defi/binding/hyearn"
    19  
    20  	"github.com/524119574/go-defi/binding/herc20tokenin"
    21  	"github.com/ethereum/go-ethereum/accounts/abi"
    22  
    23  	"github.com/524119574/go-defi/binding/aave/lendingpool"
    24  	ceth_binding "github.com/524119574/go-defi/binding/compound/cETH"
    25  	"github.com/524119574/go-defi/binding/compound/cToken"
    26  	"github.com/524119574/go-defi/binding/erc20"
    27  	"github.com/524119574/go-defi/binding/furucombo"
    28  	"github.com/524119574/go-defi/binding/swapper"
    29  	"github.com/524119574/go-defi/binding/uniswap"
    30  	"github.com/524119574/go-defi/binding/yearn/yregistry"
    31  	"github.com/524119574/go-defi/binding/yearn/yvault"
    32  	"github.com/524119574/go-defi/binding/yearn/yweth"
    33  	"github.com/ethereum/go-ethereum/accounts/abi/bind"
    34  	"github.com/ethereum/go-ethereum/common"
    35  	"github.com/ethereum/go-ethereum/core/types"
    36  	"github.com/ethereum/go-ethereum/ethclient"
    37  )
    38  
    39  type rateModel int64
    40  
    41  const (
    42  	// StableRate https://medium.com/aave/aave-borrowing-rates-upgraded-f6c8b27973a7
    43  	StableRate rateModel = 1
    44  	// VariableRate https://medium.com/aave/aave-borrowing-rates-upgraded-f6c8b27973a7
    45  	VariableRate rateModel = 2
    46  )
    47  
    48  type coinType int
    49  
    50  const (
    51  	// ETH is ether.
    52  	ETH coinType = iota
    53  	// BAT is basic attention token.
    54  	BAT coinType = iota
    55  	// COMP is the governance token for Compound.
    56  	COMP coinType = iota
    57  	// DAI is the stable coin.
    58  	DAI coinType = iota
    59  	// REP is Augur reputation token.
    60  	REP coinType = iota
    61  	// SAI is Single Collateral DAI.
    62  	SAI coinType = iota
    63  	// UNI is the governance token for Uniswap.
    64  	UNI coinType = iota
    65  	// USDC is the stable coin by Circle.
    66  	USDC coinType = iota
    67  	// USDT is the stable coin.
    68  	USDT coinType = iota
    69  	// WBTC is wrapped BTC.
    70  	WBTC coinType = iota
    71  	// ZRX is the utility token for 0x.
    72  	ZRX coinType = iota
    73  	// BUSD is the Binance USD token.
    74  	BUSD coinType = iota
    75  	// YFI is the yearn governance token.
    76  	YFI coinType = iota
    77  	// AAVE is the Aave governance token.
    78  	AAVE coinType = iota
    79  
    80  	// cToken is the token that user receive after deposit into Yearn
    81  	cETH = iota
    82  
    83  	cDAI = iota
    84  
    85  	cUSDC = iota
    86  
    87  	yWETH = iota
    88  )
    89  
    90  const (
    91  	// uniswapAddr is UniswapV2Router, see here: https://uniswap.org/docs/v2/smart-contracts/router02/#address
    92  	uniswapAddr             string = "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"
    93  	yRegistryAddr           string = "0x3eE41C098f9666ed2eA246f4D2558010e59d63A0"
    94  	yETHVaultAddr           string = "0xe1237aA7f535b0CC33Fd973D66cBf830354D16c7"
    95  	aaveLendingPoolAddr     string = "0x398eC7346DcD622eDc5ae82352F02bE94C62d119"
    96  	aaveLendingPoolCoreAddr string = "0x3dfd23A6c5E8BbcFc9581d2E864a68feb6a076d3"
    97  	// Proxy and Handler related addresses
    98  
    99  	// ProxyAddr is the address of the proxy contract.
   100  	ProxyAddr             string = "0x57805e5a227937bac2b0fdacaa30413ddac6b8e1"
   101  	hCEtherAddr           string = "0x9A1049f7f87Dbb0468C745d9B3952e23d5d6CE5e"
   102  	hErcInAddr            string = "0x914490a362f4507058403a99e28bdf685c5c767f"
   103  	hCTokenAddr           string = "0x8973D623d883c5641Dd3906625Aac31cdC8790c5"
   104  	hMakerDaoAddr         string = "0x294fbca49c8a855e04d7d82b28256b086d39afea"
   105  	hUniswapAddr          string = "0x58a21cfcee675d65d577b251668f7dc46ea9c3a0"
   106  	hCurveAddr            string = "0xa36dfb057010c419c5917f3d68b4520db3671cdb"
   107  	hYearnAddr            string = "0xC50C8F34c9955217a6b3e385a069184DCE17fD2A"
   108  	hAaveAddr             string = "0xf579b009748a62b1978639d6b54259f8dc915229"
   109  	hOneInch              string = "0x783f5c56e3c8b23d90e4a271d7acbe914bfcd319"
   110  	hFunds                string = "0xf9b03e9ea64b2311b0221b2854edd6df97669c09"
   111  	hKyberAddr            string = "0xe2a3431508cd8e72d53a0e4b57c24af2899322a0"
   112  	hBalancerExchangeAddr string = "0x892dD6ebd2e3E1c0D6592309bA82a0095830D6d6"
   113  
   114  	// TODO: The following is not on mainnet yet
   115  	hSushiswapAddr string = "0xB6F469a8930dd5111c0EA76571c7E86298A171f7"
   116  	hSwapper       string = "0x017F3f2EB0c55DDF49B95ad38Cd2737ACf64AB4d"
   117  
   118  	// Curve pool addresses
   119  	cCompound string = "0xA2B47E3D5c44877cca798226B7B8118F9BFb7A56"
   120  	cUsdt     string = "0x52EA46506B9CC5Ef470C5bf89f17Dc28bB35D85C"
   121  	cY        string = "0x45F783CCE6B7FF23B2ab2D70e416cdb7D6055f51"
   122  	cBusd     string = "0x79a8C46DeA5aDa233ABaFFD40F3A0A2B1e5A4F27"
   123  	cSusd     string = "0xA5407eAE9Ba41422680e2e00537571bcC53efBfD"
   124  	cRen      string = "0x93054188d876f558f4a66B2EF1d97d16eDf0895B"
   125  	cSbtc     string = "0x7fC77b5c7614E1533320Ea6DDc2Eb61fa00A9714"
   126  	cHbtc     string = "0x4ca9b3063ec5866a4b82e437059d2c43d1be596f"
   127  	c3Pool    string = "0xbebc44782c7db0a1a60cb6fe97d0b483032ff1c7"
   128  	cGusd     string = "0x4f062658eaaf2c1ccf8c8e36d6824cdf41167956"
   129  	cHusd     string = "0x3eF6A01A0f81D6046290f3e2A8c5b843e738E604"
   130  	cUsdk     string = "0x3e01dd8a5e1fb3481f0f589056b428fc308af0fb"
   131  	cUsdn     string = "0x0f9cb53Ebe405d49A0bbdBD291A65Ff571bC83e1"
   132  
   133  	// Curve token addresses
   134  	compCrv      string = "0x845838DF265Dcd2c412A1Dc9e959c7d08537f8a2"
   135  	usdtCrv      string = "0x9fC689CCaDa600B6DF723D9E47D84d76664a1F23"
   136  	yCrv         string = "0xdF5e0e81Dff6FAF3A7e52BA697820c5e32D806A8"
   137  	busdCrv      string = "0x3B3Ac5386837Dc563660FB6a0937DFAa5924333B"
   138  	susdCrv      string = "0xC25a3A3b969415c80451098fa907EC722572917F"
   139  	renCrv       string = "0x49849C98ae39Fff122806C06791Fa73784FB3675"
   140  	sbtcCrv      string = "0x075b1bb99792c9E1041bA13afEf80C91a1e70fB3"
   141  	hbtcCrv      string = "0xb19059ebb43466C323583928285a49f558E572Fd"
   142  	threePoolCrv string = "0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490"
   143  	gusdCrv      string = "0xD2967f45c4f384DEEa880F807Be904762a3DeA07"
   144  	husdCrv      string = "0x5B5CFE992AdAC0C9D48E05854B2d91C73a003858"
   145  	usdkCrv      string = "0x97E2768e8E73511cA874545DC5Ff8067eB19B787"
   146  	usdnCrv      string = "0x4f3E8F405CF5aFC05D68142F3783bDfE13811522"
   147  )
   148  
   149  // CoinToAddressMap returns a mapping from coin to address
   150  var CoinToAddressMap = map[coinType]common.Address{
   151  	ETH:   common.HexToAddress("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"),
   152  	BAT:   common.HexToAddress("0x0d8775f648430679a709e98d2b0cb6250d2887ef"),
   153  	COMP:  common.HexToAddress("0xc00e94cb662c3520282e6f5717214004a7f26888"),
   154  	DAI:   common.HexToAddress("0x6b175474e89094c44da98b954eedeac495271d0f"),
   155  	USDC:  common.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"),
   156  	USDT:  common.HexToAddress("0xdac17f958d2ee523a2206206994597c13d831ec7"),
   157  	cETH:  common.HexToAddress("0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5"),
   158  	cDAI:  common.HexToAddress("0x5d3a536e4d6dbd6114cc1ead35777bab948e3643"),
   159  	cUSDC: common.HexToAddress("0x39aa39c021dfbae8fac545936693ac917d5e7563"),
   160  	BUSD:  common.HexToAddress("0x4Fabb145d64652a948d72533023f6E7A623C7C53"),
   161  	yWETH: common.HexToAddress("0xe1237aA7f535b0CC33Fd973D66cBf830354D16c7"),
   162  }
   163  
   164  // CoinToCompoundMap returns a mapping from coin to compound address
   165  var CoinToCompoundMap = map[coinType]common.Address{
   166  	ETH:  common.HexToAddress("0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5"),
   167  	DAI:  common.HexToAddress("0x5d3a536e4d6dbd6114cc1ead35777bab948e3643"),
   168  	USDC: common.HexToAddress("0x39aa39c021dfbae8fac545936693ac917d5e7563"),
   169  }
   170  
   171  // CoinToJoinMap maps the coin type to its corresponding Join which is a MakerDao terminology meaning an adapter to
   172  // deposit and withdraw unlocked collateral.
   173  var CoinToJoinMap = map[coinType]common.Address{
   174  	DAI:  common.HexToAddress("0x9759A6Ac90977b93B58547b4A71c78317f391A28"),
   175  	ETH:  common.HexToAddress("0x2F0b23f53734252Bda2277357e97e1517d6B042A"),
   176  	USDC: common.HexToAddress("0x2600004fd1585f7270756DDc88aD9cfA10dD0428"),
   177  	YFI:  common.HexToAddress("0x3ff33d9162aD47660083D7DC4bC02Fb231c81677"),
   178  	USDT: common.HexToAddress("0x0Ac6A1D74E84C2dF9063bDDc31699FF2a2BB22A2"),
   179  	UNI:  common.HexToAddress("0x2502F65D77cA13f183850b5f9272270454094A08"),
   180  	AAVE: common.HexToAddress("0x24e459F61cEAa7b1cE70Dbaea938940A7c5aD46e"),
   181  }
   182  
   183  // CoinToIlkMap maps the coin type to the corresponding Ilk as found in here:
   184  // https://etherscan.io/address/0x8b4ce5DCbb01e0e1f0521cd8dCfb31B308E52c24
   185  // Ilk is a MakerDao collateral type, each Ilk correspond to a type of collateral and
   186  // user can query it's name, symbol, dec, gem, pip, join and flip.
   187  var CoinToIlkMap = map[coinType][32]byte{
   188  	ETH:  byte32PutString("4554482d41000000000000000000000000000000000000000000000000000000"),
   189  	YFI:  byte32PutString("5946492d41000000000000000000000000000000000000000000000000000000"),
   190  	USDC: byte32PutString("555344432d420000000000000000000000000000000000000000000000000000"),
   191  	USDT: byte32PutString("555344542d410000000000000000000000000000000000000000000000000000"),
   192  	UNI:  byte32PutString("554e4956324441494554482d4100000000000000000000000000000000000000"),
   193  	AAVE: byte32PutString("414156452d410000000000000000000000000000000000000000000000000000"),
   194  }
   195  
   196  // Client is the new interface
   197  type Client interface {
   198  	Uniswap() UniswapClient
   199  }
   200  
   201  // NewClient Create a new client
   202  // opts can be created using your private key
   203  // ethclient can be created when you dial an ETH end point
   204  func NewClient(opts *bind.TransactOpts, ethClient *ethclient.Client) *DefiClient {
   205  	c := new(DefiClient)
   206  	c.conn = ethClient
   207  	c.opts = opts
   208  	return c
   209  }
   210  
   211  // DefiClient is the struct that stores the information.
   212  type DefiClient struct {
   213  	opts *bind.TransactOpts
   214  	conn *ethclient.Client
   215  }
   216  
   217  // BalanceOf returns the balance of a given coin.
   218  func (c *DefiClient) BalanceOf(coin coinType) (*big.Int, error) {
   219  	return c.balanceOf(CoinToAddressMap[coin])
   220  }
   221  
   222  func (c *DefiClient) balanceOf(addr common.Address) (*big.Int, error) {
   223  	erc20, err := erc20.NewErc20(addr, c.conn)
   224  	if err != nil {
   225  		return nil, err
   226  	}
   227  	balance, err := erc20.BalanceOf(nil, c.opts.From)
   228  	if err != nil {
   229  		return nil, err
   230  	}
   231  
   232  	return balance, nil
   233  }
   234  
   235  // ExecuteActions sends one transaction for all the Defi interactions.
   236  func (c *DefiClient) ExecuteActions(actions *Actions) error {
   237  	gasPrice, err := c.SuggestGasPrice(nil)
   238  	if err != nil {
   239  		return err
   240  	}
   241  
   242  	return c.ExecuteActionsWithGasPrice(actions, gasPrice)
   243  }
   244  
   245  // SuggestGasPrice provides an estimation of the gas price based on the `blockNum`.
   246  // If the blockNum is `nil`, it will automatically use the latest block data.
   247  // The user can also specify a specific `blockNum` so that block will be used for the prediction.
   248  func (c *DefiClient) SuggestGasPrice(blockNum *big.Int) (*big.Int, error) {
   249  	if blockNum == nil {
   250  		header, err := c.conn.HeaderByNumber(context.Background(), nil)
   251  		if err != nil {
   252  			return nil, err
   253  		}
   254  		blockNum = header.Number
   255  	}
   256  
   257  	block, err := c.conn.BlockByNumber(context.Background(), blockNum)
   258  	if err != nil {
   259  		return nil, err
   260  	}
   261  
   262  	// If there is no transaction in the current block we fail back to the previous block.
   263  	if block.Transactions().Len() == 0 {
   264  		prvBlock := big.NewInt(0)
   265  		prvBlock.Sub(blockNum, big.NewInt(1))
   266  		return c.SuggestGasPrice(prvBlock)
   267  	}
   268  
   269  	sum := big.NewInt(0)
   270  	for _, transaction := range block.Transactions() {
   271  		sum.Add(sum, transaction.GasPrice())
   272  	}
   273  
   274  	size := big.NewInt(int64(len(block.Transactions())))
   275  	average := big.NewInt(0)
   276  
   277  	average.Div(sum, size)
   278  	return average, nil
   279  }
   280  
   281  // ExecuteActionsWithGasPrice sends one transaction for all the Defi interactions with given gasPrice.
   282  func (c *DefiClient) ExecuteActionsWithGasPrice(actions *Actions, gasPrice *big.Int) error {
   283  	handlers, datas, totalEthers, err := c.CombineActions(actions)
   284  
   285  	if err != nil {
   286  		return err
   287  	}
   288  
   289  	proxy, err := furucombo.NewFurucombo(common.HexToAddress(ProxyAddr), c.conn)
   290  	if err != nil {
   291  		return nil
   292  	}
   293  
   294  	opts := &bind.TransactOpts{
   295  		Value:    totalEthers,
   296  		Signer:   c.opts.Signer,
   297  		From:     c.opts.From,
   298  		GasLimit: 5000000,
   299  		GasPrice: gasPrice,
   300  	}
   301  	tx, err := proxy.BatchExec(opts, handlers, datas)
   302  	if err != nil {
   303  		return nil
   304  	}
   305  	receipt, err := bind.WaitMined(context.Background(), c.conn, tx)
   306  	if err != nil {
   307  		return err
   308  	}
   309  	if receipt.Status != 1 {
   310  		return fmt.Errorf("tx receipt status is not 1, indicating a failure occurred")
   311  	}
   312  	return nil
   313  }
   314  
   315  // CombineActions takes in an `Actions` and returns a slice of handler address and a slice of call data
   316  // if the combine is not successful, it will return the error.
   317  func (c *DefiClient) CombineActions(actions *Actions) ([]common.Address, [][]byte, *big.Int, error) {
   318  	handlers := []common.Address{}
   319  	datas := make([][]byte, 0)
   320  	totalEthers := big.NewInt(0)
   321  	approvalTokens := make([]common.Address, 0)
   322  	approvalAmounts := make([]*big.Int, 0)
   323  
   324  	for i := 0; i < len(actions.Actions); i++ {
   325  		handlers = append(handlers, actions.Actions[i].handlerAddr)
   326  		datas = append(datas, actions.Actions[i].data)
   327  		totalEthers.Add(totalEthers, actions.Actions[i].ethersNeeded)
   328  		if len(actions.Actions[i].approvalTokens) > 0 {
   329  			for j := 0; j < len(actions.Actions[i].approvalTokens); j++ {
   330  				tokenAddr := actions.Actions[i].approvalTokens[j]
   331  				tokenAmount := actions.Actions[i].approvalTokenAmounts[j]
   332  				approvalTokens = append(approvalTokens, tokenAddr)
   333  				balance, err := c.balanceOf(tokenAddr)
   334  				if err != nil {
   335  					return nil, nil, nil, err
   336  				}
   337  				if balance.Cmp(tokenAmount) == 1 {
   338  					approvalAmounts = append(approvalAmounts, tokenAmount)
   339  				} else {
   340  					approvalAmounts = append(approvalAmounts, balance)
   341  				}
   342  			}
   343  
   344  		}
   345  	}
   346  
   347  	if len(approvalTokens) > 0 {
   348  		parsed, err := abi.JSON(strings.NewReader(herc20tokenin.Herc20tokeninABI))
   349  		if err != nil {
   350  			return nil, nil, nil, err
   351  		}
   352  		injectData, err := parsed.Pack("inject", approvalTokens, approvalAmounts)
   353  
   354  		if err != nil {
   355  			return nil, nil, nil, err
   356  		}
   357  
   358  		handlers = append([]common.Address{common.HexToAddress(hFunds)}, handlers...)
   359  		datas = append([][]byte{injectData}, datas...)
   360  	}
   361  
   362  	return handlers, datas, totalEthers, nil
   363  }
   364  
   365  // SupplyFundActions transfer a certain amount of fund to the proxy
   366  func (c *DefiClient) SupplyFundActions(size *big.Int, coin coinType) *Actions {
   367  	parsed, err := abi.JSON(strings.NewReader(herc20tokenin.Herc20tokeninABI))
   368  	if err != nil {
   369  		return nil
   370  	}
   371  	injectData, err := parsed.Pack(
   372  		"inject", []common.Address{CoinToAddressMap[coin]}, []*big.Int{size})
   373  
   374  	if err != nil {
   375  		return nil
   376  	}
   377  
   378  	return &Actions{
   379  		Actions: []action{
   380  			{
   381  				handlerAddr:  common.HexToAddress(hFunds),
   382  				data:         injectData,
   383  				ethersNeeded: big.NewInt(0),
   384  			},
   385  		},
   386  	}
   387  }
   388  
   389  // action represents one action, e.g. supply to Compound, swap on Uniswap
   390  type action struct {
   391  	handlerAddr  common.Address
   392  	data         []byte
   393  	ethersNeeded *big.Int
   394  	// There could be multiple tokens that we need to approve in the case of say Curve add liquidity or Flash loan
   395  	approvalTokens       []common.Address
   396  	approvalTokenAmounts []*big.Int
   397  }
   398  
   399  // Actions represents a list of Action.
   400  type Actions struct {
   401  	Actions []action
   402  }
   403  
   404  // Add adds actions together
   405  // This is a variadic function so user can pass in any number of actions.
   406  func (actions *Actions) Add(newActionss ...*Actions) error {
   407  	if newActionss == nil {
   408  		return fmt.Errorf("new action is nil")
   409  	}
   410  	for _, newActions := range newActionss {
   411  		actions.Actions = append(actions.Actions, newActions.Actions...)
   412  	}
   413  	return nil
   414  }
   415  
   416  // Uniswap---------------------------------------------------------------------
   417  
   418  // UniswapClient struct
   419  type UniswapClient struct {
   420  	client  *DefiClient
   421  	uniswap *uniswap.Uniswap
   422  }
   423  
   424  // Uniswap returns a uniswap client.
   425  func (c *DefiClient) Uniswap() *UniswapClient {
   426  	uniClient := new(UniswapClient)
   427  	uniClient.client = c
   428  	uniswap, err := uniswap.NewUniswap(common.HexToAddress(uniswapAddr), c.conn)
   429  
   430  	if err != nil {
   431  		return nil
   432  	}
   433  
   434  	uniClient.uniswap = uniswap
   435  	return uniClient
   436  }
   437  
   438  // TxHash represents a transaction hash.
   439  type TxHash string
   440  
   441  // Swap in the Uniswap Exchange.
   442  func (c *UniswapClient) Swap(size int64, baseCurrency coinType, quoteCurrency coinType, receipient common.Address) error {
   443  	if quoteCurrency == ETH {
   444  		return c.swapETHToToken(size, baseCurrency, receipient)
   445  	} else {
   446  		err := Approve(c.client, quoteCurrency, common.HexToAddress(uniswapAddr), big.NewInt(size))
   447  		if err != nil {
   448  			return err
   449  		}
   450  		if baseCurrency == ETH {
   451  			return c.swapTokenToETH(size, quoteCurrency, receipient)
   452  		} else {
   453  			return c.swapTokenToToken(size, baseCurrency, quoteCurrency, receipient)
   454  		}
   455  	}
   456  }
   457  
   458  func (c *UniswapClient) swapETHToToken(size int64, baseCurrency coinType, receipient common.Address) error {
   459  	path := []common.Address{CoinToAddressMap[ETH], CoinToAddressMap[baseCurrency]}
   460  	tx, err := c.uniswap.SwapExactETHForTokens(
   461  		// TODO: there is basically no minimum output amount set, so this could cause huge slippage, need to fix.
   462  		// Also the time stamp is set to 2038 January 1, it's better to set it dynamically.
   463  		&bind.TransactOpts{
   464  			Value:    big.NewInt(size),
   465  			Signer:   c.client.opts.Signer,
   466  			From:     c.client.opts.From,
   467  			GasLimit: 500000,
   468  			GasPrice: big.NewInt(20000000000),
   469  		},
   470  		big.NewInt(0),
   471  		path, receipient,
   472  		big.NewInt(2145916800),
   473  	)
   474  	if err != nil {
   475  		return err
   476  	}
   477  	bind.WaitMined(context.Background(), c.client.conn, tx)
   478  	return nil
   479  }
   480  
   481  func (c *UniswapClient) swapTokenToToken(size int64, baseCurrency coinType, quoteCurrency coinType, receipient common.Address) error {
   482  	path := []common.Address{CoinToAddressMap[quoteCurrency], CoinToAddressMap[ETH], CoinToAddressMap[baseCurrency]}
   483  
   484  	tx, err := c.uniswap.SwapExactTokensForTokens(
   485  		// TODO: there is basically no minimum output amount set, so this could cause huge slippage, need to fix.
   486  		// Also the time stamp is set to 2038 January 1, it's better to set it dynamically.
   487  		c.client.opts, big.NewInt(size), big.NewInt(0), path, receipient, big.NewInt(2145916800))
   488  	if err != nil {
   489  		return err
   490  	}
   491  	bind.WaitMined(context.Background(), c.client.conn, tx)
   492  	return nil
   493  }
   494  
   495  func (c *UniswapClient) swapTokenToETH(size int64, quoteCurrency coinType, receipient common.Address) error {
   496  	path := []common.Address{CoinToAddressMap[quoteCurrency], CoinToAddressMap[ETH]}
   497  
   498  	tx, err := c.uniswap.SwapExactTokensForETH(
   499  		// TODO: there is basically no minimum output amount set, so this could cause huge slippage, need to fix.
   500  		// Also the time stamp is set to 2038 January 1, it's better to set it dynamically.
   501  		c.client.opts, big.NewInt(size), big.NewInt(0), path, receipient, big.NewInt(2145916800))
   502  	if err != nil {
   503  		return err
   504  	}
   505  	bind.WaitMined(context.Background(), c.client.conn, tx)
   506  	return nil
   507  }
   508  
   509  // SwapActions create a new swap action.
   510  func (c *UniswapClient) SwapActions(size *big.Int, baseCurrency coinType, quoteCurrency coinType) *Actions {
   511  	var callData []byte
   512  	var ethersNeeded = big.NewInt(0)
   513  	if quoteCurrency == ETH {
   514  		ethersNeeded = size
   515  		callData = swapETHToTokenData(size, baseCurrency)
   516  	} else {
   517  		if baseCurrency == ETH {
   518  			callData = swapTokenToETHData(size, quoteCurrency)
   519  		} else {
   520  			callData = swapTokenToTokenData(size, baseCurrency, quoteCurrency)
   521  		}
   522  	}
   523  
   524  	return &Actions{
   525  		Actions: []action{
   526  			{
   527  				handlerAddr:  common.HexToAddress(hUniswapAddr),
   528  				data:         callData,
   529  				ethersNeeded: ethersNeeded,
   530  			},
   531  		},
   532  	}
   533  }
   534  
   535  func swapETHToTokenData(size *big.Int, baseCurrency coinType) []byte {
   536  	parsed, err := abi.JSON(strings.NewReader(huniswap.HuniswapABI))
   537  	if err != nil {
   538  		return nil
   539  	}
   540  	data, err := parsed.Pack(
   541  		"swapExactETHForTokens", size, big.NewInt(0), []common.Address{CoinToAddressMap[ETH], CoinToAddressMap[baseCurrency]})
   542  	if err != nil {
   543  		return nil
   544  	}
   545  	return data
   546  }
   547  
   548  func swapTokenToETHData(size *big.Int, quoteCurrency coinType) []byte {
   549  	parsed, err := abi.JSON(strings.NewReader(huniswap.HuniswapABI))
   550  	if err != nil {
   551  		return nil
   552  	}
   553  	data, err := parsed.Pack(
   554  		"swapExactTokensForETH", size, big.NewInt(0), []common.Address{CoinToAddressMap[quoteCurrency], CoinToAddressMap[ETH]})
   555  	if err != nil {
   556  		return nil
   557  	}
   558  	return data
   559  }
   560  
   561  func swapTokenToTokenData(size *big.Int, baseCurrency coinType, quoteCurrency coinType) []byte {
   562  	parsed, err := abi.JSON(strings.NewReader(huniswap.HuniswapABI))
   563  	if err != nil {
   564  		return nil
   565  	}
   566  	data, err := parsed.Pack(
   567  		"swapExactTokensForTokens", size, big.NewInt(0), []common.Address{
   568  			CoinToAddressMap[quoteCurrency], CoinToAddressMap[ETH], CoinToAddressMap[baseCurrency]})
   569  	if err != nil {
   570  		return nil
   571  	}
   572  	return data
   573  }
   574  
   575  // FlashSwapActions create an action to perform flash swap on Uniswap.
   576  func (c *UniswapClient) FlashSwapActions(size *big.Int, coinBorrow coinType, coinRepay coinType, actions *Actions) *Actions {
   577  	handlers := []common.Address{}
   578  	datas := make([][]byte, 0)
   579  	totalEthers := big.NewInt(0)
   580  	for i := 0; i < len(actions.Actions); i++ {
   581  		handlers = append(handlers, actions.Actions[i].handlerAddr)
   582  		datas = append(datas, actions.Actions[i].data)
   583  		totalEthers.Add(totalEthers, actions.Actions[i].ethersNeeded)
   584  	}
   585  
   586  	proxy, err := abi.JSON(strings.NewReader(furucombo.FurucomboABI))
   587  	if err != nil {
   588  		return nil
   589  	}
   590  	payloadData, err := proxy.Pack("execs", handlers, datas)
   591  	if err != nil {
   592  		return nil
   593  	}
   594  	swapperAbi, err := abi.JSON(strings.NewReader(swapper.SwapperABI))
   595  	if err != nil {
   596  		return nil
   597  	}
   598  	// skip the first 4 bytes to omit the function selector
   599  	flashSwapData, err := swapperAbi.Pack("startSwap", CoinToAddressMap[coinBorrow], size, CoinToAddressMap[coinRepay], payloadData[4:])
   600  	if err != nil {
   601  		return nil
   602  	}
   603  
   604  	return &Actions{
   605  		Actions: []action{
   606  			{
   607  				handlerAddr:  common.HexToAddress(hSwapper),
   608  				data:         flashSwapData,
   609  				ethersNeeded: totalEthers,
   610  			},
   611  		},
   612  	}
   613  }
   614  
   615  // Compound---------------------------------------------------------------------
   616  
   617  // CompoundClient is an instance of Compound protocol.
   618  type CompoundClient struct {
   619  	client *DefiClient
   620  }
   621  
   622  // Compound returns a compound client.
   623  func (c *DefiClient) Compound() *CompoundClient {
   624  	compoundClient := new(CompoundClient)
   625  	compoundClient.client = c
   626  
   627  	return compoundClient
   628  }
   629  
   630  // Supply supplies token to compound.
   631  func (c *CompoundClient) Supply(amount int64, coin coinType) error {
   632  	var (
   633  		tx  *types.Transaction
   634  		err error
   635  	)
   636  
   637  	cTokenAddr, err := c.getPoolAddrFromCoin(coin)
   638  	opts := &bind.TransactOpts{
   639  		From:     c.client.opts.From,
   640  		Signer:   c.client.opts.Signer,
   641  		GasLimit: 500000,
   642  		GasPrice: big.NewInt(20000000000),
   643  	}
   644  
   645  	switch coin {
   646  	case ETH:
   647  		opts.Value = big.NewInt(amount)
   648  		cETHContract, err := ceth_binding.NewCETH(cTokenAddr, c.client.conn)
   649  		if err != nil {
   650  			return err
   651  		}
   652  
   653  		tx, err = cETHContract.Mint(opts)
   654  	case BAT, COMP, DAI, REP, SAI, UNI, USDC, USDT, WBTC, ZRX:
   655  		err = Approve(c.client, coin, cTokenAddr, big.NewInt(amount))
   656  		if err != nil {
   657  			return err
   658  		}
   659  		cTokenContract, err := cToken.NewCToken(cTokenAddr, c.client.conn)
   660  		if err != nil {
   661  			return err
   662  		}
   663  		tx, err = cTokenContract.Mint(opts, big.NewInt(amount))
   664  	default:
   665  		return fmt.Errorf("Not supported")
   666  	}
   667  
   668  	if err != nil {
   669  		fmt.Printf("Error mint ctoken: %v", err)
   670  		return err
   671  	}
   672  
   673  	bind.WaitMined(context.Background(), c.client.conn, tx)
   674  
   675  	return nil
   676  }
   677  
   678  // Redeem supplies token to compound.
   679  func (c *CompoundClient) Redeem(amount int64, coin coinType) error {
   680  	var (
   681  		tx  *types.Transaction
   682  		err error
   683  	)
   684  
   685  	cTokenAddr, err := c.getPoolAddrFromCoin(coin)
   686  	if err != nil {
   687  		return err
   688  	}
   689  
   690  	opts := &bind.TransactOpts{
   691  		From:     c.client.opts.From,
   692  		Signer:   c.client.opts.Signer,
   693  		GasLimit: 500000,
   694  		GasPrice: big.NewInt(20000000000),
   695  	}
   696  
   697  	switch coin {
   698  	case ETH:
   699  		cETHContract, err := ceth_binding.NewCETH(cTokenAddr, c.client.conn)
   700  		if err != nil {
   701  			return fmt.Errorf("Error getting cETH contract: %v", err)
   702  		}
   703  
   704  		tx, err = cETHContract.Redeem(opts, big.NewInt(amount))
   705  	case BAT, COMP, DAI, REP, SAI, UNI, USDC, USDT, WBTC, ZRX:
   706  		cTokenContract, err := cToken.NewCToken(cTokenAddr, c.client.conn)
   707  		if err != nil {
   708  			return fmt.Errorf("Error getting cToken contract: %v", err)
   709  		}
   710  
   711  		tx, err = cTokenContract.Redeem(opts, big.NewInt(amount))
   712  	}
   713  
   714  	if err != nil {
   715  		return err
   716  	}
   717  
   718  	bind.WaitMined(context.Background(), c.client.conn, tx)
   719  
   720  	return nil
   721  }
   722  
   723  // BalanceOf return the balance of given cToken.
   724  func (c *CompoundClient) BalanceOf(coin coinType) (*big.Int, error) {
   725  	var (
   726  		val *big.Int
   727  		err error
   728  	)
   729  
   730  	cTokenAddr, err := c.getPoolAddrFromCoin(coin)
   731  	if err != nil {
   732  		return nil, err
   733  	}
   734  
   735  	switch coin {
   736  	case ETH:
   737  		cETHContract, err := ceth_binding.NewCETH(cTokenAddr, c.client.conn)
   738  		if err != nil {
   739  			return nil, fmt.Errorf("Error getting cETH contract")
   740  		}
   741  
   742  		val, err = cETHContract.BalanceOf(nil, c.client.opts.From)
   743  	case BAT, COMP, DAI, REP, SAI, UNI, USDC, USDT, WBTC, ZRX:
   744  		cTokenContract, err := cToken.NewCToken(cTokenAddr, c.client.conn)
   745  		if err != nil {
   746  			return nil, fmt.Errorf("Error getting cDai contract")
   747  		}
   748  
   749  		val, err = cTokenContract.BalanceOf(nil, c.client.opts.From)
   750  	default:
   751  		return nil, fmt.Errorf("Not support token in balanceOf: %v", coin)
   752  	}
   753  
   754  	if err != nil {
   755  		return big.NewInt(0), fmt.Errorf("Error getting balance of cToken: %v", err)
   756  	}
   757  	return val, nil
   758  }
   759  
   760  // BalanceOfUnderlying return the balance of given cToken
   761  func (c *CompoundClient) BalanceOfUnderlying(coin coinType) (*types.Transaction, error) {
   762  	var (
   763  		tx  *types.Transaction
   764  		err error
   765  	)
   766  
   767  	cTokenAddr, err := c.getPoolAddrFromCoin(coin)
   768  	if err != nil {
   769  		return nil, err
   770  	}
   771  
   772  	switch coin {
   773  	case ETH:
   774  		cETHContract, err := ceth_binding.NewCETH(cTokenAddr, c.client.conn)
   775  		if err != nil {
   776  			fmt.Printf("Error getting cETH contract")
   777  		}
   778  
   779  		tx, err = cETHContract.BalanceOfUnderlying(nil, c.client.opts.From)
   780  	case BAT, COMP, DAI, REP, SAI, UNI, USDC, USDT, WBTC, ZRX:
   781  		cTokenContract, err := cToken.NewCToken(cTokenAddr, c.client.conn)
   782  		if err != nil {
   783  			return nil, fmt.Errorf("Error getting cDai contract")
   784  		}
   785  
   786  		tx, err = cTokenContract.BalanceOfUnderlying(nil, c.client.opts.From)
   787  	}
   788  
   789  	if err != nil {
   790  		fmt.Printf("Error getting balance of cToken: %v", err)
   791  		return nil, err
   792  	}
   793  	return tx, nil
   794  }
   795  
   796  // SupplyActions create a supply action to supply asset to Compound.
   797  func (c *CompoundClient) SupplyActions(size *big.Int, coin coinType) *Actions {
   798  	if coin == ETH {
   799  		return c.supplyActionsETH(size, coin)
   800  	} else {
   801  		return c.supplyActionsERC20(size, coin)
   802  	}
   803  }
   804  
   805  func (c *CompoundClient) supplyActionsETH(size *big.Int, coin coinType) *Actions {
   806  	parsed, err := abi.JSON(strings.NewReader(hcether.HcetherABI))
   807  	if err != nil {
   808  		return nil
   809  	}
   810  	data, err := parsed.Pack("mint", size)
   811  	if err != nil {
   812  		return nil
   813  	}
   814  	return &Actions{
   815  		Actions: []action{
   816  			{
   817  				handlerAddr:  common.HexToAddress(hCEtherAddr),
   818  				data:         data,
   819  				ethersNeeded: size,
   820  			},
   821  		},
   822  	}
   823  }
   824  
   825  func (c *CompoundClient) supplyActionsERC20(size *big.Int, coin coinType) *Actions {
   826  	parsed, err := abi.JSON(strings.NewReader(hctoken.HctokenABI))
   827  	if err != nil {
   828  		return nil
   829  	}
   830  	mintData, err := parsed.Pack("mint", CoinToCompoundMap[DAI], size)
   831  	if err != nil {
   832  		return nil
   833  	}
   834  	return &Actions{
   835  		Actions: []action{
   836  			{
   837  				handlerAddr:          common.HexToAddress(hCTokenAddr),
   838  				data:                 mintData,
   839  				ethersNeeded:         big.NewInt(0),
   840  				approvalTokens:       []common.Address{CoinToAddressMap[coin]},
   841  				approvalTokenAmounts: []*big.Int{size},
   842  			},
   843  		},
   844  	}
   845  }
   846  
   847  // RedeemActions create a Compound redeem action to be executed.
   848  func (c *CompoundClient) RedeemActions(size *big.Int, coin coinType) *Actions {
   849  	if coin == ETH {
   850  		return c.redeemActionsETH(size, coin)
   851  	} else {
   852  		return c.redeemActionsERC20(size, coin)
   853  	}
   854  }
   855  
   856  func (c *CompoundClient) redeemActionsETH(size *big.Int, coin coinType) *Actions {
   857  	parsed, err := abi.JSON(strings.NewReader(hcether.HcetherABI))
   858  	if err != nil {
   859  		return nil
   860  	}
   861  	data, err := parsed.Pack("redeem", size)
   862  	if err != nil {
   863  		return nil
   864  	}
   865  	return &Actions{
   866  		Actions: []action{
   867  			{
   868  				handlerAddr:  common.HexToAddress(hCEtherAddr),
   869  				data:         data,
   870  				ethersNeeded: size,
   871  			},
   872  		},
   873  	}
   874  }
   875  
   876  func (c *CompoundClient) redeemActionsERC20(size *big.Int, coin coinType) *Actions {
   877  	parsed, err := abi.JSON(strings.NewReader(hctoken.HctokenABI))
   878  	if err != nil {
   879  		return nil
   880  	}
   881  	redeemData, err := parsed.Pack("redeem", CoinToCompoundMap[coin], size)
   882  	if err != nil {
   883  		return nil
   884  	}
   885  	return &Actions{
   886  		Actions: []action{
   887  			{
   888  				handlerAddr:          common.HexToAddress(hCTokenAddr),
   889  				data:                 redeemData,
   890  				ethersNeeded:         big.NewInt(0),
   891  				approvalTokens:       []common.Address{CoinToCompoundMap[coin]},
   892  				approvalTokenAmounts: []*big.Int{size},
   893  			},
   894  		},
   895  	}
   896  }
   897  
   898  // FlashLoanActions create an action to perform Uniswap flashloan.
   899  func (c *AaveClient) FlashLoanActions(size *big.Int, coin coinType, actions *Actions) *Actions {
   900  	handlers := []common.Address{}
   901  	datas := make([][]byte, 0)
   902  	totalEthers := big.NewInt(0)
   903  	for i := 0; i < len(actions.Actions); i++ {
   904  		handlers = append(handlers, actions.Actions[i].handlerAddr)
   905  		datas = append(datas, actions.Actions[i].data)
   906  		totalEthers.Add(totalEthers, actions.Actions[i].ethersNeeded)
   907  	}
   908  
   909  	proxy, err := abi.JSON(strings.NewReader(furucombo.FurucomboABI))
   910  	if err != nil {
   911  		return nil
   912  	}
   913  	payloadData, err := proxy.Pack("execs", handlers, datas)
   914  	if err != nil {
   915  		return nil
   916  	}
   917  	haave, err := abi.JSON(strings.NewReader(haave.HaaveABI))
   918  	if err != nil {
   919  		return nil
   920  	}
   921  	// skip the first 4 bytes to omit the function selector
   922  	flashLoanData, err := haave.Pack("flashLoan", CoinToAddressMap[coin], size, payloadData[4:])
   923  	return &Actions{
   924  		Actions: []action{
   925  			{
   926  				handlerAddr:          common.HexToAddress(hAaveAddr),
   927  				data:                 flashLoanData,
   928  				ethersNeeded:         totalEthers,
   929  			},
   930  		},
   931  	}
   932  }
   933  
   934  func (c *CompoundClient) getPoolAddrFromCoin(coin coinType) (common.Address, error) {
   935  	if val, ok := CoinToCompoundMap[coin]; ok {
   936  		return val, nil
   937  	}
   938  	return common.Address{}, fmt.Errorf("No corresponding compound pool for token: %v", coin)
   939  }
   940  
   941  // yearn-----------------------------------------------------------------------------------------------------
   942  
   943  // YearnClient is an instance of Compound protocol.
   944  type YearnClient struct {
   945  	client       *DefiClient
   946  	tokenToVault map[common.Address]common.Address
   947  }
   948  
   949  // Yearn returns a Yearn client.
   950  func (c *DefiClient) Yearn() *YearnClient {
   951  	yearnClient := new(YearnClient)
   952  	yearnClient.client = c
   953  
   954  	yregistry, err := yregistry.NewYregistry(common.HexToAddress(yRegistryAddr), c.conn)
   955  	if err != nil {
   956  		return nil
   957  	}
   958  
   959  	vaults, err := yregistry.GetVaults(nil)
   960  	if err != nil {
   961  		return nil
   962  	}
   963  
   964  	vaultInfos, err := yregistry.GetVaultsInfo(nil)
   965  	if err != nil {
   966  		return nil
   967  	}
   968  
   969  	yearnClient.tokenToVault = make(map[common.Address]common.Address)
   970  	for i := 0; i < len(vaults); i++ {
   971  		yearnClient.tokenToVault[vaultInfos.TokenArray[i]] = vaults[i]
   972  	}
   973  
   974  	return yearnClient
   975  }
   976  
   977  func (c *YearnClient) addLiquidity(size *big.Int, coin coinType) error {
   978  	var (
   979  		tx  *types.Transaction
   980  		err error
   981  	)
   982  	opts := &bind.TransactOpts{
   983  		From:     c.client.opts.From,
   984  		Signer:   c.client.opts.Signer,
   985  		GasLimit: 500000,
   986  		GasPrice: big.NewInt(20000000000),
   987  	}
   988  
   989  	if coin == ETH {
   990  		weth, err := yweth.NewYweth(common.HexToAddress(yETHVaultAddr), c.client.conn)
   991  		if err != nil {
   992  			return fmt.Errorf("Error getting weth contract")
   993  		}
   994  		opts.Value = size
   995  		tx, err = weth.DepositETH(opts)
   996  	} else if coin != ETH {
   997  		tokenAddr := CoinToAddressMap[coin]
   998  		vaultAddr, ok := c.tokenToVault[tokenAddr]
   999  		if !ok {
  1000  			return fmt.Errorf("No corresponding vault found for: %v ", coin)
  1001  		}
  1002  		err = Approve(c.client, coin, vaultAddr, size)
  1003  		yvault, err := yvault.NewYvault(vaultAddr, c.client.conn)
  1004  		if err != nil {
  1005  			return fmt.Errorf("Error getting weth contract")
  1006  		}
  1007  		opts.Value = size
  1008  		tx, err = yvault.Deposit(opts, size)
  1009  	}
  1010  
  1011  	if err != nil {
  1012  		fmt.Printf("Error deposit into vault: %v", err)
  1013  		return err
  1014  	}
  1015  
  1016  	bind.WaitMined(context.Background(), c.client.conn, tx)
  1017  
  1018  	return nil
  1019  }
  1020  
  1021  func (c *YearnClient) removeLiquidity(size *big.Int, coin coinType) error {
  1022  	var (
  1023  		tx  *types.Transaction
  1024  		err error
  1025  	)
  1026  	opts := &bind.TransactOpts{
  1027  		From:     c.client.opts.From,
  1028  		Signer:   c.client.opts.Signer,
  1029  		GasLimit: 500000,
  1030  		GasPrice: big.NewInt(20000000000),
  1031  	}
  1032  
  1033  	if coin == ETH {
  1034  		weth, err := yweth.NewYweth(common.HexToAddress(yETHVaultAddr), c.client.conn)
  1035  		if err != nil {
  1036  			return fmt.Errorf("Error getting weth contract")
  1037  		}
  1038  		tx, err = weth.WithdrawETH(opts, size)
  1039  	} else if coin != ETH {
  1040  		tokenAddr := CoinToAddressMap[coin]
  1041  		vaultAddr, ok := c.tokenToVault[tokenAddr]
  1042  		if !ok {
  1043  			return fmt.Errorf("No corresponding vault found for: %v ", coin)
  1044  		}
  1045  		yvault, err := yvault.NewYvault(vaultAddr, c.client.conn)
  1046  		if err != nil {
  1047  			return fmt.Errorf("Error getting weth contract")
  1048  		}
  1049  		opts.Value = size
  1050  		tx, err = yvault.Withdraw(opts, size)
  1051  	}
  1052  
  1053  	if err != nil {
  1054  		fmt.Printf("Error withdraw from vault: %v", err)
  1055  		return err
  1056  	}
  1057  
  1058  	bind.WaitMined(context.Background(), c.client.conn, tx)
  1059  
  1060  	return nil
  1061  }
  1062  
  1063  // AddLiquidityActions creates an add liquidity action to Yearn.
  1064  func (c *YearnClient) AddLiquidityActions(size *big.Int, coin coinType) *Actions {
  1065  	if coin == ETH {
  1066  		return c.addLiquidityActionsETH(size, coin)
  1067  	} else {
  1068  		return c.addLiquidityActionsERC20(size, coin)
  1069  	}
  1070  }
  1071  
  1072  func (c *YearnClient) addLiquidityActionsETH(size *big.Int, coin coinType) *Actions {
  1073  	parsed, err := abi.JSON(strings.NewReader(hyearn.HyearnABI))
  1074  	if err != nil {
  1075  		return nil
  1076  	}
  1077  	data, err := parsed.Pack("depositETH", size, common.HexToAddress(yETHVaultAddr))
  1078  	if err != nil {
  1079  		return nil
  1080  	}
  1081  	return &Actions{
  1082  		Actions: []action{
  1083  			{
  1084  				handlerAddr:  common.HexToAddress(hYearnAddr),
  1085  				data:         data,
  1086  				ethersNeeded: size,
  1087  			},
  1088  		},
  1089  	}
  1090  }
  1091  
  1092  func (c *YearnClient) addLiquidityActionsERC20(size *big.Int, coin coinType) *Actions {
  1093  	parsed, err := abi.JSON(strings.NewReader(hyearn.HyearnABI))
  1094  	if err != nil {
  1095  		return nil
  1096  	}
  1097  	tokenAddr := CoinToAddressMap[coin]
  1098  	vaultAddr, ok := c.tokenToVault[tokenAddr]
  1099  	if !ok {
  1100  		return nil
  1101  	}
  1102  	data, err := parsed.Pack("deposit", vaultAddr, size)
  1103  	if err != nil {
  1104  		return nil
  1105  	}
  1106  	return &Actions{
  1107  		Actions: []action{
  1108  			{
  1109  				handlerAddr:          common.HexToAddress(hYearnAddr),
  1110  				data:                 data,
  1111  				ethersNeeded:         big.NewInt(0),
  1112  				approvalTokens:       []common.Address{CoinToAddressMap[coin]},
  1113  				approvalTokenAmounts: []*big.Int{size},
  1114  			},
  1115  		},
  1116  	}
  1117  }
  1118  
  1119  // RemoveLiquidityActions creates a remove liquidity action to Yearn.
  1120  func (c *YearnClient) RemoveLiquidityActions(size *big.Int, coin coinType) *Actions {
  1121  	if coin == ETH {
  1122  		return c.removeLiquidityActionsETH(size, coin)
  1123  	} else {
  1124  		return c.removeLiquidityActionsERC20(size, coin)
  1125  	}
  1126  }
  1127  
  1128  func (c *YearnClient) removeLiquidityActionsETH(size *big.Int, coin coinType) *Actions {
  1129  	parsed, err := abi.JSON(strings.NewReader(hyearn.HyearnABI))
  1130  	if err != nil {
  1131  		return nil
  1132  	}
  1133  	data, err := parsed.Pack("withdrawETH", common.HexToAddress(yETHVaultAddr), size)
  1134  	if err != nil {
  1135  		return nil
  1136  	}
  1137  	return &Actions{
  1138  		Actions: []action{
  1139  			{
  1140  				handlerAddr:          common.HexToAddress(hYearnAddr),
  1141  				data:                 data,
  1142  				ethersNeeded:         big.NewInt(0),
  1143  				approvalTokens:       []common.Address{common.HexToAddress(yETHVaultAddr)},
  1144  				approvalTokenAmounts: []*big.Int{size},
  1145  			},
  1146  		},
  1147  	}
  1148  }
  1149  
  1150  func (c *YearnClient) removeLiquidityActionsERC20(size *big.Int, coin coinType) *Actions {
  1151  	parsed, err := abi.JSON(strings.NewReader(hyearn.HyearnABI))
  1152  	if err != nil {
  1153  		return nil
  1154  	}
  1155  	data, err := parsed.Pack("withdraw", common.HexToAddress(yETHVaultAddr), size)
  1156  	if err != nil {
  1157  		return nil
  1158  	}
  1159  	return &Actions{
  1160  		Actions: []action{
  1161  			{
  1162  				handlerAddr:  common.HexToAddress(hYearnAddr),
  1163  				data:         data,
  1164  				ethersNeeded: big.NewInt(0),
  1165  			},
  1166  		},
  1167  	}
  1168  }
  1169  
  1170  // Aave----------------------------------------------------------------------------
  1171  
  1172  // AaveClient is an instance of Aave protocol.
  1173  type AaveClient struct {
  1174  	client      *DefiClient
  1175  	lendingPool *lendingpool.Lendingpool
  1176  }
  1177  
  1178  // Aave returns an Aave client which contains functions that you can use to interact with Aave.
  1179  func (c *DefiClient) Aave() *AaveClient {
  1180  	aaveClient := new(AaveClient)
  1181  	aaveClient.client = c
  1182  
  1183  	lendingpool, err := lendingpool.NewLendingpool(common.HexToAddress(aaveLendingPoolAddr), c.conn)
  1184  	if err != nil {
  1185  		return nil
  1186  	}
  1187  	aaveClient.lendingPool = lendingpool
  1188  	return aaveClient
  1189  }
  1190  
  1191  // Lend lend to the Aave lending pool.
  1192  func (c *AaveClient) Lend(size *big.Int, coin coinType) error {
  1193  	opts := &bind.TransactOpts{
  1194  		From:     c.client.opts.From,
  1195  		Signer:   c.client.opts.Signer,
  1196  		GasLimit: 500000,
  1197  		GasPrice: big.NewInt(20000000000),
  1198  	}
  1199  
  1200  	if coin != ETH {
  1201  		Approve(c.client, coin, common.HexToAddress(aaveLendingPoolCoreAddr), size)
  1202  	}
  1203  
  1204  	tx, err := c.lendingPool.Deposit(opts, CoinToAddressMap[coin], size, 0)
  1205  	if err != nil {
  1206  		return err
  1207  	}
  1208  	bind.WaitMined(context.Background(), c.client.conn, tx)
  1209  	return nil
  1210  }
  1211  
  1212  // Borrow borrow money from lending pool.
  1213  func (c *AaveClient) Borrow(size *big.Int, coin coinType, interestRate rateModel) error {
  1214  	return nil
  1215  }
  1216  
  1217  // ReserveData is a struct described the status of Aave lending pool.
  1218  type ReserveData struct {
  1219  	CurrentATokenBalance     *big.Int
  1220  	CurrentBorrowBalance     *big.Int
  1221  	PrincipalBorrowBalance   *big.Int
  1222  	BorrowRateMode           *big.Int
  1223  	BorrowRate               *big.Int
  1224  	LiquidityRate            *big.Int
  1225  	OriginationFee           *big.Int
  1226  	VariableBorrowIndex      *big.Int
  1227  	LastUpdateTimestamp      *big.Int
  1228  	UsageAsCollateralEnabled bool
  1229  }
  1230  
  1231  // GetUserReserveData get the reserve data.
  1232  func (c *AaveClient) GetUserReserveData(addr common.Address, user common.Address) (ReserveData, error) {
  1233  	data, err := c.lendingPool.GetUserReserveData(nil, addr, user)
  1234  	if err != nil {
  1235  		return ReserveData{}, err
  1236  	}
  1237  	return data, nil
  1238  }
  1239  
  1240  // Kyberswap----------------------------------------------------------------------
  1241  
  1242  // KyberswapClient struct
  1243  type KyberswapClient struct {
  1244  	client *DefiClient
  1245  }
  1246  
  1247  // Kyberswap returns a Kyberswap client.
  1248  func (c *DefiClient) Kyberswap() *KyberswapClient {
  1249  	kyberClient := new(KyberswapClient)
  1250  	kyberClient.client = c
  1251  	return kyberClient
  1252  }
  1253  
  1254  // SwapActions creates a swap action.
  1255  func (c *KyberswapClient) SwapActions(size *big.Int, baseCurrency coinType, quoteCurrency coinType) *Actions {
  1256  	var (
  1257  		data         []byte
  1258  		err          error
  1259  		ethersNeeded *big.Int = big.NewInt(0)
  1260  	)
  1261  
  1262  	parsed, err := abi.JSON(strings.NewReader(hkyber.HkyberABI))
  1263  	if err != nil {
  1264  		return nil
  1265  	}
  1266  
  1267  	if quoteCurrency == ETH {
  1268  		ethersNeeded = size
  1269  		data, err = parsed.Pack("swapEtherToToken", size, CoinToAddressMap[baseCurrency], big.NewInt(0))
  1270  	} else {
  1271  		if baseCurrency == ETH {
  1272  			data, err = parsed.Pack("swapTokenToEther", CoinToAddressMap[baseCurrency], size, big.NewInt(0))
  1273  		} else {
  1274  			data, err = parsed.Pack("swapTokenToToken", CoinToAddressMap[baseCurrency], size, CoinToAddressMap[quoteCurrency], big.NewInt(0))
  1275  		}
  1276  	}
  1277  
  1278  	if err != nil {
  1279  		return nil
  1280  	}
  1281  
  1282  	return &Actions{
  1283  		Actions: []action{
  1284  			{
  1285  				handlerAddr:  common.HexToAddress(hKyberAddr),
  1286  				data:         data,
  1287  				ethersNeeded: ethersNeeded,
  1288  			},
  1289  		},
  1290  	}
  1291  
  1292  }
  1293  
  1294  // Sushiswap----------------------------------------------------------------------
  1295  
  1296  // SushiswapClient struct
  1297  type SushiswapClient struct {
  1298  	client *DefiClient
  1299  }
  1300  
  1301  // Sushiswap returns a Sushiswap client.
  1302  func (c *DefiClient) Sushiswap() *SushiswapClient {
  1303  	sushiswapClient := new(SushiswapClient)
  1304  	sushiswapClient.client = c
  1305  	return sushiswapClient
  1306  }
  1307  
  1308  // SwapActions create a new swap action.
  1309  func (c *SushiswapClient) SwapActions(size *big.Int, baseCurrency coinType, quoteCurrency coinType) *Actions {
  1310  	var callData []byte
  1311  	var ethersNeeded = big.NewInt(0)
  1312  	var approvalTokens []common.Address = nil
  1313  	var approvalTokenAmounts []*big.Int = nil
  1314  
  1315  	if quoteCurrency == ETH {
  1316  		ethersNeeded = size
  1317  		callData = swapETHToTokenData(size, baseCurrency)
  1318  	} else {
  1319  		if baseCurrency == ETH {
  1320  			approvalTokens = []common.Address{CoinToAddressMap[quoteCurrency]}
  1321  			approvalTokenAmounts = []*big.Int{size}
  1322  			callData = swapTokenToETHData(size, quoteCurrency)
  1323  		} else {
  1324  			approvalTokens = []common.Address{CoinToAddressMap[quoteCurrency]}
  1325  			approvalTokenAmounts = []*big.Int{size}
  1326  			callData = swapTokenToTokenData(size, baseCurrency, quoteCurrency)
  1327  		}
  1328  	}
  1329  
  1330  	return &Actions{
  1331  		Actions: []action{
  1332  			{
  1333  				handlerAddr:          common.HexToAddress(hSushiswapAddr),
  1334  				data:                 callData,
  1335  				ethersNeeded:         ethersNeeded,
  1336  				approvalTokens:       approvalTokens,
  1337  				approvalTokenAmounts: approvalTokenAmounts,
  1338  			},
  1339  		},
  1340  	}
  1341  }
  1342  
  1343  // Curve-------------------------------------------------------------------------
  1344  
  1345  // CurveClient struct
  1346  type CurveClient struct {
  1347  	client *DefiClient
  1348  }
  1349  
  1350  // Curve returns a Curve client.
  1351  func (c *DefiClient) Curve() *CurveClient {
  1352  	curveClient := new(CurveClient)
  1353  	curveClient.client = c
  1354  	return curveClient
  1355  }
  1356  
  1357  // ExchangeActions creates a Curve exchange action to swap from one stable coin to another.
  1358  func (c *CurveClient) ExchangeActions(
  1359  	handler common.Address, token1Addr common.Address, token2Addr common.Address,
  1360  	i *big.Int, j *big.Int, dx *big.Int, minDy *big.Int) *Actions {
  1361  
  1362  	parsed, err := abi.JSON(strings.NewReader(hcurve.HcurveABI))
  1363  	if err != nil {
  1364  		return nil
  1365  	}
  1366  
  1367  	data, err := parsed.Pack("exchange", handler, token1Addr, token2Addr, i, j, dx, minDy)
  1368  
  1369  	if err != nil {
  1370  		return nil
  1371  	}
  1372  	return &Actions{
  1373  		Actions: []action{
  1374  			{
  1375  				handlerAddr:          common.HexToAddress(hCurveAddr),
  1376  				data:                 data,
  1377  				ethersNeeded:         big.NewInt(0),
  1378  				approvalTokens:       []common.Address{token1Addr},
  1379  				approvalTokenAmounts: []*big.Int{dx},
  1380  			},
  1381  		},
  1382  	}
  1383  }
  1384  
  1385  // ExchangeUnderlyingActions creates a Curve exchangeUnderlying action.
  1386  // `handler` is the address of the Curve pool.
  1387  // `token1Addr` is the address of the input token.
  1388  // `token2Addr` is the address of the output token.
  1389  // `i` is the index of the input token in the pool.
  1390  // `j` is the index of the output token in the pool.
  1391  // `dx` is the amount of the input token that you want to swap
  1392  // `minDy` is the minimum amount of the output token that you want to receive.
  1393  func (c *CurveClient) ExchangeUnderlyingActions(handler common.Address, token1Addr common.Address, token2Addr common.Address, i *big.Int, j *big.Int, dx *big.Int, minDy *big.Int) *Actions {
  1394  	parsed, err := abi.JSON(strings.NewReader(hcurve.HcurveABI))
  1395  	if err != nil {
  1396  		return nil
  1397  	}
  1398  
  1399  	data, err := parsed.Pack("exchangeUnderlying", handler, token1Addr, token2Addr, i, j, dx, minDy)
  1400  	if err != nil {
  1401  		return nil
  1402  	}
  1403  
  1404  	return &Actions{
  1405  		Actions: []action{
  1406  			{
  1407  				handlerAddr:          common.HexToAddress(hCurveAddr),
  1408  				data:                 data,
  1409  				ethersNeeded:         big.NewInt(0),
  1410  				approvalTokens:       []common.Address{token1Addr},
  1411  				approvalTokenAmounts: []*big.Int{dx},
  1412  			},
  1413  		},
  1414  	}
  1415  }
  1416  
  1417  // AddLiquidityActions adds liqudity to the given pool.
  1418  // `handler` is the address of the Curve pool.
  1419  // `pool` is the address of the pool token, e.g. bCRV token or 3CRV token.
  1420  // `tokens` is the addresses of the tokens that is in the pool.
  1421  // `amounts` is how much amount of each tokens you want to deposit.
  1422  // `minAmount` is the minimum amount of pool token that you want to get back as a result.
  1423  func (c *CurveClient) AddLiquidityActions(
  1424  	handler common.Address, pool common.Address, tokens []common.Address,
  1425  	amounts []*big.Int, minAmount *big.Int) *Actions {
  1426  
  1427  	parsed, err := abi.JSON(strings.NewReader(hcurve.HcurveABI))
  1428  	if err != nil {
  1429  		return nil
  1430  	}
  1431  
  1432  	data, err := parsed.Pack("addLiquidity", handler, pool, tokens, amounts, minAmount)
  1433  
  1434  	if err != nil {
  1435  		return nil
  1436  	}
  1437  	return &Actions{
  1438  		Actions: []action{
  1439  			{
  1440  				handlerAddr:          common.HexToAddress(hCurveAddr),
  1441  				data:                 data,
  1442  				ethersNeeded:         big.NewInt(0),
  1443  				approvalTokens:       tokens,
  1444  				approvalTokenAmounts: amounts,
  1445  			},
  1446  		},
  1447  	}
  1448  }
  1449  
  1450  // RemoveLiquidityActions creates remove liquidity action on Curve.
  1451  // `handler` is the address of the Curve pool.
  1452  // `pool` is the address of the pool token, e.g. bCRV token or 3CRV token.
  1453  // `tokenI` is the addresse of the tokens that you want to remove.
  1454  // `tokenAmount` is how much amount of token you want to deposit.
  1455  // `i` is the index of the token in the given pool.
  1456  // `minAmount` is the minimum amount of the underlying token that you want to get back as a result.
  1457  func (c *CurveClient) RemoveLiquidityActions(
  1458  	handler common.Address, pool common.Address, tokenI common.Address, tokenAmount *big.Int, i *big.Int, minAmount *big.Int,
  1459  ) *Actions {
  1460  	parsed, err := abi.JSON(strings.NewReader(hcurve.HcurveABI))
  1461  	if err != nil {
  1462  		return nil
  1463  	}
  1464  
  1465  	data, err := parsed.Pack("removeLiquidityOneCoin", handler, pool, tokenI, tokenAmount, i, minAmount)
  1466  
  1467  	if err != nil {
  1468  		return nil
  1469  	}
  1470  	return &Actions{
  1471  		Actions: []action{
  1472  			{
  1473  				handlerAddr:          common.HexToAddress(hCurveAddr),
  1474  				data:                 data,
  1475  				ethersNeeded:         big.NewInt(0),
  1476  				approvalTokens:       []common.Address{pool},
  1477  				approvalTokenAmounts: []*big.Int{tokenAmount},
  1478  			},
  1479  		},
  1480  	}
  1481  }
  1482  
  1483  // Maker------------------------------------------------------------------------
  1484  
  1485  // MakerClient is an instance of Maker protocol.
  1486  type MakerClient struct {
  1487  	client *DefiClient
  1488  }
  1489  
  1490  // Maker creates a new instance of MakerClient
  1491  func (c *DefiClient) Maker() *MakerClient {
  1492  	makerClient := new(MakerClient)
  1493  	makerClient.client = c
  1494  	return makerClient
  1495  }
  1496  
  1497  // GenerateDaiAction generate an action to create a vault and get some DAI
  1498  func (c *MakerClient) GenerateDaiAction(collateralAmount *big.Int, daiAmount *big.Int, collateralType coinType) *Actions {
  1499  	if collateralType == ETH {
  1500  		return c.generateDaiActionETH(collateralAmount, daiAmount)
  1501  	} else {
  1502  		return c.generateDaiActionErc20(collateralAmount, daiAmount, collateralType)
  1503  	}
  1504  }
  1505  
  1506  func (c *MakerClient) generateDaiActionETH(collateralAmount *big.Int, daiAmount *big.Int) *Actions {
  1507  
  1508  	parsed, err := abi.JSON(strings.NewReader(hmaker.HmakerABI))
  1509  	if err != nil {
  1510  		return nil
  1511  	}
  1512  
  1513  	data, err := parsed.Pack("openLockETHAndDraw", collateralAmount, CoinToJoinMap[ETH], CoinToJoinMap[DAI], CoinToIlkMap[ETH], daiAmount)
  1514  
  1515  	if err != nil {
  1516  		return nil
  1517  	}
  1518  	return &Actions{
  1519  		Actions: []action{
  1520  			{
  1521  				handlerAddr:  common.HexToAddress(hMakerDaoAddr),
  1522  				data:         data,
  1523  				ethersNeeded: collateralAmount,
  1524  			},
  1525  		},
  1526  	}
  1527  }
  1528  
  1529  func (c *MakerClient) generateDaiActionErc20(collateralAmount *big.Int, daiAmount *big.Int, collateralType coinType) *Actions {
  1530  	parsed, err := abi.JSON(strings.NewReader(hmaker.HmakerABI))
  1531  	if err != nil {
  1532  		return nil
  1533  	}
  1534  
  1535  	data, err := parsed.Pack("openLockGemAndDraw", CoinToJoinMap[collateralType], CoinToJoinMap[DAI], CoinToIlkMap[collateralType], collateralAmount, daiAmount)
  1536  
  1537  	if err != nil {
  1538  		return nil
  1539  	}
  1540  	return &Actions{
  1541  		Actions: []action{
  1542  			{
  1543  				handlerAddr:          common.HexToAddress(hMakerDaoAddr),
  1544  				data:                 data,
  1545  				ethersNeeded:         big.NewInt(0),
  1546  				approvalTokens:       []common.Address{CoinToAddressMap[collateralType]},
  1547  				approvalTokenAmounts: []*big.Int{collateralAmount},
  1548  			},
  1549  		},
  1550  	}
  1551  }
  1552  
  1553  // DepositCollateralActions deposits additional collateral to the given vault.
  1554  func (c *MakerClient) DepositCollateralActions(collateralAmount *big.Int, collateralType coinType, cdp *big.Int) *Actions {
  1555  	if collateralType == ETH {
  1556  		return c.depositETHActions(collateralAmount, collateralType, cdp)
  1557  	} else {
  1558  		return c.depositERC20Actions(collateralAmount, collateralType, cdp)
  1559  	}
  1560  }
  1561  
  1562  func (c *MakerClient) depositETHActions(collateralAmount *big.Int, collateralType coinType, cdp *big.Int) *Actions {
  1563  	parsed, err := abi.JSON(strings.NewReader(hmaker.HmakerABI))
  1564  	if err != nil {
  1565  		return nil
  1566  	}
  1567  
  1568  	data, err := parsed.Pack("safeLockETH", collateralAmount, CoinToJoinMap[ETH], cdp)
  1569  
  1570  	if err != nil {
  1571  		return nil
  1572  	}
  1573  	return &Actions{
  1574  		Actions: []action{
  1575  			{
  1576  				handlerAddr:  common.HexToAddress(hMakerDaoAddr),
  1577  				data:         data,
  1578  				ethersNeeded: collateralAmount,
  1579  			},
  1580  		},
  1581  	}
  1582  }
  1583  
  1584  func (c *MakerClient) depositERC20Actions(collateralAmount *big.Int, collateralType coinType, cdp *big.Int) *Actions {
  1585  	parsed, err := abi.JSON(strings.NewReader(hmaker.HmakerABI))
  1586  	if err != nil {
  1587  		return nil
  1588  	}
  1589  
  1590  	data, err := parsed.Pack("safeLockGem", CoinToJoinMap[collateralType], cdp, collateralAmount)
  1591  
  1592  	if err != nil {
  1593  		return nil
  1594  	}
  1595  	return &Actions{
  1596  		Actions: []action{
  1597  			{
  1598  				handlerAddr:          common.HexToAddress(hMakerDaoAddr),
  1599  				data:                 data,
  1600  				ethersNeeded:         big.NewInt(0),
  1601  				approvalTokens:       []common.Address{CoinToAddressMap[collateralType]},
  1602  				approvalTokenAmounts: []*big.Int{collateralAmount},
  1603  			},
  1604  		},
  1605  	}
  1606  }
  1607  
  1608  // WipeAction creates a wipe action to decrease debt for th given cdp/vault.
  1609  func (c *MakerClient) WipeAction(daiAmount *big.Int, cdp *big.Int) *Actions {
  1610  	parsed, err := abi.JSON(strings.NewReader(hmaker.HmakerABI))
  1611  	if err != nil {
  1612  		return nil
  1613  	}
  1614  
  1615  	data, err := parsed.Pack("wipe", CoinToJoinMap[DAI], cdp, daiAmount)
  1616  
  1617  	if err != nil {
  1618  		return nil
  1619  	}
  1620  	return &Actions{
  1621  		Actions: []action{
  1622  			{
  1623  				handlerAddr:  common.HexToAddress(hMakerDaoAddr),
  1624  				data:         data,
  1625  				ethersNeeded: big.NewInt(0),
  1626  			},
  1627  		},
  1628  	}
  1629  }
  1630  
  1631  // Balancer-----------------------------------------------------------
  1632  
  1633  // BalancerClient is an instance of Balancer protocol.
  1634  type BalancerClient struct {
  1635  	client *DefiClient
  1636  }
  1637  
  1638  // Balancer creates a new instance of BalancerClient
  1639  func (c *DefiClient) Balancer() *BalancerClient {
  1640  	balancerClient := new(BalancerClient)
  1641  	balancerClient.client = c
  1642  	return balancerClient
  1643  }
  1644  
  1645  // Swap swaps on Balancer Exchange
  1646  func (c *BalancerClient) Swap(inputCoin coinType, outputCoin coinType, inputAmount *big.Int) *Actions {
  1647  	parsed, err := abi.JSON(strings.NewReader(hbalancer_exchange.HbalancerExchangeABI))
  1648  	if err != nil {
  1649  		return nil
  1650  	}
  1651  
  1652  	data, err := parsed.Pack("smartSwapExactIn", CoinToAddressMap[inputCoin], CoinToAddressMap[outputCoin], inputAmount, big.NewInt(0), big.NewInt(10))
  1653  
  1654  	if err != nil {
  1655  		return nil
  1656  	}
  1657  
  1658  	if inputCoin == ETH {
  1659  		return &Actions{
  1660  			Actions: []action{
  1661  				{
  1662  					handlerAddr:  common.HexToAddress(hBalancerExchangeAddr),
  1663  					data:         data,
  1664  					ethersNeeded: inputAmount,
  1665  				},
  1666  			},
  1667  		}
  1668  	} else {
  1669  		return &Actions{
  1670  			Actions: []action{
  1671  				{
  1672  					handlerAddr:          common.HexToAddress(hBalancerExchangeAddr),
  1673  					data:                 data,
  1674  					ethersNeeded:         big.NewInt(0),
  1675  					approvalTokens:       []common.Address{CoinToAddressMap[inputCoin]},
  1676  					approvalTokenAmounts: []*big.Int{inputAmount},
  1677  				},
  1678  			},
  1679  		}
  1680  	}
  1681  
  1682  }
  1683  
  1684  // utility------------------------------------------------------------------------
  1685  
  1686  // Approve approves ERC-20 token transfer.
  1687  func Approve(client *DefiClient, coin coinType, addr common.Address, size *big.Int) error {
  1688  	erc20Contract, err := erc20.NewErc20(CoinToAddressMap[coin], client.conn)
  1689  	if err != nil {
  1690  		return err
  1691  	}
  1692  	opts := &bind.TransactOpts{
  1693  		Signer:   client.opts.Signer,
  1694  		From:     client.opts.From,
  1695  		GasLimit: 500000,
  1696  		GasPrice: big.NewInt(20000000000),
  1697  	}
  1698  	tx, err := erc20Contract.Approve(opts, addr, size)
  1699  	bind.WaitMined(context.Background(), client.conn, tx)
  1700  	return nil
  1701  }
  1702  
  1703  // Convert string to a fixed length 32 byte.
  1704  func byte32PutString(s string) [32]byte {
  1705  	var res [32]byte
  1706  	decoded, err := hex.DecodeString(s)
  1707  	if err != nil {
  1708  		return res
  1709  	}
  1710  	if len(s) > 32 {
  1711  		copy(res[:], decoded)
  1712  	} else {
  1713  		copy(res[32-len(s):], decoded)
  1714  	}
  1715  	return res
  1716  }