-
-
Save jp4g/c800ed2f9b2bf7318ed656cc275f1e1d to your computer and use it in GitHub Desktop.
Magic Eden On-Chain Transaction Parsing
This file contains 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
const solanaWeb3 = require('@solana/web3.js'); | |
// replace with private paid RPC for best functionality | |
const rpc = "https://ssc-dao.genesysgo.net"; | |
// magic eden v2 solana program ID | |
const magicEdenPID = new solanaWeb3.PublicKey("M2mx93ekt1fmXSVkTrUL9xVFHkmME8HTUi5Cyc5aF7K"); | |
// only return confirmed transactions | |
const solana = new solanaWeb3.Connection(rpc, 'confirmed'); | |
/** | |
* Given a transaction and a type, extract specific addresses | |
* @param {Object} tx - the transaction returned from the rpc | |
* @param {string} txType - string for type switch case | |
* @returns {Object | null} - needed addresses from tx or null if unrecognized txType | |
*/ | |
function parseAccounts(tx, txType) { | |
const accounts = tx.transaction.message.accountKeys; | |
switch (txType) { | |
case 'buy': | |
return { | |
buyer: accounts[0], | |
seller: accounts[4], | |
token: accounts[11] | |
} | |
case 'sell': | |
return { | |
seller: accounts[0], | |
buyer: accounts[4], | |
token: accounts[11] | |
} | |
case 'list': | |
return { | |
seller: accounts[0], | |
token: accounts[4], | |
} | |
case 'update': | |
return { | |
seller: accounts[0], | |
token: accounts[4], | |
} | |
case 'delist': | |
return { | |
seller: accounts[0], | |
token: accounts[5], | |
} | |
default: | |
return null; | |
} | |
} | |
/** | |
* Classify and parse a transaction from the Solana RPC | |
* | |
* @param {Object} tx - the transaction object returned from the solana RPC | |
* @return {Object} - formatted tx classification & data extracted from RPC response | |
*/ | |
function parseTransaction(tx) { | |
const logs = tx.meta.logMessages; | |
const instructions = logs.filter(log => log.includes("Instruction")); | |
const price = JSON.parse(logs.filter(log => log.includes("price"))[0].slice(13)).price; | |
let txType = null; | |
if (instructions.filter(entry => entry.includes("ExecuteSale")).length != 0) { | |
/* If instruction includes 'ExecuteSale' determine if buyer or seller executed */ | |
if (instructions.filter(entry => entry.includes("Buy")).length != 0) | |
txType = "buy"; | |
else | |
txType = "sell"; | |
} else if (instructions.filter(entry => entry.includes(" Sell")).length != 0) { | |
/* If instruction is not a sale and includes 'sell' determine if initial listing or listing update */ | |
// @notice needs a more robust decoding feature - will fail with custom instruction sequences | |
if (logs.length == 5) | |
txType = "update"; | |
else | |
txType = "list"; | |
} else if (instructions.filter(entry => entry.includes("CancelSell")).length != 0) | |
/* Instruction delists token from exchange */ | |
txType = "delist"; | |
if (txType !== null) return { | |
/* Instruction is not recognized */ | |
// @notice purposely ignoring bids and cancel bids. Can easily be added with same scheme as above | |
txType, | |
price, | |
...parseAccounts(tx, txType) | |
}; | |
else return null; | |
} | |
/** | |
* Get the last 1000 transactions from Magic Eden Solana Program on-chain and parse them | |
* @return {Object[]} - array of parsed transactions | |
*/ | |
async function main() { | |
const signatures = await solana.getSignaturesForAddress(magicEdenPID); | |
return await Promise.all( | |
signatures.map(sig => new Promise(async (resolve, reject) => { | |
try { | |
solana.getTransaction(signatures[0].signature) | |
.then(res => parseTransaction(res)) | |
.then(res => resolve(res)); | |
} catch (e) { | |
reject(e) | |
} | |
})) | |
); | |
} | |
main().then(console.log); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
<3