Skip to content

Commit

Permalink
feat: calculate rate of pay slider values for different assets (#219)
Browse files Browse the repository at this point in the history
* Fetch exchange rates and convert rate of pay

* Fix conversion

* Rename to `rateOfPay`
  • Loading branch information
raducristianpopa authored Apr 16, 2024
1 parent 641bfdf commit bf0ecc0
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 7 deletions.
5 changes: 4 additions & 1 deletion src/background/config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
export const DEFAULT_AMOUNT = '0.6'
export const DEFAULT_SCALE = 2
export const DEFAULT_INTERVAL_MS = 3_600_000

export const DEFAULT_RATE_OF_PAY = '60'
export const MIN_RATE_OF_PAY = '1'
export const MAX_RATE_OF_PAY = '100'
41 changes: 39 additions & 2 deletions src/background/services/openPayments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,17 @@ import { type Request } from 'http-message-signatures'
import { signMessage } from 'http-message-signatures/lib/httpbis'
import { createContentDigestHeader } from 'httpbis-digest-headers'
import { Browser } from 'webextension-polyfill'
import { getCurrentActiveTabId, toAmount } from '../utils'
import { getCurrentActiveTabId, getRateOfPay, toAmount } from '../utils'
import { StorageService } from '@/background/services/storage'
import { exportJWK, generateEd25519KeyPair } from '@/shared/crypto'
import { bytesToHex } from '@noble/hashes/utils'
import { getWalletInformation } from '@/shared/helpers'
import { getExchangeRates, getWalletInformation } from '@/shared/helpers'
import { ConnectWalletPayload } from '@/shared/messages'
import {
DEFAULT_RATE_OF_PAY,
MAX_RATE_OF_PAY,
MIN_RATE_OF_PAY
} from '../config'

interface KeyInformation {
privateKey: string
Expand Down Expand Up @@ -217,6 +222,35 @@ export class OpenPaymentsService {
recurring
}: ConnectWalletPayload) {
const walletAddress = await getWalletInformation(walletAddressUrl)
const exchangeRates = await getExchangeRates()

let rateOfPay = DEFAULT_RATE_OF_PAY
let minRateOfPay = MIN_RATE_OF_PAY
let maxRateOfPay = MAX_RATE_OF_PAY

if (!exchangeRates.rates[walletAddress.assetCode]) {
throw new Error(`Exchange rate for ${walletAddress.assetCode} not found.`)
}

const exchangeRate = exchangeRates.rates[walletAddress.assetCode]
if (exchangeRate < 0.8 || exchangeRate > 1.5) {
rateOfPay = getRateOfPay({
rate: DEFAULT_RATE_OF_PAY,
exchangeRate,
assetScale: walletAddress.assetScale
})
minRateOfPay = getRateOfPay({
rate: MIN_RATE_OF_PAY,
exchangeRate,
assetScale: walletAddress.assetScale
})
maxRateOfPay = getRateOfPay({
rate: MAX_RATE_OF_PAY,
exchangeRate,
assetScale: walletAddress.assetScale
})
}

const transformedAmount = toAmount({
value: amount,
recurring,
Expand Down Expand Up @@ -262,6 +296,9 @@ export class OpenPaymentsService {

this.storage.set({
walletAddress,
rateOfPay,
minRateOfPay,
maxRateOfPay,
amount: transformedAmount,
token: {
value: continuation.access_token.value,
Expand Down
7 changes: 5 additions & 2 deletions src/background/services/storage.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DEFAULT_AMOUNT, DEFAULT_INTERVAL_MS } from '@/background/config'
import { DEFAULT_RATE_OF_PAY, DEFAULT_INTERVAL_MS } from '@/background/config'
import { Logger } from '@/shared/logger'
import type {
PopupStore,
Expand All @@ -16,6 +16,9 @@ const defaultStorage = {
amount: null,
token: null,
grant: null,
rateOfPay: null,
minRateOfPay: null,
maxRateOfPay: null
} satisfies Omit<Storage, 'publicKey' | 'privateKey' | 'keyId'>

export class StorageService {
Expand Down Expand Up @@ -87,7 +90,7 @@ export class StorageService {
website.amount = data.exceptionList[url]
} else {
website.amount = {
value: DEFAULT_AMOUNT,
value: DEFAULT_RATE_OF_PAY,
interval: DEFAULT_INTERVAL_MS
}
}
Expand Down
18 changes: 18 additions & 0 deletions src/background/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { WalletAmount } from '@/shared/types'
import { type Browser, action, runtime } from 'webextension-polyfill'
import { DEFAULT_SCALE } from './config'

const iconActive34 = runtime.getURL('assets/icons/icon-active-34.png')
const iconActive128 = runtime.getURL('assets/icons/icon-active-128.png')
Expand Down Expand Up @@ -49,3 +50,20 @@ export const OPEN_PAYMENTS_ERRORS: Record<string, string> = {
'invalid client':
'Please make sure that you uploaded the public key for your desired wallet address.'
}

export interface GetRateOfPayParams {
rate: string
exchangeRate: number
assetScale: number
}

export const getRateOfPay = ({
rate,
exchangeRate,
assetScale
}: GetRateOfPayParams) => {
const scaleDiff = assetScale - DEFAULT_SCALE
const scaledExchangeRate = (1 / exchangeRate) * 10 ** scaleDiff

return BigInt(Math.round(Number(rate) * scaledExchangeRate)).toString()
}
22 changes: 22 additions & 0 deletions src/shared/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,25 @@ export const failure = (message: string) => ({
success: false,
message
})

interface ExchangeRates {
base: string
rates: Record<string, number>
}

export const getExchangeRates = async (): Promise<ExchangeRates> => {
const response = await fetch(
'https://telemetry-exchange-rates.s3.amazonaws.com/exchange-rates-usd.json'
)
if (!response.ok) {
throw new Error(
`Could not fetch exchange rates. [Status code: ${response.status}]`
)
}
const rates = await response.json()
if (!rates.base || !rates.rates) {
throw new Error('Invalid rates format')
}

return rates
}
8 changes: 6 additions & 2 deletions src/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ export interface Storage {
enabled: boolean
/** If a wallet is connected or not */
connected: boolean

rateOfPay?: string | undefined | null
minRateOfPay?: string | undefined | null
maxRateOfPay?: string | undefined | null

/** User wallet address information */
walletAddress?: WalletAddress | undefined | null
/** Overall amount */
Expand Down Expand Up @@ -60,6 +65,5 @@ export type PopupStore = Omit<
}

export type DeepNonNullable<T> = {
[P in keyof T]?: NonNullable<T[P]>;
[P in keyof T]?: NonNullable<T[P]>
}

0 comments on commit bf0ecc0

Please sign in to comment.