import StellarSdk from "stellar-sdk" ; import { browser , Runtime } from "webextension-polyfill-ts" ; import { ExternalRequest as Request } from "@shared/api/types" ; import { MessageResponder } from "background/types" ; import { FlaggedKeys , TransactionInfo } from "types/transactions" ; import { EXTERNAL_SERVICE_TYPES } from "@shared/constants/services" ; import { getNetworkDetails , MAINNET_NETWORK_DETAILS , } from "@shared/helpers/stellar" ; import { STELLAR_DIRECTORY_URL } from "background/constants/apiUrls" ; import { POPUP_WIDTH } from "constants/dimensions" ; import { ALLOWLIST_ID } from "constants/localStorageTypes" ; import { TRANSACTION_WARNING } from "constants/transaction" ; import { getIsTestnet , getIsMemoValidationEnabled , getIsSafetyValidationEnabled , } from "background/helpers/account" ; import { isSenderAllowed } from "background/helpers/allowListAuthorization" ; import { cachedFetch } from "background/helpers/cachedFetch" ; import { encodeObject , getUrlHostname , getPunycodedDomain } from "helpers/urls" ; import { store } from "background/store" ; import { publicKeySelector } from "background/ducks/session" ; import { responseQueue , transactionQueue } from "./popupMessageListener" ; interface WINDOW_PARAMS { height : number ; type : "popup" ; width : number ; } const WINDOW_SETTINGS = { type : "popup" , width : POPUP_WIDTH , height : 667 , } ; export const freighterApiMessageListener = ( request , sender ) => { const requestAccess = ( ) => { const publicKey = publicKeySelector ( store . getState ( ) ) ; const { tab , url : tabUrl = "" } = sender ; if ( isSenderAllowed ( { sender } ) && publicKey ) { return { publicKey } ; } const encodeOrigin = encodeObject ( { tab , url : tabUrl } ) ; browser . windows . create ( { url : chrome . runtime . getURL ( `/index.html#/grant-access?${ encodeOrigin }` ) , ... WINDOW_SETTINGS , } ) ; return new Promise ( ( resolve ) => { const response = ( url ? ) => { if ( url === tabUrl ) { resolve ( { publicKey : publicKeySelector ( store . getState ( ) ) } ) ; } resolve ( { error : "User declined access" } ) ; } ; responseQueue . push ( response ) ; } ) ; } ; const submitTransaction = async ( ) => { const { transactionXdr , network = MAINNET_NETWORK_DETAILS . network , } = request ; const isTestnet = getIsTestnet ( ) ; const { networkUrl } = getNetworkDetails ( isTestnet ) ; const transaction = StellarSdk . TransactionBuilder . fromXDR ( transactionXdr , StellarSdk . Networks [ network ] ) ; const { tab , url : tabUrl = "" } = sender ; const domain = getUrlHostname ( tabUrl ) ; const punycodedDomain = getPunycodedDomain ( domain ) ; const allowListStr = localStorage . getItem ( ALLOWLIST_ID ) || "" ; const allowList = allowListStr . split ( "," ) ; const isDomainListedAllowed = isSenderAllowed ( { sender } ) ; const directoryLookupJson = await cachedFetch ( STELLAR_DIRECTORY_URL ) ; const accountData = directoryLookupJson ?. _embedded ?. records || [ ] ; const _operations = transaction . _operations || transaction . _innerTransaction . _operations ; const flaggedKeys = { } ; const isValidatingMemo = getIsMemoValidationEnabled ( ) && ! isTestnet ; const isValidatingSafety = getIsSafetyValidationEnabled ( ) && ! isTestnet ; if ( isValidatingMemo || isValidatingSafety ) { _operations . forEach ( ( operation ) => { accountData . forEach ( ( { address , tags } ) => { if ( address === operation . destination ) { const collectedTags = [ ... tags ] ; if ( ! isValidatingMemo ) { collectedTags . filter ( ( tag ) => tag !== TRANSACTION_WARNING . memoRequired ) ; } if ( ! isValidatingSafety ) { collectedTags . filter ( ( tag ) => tag !== TRANSACTION_WARNING . unsafe ) ; collectedTags . filter ( ( tag ) => tag !== TRANSACTION_WARNING . malicious ) ; } flaggedKeys [ operation . destination ] = { ... flaggedKeys [ operation . destination ] , tags : collectedTags , } ; } } ) ; } ) ; } const server = new StellarSdk . Server ( networkUrl ) ; try { await server . checkMemoRequired ( transaction ) ; } catch ( e ) { flaggedKeys [ e . accountId ] = { ... flaggedKeys [ e . accountId ] , tags : [ TRANSACTION_WARNING . memoRequired ] , } ; } const transactionInfo = { transaction , tab , isDomainListedAllowed , url : tabUrl , flaggedKeys , } as TransactionInfo ; transactionQueue . push ( transaction ) ; const encodetransactionInfo = encodeObject ( transactionInfo ) ; const popup = browser . windows . create ( { url : chrome . runtime . getURL ( `/index.html#/sign-transaction?${ encodetransactionInfo }` ) , ... WINDOW_SETTINGS , } ) ; return new Promise ( ( resolve ) => { if ( ! popup ) { resolve ( { error : "Couldn't open access prompt" } ) ; } else { browser . windows . onRemoved . addListener ( ( ) => resolve ( { error : "User declined access" , } ) ) ; } const response = ( signedTransaction ) => { if ( signedTransaction ) { if ( ! isDomainListedAllowed ) { allowList . push ( punycodedDomain ) ; localStorage . setItem ( ALLOWLIST_ID , allowList . join ( ) ) ; } resolve ( { signedTransaction } ) ; } resolve ( { error : "User declined access" } ) ; } ; responseQueue . push ( response ) ; } ) ; } ; const requestNetwork = ( ) => { let network = "" ; try { ( { network } = getNetworkDetails ( getIsTestnet ( ) ) ) ; } catch ( error ) { console . error ( error ) ; return { error } ; } return { network } ; } ; const messageResponder = { [ EXTERNAL_SERVICE_TYPES . REQUEST_ACCESS ] : requestAccess , [ EXTERNAL_SERVICE_TYPES . SUBMIT_TRANSACTION ] : submitTransaction , [ EXTERNAL_SERVICE_TYPES . REQUEST_NETWORK ] : requestNetwork , } ; return messageResponder [ request . type ] ( ) ; } ;