Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into backend/tx-monitoring
Browse files Browse the repository at this point in the history
  • Loading branch information
mat1asm committed Mar 28, 2024
2 parents bc0c4b9 + 0cafdf7 commit afff929
Show file tree
Hide file tree
Showing 25 changed files with 783 additions and 131 deletions.
File renamed without changes.
2 changes: 1 addition & 1 deletion backend/src/utils/discord.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Keypair, PublicKey } from '@solana/web3.js'
import { SignedMessage } from '../types'
import nacl from 'tweetnacl'
import IDL from '../token_dispenser.json'
import IDL from '../token-dispenser.json'
import * as anchor from '@coral-xyz/anchor'
import config from '../config'

Expand Down
86 changes: 75 additions & 11 deletions backend/src/utils/fund-transactions.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import {
ComputeBudgetInstruction,
ComputeBudgetProgram,
Ed25519Program,
PublicKey,
Secp256k1Program,
TransactionInstruction,
VersionedTransaction
} from '@solana/web3.js'
import IDL from '../token_dispenser.json'
import * as anchor from '@coral-xyz/anchor'

import config from '../config'

const SET_COMPUTE_UNIT_LIMIT_DISCRIMINANT = 2
// const SET_COMPUTE_UNIT_PRICE = 1_000_000;
const SET_COMPUTE_UNIT_PRICE_DISCRIMINANT = 3

const MAX_COMPUTE_UNIT_PRICE = BigInt(1_000_000)

const coder = new anchor.BorshCoder(IDL as any)

Expand Down Expand Up @@ -52,14 +57,21 @@ export function checkAllProgramsWhitelisted(
transaction.message.staticAccountKeys[ix.programIdIndex].equals(program)
)
) {
console.error('Program not whitelisted')
return false
}
}
return true
}

export function checkV0(transaction: VersionedTransaction) {
return transaction.version === 0
const isVersion0Transaction = transaction.version === 0
if (!isVersion0Transaction) {
console.error(
'Transaction sent is a legacy transaction, expected a V0 transaction.'
)
}
return isVersion0Transaction
}

export function checkSetComputeBudgetInstructionsAreSetComputeUnitLimit(
Expand All @@ -71,15 +83,58 @@ export function checkSetComputeBudgetInstructionsAreSetComputeUnitLimit(
ComputeBudgetProgram.programId
)
) {
// Note: We continue processing tx data because setComputeUnitPrice is mandatory to be set
if (ix.data[0] === SET_COMPUTE_UNIT_PRICE_DISCRIMINANT) continue

if (ix.data[0] !== SET_COMPUTE_UNIT_LIMIT_DISCRIMINANT) {
console.error('Compute unit limit discriminant does not match')
return false
}
ix
}
}
return true
}

export function checkSetComputeBudgetInstructionsAreSetComputeUnitPrice(
transaction: VersionedTransaction
) {
let priorityFeeInstructionFound = false
for (const ix of transaction.message.compiledInstructions) {
if (
transaction.message.staticAccountKeys[ix.programIdIndex].equals(
ComputeBudgetProgram.programId
)
) {
/*
Below is a hack that was added to extract the priority fee from the transaction
ComputeBudgetInstruction.decodeInstructionType requires legacTransactionInstruction not
MessageCompiledInstruction
*/
const programId = ComputeBudgetProgram.programId
const legacTransactionInstruction = new TransactionInstruction({
keys: [],
programId,
data: Buffer.from(ix.data)
})

const instructonType = ComputeBudgetInstruction.decodeInstructionType(
legacTransactionInstruction
)
if (instructonType === 'SetComputeUnitPrice') {
priorityFeeInstructionFound = true
const priorityFee = ComputeBudgetInstruction.decodeSetComputeUnitPrice(
legacTransactionInstruction
)
if (priorityFee.microLamports >= MAX_COMPUTE_UNIT_PRICE) {
console.error('Priority fee set is too high')
return false
}
}
}
}
return priorityFeeInstructionFound
}

export function checkProgramAppears(
transaction: VersionedTransaction,
program: PublicKey
Expand All @@ -91,6 +146,7 @@ export function checkProgramAppears(
return true
}
}
console.error('Token dispenser program not found in transaction')
return false
}

Expand All @@ -117,36 +173,44 @@ export function countPrecompiledSignatures(
.reduce((acc, ix) => acc + ix.data[0], 0)
}

// TODO: Verify if this is the expected behavior
export function checkNumberOfSignatures(
transaction: VersionedTransaction
): boolean {
return countTotalSignatures(transaction) <= 3
const numberOfSignatures = countTotalSignatures(transaction)
if (numberOfSignatures > 3) {
console.error('Transaction has too many signatures')
}
return numberOfSignatures <= 3
}

export function checkTransaction(
transaction: VersionedTransaction,
tokenDispenser: PublicKey,
whitelist: PublicKey[]
): boolean {
// TODO: Also check if priority fee/compute unit price is set, also can use helius api here to verify with some diff percentage
return (
checkProgramAppears(transaction, tokenDispenser) && // Make sure at least one instruction is for the token dispenser
checkSetComputeBudgetInstructionsAreSetComputeUnitLimit(transaction) && // Make sure all compute budget instructions are set compute unit limit
checkAllProgramsWhitelisted(transaction, whitelist) && // Make sure all programs are either signature precompiles, token dispenser, or compute budget
checkV0(transaction) && // Check the transaction is V0
checkNumberOfSignatures(transaction) // Check the transaction has at most 3 signatures, since each signature costs 0.000005 SOL
checkNumberOfSignatures(transaction) && // Check the transaction has at most 3 signatures, since each signature costs 0.000005 SOL
checkSetComputeBudgetInstructionsAreSetComputeUnitPrice(transaction) // Check the transaction has set priority fee
)
}

export async function checkTransactions(
transactions: VersionedTransaction[]
): Promise<boolean> {
const whitelist = await loadWhitelistedProgramIds()
try {
const whitelist = await loadWhitelistedProgramIds()

return transactions.every((tx) =>
checkTransaction(tx, whitelist[0], whitelist)
)
return transactions.every((tx) =>
checkTransaction(tx, whitelist[0], whitelist)
)
} catch (err) {
console.error('Error occured while checking transactions', err)
return false
}
}

export function extractCallData(
Expand Down
Loading

0 comments on commit afff929

Please sign in to comment.