Skip to content

Commit

Permalink
fix(background): ensure not to pay before interval, even on page relo…
Browse files Browse the repository at this point in the history
…ad (#349)

* Prevent overpaying

* Feedback

* Fix lint

* Implement feedback

* Implement feedback

* Implement feedback

* Implement feedback

* Implement feedback

* Implement feedback

* Fix lint

---------

Co-authored-by: Sid Vishnoi <[email protected]>
  • Loading branch information
dianafulga and sidvishnoi authored Jul 2, 2024
1 parent f511b5d commit 9a51650
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 9 deletions.
5 changes: 4 additions & 1 deletion src/background/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from './services'
import { createLogger, Logger } from '@/shared/logger'
import { LOG_LEVEL } from '@/shared/defines'
import { TabState } from './services/tabState'

interface Cradle {
logger: Logger
Expand All @@ -22,6 +23,7 @@ interface Cradle {
monetizationService: MonetizationService
tabEvents: TabEvents
background: Background
tabState: TabState
}

export const configureContainer = () => {
Expand Down Expand Up @@ -60,7 +62,8 @@ export const configureContainer = () => {
.singleton()
.inject(() => ({
logger: logger.getLogger('main')
}))
})),
tabState: asClass(TabState).singleton()
})

return container
Expand Down
19 changes: 15 additions & 4 deletions src/background/services/monetization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,17 @@ import {
} from '@/shared/messages'
import { PaymentSession } from './paymentSession'
import { emitToggleWM } from '../lib/messages'
import { computeRate, getCurrentActiveTab, getSender, getTabId } from '../utils'
import {
computeRate,
getCurrentActiveTab,
getSender,
getTabId,
removeQueryParams
} from '../utils'
import { EventsService } from './events'
import { ALLOWED_PROTOCOLS } from '@/shared/defines'
import type { PopupStore } from '@/shared/types'
import { TabState } from './tabState'

export class MonetizationService {
private sessions: {
Expand All @@ -23,7 +30,8 @@ export class MonetizationService {
private openPaymentsService: OpenPaymentsService,
private storage: StorageService,
private browser: Browser,
private events: EventsService
private events: EventsService,
private tabState: TabState
) {
this.sessions = {}
this.registerEventListeners()
Expand Down Expand Up @@ -51,7 +59,7 @@ export class MonetizationService {
)
return
}
const { tabId, frameId } = getSender(sender)
const { tabId, frameId, url, tab } = getSender(sender)

if (this.sessions[tabId] == null) {
this.sessions[tabId] = new Map()
Expand All @@ -74,10 +82,13 @@ export class MonetizationService {
receiver,
connectedWallet,
requestId,
tab,
tabId,
frameId,
rate,
this.openPaymentsService
this.openPaymentsService,
this.tabState,
removeQueryParams(url!)
)

sessions.set(requestId, session)
Expand Down
25 changes: 24 additions & 1 deletion src/background/services/paymentSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { OpenPaymentsClientError } from '@interledger/open-payments/dist/client'
import { sendMonetizationEvent } from '../lib/messages'
import { convert, sleep } from '@/shared/helpers'
import { transformBalance } from '@/popup/lib/utils'
import { TabState } from './tabState'
import type { Tabs } from 'webextension-polyfill'

const DEFAULT_INTERVAL_MS = 1000
const HOUR_MS = 3600 * 1000
Expand All @@ -24,10 +26,13 @@ export class PaymentSession {
private receiver: WalletAddress,
private sender: WalletAddress,
private requestId: string,
private tab: Tabs.Tab,
private tabId: number,
private frameId: number,
private rate: string,
private openPaymentsService: OpenPaymentsService
private openPaymentsService: OpenPaymentsService,
private tabState: TabState,
private url: string
) {
this.adjustSessionAmount()
}
Expand Down Expand Up @@ -122,6 +127,14 @@ export class PaymentSession {

let outgoingPayment: OutgoingPayment | undefined

const waitTime = this.tabState.getOverpayingWaitTime(
this.tab,
this.url,
this.receiver.id
)

await sleep(waitTime)

while (this.active) {
try {
outgoingPayment = await this.openPaymentsService.createOutgoingPayment({
Expand Down Expand Up @@ -172,6 +185,16 @@ export class PaymentSession {
}
})

// TO DO: find a better source of truth for deciding if overpaying is applicable
if (this.intervalInMs > 1000) {
this.tabState.saveOverpaying(
this.tab,
this.url,
this.receiver.id,
this.intervalInMs
)
}

await sleep(this.intervalInMs)
}
}
Expand Down
59 changes: 59 additions & 0 deletions src/background/services/tabState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { Tabs } from 'webextension-polyfill'

type State = {
lastPaymentTimestamp: number
expiresAtTimestamp: number
}

export class TabState {
private state = new WeakMap<Tabs.Tab, Map<string, State>>()

constructor() {}

private getOverpayingStateKey(url: string, walletAddressId: string): string {
return `${url}:${walletAddressId}`
}

getOverpayingWaitTime(
tab: Tabs.Tab,
url: string,
walletAddressId: string
): number {
const key = this.getOverpayingStateKey(url, walletAddressId)
const state = this.state.get(tab)?.get(key)
const now = Date.now()

if (state && state.expiresAtTimestamp > now) {
return state.expiresAtTimestamp - now
}

return 0
}

saveOverpaying(
tab: Tabs.Tab,
url: string,
walletAddressId: string,
intervalInMs: number
): void {
if (!intervalInMs) return

const now = Date.now()
const expiresAtTimestamp = now + intervalInMs

const key = this.getOverpayingStateKey(url, walletAddressId)
const state = this.state.get(tab)?.get(key)

if (!state) {
const tabState = this.state.get(tab) || new Map()
tabState.set(key, {
expiresAtTimestamp: expiresAtTimestamp,
lastPaymentTimestamp: now
})
this.state.set(tab, tabState)
} else {
state.expiresAtTimestamp = expiresAtTimestamp
state.lastPaymentTimestamp = now
}
}
}
17 changes: 14 additions & 3 deletions src/background/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AmountValue, GrantDetails, WalletAmount } from '@/shared/types'
import { type Browser, Runtime } from 'webextension-polyfill'
import type { AmountValue, GrantDetails, WalletAmount } from '@/shared/types'
import type { Browser, Runtime, Tabs } from 'webextension-polyfill'
import { DEFAULT_SCALE, EXCHANGE_RATES_URL } from './config'
import { notNullOrUndef } from '@/shared/helpers'

Expand Down Expand Up @@ -81,15 +81,26 @@ export const getTabId = (sender: Runtime.MessageSender): number => {
return notNullOrUndef(notNullOrUndef(sender.tab, 'sender.tab').id, 'tab.id')
}

export const getTab = (sender: Runtime.MessageSender): Tabs.Tab => {
return notNullOrUndef(notNullOrUndef(sender.tab, 'sender.tab'), 'tab')
}

export const getSender = (sender: Runtime.MessageSender) => {
const tabId = getTabId(sender)
const frameId = notNullOrUndef(sender.frameId, 'sender.frameId')
return { tabId, frameId }
const tab = getTab(sender)

return { tabId, frameId, url: sender.url, tab }
}

export const computeRate = (rate: string, sessionsCount: number) =>
(+rate / sessionsCount).toString()

export const removeQueryParams = (urlString: string) => {
const url = new URL(urlString)
return url.origin + url.pathname
}

export function computeBalance(
grant?: GrantDetails | null,
grantSpentAmount?: AmountValue | null
Expand Down

0 comments on commit 9a51650

Please sign in to comment.