diff --git a/locksmith/src/controllers/v2/priceController.ts b/locksmith/src/controllers/v2/priceController.ts index c48a4148a60..3d79f37bb5d 100644 --- a/locksmith/src/controllers/v2/priceController.ts +++ b/locksmith/src/controllers/v2/priceController.ts @@ -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) => { @@ -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, @@ -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, @@ -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}`), diff --git a/locksmith/src/operations/creditCardOperations.ts b/locksmith/src/operations/creditCardOperations.ts index 5e01dc09695..ca2de5aefb5 100644 --- a/locksmith/src/operations/creditCardOperations.ts +++ b/locksmith/src/operations/creditCardOperations.ts @@ -14,7 +14,7 @@ export const getCreditCardEnabledStatus = async ({ lockAddress, network, totalPriceInCents, -}: CreditCardStateProps) => { +}: CreditCardStateProps): Promise => { const fulfillmentDispatcher = new Dispatcher() const [hasEnoughToPayForGas, stripeConnected, isAuthorizedForCreditCard] = diff --git a/locksmith/src/operations/pricingOperations.ts b/locksmith/src/operations/pricingOperations.ts new file mode 100644 index 00000000000..187db5aa938 --- /dev/null +++ b/locksmith/src/operations/pricingOperations.ts @@ -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> = 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 +} diff --git a/locksmith/src/utils/pricing.ts b/locksmith/src/utils/pricing.ts index 8a2ddd114f4..ede38a23b44 100644 --- a/locksmith/src/utils/pricing.ts +++ b/locksmith/src/utils/pricing.ts @@ -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> = 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)[] @@ -147,7 +76,7 @@ export const getKeyPricingInUSD = async ({ network, }) - const usdPricing = await defiLammaPrice({ + const usdPricing = await pricingOperations.getDefiLammaPrice({ network, address: !currencyContractAddress || @@ -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, }) @@ -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(