Take deployment cost from published pricing record
This commit is contained in:
parent
64b6b1082d
commit
6c2ee093a2
11
.env.example
11
.env.example
@ -1,4 +1,4 @@
|
|||||||
# Client-side environment variables (must be prefixed with NEXT_PUBLIC_)
|
# Client-side environment variables must be prefixed with NEXT_PUBLIC_
|
||||||
|
|
||||||
# Solana Payment Configuration
|
# Solana Payment Configuration
|
||||||
# TODO: Use different RPC URL or use browser wallet
|
# TODO: Use different RPC URL or use browser wallet
|
||||||
@ -17,12 +17,11 @@ NEXT_PUBLIC_SOLANA_TOKEN_RECIPIENT_ADDRESS=FFDx3SdAEeXrp6BTmStB4BDHpctGsaasZq4FF
|
|||||||
# UI Configuration
|
# UI Configuration
|
||||||
NEXT_PUBLIC_EXAMPLE_URL=https://git.vdb.to/cerc-io/test-progressive-web-app
|
NEXT_PUBLIC_EXAMPLE_URL=https://git.vdb.to/cerc-io/test-progressive-web-app
|
||||||
|
|
||||||
# Server-side environment variables
|
|
||||||
|
|
||||||
# Laconic Registry Configuration
|
# Laconic Registry Configuration
|
||||||
REGISTRY_CHAIN_ID=laconic-mainnet
|
NEXT_PUBLIC_REGISTRY_CHAIN_ID=laconic-mainnet
|
||||||
REGISTRY_RPC_ENDPOINT=https://laconicd-mainnet-1.laconic.com
|
NEXT_PUBLIC_REGISTRY_RPC_ENDPOINT=https://laconicd-mainnet-1.laconic.com
|
||||||
REGISTRY_GQL_ENDPOINT=https://laconicd-mainnet-1.laconic.com/graphql
|
NEXT_PUBLIC_REGISTRY_GQL_ENDPOINT=https://laconicd-mainnet-1.laconic.com/graphql
|
||||||
|
NEXT_PUBLIC_PRICING_RECORD_LRN=
|
||||||
REGISTRY_GAS_PRICE=0.001
|
REGISTRY_GAS_PRICE=0.001
|
||||||
REGISTRY_BOND_ID=
|
REGISTRY_BOND_ID=
|
||||||
REGISTRY_AUTHORITY=
|
REGISTRY_AUTHORITY=
|
||||||
|
@ -106,9 +106,9 @@ NEXT_PUBLIC_EXAMPLE_URL=https://git.vdb.to/cerc-io/test-progressive-web-app
|
|||||||
|
|
||||||
### Server-side Variables
|
### Server-side Variables
|
||||||
```bash
|
```bash
|
||||||
REGISTRY_CHAIN_ID=laconic-mainnet
|
NEXT_PUBLIC_REGISTRY_CHAIN_ID=laconic-mainnet
|
||||||
REGISTRY_GQL_ENDPOINT=https://laconicd-mainnet-1.laconic.com/api
|
NEXT_PUBLIC_REGISTRY_GQL_ENDPOINT=https://laconicd-mainnet-1.laconic.com/api
|
||||||
REGISTRY_RPC_ENDPOINT=https://laconicd-mainnet-1.laconic.com
|
NEXT_PUBLIC_REGISTRY_RPC_ENDPOINT=https://laconicd-mainnet-1.laconic.com
|
||||||
REGISTRY_BOND_ID=<BOND_ID>
|
REGISTRY_BOND_ID=<BOND_ID>
|
||||||
REGISTRY_AUTHORITY=<AUTHORITY_NAME>
|
REGISTRY_AUTHORITY=<AUTHORITY_NAME>
|
||||||
REGISTRY_USER_KEY=<PRIVATE_KEY>
|
REGISTRY_USER_KEY=<PRIVATE_KEY>
|
||||||
|
@ -284,9 +284,9 @@ export async function POST(request: NextRequest) {
|
|||||||
|
|
||||||
// Validate required environment variables for Solana payments
|
// Validate required environment variables for Solana payments
|
||||||
const requiredEnvVars = [
|
const requiredEnvVars = [
|
||||||
'REGISTRY_CHAIN_ID',
|
'NEXT_PUBLIC_REGISTRY_CHAIN_ID',
|
||||||
'REGISTRY_GQL_ENDPOINT',
|
'NEXT_PUBLIC_REGISTRY_GQL_ENDPOINT',
|
||||||
'REGISTRY_RPC_ENDPOINT',
|
'NEXT_PUBLIC_REGISTRY_RPC_ENDPOINT',
|
||||||
'REGISTRY_BOND_ID',
|
'REGISTRY_BOND_ID',
|
||||||
'REGISTRY_AUTHORITY',
|
'REGISTRY_AUTHORITY',
|
||||||
'REGISTRY_USER_KEY', // This is the same as the prefilled account for LNT transfers
|
'REGISTRY_USER_KEY', // This is the same as the prefilled account for LNT transfers
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useCallback, useState, useEffect } from 'react';
|
import { useCallback, useState, useEffect, useMemo } from 'react';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
|
|
||||||
import { Connection } from '@solana/web3.js';
|
import { Connection } from '@solana/web3.js';
|
||||||
import { useConnection, useWallet } from '@solana/wallet-adapter-react';
|
import { useConnection, useWallet } from '@solana/wallet-adapter-react';
|
||||||
|
|
||||||
|
import { getRegistry } from '@/config';
|
||||||
import { sendSolanaPayment } from '@/services/solana';
|
import { sendSolanaPayment } from '@/services/solana';
|
||||||
import { getRequiredTokenInfo, RequiredTokenInfo } from '@/services/jupiter-price';
|
import { getRequiredTokenInfo, RequiredTokenInfo } from '@/services/jupiter-price';
|
||||||
import { PaymentMethod, PaymentModalProps, PaymentRequest } from '@/types';
|
import { PaymentMethod, PaymentModalProps, PaymentRequest } from '@/types';
|
||||||
@ -16,6 +17,26 @@ assert(!IS_NAT_GOR_TRANSFER_ENABLED || process.env.NEXT_PUBLIC_GORBAGANA_RPC_URL
|
|||||||
|
|
||||||
const GORBAGANA_RPC_URL = process.env.NEXT_PUBLIC_GORBAGANA_RPC_URL;
|
const GORBAGANA_RPC_URL = process.env.NEXT_PUBLIC_GORBAGANA_RPC_URL;
|
||||||
|
|
||||||
|
assert(process.env.NEXT_PUBLIC_SOLANA_RPC_URL, 'SOLANA_RPC_URL is required');
|
||||||
|
assert(process.env.NEXT_PUBLIC_PRICING_RECORD_LRN, 'DEPLOYMENT_RECORD_LRN is required');
|
||||||
|
|
||||||
|
const PRICING_RECORD_LRN = process.env.NEXT_PUBLIC_PRICING_RECORD_LRN;
|
||||||
|
const SUPPORTED_CURRENCY = "USD";
|
||||||
|
const VALID_PRICING_RECORD_TYPE = "webapp-deployment";
|
||||||
|
|
||||||
|
interface DeploymentCostInfo {
|
||||||
|
amount: string;
|
||||||
|
currency: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PricingRecordAttributes {
|
||||||
|
amount: string;
|
||||||
|
currency: string;
|
||||||
|
for: string;
|
||||||
|
type: string;
|
||||||
|
version: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default function PaymentModal({
|
export default function PaymentModal({
|
||||||
isOpen,
|
isOpen,
|
||||||
onClose,
|
onClose,
|
||||||
@ -23,28 +44,55 @@ export default function PaymentModal({
|
|||||||
onPaymentComplete,
|
onPaymentComplete,
|
||||||
}: PaymentModalProps) {
|
}: PaymentModalProps) {
|
||||||
const { selectedPaymentMethod: paymentMethod } = usePaymentMethod();
|
const { selectedPaymentMethod: paymentMethod } = usePaymentMethod();
|
||||||
|
|
||||||
const { connection: solanaConnection } = useConnection();
|
const { connection: solanaConnection } = useConnection();
|
||||||
|
const { wallet, publicKey } = useWallet();
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
const [tokenAmount, setTokenAmount] = useState<number>(0);
|
const [tokenAmount, setTokenAmount] = useState<number>(0);
|
||||||
const [tokenDecimals, setTokenDecimals] = useState<number>(6); // Default fallback
|
const [tokenDecimals, setTokenDecimals] = useState<number>(6); // Default fallback
|
||||||
const [loadingPrice, setLoadingPrice] = useState(false);
|
const [loadingPrice, setLoadingPrice] = useState(true);
|
||||||
|
const [deploymentCostInfo, setDeploymentCostInfo] = useState<DeploymentCostInfo>();
|
||||||
|
|
||||||
const { wallet, publicKey } = useWallet();
|
useEffect(() => {
|
||||||
|
const registry = getRegistry();
|
||||||
|
|
||||||
|
const resolveDeploymentCostInfo = async () => {
|
||||||
|
const result = await registry.resolveNames([PRICING_RECORD_LRN!])
|
||||||
|
const PricingRecordAttributes: PricingRecordAttributes = result[0].attributes;
|
||||||
|
|
||||||
|
if (PricingRecordAttributes.type !== VALID_PRICING_RECORD_TYPE) {
|
||||||
|
throw new Error(`Incorrect pricing record type: ${PricingRecordAttributes.type}. Please provide correct pricing record lrn`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PricingRecordAttributes.currency !== SUPPORTED_CURRENCY) {
|
||||||
|
throw new Error(`Unsupported currency found in pricing record: ${PricingRecordAttributes.currency}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
setDeploymentCostInfo({
|
||||||
|
amount: PricingRecordAttributes.amount,
|
||||||
|
currency:PricingRecordAttributes.currency
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveDeploymentCostInfo();
|
||||||
|
}, []);
|
||||||
|
|
||||||
// Get configuration from environment variables
|
// Get configuration from environment variables
|
||||||
const targetUsdAmount = parseFloat(process.env.NEXT_PUBLIC_SOLANA_PAYMENT_AMOUNT_USD!);
|
const deploymentCost = useMemo(() => {
|
||||||
|
if (!deploymentCostInfo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseInt(deploymentCostInfo.amount, 10);
|
||||||
|
}, [deploymentCostInfo])
|
||||||
|
|
||||||
const mintAddress = process.env.NEXT_PUBLIC_SOLANA_TOKEN_MINT_ADDRESS!;
|
const mintAddress = process.env.NEXT_PUBLIC_SOLANA_TOKEN_MINT_ADDRESS!;
|
||||||
const tokenSymbol = process.env.NEXT_PUBLIC_SOLANA_TOKEN_SYMBOL;
|
const tokenSymbol = process.env.NEXT_PUBLIC_SOLANA_TOKEN_SYMBOL;
|
||||||
|
|
||||||
// Fetch payment amount based on USD price for both payment methods
|
// Fetch payment amount based on USD price for both payment methods
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isOpen || !paymentMethod) {
|
if (!isOpen || !deploymentCost || !paymentMethod) return;
|
||||||
setLoadingPrice(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchPaymentAmount = async () => {
|
const fetchPaymentAmount = async () => {
|
||||||
setLoadingPrice(true);
|
setLoadingPrice(true);
|
||||||
@ -54,10 +102,10 @@ export default function PaymentModal({
|
|||||||
let requiredTokenInfo: RequiredTokenInfo
|
let requiredTokenInfo: RequiredTokenInfo
|
||||||
if (paymentMethod === PaymentMethod.NAT_GOR) {
|
if (paymentMethod === PaymentMethod.NAT_GOR) {
|
||||||
// Fetch native GOR amount using solana GOR token price
|
// Fetch native GOR amount using solana GOR token price
|
||||||
requiredTokenInfo = await getRequiredTokenInfo(targetUsdAmount, SOLANA_GOR_MINT_ADDRESS);
|
requiredTokenInfo = await getRequiredTokenInfo(deploymentCost, SOLANA_GOR_MINT_ADDRESS);
|
||||||
} else if (paymentMethod === PaymentMethod.SPL_TOKEN) {
|
} else if (paymentMethod === PaymentMethod.SPL_TOKEN) {
|
||||||
// Fetch SPL token amount using token mint price
|
// Fetch SPL token amount using token mint price
|
||||||
requiredTokenInfo = await getRequiredTokenInfo(targetUsdAmount, mintAddress);
|
requiredTokenInfo = await getRequiredTokenInfo(deploymentCost, mintAddress);
|
||||||
} else {
|
} else {
|
||||||
setError('Invalid payment method');
|
setError('Invalid payment method');
|
||||||
return;
|
return;
|
||||||
@ -74,7 +122,7 @@ export default function PaymentModal({
|
|||||||
};
|
};
|
||||||
|
|
||||||
fetchPaymentAmount();
|
fetchPaymentAmount();
|
||||||
}, [isOpen, paymentMethod, targetUsdAmount, mintAddress]);
|
}, [isOpen, paymentMethod, deploymentCost, mintAddress]);
|
||||||
|
|
||||||
// Initialize state when modal opens
|
// Initialize state when modal opens
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -186,7 +234,7 @@ export default function PaymentModal({
|
|||||||
<div className="relative">
|
<div className="relative">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={targetUsdAmount}
|
value={`$${deploymentCost}`}
|
||||||
disabled={true}
|
disabled={true}
|
||||||
className="w-full p-3 pr-12 rounded-md"
|
className="w-full p-3 pr-12 rounded-md"
|
||||||
style={{
|
style={{
|
||||||
|
@ -22,9 +22,9 @@ export const getRegistry = (): Registry => {
|
|||||||
export const getRegistryConfig = (): RegistryConfig => {
|
export const getRegistryConfig = (): RegistryConfig => {
|
||||||
// Validate required environment variables
|
// Validate required environment variables
|
||||||
const requiredEnvVars = [
|
const requiredEnvVars = [
|
||||||
'REGISTRY_CHAIN_ID',
|
'NEXT_PUBLIC_REGISTRY_CHAIN_ID',
|
||||||
'REGISTRY_GQL_ENDPOINT',
|
'NEXT_PUBLIC_REGISTRY_GQL_ENDPOINT',
|
||||||
'REGISTRY_RPC_ENDPOINT',
|
'NEXT_PUBLIC_REGISTRY_RPC_ENDPOINT',
|
||||||
'REGISTRY_BOND_ID',
|
'REGISTRY_BOND_ID',
|
||||||
'REGISTRY_AUTHORITY',
|
'REGISTRY_AUTHORITY',
|
||||||
'REGISTRY_USER_KEY'
|
'REGISTRY_USER_KEY'
|
||||||
@ -37,9 +37,9 @@ export const getRegistryConfig = (): RegistryConfig => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
chainId: process.env.REGISTRY_CHAIN_ID!,
|
chainId: process.env.NEXT_PUBLIC_REGISTRY_CHAIN_ID!,
|
||||||
rpcEndpoint: process.env.REGISTRY_RPC_ENDPOINT!,
|
rpcEndpoint: process.env.NEXT_PUBLIC_REGISTRY_RPC_ENDPOINT!,
|
||||||
gqlEndpoint: process.env.REGISTRY_GQL_ENDPOINT!,
|
gqlEndpoint: process.env.NEXT_PUBLIC_REGISTRY_GQL_ENDPOINT!,
|
||||||
bondId: process.env.REGISTRY_BOND_ID!,
|
bondId: process.env.REGISTRY_BOND_ID!,
|
||||||
authority: process.env.REGISTRY_AUTHORITY!,
|
authority: process.env.REGISTRY_AUTHORITY!,
|
||||||
privateKey: process.env.REGISTRY_USER_KEY!,
|
privateKey: process.env.REGISTRY_USER_KEY!,
|
||||||
|
Loading…
Reference in New Issue
Block a user