Skip to content

Commit

Permalink
feat(locksmith) - add pricingController (#11911)
Browse files Browse the repository at this point in the history
add pricingController
  • Loading branch information
kalidiagne committed May 17, 2023
1 parent 7e47a1c commit 1fa92ee
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 125 deletions.
14 changes: 5 additions & 9 deletions locksmith/src/controllers/v2/priceController.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import { RequestHandler } from 'express'

import {
createPricingForPurchase,
createTotalCharges,
defiLammaPrice,
} from '../../utils/pricing'
import { createPricingForPurchase } from '../../utils/pricing'
import { ethers } from 'ethers'
import { getCreditCardEnabledStatus } from '../../operations/creditCardOperations'
import * as Normalizer from '../../utils/normalizer'
import { Web3Service } from '@unlock-protocol/unlock-js'
import networks from '@unlock-protocol/networks'
import * as pricingOperations from '../../operations/pricingOperations'

const MIN_PAYMENT_STRIPE = 100
export const amount: RequestHandler = async (request, response) => {
Expand All @@ -20,7 +16,7 @@ export const amount: RequestHandler = async (request, response) => {
? erc20Address
: undefined

const result = await defiLammaPrice({
const result = await pricingOperations.getDefiLammaPrice({
network,
amount,
address,
Expand All @@ -38,7 +34,7 @@ export const total: RequestHandler = async (request, response) => {
? erc20Address
: undefined

const charge = await createTotalCharges({
const charge = await pricingOperations.getTotalCharges({
network,
amount,
address,
Expand Down Expand Up @@ -110,7 +106,7 @@ export const isCardPaymentEnabledForLock: RequestHandler = async (
const web3Service = new Web3Service(networks)
const lock = await web3Service.getLock(lockAddress, network)

const result = await defiLammaPrice({
const result = await pricingOperations.getDefiLammaPrice({
network,
address: lock?.currencyContractAddress,
amount: Number(`${lock.keyPrice}`),
Expand Down
2 changes: 1 addition & 1 deletion locksmith/src/operations/creditCardOperations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const getCreditCardEnabledStatus = async ({
lockAddress,
network,
totalPriceInCents,
}: CreditCardStateProps) => {
}: CreditCardStateProps): Promise<boolean> => {
const fulfillmentDispatcher = new Dispatcher()

const [hasEnoughToPayForGas, stripeConnected, isAuthorizedForCreditCard] =
Expand Down
119 changes: 119 additions & 0 deletions locksmith/src/operations/pricingOperations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import networks from '@unlock-protocol/networks'
import { getFees, getGasCost } from '../utils/pricing'

interface Price {
decimals: number
symbol: string
price: number
timestamp: number
confidence: number
creditCardEnabled: boolean
}

export interface Options {
amount?: number
address?: string
network: number
}

export async function getDefiLammaPrice({
network,
address,
amount = 1,
}: Options): Promise<
Partial<
Price & {
priceInAmount: number
}
>
> {
const networkConfig = networks[network]
if (!network) {
return {}
}
const items: string[] = []
const coingecko = `coingecko:${networkConfig.nativeCurrency?.coingecko}`
const mainnetTokenAddress = networkConfig.tokens?.find(
(item) => item.address?.toLowerCase() === address?.toLowerCase()
)?.mainnetAddress

if (mainnetTokenAddress) {
items.push(`ethereum:${mainnetTokenAddress}`)
}

if (address) {
items.push(`${networkConfig.chain}:${address}`)
}

if (!address && coingecko) {
items.push(coingecko)
}

const endpoint = `https://coins.llama.fi/prices/current/${items.join(',')}`
const response = await fetch(endpoint)

if (!response.ok) {
return {}
}

const json: Record<'coins', Record<string, Price>> = await response.json()
const item = Object.values(json.coins).filter(
(item) => item.confidence > 0.95
)[0]

if (!item) {
return {}
}

const priceInAmount = item.price * amount

return {
...item,
priceInAmount,
}
}

/**
* Get lock total charges with fees
* @returns
*/
export const getTotalCharges = async ({
amount,
network,
address,
}: {
network: number
amount: number
address?: string
}) => {
const [pricing, gasCost] = await Promise.all([
getDefiLammaPrice({
network,
amount,
address,
}),
getGasCost({ network }),
])

if (pricing.priceInAmount === undefined) {
return {
total: 0,
subtotal: 0,
gasCost,
unlockServiceFee: 0,
creditCardProcessingFee: 0,
isCreditCardPurchasable: false,
}
}
const subtotal = Math.round(pricing.priceInAmount * 100)
const fees = getFees({
subtotal,
gasCost,
})
const result = {
...fees,
subtotal,
isCreditCardPurchasable: fees.total > 50,
}
return result
}
118 changes: 3 additions & 115 deletions locksmith/src/utils/pricing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,78 +4,7 @@ import { ethers } from 'ethers'
import logger from '../logger'
import GasPrice from './gasPrice'
import { GAS_COST, stripePercentage, baseStripeFee } from './constants'

export interface Options {
amount?: number
address?: string
network: number
}

interface Price {
decimals: number
symbol: string
price: number
timestamp: number
confidence: number
creditCardEnabled: boolean
}

export async function defiLammaPrice({
network,
address,
amount = 1,
}: Options): Promise<
Partial<
Price & {
priceInAmount: number
}
>
> {
const networkConfig = networks[network]
if (!network) {
return {}
}
const items: string[] = []
const coingecko = `coingecko:${networkConfig.nativeCurrency?.coingecko}`
const mainnetTokenAddress = networkConfig.tokens?.find(
(item) => item.address?.toLowerCase() === address?.toLowerCase()
)?.mainnetAddress

if (mainnetTokenAddress) {
items.push(`ethereum:${mainnetTokenAddress}`)
}

if (address) {
items.push(`${networkConfig.chain}:${address}`)
}

if (!address && coingecko) {
items.push(coingecko)
}

const endpoint = `https://coins.llama.fi/prices/current/${items.join(',')}`
const response = await fetch(endpoint)

if (!response.ok) {
return {}
}

const json: Record<'coins', Record<string, Price>> = await response.json()
const item = Object.values(json.coins).filter(
(item) => item.confidence > 0.95
)[0]

if (!item) {
return {}
}

const priceInAmount = item.price * amount

return {
...item,
priceInAmount,
}
}
import * as pricingOperations from '../operations/pricingOperations'

interface KeyPricingOptions {
recipients: (string | null)[]
Expand Down Expand Up @@ -147,7 +76,7 @@ export const getKeyPricingInUSD = async ({
network,
})

const usdPricing = await defiLammaPrice({
const usdPricing = await pricingOperations.getDefiLammaPrice({
network,
address:
!currencyContractAddress ||
Expand Down Expand Up @@ -224,7 +153,7 @@ export const getKeyPricingInUSD = async ({
export const getGasCost = async ({ network }: Record<'network', number>) => {
const gas = new GasPrice()
const amount = await gas.gasPriceETH(network, GAS_COST)
const price = await defiLammaPrice({
const price = await pricingOperations.getDefiLammaPrice({
network,
amount,
})
Expand Down Expand Up @@ -264,47 +193,6 @@ export const getFees = ({
}
}

export const createTotalCharges = async ({
amount,
network,
address,
}: {
network: number
amount: number
address?: string
}) => {
const [pricing, gasCost] = await Promise.all([
defiLammaPrice({
network,
amount,
address,
}),
getGasCost({ network }),
])

if (pricing.priceInAmount === undefined) {
return {
total: 0,
subtotal: 0,
gasCost,
unlockServiceFee: 0,
creditCardProcessingFee: 0,
isCreditCardPurchasable: false,
}
}
const subtotal = Math.round(pricing.priceInAmount * 100)
const fees = getFees({
subtotal,
gasCost,
})
const result = {
...fees,
subtotal,
isCreditCardPurchasable: fees.total > 50,
}
return result
}

export const createPricingForPurchase = async (options: KeyPricingOptions) => {
const recipients = await getKeyPricingInUSD(options)
const subtotal = recipients.reduce(
Expand Down

0 comments on commit 1fa92ee

Please sign in to comment.