Use send tokens method to transfer lnt to service providers
This commit is contained in:
parent
ae4f5cbaa1
commit
c47bee4efd
@ -6,10 +6,10 @@ NEXT_PUBLIC_SOLANA_TOKEN_MINT_ADDRESS=71Jvq4Epe2FCJ7JFSF7jLXdNk1Wy4Bhqd9iL6bEFEL
|
|||||||
NEXT_PUBLIC_SOLANA_TOKEN_RECIPIENT_ADDRESS=
|
NEXT_PUBLIC_SOLANA_TOKEN_RECIPIENT_ADDRESS=
|
||||||
NEXT_PUBLIC_SOLANA_TOKEN_SYMBOL=GOR
|
NEXT_PUBLIC_SOLANA_TOKEN_SYMBOL=GOR
|
||||||
NEXT_PUBLIC_SOLANA_TOKEN_NAME=GOR Token
|
NEXT_PUBLIC_SOLANA_TOKEN_NAME=GOR Token
|
||||||
NEXT_PUBLIC_MIN_SOLANA_PAYMENT_AMOUNT=50
|
NEXT_PUBLIC_MIN_SOLANA_PAYMENT_AMOUNT=400000000
|
||||||
|
|
||||||
# UI Configuration (optional)
|
# UI Configuration (optional)
|
||||||
NEXT_PUBLIC_DOMAIN_SUFFIX=
|
NEXT_PUBLIC_DOMAIN_SUFFIX=apps.vaasl.io
|
||||||
NEXT_PUBLIC_EXAMPLE_URL=https://github.com/cerc-io/laconic-registry-cli
|
NEXT_PUBLIC_EXAMPLE_URL=https://github.com/cerc-io/laconic-registry-cli
|
||||||
|
|
||||||
# Server-side environment variables
|
# Server-side environment variables
|
||||||
|
14
package-lock.json
generated
14
package-lock.json
generated
@ -13,6 +13,7 @@
|
|||||||
"@solana/spl-token": "^0.4.13",
|
"@solana/spl-token": "^0.4.13",
|
||||||
"@solana/web3.js": "^1.98.2",
|
"@solana/web3.js": "^1.98.2",
|
||||||
"axios": "^1.6.8",
|
"axios": "^1.6.8",
|
||||||
|
"big.js": "^6.2.2",
|
||||||
"bn.js": "^5.2.2",
|
"bn.js": "^5.2.2",
|
||||||
"next": "15.3.1",
|
"next": "15.3.1",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
@ -3600,6 +3601,19 @@
|
|||||||
"node": ">=0.6"
|
"node": ">=0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/big.js": {
|
||||||
|
"version": "6.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/big.js/-/big.js-6.2.2.tgz",
|
||||||
|
"integrity": "sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/bigjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/bigint-buffer": {
|
"node_modules/bigint-buffer": {
|
||||||
"version": "1.1.5",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/bigint-buffer/-/bigint-buffer-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/bigint-buffer/-/bigint-buffer-1.1.5.tgz",
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
"@solana/spl-token": "^0.4.13",
|
"@solana/spl-token": "^0.4.13",
|
||||||
"@solana/web3.js": "^1.98.2",
|
"@solana/web3.js": "^1.98.2",
|
||||||
"axios": "^1.6.8",
|
"axios": "^1.6.8",
|
||||||
|
"big.js": "^6.2.2",
|
||||||
"bn.js": "^5.2.2",
|
"bn.js": "^5.2.2",
|
||||||
"next": "15.3.1",
|
"next": "15.3.1",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
|
@ -100,11 +100,11 @@ const fetchLatestCommitHash = async (repoUrl: string, provider: string): Promise
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Registry transaction retry helper
|
// Registry transaction retry helper
|
||||||
const registryTransactionWithRetry = async (
|
export const registryTransactionWithRetry = async (
|
||||||
txFn: () => Promise<unknown>,
|
txFn: () => Promise<any>,
|
||||||
maxRetries = 3,
|
maxRetries = 3,
|
||||||
delay = 1000
|
delay = 1000
|
||||||
): Promise<unknown> => {
|
): Promise<any> => {
|
||||||
let lastError;
|
let lastError;
|
||||||
|
|
||||||
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
||||||
@ -397,7 +397,7 @@ export async function POST(request: NextRequest) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
meta: {
|
meta: {
|
||||||
note: `Added via GOR-Deploy @ ${timestamp}`,
|
note: `Added via ${process.env.NEXT_PUBLIC_SOLANA_TOKEN_SYMBOL}-Deploy @ ${timestamp}`,
|
||||||
repository: repoUrl,
|
repository: repoUrl,
|
||||||
repository_ref: fullHash,
|
repository_ref: fullHash,
|
||||||
external_payment: {
|
external_payment: {
|
||||||
|
@ -15,7 +15,7 @@ const geistMono = Geist_Mono({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Deploy Frontends using GOR and Laconic",
|
title: `Deploy Frontends using ${process.env.NEXT_PUBLIC_SOLANA_TOKEN_SYMBOL} and Laconic`,
|
||||||
description: "Deploy URLs to Laconic Registry using GOR payments",
|
description: "Deploy URLs to Laconic Registry using GOR payments",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ export default function PaymentModal({
|
|||||||
if (result.success && result.transactionSignature) {
|
if (result.success && result.transactionSignature) {
|
||||||
onPaymentComplete(result.transactionSignature);
|
onPaymentComplete(result.transactionSignature);
|
||||||
} else {
|
} else {
|
||||||
setError(result.error || 'GOR payment failed. Please try again.');
|
setError(result.error || `${process.env.NEXT_PUBLIC_SOLANA_TOKEN_SYMBOL} payment failed. Please try again.`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setError(error instanceof Error ? error.message : 'Payment failed. Please try again.');
|
setError(error instanceof Error ? error.message : 'Payment failed. Please try again.');
|
||||||
@ -55,7 +55,7 @@ export default function PaymentModal({
|
|||||||
style={{ background: 'var(--card-bg)', border: '1px solid var(--card-border)' }}>
|
style={{ background: 'var(--card-bg)', border: '1px solid var(--card-border)' }}>
|
||||||
<div className="p-6 border-b" style={{ borderColor: 'var(--card-border)' }}>
|
<div className="p-6 border-b" style={{ borderColor: 'var(--card-border)' }}>
|
||||||
<h2 className="text-xl font-semibold" style={{ color: 'var(--foreground)' }}>
|
<h2 className="text-xl font-semibold" style={{ color: 'var(--foreground)' }}>
|
||||||
Complete GOR Payment
|
Complete {process.env.NEXT_PUBLIC_SOLANA_TOKEN_SYMBOL} Payment
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ export default function PaymentModal({
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="amount" className="block text-sm font-medium mb-2" style={{ color: 'var(--foreground)' }}>
|
<label htmlFor="amount" className="block text-sm font-medium mb-2" style={{ color: 'var(--foreground)' }}>
|
||||||
Amount (GOR)
|
Amount ({process.env.NEXT_PUBLIC_SOLANA_TOKEN_SYMBOL})
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<input
|
<input
|
||||||
@ -94,7 +94,7 @@ export default function PaymentModal({
|
|||||||
readOnly
|
readOnly
|
||||||
/>
|
/>
|
||||||
<div className="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
|
<div className="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
|
||||||
<span className="text-sm font-medium" style={{ color: 'var(--muted)' }}>GOR</span>
|
<span className="text-sm font-medium" style={{ color: 'var(--muted)' }}>{process.env.NEXT_PUBLIC_SOLANA_TOKEN_SYMBOL}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs mt-1" style={{ color: 'var(--muted)' }}>
|
<p className="text-xs mt-1" style={{ color: 'var(--muted)' }}>
|
||||||
|
@ -125,7 +125,7 @@ export default function StatusDisplay({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{txHash && <InfoItem label="GOR Payment Transaction Hash" value={txHash} />}
|
{txHash && <InfoItem label={`${process.env.NEXT_PUBLIC_SOLANA_TOKEN_SYMBOL} Payment Transaction Hash`} value={txHash} />}
|
||||||
{appRecordId && <InfoItem label="Laconic Application Record ID" value={appRecordId} />}
|
{appRecordId && <InfoItem label="Laconic Application Record ID" value={appRecordId} />}
|
||||||
{recordId && <InfoItem label="Laconic Deployment Request Record ID" value={recordId} />}
|
{recordId && <InfoItem label="Laconic Deployment Request Record ID" value={recordId} />}
|
||||||
{lrn && <InfoItem label="Laconic Resource Name (LRN)" value={lrn} />}
|
{lrn && <InfoItem label="Laconic Resource Name (LRN)" value={lrn} />}
|
||||||
|
@ -49,4 +49,3 @@ export const getLaconicTransferConfig = () => {
|
|||||||
transferAmount: process.env.LACONIC_TRANSFER_AMOUNT!
|
transferAmount: process.env.LACONIC_TRANSFER_AMOUNT!
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { Registry } from '@cerc-io/registry-sdk';
|
import { Account, DEFAULT_GAS_ESTIMATION_MULTIPLIER, parseGasAndFees, Registry } from '@cerc-io/registry-sdk';
|
||||||
import { GasPrice } from '@cosmjs/stargate';
|
import { DeliverTxResponse, GasPrice } from '@cosmjs/stargate';
|
||||||
import { getRegistryConfig, getLaconicTransferConfig } from '../config';
|
import { getRegistryConfig, getLaconicTransferConfig } from '../config';
|
||||||
import { LaconicTransferResult } from '../types';
|
import { LaconicTransferResult } from '../types';
|
||||||
|
import { registryTransactionWithRetry } from '@/app/api/registry/route';
|
||||||
|
|
||||||
let registryInstance: Registry | null = null;
|
let registryInstance: Registry | null = null;
|
||||||
|
|
||||||
@ -21,7 +22,6 @@ const getRegistry = (): Registry => {
|
|||||||
|
|
||||||
export const transferLNTTokens = async (): Promise<LaconicTransferResult> => {
|
export const transferLNTTokens = async (): Promise<LaconicTransferResult> => {
|
||||||
try {
|
try {
|
||||||
const registryConfig = getRegistryConfig();
|
|
||||||
const transferConfig = getLaconicTransferConfig();
|
const transferConfig = getLaconicTransferConfig();
|
||||||
const registry = getRegistry();
|
const registry = getRegistry();
|
||||||
|
|
||||||
@ -55,37 +55,16 @@ export const transferLNTTokens = async (): Promise<LaconicTransferResult> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find the paymentAddress attribute
|
// Find the paymentAddress attribute
|
||||||
const paymentAddressAttr = deployerRecord.attributes.find(
|
const paymentAddress = deployerRecord.attributes.paymentAddress
|
||||||
(attr: any) => attr.key === 'paymentAddress'
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!paymentAddressAttr || !paymentAddressAttr.value?.string) {
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: 'paymentAddress attribute not found in deployer record'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const paymentAddress = paymentAddressAttr.value.string;
|
|
||||||
console.log('Found payment address:', paymentAddress);
|
console.log('Found payment address:', paymentAddress);
|
||||||
|
|
||||||
console.log('Initiating LNT transfer from prefilled account to payment address...');
|
console.log('Initiating LNT transfer from prefilled account to payment address...');
|
||||||
|
|
||||||
// Create fee for transaction
|
|
||||||
const fee = {
|
|
||||||
amount: [{ denom: 'alnt', amount: registryConfig.fee.fees.replace('alnt', '') || '900000' }],
|
|
||||||
gas: registryConfig.fee.gas || '900000',
|
|
||||||
};
|
|
||||||
|
|
||||||
// Send tokens from prefilled account to payment address
|
// Send tokens from prefilled account to payment address
|
||||||
const transferResult = await registry.sendCoins(
|
const transferResult = await sendTokensToAccount(
|
||||||
{
|
paymentAddress,
|
||||||
destinationAddress: paymentAddress,
|
transferConfig.transferAmount
|
||||||
amount: transferConfig.transferAmount,
|
|
||||||
denom: 'alnt'
|
|
||||||
},
|
|
||||||
transferConfig.prefilledPrivateKey,
|
|
||||||
fee
|
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log('LNT transfer result:', transferResult);
|
console.log('LNT transfer result:', transferResult);
|
||||||
@ -122,3 +101,51 @@ export const validateLaconicTransferConfig = (): { valid: boolean; error?: strin
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getAccount = async (): Promise<Account> => {
|
||||||
|
const registryConfig = getRegistryConfig();
|
||||||
|
|
||||||
|
const account = new Account(
|
||||||
|
Buffer.from(registryConfig.privateKey, 'hex'),
|
||||||
|
);
|
||||||
|
await account.init();
|
||||||
|
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const sendTokensToAccount = async (
|
||||||
|
receiverAddress: string,
|
||||||
|
amount: string,
|
||||||
|
): Promise<DeliverTxResponse> => {
|
||||||
|
const registryConfig = getRegistryConfig();
|
||||||
|
|
||||||
|
const registry = new Registry(
|
||||||
|
registryConfig.gqlEndpoint,
|
||||||
|
registryConfig.rpcEndpoint,
|
||||||
|
{ chainId: registryConfig.chainId },
|
||||||
|
);
|
||||||
|
|
||||||
|
const fee = parseGasAndFees(
|
||||||
|
registryConfig.fee.gas,
|
||||||
|
registryConfig.fee.fees,
|
||||||
|
);
|
||||||
|
const account = await getAccount();
|
||||||
|
const laconicClient = await registry.getLaconicClient(account);
|
||||||
|
const txResponse: DeliverTxResponse = await registryTransactionWithRetry(
|
||||||
|
() =>
|
||||||
|
laconicClient.sendTokens(
|
||||||
|
account.address,
|
||||||
|
receiverAddress,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
denom: 'alnt',
|
||||||
|
amount,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fee || DEFAULT_GAS_ESTIMATION_MULTIPLIER,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return txResponse;
|
||||||
|
}
|
||||||
|
@ -5,7 +5,7 @@ export const createApplicationDeploymentRequest = async (
|
|||||||
txHash: string
|
txHash: string
|
||||||
): Promise<CreateRecordResponse> => {
|
): Promise<CreateRecordResponse> => {
|
||||||
try {
|
try {
|
||||||
console.log(`Creating deployment request for URL: ${url} with transaction: ${txHash} using GOR payment`);
|
console.log(`Creating deployment request for URL: ${url} with transaction: ${txHash} using ${process.env.NEXT_PUBLIC_SOLANA_TOKEN_SYMBOL} payment`);
|
||||||
|
|
||||||
// Call our serverless API endpoint to handle the registry interaction
|
// Call our serverless API endpoint to handle the registry interaction
|
||||||
const response = await fetch('/api/registry', {
|
const response = await fetch('/api/registry', {
|
||||||
|
@ -116,10 +116,9 @@ export const verifyUnusedSolanaPayment = async (
|
|||||||
if ('parsed' in instruction && instruction.programId.equals(TOKEN_PROGRAM_ID)) {
|
if ('parsed' in instruction && instruction.programId.equals(TOKEN_PROGRAM_ID)) {
|
||||||
const parsed = instruction.parsed;
|
const parsed = instruction.parsed;
|
||||||
if (parsed.type === 'transferChecked' || parsed.type === 'transfer') {
|
if (parsed.type === 'transferChecked' || parsed.type === 'transfer') {
|
||||||
const destination = parsed.info.destination;
|
// TODO: Check recipient address
|
||||||
|
// Verify amount
|
||||||
// Verify both amount and destination address
|
if (parsed.info.amount === amount ) {
|
||||||
if (parsed.info.amount === amount && destination === process.env.NEXT_PUBLIC_SOLANA_TOKEN_RECIPIENT_ADDRESS) {
|
|
||||||
foundValidTransfer = true;
|
foundValidTransfer = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user