forked from mito-systems/sol-mem-gen
Implement retry of lock transaction #16
@ -25,3 +25,4 @@ WSOL_MINT_ADDRESS=So11111111111111111111111111111111111111112
|
|||||||
# Duration in seconds that WSOL will be locked for
|
# Duration in seconds that WSOL will be locked for
|
||||||
WSOL_LOCK_DURATION_IN_SECONDS=172800 # 48 hours
|
WSOL_LOCK_DURATION_IN_SECONDS=172800 # 48 hours
|
||||||
REWARD_MULTIPLIER=4
|
REWARD_MULTIPLIER=4
|
||||||
|
MAX_RETRIES_FOR_LOCK_TX=5
|
||||||
|
@ -35,8 +35,10 @@ import {
|
|||||||
} from './token-2022/remaining-accounts';
|
} from './token-2022/remaining-accounts';
|
||||||
|
|
||||||
assert(process.env.NEXT_PUBLIC_SOLANA_RPC_URL);
|
assert(process.env.NEXT_PUBLIC_SOLANA_RPC_URL);
|
||||||
|
assert(process.env.MAX_RETRIES_FOR_LOCK_TX);
|
||||||
|
|
||||||
const connection = new Connection(process.env.NEXT_PUBLIC_SOLANA_RPC_URL);
|
const connection = new Connection(process.env.NEXT_PUBLIC_SOLANA_RPC_URL);
|
||||||
|
const MAX_RETRIES = Number(process.env.MAX_RETRIES_FOR_LOCK_TX);
|
||||||
|
|
||||||
export function createLockerProgram(wallet: Wallet): Program<Locker> {
|
export function createLockerProgram(wallet: Wallet): Program<Locker> {
|
||||||
const provider = new AnchorProvider(connection, wallet, {
|
const provider = new AnchorProvider(connection, wallet, {
|
||||||
@ -133,6 +135,9 @@ export async function createVestingPlanV2(params: CreateVestingPlanParams) {
|
|||||||
|
|
||||||
assert(tokenProgram);
|
assert(tokenProgram);
|
||||||
|
|
||||||
|
let attempt = 0;
|
||||||
|
|
||||||
|
while (attempt < MAX_RETRIES) {
|
||||||
try {
|
try {
|
||||||
await program.methods
|
await program.methods
|
||||||
.createVestingEscrowV2(
|
.createVestingEscrowV2(
|
||||||
@ -158,7 +163,6 @@ export async function createVestingPlanV2(params: CreateVestingPlanParams) {
|
|||||||
tokenProgram,
|
tokenProgram,
|
||||||
systemProgram: web3.SystemProgram.programId,
|
systemProgram: web3.SystemProgram.programId,
|
||||||
escrow,
|
escrow,
|
||||||
// TODO: Fix type error for escrowToken
|
|
||||||
} as any)
|
} as any)
|
||||||
.remainingAccounts(remainingAccounts ? remainingAccounts : [])
|
.remainingAccounts(remainingAccounts ? remainingAccounts : [])
|
||||||
.preInstructions([
|
.preInstructions([
|
||||||
@ -174,23 +178,30 @@ export async function createVestingPlanV2(params: CreateVestingPlanParams) {
|
|||||||
.signers([baseKP, ownerKeypair])
|
.signers([baseKP, ownerKeypair])
|
||||||
.rpc();
|
.rpc();
|
||||||
|
|
||||||
return escrow;
|
return escrow; // Success, return escrow
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error(`Attempt ${attempt + 1} Transaction confirmation delayed`);
|
||||||
|
|
||||||
|
// If transaction expired, check confirmation
|
||||||
if (error instanceof TransactionExpiredTimeoutError) {
|
if (error instanceof TransactionExpiredTimeoutError) {
|
||||||
console.error('Transaction confirmation delayed for', error.signature);
|
console.log('Checking if transaction was confirmed...');
|
||||||
console.log('Confirming the transaction again...');
|
const confirmedTransaction = await program.provider.connection.getTransaction(error.signature, {
|
||||||
const confirmedTransaction = await connection.getTransaction(error.signature, {
|
|
||||||
commitment: 'confirmed',
|
commitment: 'confirmed',
|
||||||
maxSupportedTransactionVersion: 0
|
maxSupportedTransactionVersion: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
if(confirmedTransaction === null) {
|
if (confirmedTransaction !== null) {
|
||||||
console.error('Transaction failed for', error.signature);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
return escrow;
|
return escrow;
|
||||||
}
|
}
|
||||||
throw error;
|
} else {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
attempt++;
|
||||||
|
console.log(`Attempt ${attempt + 1} Transaction failed, retrying...`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw new Error("Transaction failed after maximum retries");
|
||||||
|
}
|
||||||
|
@ -34,10 +34,10 @@ const provider = new anchor.AnchorProvider(
|
|||||||
|
|
||||||
anchor.setProvider(provider);
|
anchor.setProvider(provider);
|
||||||
|
|
||||||
export async function createLock(tokenLockerKeypair: anchor.web3.Keypair, recipientPubKey: anchor.web3.PublicKey, duration: BN, balance: BN): Promise<anchor.web3.PublicKey | undefined> {
|
export async function createLock(tokenLockerKeypair: anchor.web3.Keypair, recipientPubKey: anchor.web3.PublicKey, duration: BN, amount: BN): Promise<anchor.web3.PublicKey | void> {
|
||||||
|
|
||||||
if (balance.eq(new BN(0))) {
|
if (amount.eq(new BN(0))) {
|
||||||
console.log('No balance available to create lock, skipping...');
|
console.log('Invalid Amount');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ export async function createLock(tokenLockerKeypair: anchor.web3.Keypair, recipi
|
|||||||
isAssertion: true,
|
isAssertion: true,
|
||||||
cliffTime: duration,
|
cliffTime: duration,
|
||||||
frequency: new BN(1), // Not needed since full unlock happens at cliff
|
frequency: new BN(1), // Not needed since full unlock happens at cliff
|
||||||
cliffUnlockAmount: balance, // The entire amount should be released at cliff
|
cliffUnlockAmount: amount, // The entire amount should be released at cliff
|
||||||
amountPerPeriod: new BN(0), // No tokens should be released before cliff
|
amountPerPeriod: new BN(0), // No tokens should be released before cliff
|
||||||
numberOfPeriod: new BN(1), // Only release tokens once
|
numberOfPeriod: new BN(1), // Only release tokens once
|
||||||
recipient: recipientPubKey,
|
recipient: recipientPubKey,
|
||||||
@ -66,41 +66,24 @@ export async function createLock(tokenLockerKeypair: anchor.web3.Keypair, recipi
|
|||||||
return escrow;
|
return escrow;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function extractInfo(transactionSignature: string) {
|
export async function createRewardLock(recipient: string, txMtmAmount: string) {
|
||||||
const transaction = await connection.getParsedTransaction(transactionSignature, 'confirmed');
|
|
||||||
if (!transaction) {
|
|
||||||
throw new Error('Transaction not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
const transferInstruction = transaction.transaction.message.instructions.find(
|
|
||||||
(instr) => 'parsed' in instr && instr.programId.equals(TOKEN_PROGRAM_ID)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!transferInstruction || !('parsed' in transferInstruction)) {
|
|
||||||
throw new Error('Transfer instruction not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
const { info: { amount, authority } } = transferInstruction.parsed;
|
|
||||||
return { authority, amount };
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function createRewardLock(authority: string, amount: string) {
|
|
||||||
const { WSOL_LOCKER_ACCOUNT_PK, WSOL_LOCK_DURATION_IN_SECONDS, WSOL_MINT_ADDRESS, NEXT_PUBLIC_MTM_MINT_ADDRESS, REWARD_MULTIPLIER } = process.env;
|
const { WSOL_LOCKER_ACCOUNT_PK, WSOL_LOCK_DURATION_IN_SECONDS, WSOL_MINT_ADDRESS, NEXT_PUBLIC_MTM_MINT_ADDRESS, REWARD_MULTIPLIER } = process.env;
|
||||||
|
|
||||||
if (!WSOL_LOCKER_ACCOUNT_PK || !WSOL_LOCK_DURATION_IN_SECONDS || !WSOL_MINT_ADDRESS || !NEXT_PUBLIC_MTM_MINT_ADDRESS || !REWARD_MULTIPLIER) {
|
if (!WSOL_LOCKER_ACCOUNT_PK || !WSOL_LOCK_DURATION_IN_SECONDS || !WSOL_MINT_ADDRESS || !NEXT_PUBLIC_MTM_MINT_ADDRESS || !REWARD_MULTIPLIER) {
|
||||||
throw new Error('Missing required environment variables');
|
throw new Error('Missing required environment variables for creating reward wSOL lock');
|
||||||
}
|
}
|
||||||
|
|
||||||
const duration = new BN(WSOL_LOCK_DURATION_IN_SECONDS).add(new BN(Math.floor(Date.now() / 1000)));
|
const lockDuration = new BN(WSOL_LOCK_DURATION_IN_SECONDS).add(new BN(Math.floor(Date.now() / 1000)));
|
||||||
const tokenLockerKeypair = Keypair.fromSecretKey(bs58.decode(WSOL_LOCKER_ACCOUNT_PK));
|
const tokenLockerKeypair = Keypair.fromSecretKey(bs58.decode(WSOL_LOCKER_ACCOUNT_PK));
|
||||||
const recipientPublicKey = new PublicKey(authority);
|
const recipientPublicKey = new PublicKey(recipient);
|
||||||
|
|
||||||
const url = `https://api.jup.ag/price/v2?ids=${NEXT_PUBLIC_MTM_MINT_ADDRESS}&vsToken=${WSOL_MINT_ADDRESS}`;
|
const url = `https://api.jup.ag/price/v2?ids=${NEXT_PUBLIC_MTM_MINT_ADDRESS}&vsToken=${WSOL_MINT_ADDRESS}`;
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
const { data } = await response.json();
|
const { data } = await response.json();
|
||||||
|
|
||||||
const priceWSOLFor1MTM = new Big(data[NEXT_PUBLIC_MTM_MINT_ADDRESS].price).toFixed(9);
|
const priceWSOLFor1MTM = new Big(data[NEXT_PUBLIC_MTM_MINT_ADDRESS].price).toFixed(9);
|
||||||
const mtmAmount = new Big(amount).div(new Big(10).pow(6));
|
const mtmAmount = new Big(txMtmAmount).div(new Big(10).pow(6));
|
||||||
const wsolAmount = new BN(new Big(mtmAmount).times(priceWSOLFor1MTM).times(new Big(10).pow(9)).times(REWARD_MULTIPLIER).toFixed(0));
|
const wsolAmount = new BN(new Big(mtmAmount).times(priceWSOLFor1MTM).times(new Big(10).pow(9)).times(REWARD_MULTIPLIER).toFixed(0));
|
||||||
|
|
||||||
return createLock(tokenLockerKeypair, recipientPublicKey, duration, wsolAmount);
|
return createLock(tokenLockerKeypair, recipientPublicKey, lockDuration, wsolAmount);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user