import BigNumber from 'bignumber.js' ; import { NATIVE_SOLANA_MINT_ADDRESS } from '@shared/constants/blockchain/native-token-address' ; import { BIG_NUMBER_FORMAT } from '@shared/constants/formats/big-number-format' ; import { Account , AccountInfo , Connection , PublicKey , RpcResponseAndContext , Transaction , TransactionResponse } from '@solana/web3.js' ; import { compareAddresses } from '@shared/utils/utils' ; import { SolanaWallet } from '@core/services/blockchain/wallets/wallets-adapters/solana/models/types' ; import { BlockchainTokenExtended } from '@shared/models/tokens/blockchain-token-extended' ; import { CommonWalletAdapter } from '@core/services/blockchain/wallets/wallets-adapters/common-wallet-adapter' ; import { Web3Public } from '@core/services/blockchain/blockchain-adapters/common/web3-public' ; import { Web3Pure } from '@core/services/blockchain/blockchain-adapters/common/web3-pure' ; import InsufficientFundsError from '@core/errors/models/instant-trade/insufficient-funds-error' ; import { base58 } from 'ethers/lib/utils' ; type ReturnValue = Promise < { result : RpcResponseAndContext < Array < { pubkey : PublicKey ; account : AccountInfo < { parsed : { info : { tokenAmount : { amount : number ; decimals : number ; } ; mint : string ; } ; } ; } > ; } > > ; } > ; export class SolanaWeb3Public extends Web3Public < null , TransactionResponse > { public static addressToBytes32 ( address ) { return ( '0x' + Array . from ( base58 . decode ( address ) ) . map ( num => num . toString ( 16 ) . padStart ( 2 , '0' ) ) . reduce ( ( acc , hexNum ) => acc + hexNum , '' ) ) ; } public readonly connection : Connection ; constructor ( connection ) { super ( ) ; this . connection = connection ; } /**
     * Gets allowance.
     */ public async getAllowance ( ) { return new BigNumber ( Infinity ) ; } param address public isAddressCorrect ( address ) { try { return Boolean ( new PublicKey ( address ) ?. toBase58 ( ) ) ; } catch { return false ; } } param address public isNativeAddress = ( address ) : boolean => { return address === NATIVE_SOLANA_MINT_ADDRESS ; } ; public async getTokenInfo ( ) { return null ; } param userAddress param tokenAddress public async getTokenOrNativeBalance ( userAddress , tokenAddress ) { return this . isNativeAddress ( tokenAddress ) ? new BigNumber ( ( await this . connection . getBalanceAndContext ( new PublicKey ( userAddress ) , 'confirmed' ) ) . value . toString ( ) ) : ( await this . getTokensBalances ( userAddress , [ tokenAddress ] ) ) ?. [ 0 ] ; } param tokenAddress param address return public async getTokenBalance ( address , tokenAddress ) { return ( await this . getTokensBalances ( address , [ tokenAddress ] ) ) ?. [ 0 ] ; } /**
     * Predicts the volume of gas required to execute the contract method
     */ public async getEstimatedGas ( ) { const { feeCalculator } = await this . connection . getRecentBlockhash ( ) ; return new BigNumber ( feeCalculator . lamportsPerSignature ) ; } param hash param attempt param attemptsLimit param delay public async getTransactionByHash ( hash , attempt ? , attemptsLimit ? , delay ? ) { attempt = attempt || 0 ; const limit = attemptsLimit || 10 ; const timeoutMs = delay || 500 ; if ( attempt >= limit ) { return null ; } const transaction = await this . connection . getTransaction ( hash ) ; if ( transaction === null ) { return new Promise ( resolve => setTimeout ( ( ) => resolve ( this . getTransactionByHash ( hash , attempt + 1 ) ) , timeoutMs ) ) ; } return transaction ; } param address param tokensAddresses public async getTokensBalances ( address , tokensAddresses ) { const resp = await ( this . connection as Connection & { _rpcRequest : ( owner : string , data : unknown [ ] ) => ReturnValue ; } ) . _rpcRequest ( 'getTokenAccountsByOwner' , [ address , { programId : 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA' } , { encoding : 'jsonParsed' } ] ) ; const nativeSolBalance = await this . connection . getBalanceAndContext ( new PublicKey ( address ) , 'confirmed' ) ; return tokensAddresses . map ( tokenAddress => { if ( tokenAddress === NATIVE_SOLANA_MINT_ADDRESS ) { return new BigNumber ( nativeSolBalance . value . toString ( ) ) ; } const tokenWithBalance = resp . result . value . find ( token => { const { info } = token . account . data . parsed ; return compareAddresses ( info . mint , tokenAddress ) ; } ) ; return new BigNumber ( tokenWithBalance ? tokenWithBalance . account . data . parsed . info . tokenAmount . amount : NaN ) ; } ) ; } public async signTransaction ( walletAdapter , transaction , signers = [ ] ) { transaction . recentBlockhash = ( await this . connection . getRecentBlockhash ( ) ) . blockhash ; transaction . setSigners ( new PublicKey ( walletAdapter . address ) , ... signers . map ( s => s . publicKey ) ) ; if ( signers . length > 0 ) { transaction . partialSign ( ... signers ) ; } return await walletAdapter . wallet . signTransaction ( transaction ) ; } param token param amount param userAddress public async checkBalance ( token , amount , userAddress ) { const balance = await this . getTokenBalance ( userAddress , token . address ) ; const amountAbsolute = Web3Pure . toWei ( amount , token . decimals ) ; if ( balance . lt ( amountAbsolute ) ) { const formattedTokensBalance = Web3Pure . fromWei ( balance , token . decimals ) . toFormat ( BIG_NUMBER_FORMAT ) ; throw new InsufficientFundsError ( token . symbol , formattedTokensBalance , amount . toFormat ( BIG_NUMBER_FORMAT ) ) ; } } }