Refactor monero subscription component
Using computed properties and atomic units.
This commit is contained in:
parent
df7748ca72
commit
b65ad1057c
6 changed files with 78 additions and 46 deletions
|
@ -1,7 +1,34 @@
|
|||
import { BigNumber, FixedNumber } from "@ethersproject/bignumber"
|
||||
|
||||
import { BACKEND_URL } from "@/constants"
|
||||
import { floatToBigNumber, roundBigNumber } from "@/utils/numbers"
|
||||
import { http } from "./common"
|
||||
import { Profile, User } from "./users"
|
||||
|
||||
const SECONDS_IN_DAY = 3600 * 24
|
||||
const SECONDS_IN_MONTH = SECONDS_IN_DAY * 30
|
||||
|
||||
export function getPricePerSec(
|
||||
pricePerMonth: number,
|
||||
tokenDecimals: number,
|
||||
): BigNumber {
|
||||
const pricePerMonthInt = floatToBigNumber(pricePerMonth, tokenDecimals)
|
||||
return pricePerMonthInt.div(SECONDS_IN_MONTH)
|
||||
}
|
||||
|
||||
export function getPricePerMonth(
|
||||
pricePerSec: BigNumber,
|
||||
): BigNumber {
|
||||
return roundBigNumber(pricePerSec.mul(SECONDS_IN_MONTH), 4)
|
||||
}
|
||||
|
||||
export function formatAmount(
|
||||
value: BigNumber,
|
||||
tokenDecimals: number,
|
||||
): FixedNumber {
|
||||
return FixedNumber.fromValue(value, tokenDecimals)
|
||||
}
|
||||
|
||||
export interface SubscriptionOption {
|
||||
type: string;
|
||||
price: number | null;
|
||||
|
|
|
@ -7,12 +7,14 @@ import { ethereumAddressMatch, EthereumSignature } from "@/utils/ethereum"
|
|||
import { floatToBigNumber, roundBigNumber } from "@/utils/numbers"
|
||||
import { http } from "./common"
|
||||
import { Contracts, getContract } from "./contracts"
|
||||
import { registerSubscriptionOption } from "./subscriptions-common"
|
||||
import {
|
||||
formatAmount,
|
||||
getPricePerMonth,
|
||||
getPricePerSec,
|
||||
registerSubscriptionOption,
|
||||
} from "./subscriptions-common"
|
||||
import { Profile, User } from "./users"
|
||||
|
||||
const SECONDS_IN_DAY = 3600 * 24
|
||||
const SECONDS_IN_MONTH = SECONDS_IN_DAY * 30
|
||||
|
||||
export interface SubscriptionToken {
|
||||
address: string;
|
||||
symbol: string;
|
||||
|
@ -33,27 +35,6 @@ export async function getSubscriptionToken(
|
|||
}
|
||||
}
|
||||
|
||||
export function getPricePerSec(
|
||||
pricePerMonth: number,
|
||||
tokenDecimals: number,
|
||||
): BigNumber {
|
||||
const pricePerMonthInt = floatToBigNumber(pricePerMonth, tokenDecimals)
|
||||
return pricePerMonthInt.div(SECONDS_IN_MONTH)
|
||||
}
|
||||
|
||||
export function getPricePerMonth(
|
||||
pricePerSec: BigNumber,
|
||||
): BigNumber {
|
||||
return roundBigNumber(pricePerSec.mul(SECONDS_IN_MONTH), 4)
|
||||
}
|
||||
|
||||
export function formatAmount(
|
||||
value: BigNumber,
|
||||
tokenDecimals: number,
|
||||
): FixedNumber {
|
||||
return FixedNumber.fromValue(value, tokenDecimals)
|
||||
}
|
||||
|
||||
export async function getSubscriptionAuthorization(
|
||||
authToken: string,
|
||||
pricePerSec: BigNumber,
|
||||
|
|
|
@ -2,22 +2,40 @@ import { BigNumber } from "@ethersproject/bignumber"
|
|||
|
||||
import { BACKEND_URL } from "@/constants"
|
||||
import { http } from "./common"
|
||||
import { registerSubscriptionOption } from "./subscriptions-common"
|
||||
import {
|
||||
formatAmount,
|
||||
getPricePerMonth as _getPricePerMonth,
|
||||
getPricePerSec as _getPricePerSec,
|
||||
} from "./subscriptions-ethereum"
|
||||
registerSubscriptionOption,
|
||||
} from "./subscriptions-common"
|
||||
import { Profile, User } from "./users"
|
||||
|
||||
const MONERO_DECIMALS = 12
|
||||
|
||||
export function formatXmrAmount(value: number | BigNumber): number {
|
||||
if (typeof value === "number") {
|
||||
value = BigNumber.from(value)
|
||||
}
|
||||
return formatAmount(value, MONERO_DECIMALS).toUnsafeFloat()
|
||||
}
|
||||
|
||||
export function getPricePerSec(pricePerMonth: number): number {
|
||||
return _getPricePerSec(pricePerMonth, 12).toNumber()
|
||||
return _getPricePerSec(pricePerMonth, MONERO_DECIMALS).toNumber()
|
||||
}
|
||||
|
||||
export function getPricePerMonth(pricePerSec: number): number {
|
||||
const pricePerSecInt = BigNumber.from(pricePerSec)
|
||||
const pricePerMonthInt = _getPricePerMonth(pricePerSecInt)
|
||||
return formatAmount(pricePerMonthInt, 12).toUnsafeFloat()
|
||||
return formatXmrAmount(pricePerMonthInt)
|
||||
}
|
||||
|
||||
export function getPaymentAmount(
|
||||
pricePerSec: number,
|
||||
durationMonths: number,
|
||||
): number {
|
||||
const pricePerSecInt = BigNumber.from(pricePerSec)
|
||||
const pricePerMonthInt = _getPricePerMonth(pricePerSecInt)
|
||||
return Math.round(pricePerMonthInt.toNumber() * durationMonths)
|
||||
}
|
||||
|
||||
export async function registerMoneroSubscriptionOption(
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
</div>
|
||||
<div class="payment-amount">
|
||||
<label>Amount</label>
|
||||
<div>{{ getPaymentAmount() }} XMR</div>
|
||||
<div>{{ formatXmrAmount(paymentAmount) }} XMR</div>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
|
@ -78,7 +78,7 @@
|
|||
</button>
|
||||
</form>
|
||||
<div class="invoice" v-if="invoice">
|
||||
<div>Please send {{ getPaymentAmount() }} XMR to this address:</div>
|
||||
<div>Please send {{ formatXmrAmount(paymentAmount) }} XMR to this address:</div>
|
||||
<div class="payment-address">{{ invoice.payment_address }}</div>
|
||||
<div class="invoice-status">
|
||||
<template v-if="invoice.status === 'open'">Waiting for payment</template>
|
||||
|
@ -92,14 +92,16 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from "vue"
|
||||
import { $, $ref } from "vue/macros"
|
||||
import { $, $computed, $ref } from "vue/macros"
|
||||
import { DateTime } from "luxon"
|
||||
|
||||
import { searchProfilesByAcct } from "@/api/search"
|
||||
import { findSubscription, SubscriptionDetails } from "@/api/subscriptions-common"
|
||||
import {
|
||||
createInvoice,
|
||||
formatXmrAmount,
|
||||
getInvoice,
|
||||
getPaymentAmount,
|
||||
getPricePerMonth,
|
||||
Invoice,
|
||||
} from "@/api/subscriptions-monero"
|
||||
|
@ -120,7 +122,6 @@ const senderAcct = $ref("")
|
|||
let senderError = $ref<string | null>(null)
|
||||
let sender = $ref<ProfileWrapper>(new ProfileWrapper(currentUser || guest()))
|
||||
let subscriptionOption = $ref<ProfilePaymentOption | null>(null)
|
||||
let subscriptionPrice = $ref<number | null>(null)
|
||||
let subscriptionDetails = $ref<SubscriptionDetails | null>(null)
|
||||
const paymentDuration = $ref<number>(1)
|
||||
let invoice = $ref<Invoice | null>(null)
|
||||
|
@ -131,14 +132,19 @@ onMounted(async () => {
|
|||
subscriptionOption = recipient.payment_options.find((option) => {
|
||||
return option.type === "monero-subscription" && option.price !== undefined
|
||||
}) || null
|
||||
if (subscriptionOption?.price) {
|
||||
subscriptionPrice = getPricePerMonth(subscriptionOption.price)
|
||||
if (sender.id !== "") {
|
||||
subscriptionDetails = await findSubscription(sender.id, recipient.id)
|
||||
}
|
||||
if (subscriptionOption && sender.id !== "") {
|
||||
subscriptionDetails = await findSubscription(sender.id, recipient.id)
|
||||
}
|
||||
})
|
||||
|
||||
// Human-readable subscription price
|
||||
const subscriptionPrice = $computed<number | null>(() => {
|
||||
if (!subscriptionOption?.price) {
|
||||
return null
|
||||
}
|
||||
return getPricePerMonth(subscriptionOption.price)
|
||||
})
|
||||
|
||||
async function identifySender() {
|
||||
if (!senderAcct) {
|
||||
return
|
||||
|
@ -176,16 +182,15 @@ function canSubscribe(): boolean {
|
|||
)
|
||||
}
|
||||
|
||||
function getPaymentAmount(): number {
|
||||
if (!subscriptionPrice) {
|
||||
return 0
|
||||
const paymentAmount = $computed<number | null>(() => {
|
||||
if (!subscriptionOption?.price) {
|
||||
return null
|
||||
}
|
||||
const amount = subscriptionPrice * paymentDuration
|
||||
return amount
|
||||
}
|
||||
return getPaymentAmount(subscriptionOption.price, paymentDuration)
|
||||
})
|
||||
|
||||
function canPay(): boolean {
|
||||
return getPaymentAmount() > 0
|
||||
return paymentAmount !== null
|
||||
}
|
||||
|
||||
async function onCreateInvoice() {
|
||||
|
|
|
@ -79,6 +79,7 @@ import { DateTime } from "luxon"
|
|||
|
||||
import { ProfileWrapper } from "@/api/users"
|
||||
import {
|
||||
getPricePerSec,
|
||||
getSubscriptionOptions,
|
||||
getReceivedSubscriptions,
|
||||
Subscription,
|
||||
|
@ -86,7 +87,6 @@ import {
|
|||
} from "@/api/subscriptions-common"
|
||||
import {
|
||||
configureSubscriptions,
|
||||
getPricePerSec,
|
||||
getSubscriptionAuthorization,
|
||||
getSubscriptionConfig,
|
||||
getSubscriptionState,
|
||||
|
|
|
@ -117,6 +117,7 @@ function getSubscriptionPageUrl(): string {
|
|||
|
||||
function isFormValid(): boolean {
|
||||
return (
|
||||
// Price must be greater than 0 when expressed in piconeros
|
||||
getPricePerSec(subscriptionPrice) > 0 &&
|
||||
subscriptionPayoutAddress.length > 0
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue