Created
August 27, 2024 04:29
-
-
Save NicolasPennie/194a9fac7c128baf8cc6241380151891 to your computer and use it in GitHub Desktop.
Helius Ping Test
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
Commitment, | |
ComputeBudgetProgram, | |
Connection, | |
Keypair, | |
PublicKey, | |
SystemProgram, | |
Transaction, | |
} from '@solana/web3.js'; | |
import * as base58 from 'bs58'; | |
import { client, v1, v2 } from '@datadog/datadog-api-client'; | |
const PRIORITY_FEE_MICROLAMPORTS = 200_000; | |
const COMPUTE_LIMIT = 10_000; | |
const TEST_CASES = [ | |
{ | |
label: 'helius_high_priority', | |
url: 'REDACTED', | |
}, | |
{ | |
label: 'quicknode', | |
url: 'REDACTED', | |
}, | |
{ | |
label: 'triton_jet', | |
url: 'REDACTED', | |
}, | |
]; | |
const DEFAULT_RPC = 'https://mainnet.helius-rpc.com/?api-key=REDACTED'; | |
const DESTINATION_ACCOUNT = 'REDACTED'; | |
const KEYPAIR = Keypair.fromSecretKey(base58.decode('OBVIOUSLY_REDACTED')); | |
const TIMEOUT_MS = 60_000; | |
const REGION_MAP: Record<string, string> = { | |
'us-east-2': 'pitt', | |
'eu-central-1': 'fra', | |
'ap-southeast-1': 'sg', | |
}; | |
const configuration = client.createConfiguration(); | |
const datadogV1 = new v1.MetricsApi(configuration); | |
const datadogV2 = new v2.MetricsApi(configuration); | |
interface SignatureStatus { | |
label: string; | |
signature: string; | |
landed: boolean; | |
} | |
async function main() { | |
let commitment: Commitment = 'confirmed'; | |
const defaultConnection = new Connection(DEFAULT_RPC, { commitment }); | |
const { blockhash, lastValidBlockHeight } = await defaultConnection.getLatestBlockhash({ | |
commitment, | |
}); | |
let awsRegion = process.env['AWS_REGION']; | |
let getSlotPromise = defaultConnection.getSlot(); | |
let statusPromises: Promise<SignatureStatus>[] = []; | |
let submittedAt = Date.now(); | |
for (let i = 0; i < TEST_CASES.length; i++) { | |
// Generate a unique nonce for each test case to avoid sending the same tx twice. | |
let regionNonce = awsRegion ? Object.keys(REGION_MAP).indexOf(awsRegion) + 1 : 0; | |
let nonce = regionNonce * TEST_CASES.length + i; | |
const { label, url } = TEST_CASES[i]; | |
try { | |
const testConn = new Connection(url, { | |
commitment, | |
}); | |
const tx = generateCanaryTx(blockhash, nonce); | |
const signature = base58.encode(tx.signature!); | |
let status: Promise<SignatureStatus> = new Promise(async (resolve, _) => { | |
try { | |
await testConn.sendRawTransaction(tx.serialize(), { skipPreflight: true }); | |
let landed = false; | |
while (landed == false) { | |
let s = await defaultConnection.getSignatureStatus(signature); | |
if ( | |
s.value?.confirmationStatus === 'confirmed' || | |
s.value?.confirmationStatus === 'finalized' | |
) { | |
landed = true; | |
} else { | |
await sleep(1000); | |
} | |
} | |
resolve({ | |
label, | |
signature, | |
landed, | |
}); | |
} catch (e: any) { | |
console.error('error while sending tx', { | |
label, | |
rpcUrl: url, | |
message: e.message, | |
}); | |
resolve({ | |
label, | |
signature, | |
landed: false, | |
}); | |
} | |
}); | |
let timeout: Promise<SignatureStatus> = sleep(TIMEOUT_MS).then(() => ({ label, signature, landed: false })); | |
statusPromises.push(Promise.race([status, timeout])); | |
} catch (e: any) { | |
console.error('error while sending tx', { | |
rpcUrl: url, | |
message: e.message, | |
stack: e.stack, | |
}); | |
} | |
} | |
let [submissionSlot, ...statuses] = await Promise.all([getSlotPromise, ...statusPromises]); | |
// To avoid cluster sync issues when we fetch the slot. | |
await sleep(400); | |
for (const { signature, label, landed } of statuses) { | |
let tags = [`label:${label}`]; | |
let regionTag = REGION_MAP[awsRegion!]; | |
if (regionTag) { | |
tags.push(`region:${regionTag}`); | |
} | |
if (landed) { | |
await submitMetric('tx_landing_canary.landed', 1, tags, MetricType.Count); | |
let slot = await defaultConnection.getSignatureStatuses([signature]).then((r) => r.value[0]?.slot); | |
if (slot == null) { | |
console.error(`Failed to find slot for signature (label: ${label})`, signature); | |
} | |
let slotLatency = Math.max(slot! - submissionSlot, 0); | |
await submitDistribution('tx_landing_canary.slotLatency', slotLatency, tags, MetricType.Gauge); | |
} else { | |
await submitMetric('tx_landing_canary.not_landed', 1, tags, MetricType.Count); | |
} | |
} | |
} | |
function generateCanaryTx(blockhash: string, nonce: number): Transaction { | |
const transferIx = SystemProgram.transfer({ | |
fromPubkey: KEYPAIR.publicKey, | |
toPubkey: new PublicKey(DESTINATION_ACCOUNT), | |
lamports: 1, | |
}); | |
const computePriceIx = ComputeBudgetProgram.setComputeUnitPrice({ | |
microLamports: Math.round(PRIORITY_FEE_MICROLAMPORTS), | |
}); | |
const computeLimitIx = ComputeBudgetProgram.setComputeUnitLimit({ | |
// nonce ensures we send a unique tx each time. | |
units: COMPUTE_LIMIT + nonce, | |
}); | |
let tx = new Transaction().add(computeLimitIx, computePriceIx, transferIx); | |
tx.recentBlockhash = blockhash; | |
tx.sign(KEYPAIR); | |
return tx; | |
} | |
enum MetricType { | |
None = 0, | |
Count = 1, | |
Rate = 2, | |
Gauge = 3, | |
} | |
async function submitMetric(metric: string, value: number, tags: string[], type: MetricType) { | |
await datadogV2.submitMetrics({ | |
body: { | |
series: [ | |
{ | |
metric, | |
type, | |
points: [ | |
{ | |
timestamp: Math.round(new Date().getTime() / 1000), | |
value, | |
}, | |
], | |
resources: [ | |
{ | |
name: 'lambda', | |
type: 'host', | |
}, | |
], | |
tags, | |
}, | |
], | |
}, | |
}); | |
console.info(metric, value, tags); | |
} | |
async function submitDistribution(metric: string, value: number, tags: string[], type: MetricType) { | |
await datadogV1.submitDistributionPoints({ | |
body: { | |
series: [ | |
{ | |
metric, | |
points: [[Math.round(new Date().getTime() / 1000), [value]]], | |
host: 'lambda', | |
tags, | |
}, | |
], | |
}, | |
contentEncoding: 'deflate', | |
}); | |
console.info(metric, value, tags); | |
} | |
function sleep(ms: number) { | |
return new Promise((resolve) => setTimeout(resolve, ms)); | |
} | |
export const handler = async (): Promise<void> => { | |
console.log('Running lambda'); | |
await main(); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Helius has all my stakes on a dedicated rpc node I am not a developer I need an easy to use full service product. I don’t know how nor do I have the know how. If you could please connect this correctly id appreciate it. Right now this is like a block and I have no gd access to my stuff smh. They did not have my permission to do all of this there’s a way in but no way out bs. Please if you can