# SDK Event Listeners

The Wallet emits events when properties change, for example, when the authenticated user changes their network. Events allow your dApp to keep in sync. You can hook onto event listeners via methods exposed in the SDK, writing your own handling logic that you want to fire when the event is received.

You can only register one event listener per message listener - if you do try to listen to a listener which has already been registered, it will throw a error. You can cancel listeners as necessary, and you'll then be able to register them again without an error being thrown.

The SDK exposes two different ways to register an event listener:

  1. Event listeners: listen to all events of a specified type until the listener is cancelled explicitly.
  2. One-off listeners: subscribe only to the first event of a specified type. Once the event has fired, your dApp will be unsubscribed and will receive no further events of the type. An example use case of a one-off listener might be when your dApp wants to update its state based on the next block mined, but isn't interested in subsequent changes to the blockchain.

Once the SDK is initialized, you should hook onto all the event listeners, as each one is important to the integration. We recommend that the callback you write for each message listener is just a simple state change to your Redux store, allowing that state change to be reflected in your UI components. Reactive programming (e.g. RxJS) will simplify the integration, and while it's not required (the SDK itself doesn't impose any constraints on this), it is our recommended integration approach for a clean solution.

# Registering an Event Listener

    # Registering a One-Off Listener

      # Event listeners response formats

      Results are returned to the listener as follows:

      {
        data: TStronglyTypedResponse,
        origin: string,
        source: Window
      }
      

      This is the result of the postMessage operation performed by the SDK.

      As well as indicating the origin and the source of the received data, the returned result will always have a data property which specifies the data for the event being received.

      data The object passed from the other window.

      origin The origin of the window that sent the message at the time postMessage was called. This string is the concatenation of the protocol and "://", the host name if one exists, and ":" followed by a port number if a port is present and differs from the default port for the given protocol. Examples of typical origins are https://example.org/ (implying port 443), http://example.net/ (implying port 80), and http://example.com:8080/. The origin will always put the / at the end so make sure you include that in your compare.

      Note that this origin is not guaranteed to be the current or future origin of that window, which might have been navigated to a different location since postMessage was called.

      You should always check that its https://wallet.funfair.io/ sending the post messages to be sure its came from us.

      source A reference to the window object that sent the message. You can use this to establish two-way communication between two windows with different origins.

      # Security Concerns

      💡 Always verify the sender's identity using the origin and possibly source properties. Any window (including, for example, http://evil.example.com) can send a message to any other window, and you have no guarantee that an unknown sender will not send malicious messages. Having verified identity, however, you still should always verify the syntax of the received message. Otherwise, a security hole in the site you trusted to send only trusted messages could then open a cross-site scripting hole in your site.

      💡 To protect your dApp's users from cross-site scripting attacks, make sure you NEVER assign the data result from the postMessage to any HTML elements:

      window.funwallet.sdk.once(MessageListeners.TypeYouWantToUse, (result) => {
        if (result.origin === 'https://wallet.funfair.io/') {
          // OUCH!! YOU HAVE OPENED YOURSELF UP FOR THE TRUSTED DOMAINS TO
          // INJECT BAD SCRIPTS INTO YOUR PAGE. AS RULE OF THUMB, NEVER, *EVER*
          // DO THIS (DON'T WORRY WE WOULD NEVER DO SOMETHING SO MEAN :D)
          document.getElementById('message').innerHTML = result.data;
        }
      });
      

      Always specify an exact target origin, not "*", when you use postMessage to send data to other windows. A malicious site can change the location of the window without your knowledge, and therefore it can intercept the data sent using postMessage.

      Please note, the SDK does check this as well and only connects messages from the Wallet but as the SDK is hosted on your side and exported globally on the window we suggest you check the origin as well. The messages the Wallet sends you are just information based anyway, we never register a event which says "go and execute this script on the parent site" so this makes it a lot less to worry about but we still suggest you abide by the security concerns addressed. If you fail to check this you can not be sure that the message has came from the Wallet.

      # List of All Available Listeners

      {
        restoreAuthenticationCompleted = 'restoreAuthenticationCompleted',
        // @deprecated Please use `window.funwallet.sdk.login` promise instead.
        authenticationCompleted = 'authenticationCompleted',
        followerAuthenticationCompleted = 'followerAuthenticationCompleted',
        changeNetwork = 'changeNetwork',
        walletInactivityLoggedOut = 'walletInactivityLoggedOut',
        walletDeviceDeletedLoggedOut = 'walletDeviceDeletedLoggedOut',
        pendingTransaction = 'pendingTransaction',
        completedTransaction = 'completedTransaction',
        erc20TokenBalanceChanged = 'erc20TokenBalanceChanged',
        erc20TokenFiatPriceChanged = 'erc20TokenFiatPriceChanged',
        ethBalanceChanged = 'ethBalanceChanged',
        ethFiatPriceChanged = 'ethFiatPriceChanged',
        changeCurrency = 'changeCurrency',
        isKycVerified = 'isKycVerified',
        kycProcessCancelled = 'kycProcessCancelled',
        websocketConnected = 'websocketConnected',
        websocketDisconnected = 'websocketDisconnected',
        newBlock = 'newBlock',
        playerProtectionUpdated = 'playerProtectionUpdated',
        walletTracking = 'walletTracking',
        authenticationPopUpClosed = 'authenticationPopUpClosed',
        transactionReplaced = 'transactionReplaced',
        appJwtExtended = 'appJwtExtended',
      }
      

      All the examples of code here will use the on but in all of these cases, you can also use once if you require one-off listening functionality.

      # restoreAuthenticationCompleted

      To allow restoring someone to be logged in after they refresh on initial load the Wallet tries to restore a session. Upon success, it will emit restoreAuthenticationCompleted telling you if it's restored a user's session or not with any authentication data. You should disable any sign-in/up click button until you get this event.

        result.data returns:

        {
          // this will be defined if `isAuthenticated` is true
          result?: AuthenticationCompletedResponeData | undefined,
          isAuthenticated: boolean,
        }
        
        export interface AuthenticationCompletedResponeData {
          authenticationCompleted: {
            playerProtection: ExclusionStatusResponse;
            ethereumAddress: string;
            currentCurrency: string;
            // details of this interface is just below in `changeNetwork` response data
            currentNetwork: NetworkDetails;
            userAccountId: string;
          };
        }
        

        ExclusionStatusResponse:

        {
            status: ExclusionStatusType;
            startTimestamp?: number | undefined;
            durationDays?: number | undefined;
            activeTimestamp?: number | undefined;
        }
        

        ExclusionStatusType:

        export enum ExclusionStatusType {
          ACTIVE = 'ACTIVE',
          ON_BREAK = 'ON_BREAK',
          EXCLUDED = 'EXCLUDED',
        }
        

        NetworkDetails:

        {
          selectedNode: string;
          backupNodes: string[];
          enabled: boolean;
          name: string;
          id: number;
          chainId: number;
          nativeCurrency: string;
          networkBlockExplorer?: NetworkBlockExplorer | undefined;
          feeWarningThreshold: BigNumber;
          supportsFiatPrices: boolean;
          supportsWalletConnect: boolean;
          isProductionNetwork: boolean;
          fixedGasPrice?: string | undefined;
        }
        

        # authenticationCompleted

        DEPRECATED

        Please use window.funwallet.sdk.login promise instead.

        This will fire when the leader instance has been authenticated by a user. It is a much better flow in your dApp to use the login async method now so we do not recommend using this approach.

          result.data returns:

          {
            // can just be ignored if your dapp does not care about player protection
            playerProtection: ExclusionStatusResponse;
            ethereumAddress: string;
            currentCurrency: string;
            currentNetwork: NetworkDetails;
            // the fun wallets id for this user
            userAccountId: string;
          }
          

          ExclusionStatusResponse:

          {
              status: ExclusionStatusType;
              startTimestamp?: number | undefined;
              durationDays?: number | undefined;
              activeTimestamp?: number | undefined;
          }
          

          ExclusionStatusType:

          export enum ExclusionStatusType {
            ACTIVE = 'ACTIVE',
            ON_BREAK = 'ON_BREAK',
            EXCLUDED = 'EXCLUDED',
          }
          

          NetworkDetails:

          {
            selectedNode: string;
            backupNodes: string[];
            enabled: boolean;
            name: string;
            id: number;
            chainId: number;
            nativeCurrency: string;
            networkBlockExplorer?: NetworkBlockExplorer | undefined;
            feeWarningThreshold: BigNumber;
            supportsFiatPrices: boolean;
            supportsWalletConnect: boolean;
            isProductionNetwork: boolean;
            fixedGasPrice?: string | undefined;
          }
          

          # followerAuthenticationCompleted

          This will fire when the follower instance has authenticated itself successfully and indicates that you should re-enable any disabled Wallet buttons.

            result.data returns:

            {
              followerAuthenticationCompleted: boolean,
            }
            

            # changeNetwork

            This will fire when the Wallet network has been changed. Note: this will always fire upon initial authentication of the leader as networks will update as a result of authentication.

              result.data returns:

              {
                network: NetworkDetails,
              }
              

              NetworkDetails:

              {
                selectedNode: string;
                backupNodes: string[];
                enabled: boolean;
                name: string;
                id: number;
                chainId: number;
                nativeCurrency: string;
                networkBlockExplorer?: NetworkBlockExplorer | undefined;
                feeWarningThreshold: BigNumber;
                supportsFiatPrices: boolean;
                supportsWalletConnect: boolean;
                isProductionNetwork: boolean;
                fixedGasPrice?: string | undefined;
              }
              

              # walletInactivityLoggedOut

              This will fire when the inactivity timeout has expired, meaning all authenticated instances have now been logged out.

                result.data returns:

                {
                  loggedOut: boolean,
                }
                

                # walletDeviceDeletedLoggedOut

                This will fire when the current device the user is using has been deleted, meaning all authenticated instances have now been logged out.

                  result.data returns:

                  {
                    loggedOut: boolean,
                  }
                  

                  # appJwtExtended

                  This will fire when the appJwt token has been extended.

                    result.data returns:

                    {
                      jwtToken: string;
                    }
                    

                    # pendingTransaction

                    This will fire when a pending transaction has occurred on the Wallet. We suggest if your dApp has sent this transaction and wants to hook onto certain notifications, e.g. the transaction hash, receipt etc just use the framework your using to get that data (ethers/web3).

                      result.data returns:

                      {
                        transactionHash: string,
                        transaction: {
                          to: string;
                          from: string;
                          nonce: string;
                          gasLimit: string;
                          gasPrice: string;
                          data: string;
                          value: string;
                          chainId: number;
                        }
                      }
                      

                      # completedTransaction

                      This will fire when a completed transaction has occurred on the Wallet (i.e. upon the first confirmation). We suggest if your dApp has sent this transaction and wants to hook onto certain notifications, e.g. the transaction hash, receipt etc just use the framework your using to get that data (ethers/web3).

                        result.data returns:

                        {
                          transactionReceipt: {
                            to: string;
                            from: string;
                            contractAddress: string;
                            transactionIndex: number;
                            root?: string;
                            gasUsed: string;
                            logsBloom: string;
                            blockHash: string;
                            transactionHash: string;
                            logs: Array<{
                              blockNumber: number;
                              blockHash: string;
                              transactionIndex: number;
                              removed: boolean;
                              address: string;
                              data: string;
                              topics: Array<string>;
                              transactionHash: string;
                              logIndex: number;
                            }>;
                            blockNumber: number;
                            confirmations: number;
                            cumulativeGasUsed: string;
                            byzantium: boolean;
                            status?: number;
                          },
                          blockTimestamp: number,
                        }
                        

                        # transactionReplaced

                        This will fire when the user cancels the transaction or speeds it up within the Wallet itself. Most wallets do not handle this meaning that your dApp polls forever but if it happens when using the FunFair Wallet, we will throw an error if you're waiting for the receipt. Also, we will emit this event which tells you the oldHash the newHash and the reason it got replaced allowing you to link the old transaction to the new one without having to monitor events.

                          result.data returns:

                          {
                            oldHash: string,
                            newHash: string,
                            replacedReason: 'gasIncreased' | 'cancelled',
                          }
                          

                          # erc20TokenBalanceChanged

                          This will fire when an ERC20 token balance changes for the authenticated user.

                            result.data returns:

                            {
                              symbol: string;
                              contractAddress: string;
                              networkId: number;
                              // the balance is pre-formatted
                              // to the correct maximum decimal
                              tokenBalance: string;
                              tokenIndex: number;
                            }
                            

                            # erc20TokenFiatPriceChanged

                            This will fire when an ERC20 token's fiat price changes. Fiat prices are monitored by the Wallet server and updated regularly. Any change will trigger this event.

                              result.data returns:

                              {
                                symbol: string;
                                contractAddress: string;
                                networkId: number;
                                fiatPrice: number;
                                tokenIndex: number;
                              }
                              

                              # ethBalanceChanged

                              This will fire when the ETH balance changes for the authenticated user.

                                result.data returns:

                                {
                                  // the ethBalance is pre-formatted
                                  // to the correct maximum decimal
                                  ethBalance: string,
                                }
                                

                                # ethFiatPriceChanged

                                This will fire when the ETH fiat price changes. Fiat prices are monitored by the Wallet server and updated regularly. Any change will trigger this event.

                                  result.data returns:

                                  {
                                    fiatPrice: number,
                                  }
                                  

                                  # changeCurrency

                                  This will fire when the authenticated user's selected currency has changed.

                                    result.data returns:

                                    {
                                      currency: string,
                                    }
                                    

                                    # isKycVerified

                                    This will fire upon initial login, whether the account is KYC-verified or not. This allows you to pop up the KYC modal automatically when a user logs in, if necessary.

                                      result.data returns:

                                      {
                                        isVerified: boolean;
                                      }
                                      

                                      # kycProcessCancelled

                                      This will fire when the authenticated account going through the KYC process cancels the modal and goes back to the dApp website.

                                        result.data returns:

                                        {
                                          cancelled: boolean;
                                        }
                                        

                                        # websocketConnected

                                        This will fire when a successful WebSocket connection is made, including any time a dApp reconnects after being disconnected. Please keep the WebSocket connected status in memory, and when websocketDisconnected is received, update the value of the WebSocket connection status.

                                          result.data returns:

                                          {
                                            connected: boolean;
                                          }
                                          

                                          # websocketDisconnected

                                          This will fire when the WebSocket disconnects or gets closed.

                                            result.data returns:

                                            {
                                              disconnected: boolean;
                                            }
                                            

                                            # newBlock

                                            This will fire when the Wallet receives a new block alert through the WebSocket connection. This removes the need for any polling - the dApp can just listen for these events. You can hook onto this even if the user is not authenticated.

                                              result.data returns:

                                              {
                                                networkId: Networks;
                                                blockNumber: number;
                                                blockHash: string;
                                                bloomFilter: string;
                                                timestamp: number;
                                              }
                                              

                                              # playerProtectionUpdated

                                              This will fire when the player protection data has been updated, for example if they have self-excluded or changed their exclusion reactivation date.

                                                result.data returns:

                                                {
                                                  status: ExclusionStatusType;
                                                  startTimestamp?: number | undefined;
                                                  durationDays?: number | undefined;
                                                  activeTimestamp?: number | undefined;
                                                }
                                                

                                                ExclusionStatusType

                                                export enum ExclusionStatusType {
                                                  ACTIVE = 'ACTIVE',
                                                  ON_BREAK = 'ON_BREAK',
                                                  EXCLUDED = 'EXCLUDED',
                                                }
                                                

                                                # walletTracking

                                                Due to the Wallet holding private keys in memory, we don't allow Google analytics scripts in the Wallet itself. However, this event emits tracking data back to the client so the you can pass them to any tracking software you want to use.

                                                  result.data returns:

                                                  {
                                                      eventCategory: TrackingEventCategory;
                                                      eventAction: TrackingEventAction;
                                                      eventLabel?: TrackingEventLabel | undefined;
                                                      eventValue?: number | undefined;
                                                  }
                                                  
                                                  export declare enum TrackingEventCategory {
                                                    walletRegistration = 'walletRegistration',
                                                    walletRecovery = 'walletRecovery',
                                                    wallet = 'wallet',
                                                    accountDeposit = 'accountDeposit',
                                                    accountWithdrawal = 'accountWithdrawal',
                                                    accountTokenTransfers = 'accountTokenTransfers',
                                                    accountTransactions = 'accountTransactions',
                                                    accountSettings = 'accountSettings',
                                                    accountSecurity = 'accountSecurity',
                                                    accountResponsibleGaming = 'accountResponsibleGaming',
                                                    approveTransaction = 'approveTransaction',
                                                    approveSigningText = 'approveSigningText',
                                                  }
                                                  
                                                  export declare enum TrackingEventAction {
                                                    step = 'step',
                                                    click = 'click',
                                                  }
                                                  
                                                  export declare enum TrackingEventLabel {
                                                    emailValidatingRequest = 'emailValidatingRequest',
                                                    resendEmail = 'resendEmail',
                                                    emailValidationComplete = 'emailValidationComplete',
                                                    backToSignIn = 'backToSignIn',
                                                    emailValidationFailed = 'emailValidationFailed',
                                                    closeAndReturnToApp = 'closeAndReturnToApp',
                                                    passwordSetup = 'passwordSetup',
                                                    registrationComplete = 'registrationComplete',
                                                    startKyc = 'startKyc',
                                                    closeKyc = 'closeKyc',
                                                    verifyMyId = 'verifyMyId',
                                                    selectCountry = 'selectCountry',
                                                    kycLoaded = 'kycLoaded',
                                                    kycCompleted = 'kycCompleted',
                                                    kycFailed = 'kycFailed',
                                                    kycFallbackLoaded = 'kycFallbackLoaded',
                                                    recoveryRequest = 'recoveryRequest',
                                                    noRecoveryPossible = 'noRecoveryPossible',
                                                    recoveryEmailSent = 'recoveryEmailSent',
                                                    recoveryEmailValidated = 'recoveryEmailValidated',
                                                    recoveryComplete = 'recoveryComplete',
                                                    createAccount = 'createAccount',
                                                    signIn = 'signIn',
                                                    signInFailed = 'signInFailed',
                                                    signInCompleted = 'signInCompleted',
                                                    forgotPassword = 'forgotPassword',
                                                    deposit = 'deposit',
                                                    ethOrFun = 'ethOrFun',
                                                    copyAddress = 'copyAddress',
                                                    coinSwap = 'coinSwap',
                                                    createdCoinSwap = 'createdCoinSwap',
                                                    withdrawal = 'withdrawal',
                                                    withdrawalMax = 'withdrawalMax',
                                                    withdrawalAdvanced = 'widhtrawalAdvanced',
                                                    withdrawalChangeGasPrice = 'withdrawalChangeGasPrice',
                                                    withdrawalClick = 'withdrawalClick',
                                                    withdrawalCompleted = 'withdrawalCompleted',
                                                    withdrawalFailed = 'withdrawalFailed',
                                                    withdrawalCancelled = 'withdrawalCancelled',
                                                    tokenTransfers = 'tokenTransfers',
                                                    tokenTransfersNext = 'tokenTransfersNext',
                                                    tokenTransfersPrevious = 'tokenTransfersPrevious',
                                                    transactions = 'transactions',
                                                    transactoinViewMoreInfo = 'transactoinViewMoreInfo',
                                                    transactionGoToEtherscan = 'transactionGoToEtherscan',
                                                    transactionSpeedUp = 'transactionSpeedUp',
                                                    transactionCancel = 'transactionCancel',
                                                    transactionsNext = 'transactionsNext',
                                                    transactionsPrevious = 'transactionsPrevious',
                                                    accountSettings = 'accountSettings',
                                                    networkChange = 'networkChange',
                                                    currencyChange = 'currencyChange',
                                                    signerChange = 'signerChange',
                                                    exportWallet = 'exportWallet',
                                                    accountSecurity = 'accountSecurity',
                                                    changeEmail = 'changeEmail',
                                                    changeEmailcompleted = 'changeEmailcompleted',
                                                    changePassword = 'changePassword',
                                                    changePasswordCompleted = 'changePasswordCompleted',
                                                    secondaryEmail = 'secondaryEmail',
                                                    secondaryEmailCompleted = 'secondaryEmailCompleted',
                                                    secondaryEmailDeleted = 'secondaryEmailDeleted',
                                                    twoFactorAuthentication = 'twoFactorAuthentication',
                                                    twoFactorAuthenticationEnabled = 'twoFactorAuthenticationEnabled',
                                                    twoFactorAuthenticationDisabled = 'twoFactorAuthenticationDisabled',
                                                    deviceActivity = 'deviceActivity',
                                                    deviceDeleted = 'deviceDeleted',
                                                    thirdPartySigners = 'thirdPartySigners',
                                                    thirdPartyAddSigner = 'thirdPartyAddSigner',
                                                    thirdPartyAddSignerCompleted = 'thirdPartyAddSignerCompleted',
                                                    thirdPartyAddSignerFailed = 'thirdPartyAddSignerFailed',
                                                    responsibleGaming = 'responsibleGaming',
                                                    takeABreak = 'takeABreak',
                                                    takeABreakClick = 'takeABreakClick',
                                                    takeABreakCompleted = 'takeABreakCompleted',
                                                    takeABreakCancelled = 'takeABreakCancelled',
                                                    extendBreak = 'extendBreak',
                                                    extendBreakClick = 'extendBreakClick',
                                                    extendBreakCompleted = 'extendBreakCompleted',
                                                    extendBreakFailed = 'extendBreakFailed',
                                                    extendBreakCancelled = 'extendBreakCancelled',
                                                    selfExcluse = 'selfExcluse',
                                                    selfExcluseCompleted = 'selfExcluseCompleted',
                                                    selfExcluseCancelled = 'selfExcluseCancelled',
                                                    reactivateAccountModal = 'reactivateAccountModal',
                                                    reactivateAccount = 'reactivateAccount',
                                                    approveTransaction = 'approveTransaction',
                                                    approveTransactionAdvanced = 'approveTransactionAdvanced',
                                                    approveTransactionApproved = 'approveTransactionApproved',
                                                    approveTransactionRejected = 'approveTransactionRejected',
                                                    approveSigningText = 'approveSigningText',
                                                    approveSigningTextApproved = 'approveSigningTextApproved',
                                                    approveSigningTextRejected = 'approveSigningTextRejected',
                                                  }
                                                  

                                                  # authenticationPopUpClosed

                                                  This emits an event when the authentication popup is closed. If isAuthenticated in the response is true, it means the close happened after a successful login; if it's false it means the user didn't go through the whole login process (for example, if they closed the popup).

                                                    result.data returns:

                                                    {
                                                      isAuthenticated: boolean;
                                                    }
                                                    

                                                    # Cancelling event listeners

                                                      This works the same as once:

                                                        Now if you try to listen to that message listener again it will work, as you have cancelled the other listener. On the once calls, once the once has been fired you will be able to register a new listener without an error being thrown.