github.com/status-im/status-go@v1.1.0/services/wallet/router/router_helper.go (about) 1 package router 2 3 import ( 4 "context" 5 "errors" 6 "math/big" 7 "strings" 8 9 "github.com/ethereum/go-ethereum" 10 "github.com/ethereum/go-ethereum/accounts/abi" 11 "github.com/ethereum/go-ethereum/accounts/abi/bind" 12 "github.com/ethereum/go-ethereum/common" 13 "github.com/ethereum/go-ethereum/common/hexutil" 14 "github.com/status-im/status-go/contracts" 15 gaspriceoracle "github.com/status-im/status-go/contracts/gas-price-oracle" 16 "github.com/status-im/status-go/contracts/ierc20" 17 "github.com/status-im/status-go/params" 18 "github.com/status-im/status-go/services/wallet/bigint" 19 walletCommon "github.com/status-im/status-go/services/wallet/common" 20 "github.com/status-im/status-go/services/wallet/router/fees" 21 "github.com/status-im/status-go/services/wallet/router/pathprocessor" 22 routs "github.com/status-im/status-go/services/wallet/router/routes" 23 "github.com/status-im/status-go/services/wallet/router/sendtype" 24 "github.com/status-im/status-go/services/wallet/token" 25 ) 26 27 func (r *Router) requireApproval(ctx context.Context, sendType sendtype.SendType, approvalContractAddress *common.Address, params pathprocessor.ProcessorInputParams) ( 28 bool, *big.Int, error) { 29 if sendType.IsCollectiblesTransfer() || sendType.IsEnsTransfer() || sendType.IsStickersTransfer() { 30 return false, nil, nil 31 } 32 33 if params.FromToken.IsNative() { 34 return false, nil, nil 35 } 36 37 contractMaker, err := contracts.NewContractMaker(r.rpcClient) 38 if err != nil { 39 return false, nil, err 40 } 41 42 contract, err := contractMaker.NewERC20(params.FromChain.ChainID, params.FromToken.Address) 43 if err != nil { 44 return false, nil, err 45 } 46 47 if approvalContractAddress == nil || *approvalContractAddress == pathprocessor.ZeroAddress { 48 return false, nil, nil 49 } 50 51 if params.TestsMode { 52 return true, params.AmountIn, nil 53 } 54 55 allowance, err := contract.Allowance(&bind.CallOpts{ 56 Context: ctx, 57 }, params.FromAddr, *approvalContractAddress) 58 59 if err != nil { 60 return false, nil, err 61 } 62 63 if allowance.Cmp(params.AmountIn) >= 0 { 64 return false, nil, nil 65 } 66 67 return true, params.AmountIn, nil 68 } 69 70 func (r *Router) packApprovalInputData(amountIn *big.Int, approvalContractAddress *common.Address) ([]byte, error) { 71 if approvalContractAddress == nil || *approvalContractAddress == pathprocessor.ZeroAddress { 72 return []byte{}, nil 73 } 74 75 erc20ABI, err := abi.JSON(strings.NewReader(ierc20.IERC20ABI)) 76 if err != nil { 77 return []byte{}, err 78 } 79 80 return erc20ABI.Pack("approve", approvalContractAddress, amountIn) 81 } 82 83 func (r *Router) estimateGasForApproval(params pathprocessor.ProcessorInputParams, approvalContractAddress *common.Address) (uint64, error) { 84 data, err := r.packApprovalInputData(params.AmountIn, approvalContractAddress) 85 if err != nil { 86 return 0, err 87 } 88 89 ethClient, err := r.rpcClient.EthClient(params.FromChain.ChainID) 90 if err != nil { 91 return 0, err 92 } 93 94 return ethClient.EstimateGas(context.Background(), ethereum.CallMsg{ 95 From: params.FromAddr, 96 To: ¶ms.FromToken.Address, 97 Value: pathprocessor.ZeroBigIntValue, 98 Data: data, 99 }) 100 } 101 102 func (r *Router) calculateApprovalL1Fee(amountIn *big.Int, chainID uint64, approvalContractAddress *common.Address) (uint64, error) { 103 data, err := r.packApprovalInputData(amountIn, approvalContractAddress) 104 if err != nil { 105 return 0, err 106 } 107 108 ethClient, err := r.rpcClient.EthClient(chainID) 109 if err != nil { 110 return 0, err 111 } 112 113 var l1Fee uint64 114 oracleContractAddress, err := gaspriceoracle.ContractAddress(chainID) 115 if err == nil { 116 oracleContract, err := gaspriceoracle.NewGaspriceoracleCaller(oracleContractAddress, ethClient) 117 if err != nil { 118 return 0, err 119 } 120 121 callOpt := &bind.CallOpts{} 122 123 l1FeeResult, _ := oracleContract.GetL1Fee(callOpt, data) 124 l1Fee = l1FeeResult.Uint64() 125 } 126 127 return l1Fee, nil 128 } 129 130 func (r *Router) getERC1155Balance(ctx context.Context, network *params.Network, token *token.Token, account common.Address) (*big.Int, error) { 131 tokenID, success := new(big.Int).SetString(token.Symbol, 10) 132 if !success { 133 return nil, errors.New("failed to convert token symbol to big.Int") 134 } 135 136 balances, err := r.collectiblesManager.FetchERC1155Balances( 137 ctx, 138 account, 139 walletCommon.ChainID(network.ChainID), 140 token.Address, 141 []*bigint.BigInt{&bigint.BigInt{Int: tokenID}}, 142 ) 143 if err != nil { 144 return nil, err 145 } 146 147 if len(balances) != 1 || balances[0] == nil { 148 return nil, errors.New("invalid ERC1155 balance fetch response") 149 } 150 151 return balances[0].Int, nil 152 } 153 154 func (r *Router) getBalance(ctx context.Context, chainID uint64, token *token.Token, account common.Address) (*big.Int, error) { 155 client, err := r.rpcClient.EthClient(chainID) 156 if err != nil { 157 return nil, err 158 } 159 160 return r.tokenManager.GetBalance(ctx, client, account, token.Address) 161 } 162 163 func (r *Router) cacluateFees(ctx context.Context, path *routs.Path, fetchedFees *fees.SuggestedFees, testsMode bool, testApprovalL1Fee uint64) (err error) { 164 165 var ( 166 l1ApprovalFee uint64 167 ) 168 if path.ApprovalRequired { 169 if testsMode { 170 l1ApprovalFee = testApprovalL1Fee 171 } else { 172 l1ApprovalFee, err = r.calculateApprovalL1Fee(path.AmountIn.ToInt(), path.FromChain.ChainID, path.ApprovalContractAddress) 173 if err != nil { 174 return err 175 } 176 } 177 } 178 179 // TODO: keep l1 fees at 0 until we have the correct algorithm, as we do base fee x 2 that should cover the l1 fees 180 var l1FeeWei uint64 = 0 181 // if input.SendType.needL1Fee() { 182 // txInputData, err := pProcessor.PackTxInputData(processorInputParams) 183 // if err != nil { 184 // continue 185 // } 186 187 // l1FeeWei, _ = r.feesManager.GetL1Fee(ctx, network.ChainID, txInputData) 188 // } 189 190 r.lastInputParamsMutex.Lock() 191 gasFeeMode := r.lastInputParams.GasFeeMode 192 r.lastInputParamsMutex.Unlock() 193 maxFeesPerGas := fetchedFees.FeeFor(gasFeeMode) 194 195 // calculate ETH fees 196 ethTotalFees := big.NewInt(0) 197 txFeeInWei := new(big.Int).Mul(maxFeesPerGas, big.NewInt(int64(path.TxGasAmount))) 198 ethTotalFees.Add(ethTotalFees, txFeeInWei) 199 200 txL1FeeInWei := big.NewInt(0) 201 if l1FeeWei > 0 { 202 txL1FeeInWei = big.NewInt(int64(l1FeeWei)) 203 ethTotalFees.Add(ethTotalFees, txL1FeeInWei) 204 } 205 206 approvalFeeInWei := big.NewInt(0) 207 approvalL1FeeInWei := big.NewInt(0) 208 if path.ApprovalRequired { 209 approvalFeeInWei.Mul(maxFeesPerGas, big.NewInt(int64(path.ApprovalGasAmount))) 210 ethTotalFees.Add(ethTotalFees, approvalFeeInWei) 211 212 if l1ApprovalFee > 0 { 213 approvalL1FeeInWei = big.NewInt(int64(l1ApprovalFee)) 214 ethTotalFees.Add(ethTotalFees, approvalL1FeeInWei) 215 } 216 } 217 218 // calculate required balances (bonder and token fees are already included in the amountIn by Hop bridge (once we include Celar we need to check how they handle the fees)) 219 requiredNativeBalance := big.NewInt(0) 220 requiredTokenBalance := big.NewInt(0) 221 222 if path.FromToken.IsNative() { 223 requiredNativeBalance.Add(requiredNativeBalance, path.AmountIn.ToInt()) 224 if !path.SubtractFees { 225 requiredNativeBalance.Add(requiredNativeBalance, ethTotalFees) 226 } 227 } else { 228 requiredTokenBalance.Add(requiredTokenBalance, path.AmountIn.ToInt()) 229 requiredNativeBalance.Add(requiredNativeBalance, ethTotalFees) 230 } 231 232 // set the values 233 path.SuggestedLevelsForMaxFeesPerGas = fetchedFees.MaxFeesLevels 234 path.MaxFeesPerGas = (*hexutil.Big)(maxFeesPerGas) 235 236 path.TxBaseFee = (*hexutil.Big)(fetchedFees.BaseFee) 237 path.TxPriorityFee = (*hexutil.Big)(fetchedFees.MaxPriorityFeePerGas) 238 239 path.TxFee = (*hexutil.Big)(txFeeInWei) 240 path.TxL1Fee = (*hexutil.Big)(txL1FeeInWei) 241 242 path.ApprovalBaseFee = (*hexutil.Big)(fetchedFees.BaseFee) 243 path.ApprovalPriorityFee = (*hexutil.Big)(fetchedFees.MaxPriorityFeePerGas) 244 245 path.ApprovalFee = (*hexutil.Big)(approvalFeeInWei) 246 path.ApprovalL1Fee = (*hexutil.Big)(approvalL1FeeInWei) 247 248 path.TxTotalFee = (*hexutil.Big)(ethTotalFees) 249 250 path.RequiredTokenBalance = requiredTokenBalance 251 path.RequiredNativeBalance = requiredNativeBalance 252 253 return nil 254 }