Skip to content

Instantly share code, notes, and snippets.

@jp4g
Forked from mertimus/magicEdenParser.js
Last active July 24, 2022 07:38
Show Gist options
  • Save jp4g/c800ed2f9b2bf7318ed656cc275f1e1d to your computer and use it in GitHub Desktop.
Save jp4g/c800ed2f9b2bf7318ed656cc275f1e1d to your computer and use it in GitHub Desktop.
Magic Eden On-Chain Transaction Parsing
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);
@jp4g
Copy link
Author

jp4g commented Apr 25, 2022

<3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment