You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/DEVELOP.md
+102-8Lines changed: 102 additions & 8 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -127,22 +127,48 @@ Each service/component/file is named so that you can have a general idea of what
127
127
128
128
Background script/service worker (it's service worker in Chrome, and script in Firefox at the moment, but there's no specific difference from our perspective) is the backend of our extension. It runs locally on the user's device, not on a remote server. The background script is composed of various services:
129
129
130
+
```mermaid
131
+
mindmap
132
+
root{{Background}}
133
+
Monetization
134
+
PaymentManager
135
+
PaymentStream
136
+
PaymentSession
137
+
(_messaging_)
138
+
SendToPopup
139
+
EventsService
140
+
TabEvents
141
+
(_state_)
142
+
TabState
143
+
WindowState
144
+
Storage
145
+
(_helpers_)
146
+
OpenPayments
147
+
HeartBeat
148
+
Wallet
149
+
KeyAutoAdd
150
+
```
151
+
130
152
-**`Background`**:
131
153
- The "main" entry point for background.
132
154
- Handles messages from popup and content scripts
133
155
- Handles post-install & post-update events
134
156
- Listens to many other "global" events and orchestrates other services to take action.
135
157
-**`MonetizationService`**:
136
-
- The real orchestrator of payments
137
-
- Sets up `PaymentSession`s
158
+
- The real orchestrator of payments, for all tabs
159
+
- Sets up `PaymentManager`s
138
160
- Handles messages from background/content-scripts/popup to maintain/update/control the payment sessions
139
-
- Handles events from different payment sessions
161
+
-**`PaymentManager`**
162
+
- Manages payments for a tab
163
+
- Abstracts all monetization link elements in a page, manages payment sessions
164
+
- Keep track of what amount to send, which session to send to, and when to send
165
+
-**`PaymentStream`**
166
+
- Contains sessions for link elements in a "frame" within the tab
167
+
(host website is main frame (id=0), rest are _iframes_)
140
168
-**`PaymentSession`**:
141
-
- Abstraction over `OpenPaymentsService`
142
-
- Maintains payment sessions to keep a tab on payments (whether active or can become active)
143
-
- Trigger continuous payment streams or send one-time payments
144
-
- Publish events to a website where a payment was sent
145
-
- Keep track of what amount to send, and when to send.
169
+
- Abstraction over a monetization link element
170
+
- Calls OpenPayments APIs to make actual payments
171
+
- Publish events to a website when a payment was sent
146
172
-**`OpenPaymentsService`**:
147
173
- An abstraction over OpenPayments client
148
174
- Preserves tokens, and manages a single client for all operations.
@@ -165,6 +191,9 @@ Background script/service worker (it's service worker in Chrome, and script in F
165
191
-**`SendToPopup`**:
166
192
- Send messages from the background to popup via `Runtime.Port`
167
193
- Exists as we send messages to the popup often, and sending via a regular message channel would be noisy.
194
+
-**`WalletService`**
195
+
- Handles wallet connection, budget updates, disconnecting and reconnecting wallet
196
+
- Uses `KeyAutoAddService` to add keys during connection
168
197
-**`KeyAutoAddService`**:
169
198
- Different wallets have different ways to upload public keys, and we don't want users to be scared by the complexity of "public keys"
170
199
- This service complimented by its counterpart content scripts, takes control of the user's browser (after their consent) to add the key via reverse engineering how different wallets upload keys.
@@ -235,6 +264,71 @@ Please read the [MDN docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-
235
264
236
265
Note that, we only support [manifest version 3](https://developer.chrome.com/docs/extensions/develop/migrate/what-is-mv3), and not version 2.
237
266
267
+
### Payment mechanism
268
+
269
+
1. The content script finds the valid `<link rel="monetization">` elements and sends a message to the background.
270
+
2. The background then sets up a payment session for this element - via `Monetization` (singleton for browser instance) → `PaymentManager` (for tab) → `PaymentStream` (for frame) → `PaymentSession` (for element).
271
+
3. Once a payment is made, the `PaymentSession` passes on an event to the website (via `polyfill.ts`), and in turn a `MonetizationEvent` is emitted.
272
+
273
+
#### `minSendAmount`
274
+
275
+
When a `PaymentSession` is created, we find a `minSendAmount` before we try to make any payment. This value is used in figuring out the amounts and timings for the payments for given link element. The `minSendAmount` is obtained by creating a quote with particular amount and seeing if the quote is accepted at that amount.
276
+
277
+
The `minSendAmount` is a property of user's connected wallet (currency/assetScale), the wallet for the given link element and associated payment fees at that time. For a browsing session (which _resets_ on navigation), we typically find this amount only once. But if future payments fail, we may need to adjust the `minSendAmount` again.
278
+
279
+
We make payments only in multiples of `minSendAmount`. This ensures some fairness for the sender and receivers - as the amounts higher than in-multiple of `minSendAmount` are absorbed by ASEs to maintain cross-currency liquidity for other transactions when user may send an amount less than multiple of `minSendAmount`.
280
+
281
+
#### Open-payments flow
282
+
283
+
To make a payment with Open Payments, following steps are taken (presuming we've the OutgoingPaymentGrant tokens available, i.e. user has connected the extension to their wallet):
284
+
285
+
1. Create incoming payment:
286
+
1. Set up a non-interactive incoming payment grant for the receiving wallet address.
287
+
1. Create an incoming payment using this grant for the receiving wallet address.
288
+
1. Optionally, cancel the incoming payment grant, as we no longer need it (we already have the incoming payment). This prevents users wallet seeing _dangling_ grants in their wallet.
289
+
1. Find the `minSendAmount`:
290
+
1. Find a possible amount for the receiving wallet using various heuristics (currency exchange rate, asset scales, asset codes etc.)
291
+
2. Exponential probing: Try creating a quote with this amount until it doesn't fail with a "non-positive receive amount" error (i.e. the receiver has to receive at least one unit for a quote to succeed), increasing it in an exponential manner.
292
+
- If the OpenPayments request with a "non-positive receive amount" error, error and includes a `minSendAmount` in error details (this is a relatively recent OpenPayments feature), stop the process to find the minimum sendable amount her1.
293
+
3. Binary search: Once a sendable amount is found, use binary search (between the sendable amount and previously attempted amount) to find the minimum sendable amount.
294
+
1. Create outgoing payment:
295
+
1. Call the create outgoing payment OpenPayments API to create a fix-send outgoing payment. Include the following details:
296
+
-`accessToken`: The access token for the outgoing payment grant (i.e. the one we get from connecting the wallet). This token needs to be rotated once in a wIfIf the token is expired, try the payment again with a refreshed token.
297
+
-`incomingPayment`: The incoming payment ID/URL we got in step 1.
298
+
-`debitAmount`: The amount we want to send.
299
+
300
+
#### One-time payments
301
+
302
+
When a user wants to send one-time payment, we distribute the amount chosen by the user across the _payable_ payment sessions, while respecting the `minSendAmount` of each session.
303
+
304
+
We then create multiple payments as described the flow above, to each session that gets a non-zero amount following the above distribution.
305
+
306
+
The distribution logic is defined in [#1098](https://github.com/interledger/web-monetization-extension/pull/1098).
307
+
308
+
#### Continuous payments
309
+
310
+
Continuous payments involve a timing component, managed by `PaymentManager`.
311
+
312
+
Depending on the user's chosen rate of pay, we find the interval at which we can increase a "pending amount" that can be paid out. Every "interval" ms, we increment the pending amount by some units.
313
+
The interval can't be lower than `MIN_PAYMENT_WAIT` - which defines a minimum time between consecutive payments, for performance reasons. We adjust the interval and increment accordingly.
314
+
315
+
From the multiple payment sessions, then we have to choose the session we want to pay next. How sessions are chosen is defined in [#1066](https://github.com/interledger/web-monetization-extension/pull/1066), but essentially we go sequentially in the order we loaded the link elements, while prioritizing the sessions on the "main frame" (host website over iframes).
316
+
317
+
<details>
318
+
319
+
- First, go through all payable link tags on the main website, one by one.
320
+
- Then, pay the first link in the first iframe, then the first link in the second iframe.
321
+
- Then, again go through all payable link tags on the main website.
322
+
- Then, pay the second link of the first iframe, then the second link (if it doesn't exist, then the first again) of the second iframe.
323
+
- Then, again go through all payable link tags on the main website.
324
+
- Then, again pay the first link in the first iframe, then the first link in the second iframe, and so on.
325
+
326
+
</details>
327
+
328
+
The "pending amount" acts like a bucket, and we empty the bucket by making a payment in a multiple of the chosen session's `minSendAmount` (the bucket isn't always emptied depending on the values of increments and min-send amounts). If there's not enough pending payment, we wait until there is (but still paying the same chosen payment session), and then move onto the next payment session.
329
+
330
+
We keep doing this indefinitely (until monetization is stopped by some user action or long inactivity), cycling through the payment sessions.
0 commit comments