How to get token transfer information

Hi,

I know time has passed after this but i want to re-itterateā€¦

This functionality for my project is needed so that iā€™m able to have a modal where i present what type of transactions have taken place (approve,swap,transfer) and display them.

With the current solution which you have showed here, this is not working since the swap method will have different log length (for example for a swap from BUSDā€“> BNB will have 8 logs but a swap BUSDā€“> SPARTA, which goes through BUSD --> BNB --> SPARTA the logs will be atleast 9 or more). Thus breaking our logic to check x and y log to get the tokens addresses and conclude which type of function it is.

Can you assist with a solution to this from Moralis?

1 Like

can you give an example of two transactions that have different number of logs?

the function name that was called should be easy to identify from me method name (hash/topic)

2 Likes

Hi, thanks for your fast answer!

I just ran 2 swaps to produce transaction logs and show you:

https://testnet.bscscan.com/tx/0x742f7e4bd25bbb07a4a19b21cfaade9cba60300da43d4c3c17dc258702f98e7e

https://testnet.bscscan.com/tx/0xc1f597fe8ba8263cabe861a7465481c1b3d44a275816186030ca8ddd6b07f016

1 Like

from logs it looks like one transaction made only one swap and the other transaction did 2 swaps, you could identify those two swap events or how many there are

2 Likes

Yes this additional swap comes from the point i made above - the router of Pancakeswap is figuring out that this route is better, thus going with it.

What i dont understand is how to identify all types of swap events - for example how can i know how many logs the swap will produce so that i use your logic above with specifying the exact log number (log[1]) for which i want to get the token address? If all swaps had exact same log structure, your solution will always work, but in this case i cannot figure out any way that it will work. Sometimes we will need log[8] to get the 2nd token address, other times it will be log[9]ā€¦ it is quite confusing. Can you clarify? Thanks!

1 Like

you will check all the logs and identify which one of them is a swap event:

Swap (index_topic_1 address sender, uint256 amount0In, uint256 amount1In, uint256 amount0Out, uint256 amount1Out, index_topic_2 address to)View Source

Topics
0 0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822

that 0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822 corresponds to that Swap function and it is computed as

x = Web3.utils.sha3('Swap(address,uint256,uint256,uint256,uint256,address)')
=>
'0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822'
2 Likes

Alright i understand what you mean!

One last thing - after i have identified this : how can i see which tokens were involved in the swap. Again this is a problem with logs number since for some swaps the token addresses will be in log[0] and log[7] but for other that will be, for example, log[0] and log [8]. I need to figure out a way not to hardcode those.

For example here - https://testnet.bscscan.com/tx/0x742f7e4bd25bbb07a4a19b21cfaade9cba60300da43d4c3c17dc258702f98e7e#eventlog
The token we want to swap into (WBNB) is on the 6th log place top to bottom, whereas here -
https://testnet.bscscan.com/tx/0xc1f597fe8ba8263cabe861a7465481c1b3d44a275816186030ca8ddd6b07f016#eventlog
The token we want to swap into (DAI) is on the 9th log place top to bottom. This is extremely confusing. Thanks for your time to deal with me <3. Appreciate your time.

1 Like

it looks like there is a sequence of events of type Transfer, Sync, Swap

so in first case you have the Transfer at logs[6] and in second case you have a transfer on logs[6] and after that Sync on logs[7] and Swap on logs[8] and after that a new Transfer on logs[9] followed by Sync and Swap

2 Likes

Hey,

For a little test app of mine, I would like to recreate exactly this information about swaps you screenshoted. I read through this feed, but it isnā€™t 100% clear to me how to get this information from the logs. The differing log size makes a generic implementation complicated, I feel like. Did you manage to reconstruct it? I would really appreciate some advice.
Thanks already for the support here :slight_smile:

1 Like

Yes it works as cryptokid suggested. They all follow same pattern of transfer,sync,swap so no matter how many logs there are the tokenTo (bottom) in the swap will be logs[length-2] (aka the last transfer) and the first token aka tokenFrom(top) will be logs[0]

2 Likes

Ok thanks, Iā€™m fairly new to the whole web3 development space, since all the data is just available in hex form, how do you parse it and is the token type included here as well? Would you be willing to share the code snippet?

1 Like

For anyone also interested in this, the above tips are gold! I managed to get a version running, but wouldnā€™t call it production ready. One thing I noticed is that the described log order for indexing does not always apply. For example, I tried to use the same logic as above, which seems to be applicable for Uniswap clones on a sushi swap (I know also a clone but seems to give different logs / order) transaction. There it didnā€™t work as intended. I try to work around it by using the transferTopicHash as an identifier for the token transfers of the swap. Then just map the logs to transactions and parse the whole thing.
E.g. in Vue3 (see ref() function and addressing of variables with ā€œ.valueā€)
You can chain a lot of these, but I wanted to keep it step wise and understandable here. Thanks again for the tips from @Cryptokid above, they send me on a good track.

const transaction = ref()
const transferTopicHash = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
        async function getTransactionFromHash(hash) { 
            const options = {
                chain: "ropsten",
                transaction_hash: hash
            };
            let trxn = await Moralis.Web3API.native.getTransaction(options);
            transaction.value = trxn;
            // TODO (sanity checks)

            // decode token addresses and values from log sequence
            let transferLogs = transaction.value['logs'].filter(log => {return log.topic0 === transferTopicHash});
            let fromTransfer = transferLogs.find(log => {return "0x"+log.topic2.slice(2,).replace(/\b0+/g, "") !== currentUser.attributes.ethAddress});
            let toTransfer = transferLogs.find(log => {return "0x"+log.topic2.slice(2,).replace(/\b0+/g, "") === currentUser.attributes.ethAddress});
            let fromTokenAmount = parseInt(Number(fromTransfer['data']), 10);
            const fromTokenAddress = fromTransfer['address'];
            let toTokenAmount = parseInt(Number(toTransfer['data']), 10);
            const toTokenAddress = toTransfer['address'];
            // get token metadata
            const tokensMetadata = await Moralis.Web3API.token.getTokenMetadata( { chain: "ropsten", addresses: [fromTokenAddress, toTokenAddress] } );
            // assign metadata to token
            const fromTokenMeta = tokensMetadata.find(token => { return token.address === fromTokenAddress} );
            const toTokenMeta = tokensMetadata.find(token => { return token.address === toTokenAddress} );
            // adjust amount by tokens decimals
            fromTokenAmount = fromTokenAmount / Math.pow(10, fromTokenMeta.decimals);
            toTokenAmount = toTokenAmount / Math.pow(10, toTokenMeta.decimals);
            
            console.log(fromTokenAmount + " " + fromTokenMeta.symbol + " => " + toTokenAmount + " " + toTokenMeta.symbol);
        }
2 Likes

How would you go about retrieving the transaction hash info for a custom table? Hereā€™s what I have so far (cloning the amazon clone project). I want to add transaction hash to the table

let result = await Moralis.transfer(options1);

//Save Transaction Details to DB

const Transaction = Moralis.Object.extend("Transaction");

const transaction = new Transaction();

transaction.set("Customer", account);

transaction.set("Delivery", delivery);

transaction.set("Product", book.name);

transaction.set("Spent", book.price);

This line may not be ok, and you also need to use .save at the end for that query to save the data

Yes i have the save.

ok, my table is named ā€œTransactionā€. you are saying to rename the table to a different name?

Iā€™m saying that the syntax seems to be different in documentation for a basic query:

https://docs.moralis.io/moralis-server/database/queries

const query = new Moralis.Query(Monster);

3

query.equalTo(ā€œownerNameā€, ā€œAegonā€);

4

const results = await query.find();

5

alert(ā€œSuccessfully retrieved " + results.length + " monsters.ā€);

6

// Do something with the returned Moralis.Object values

7

for (let i = 0; i < results.length; i++) {

8

const object = results[i];

9

alert(object.id + ā€™ - ā€™ + object.get(ā€˜ownerNameā€™));

10

Maybe something like this but query the hash? I know all the info is stored on each transaction automatically. I need to have it ā€œcalledā€ or ā€œsetā€ or ā€œgetā€ somehow aha

vs

const Monster = Moralis.Object.extend("Monster");
const query = new Moralis.Query(Monster);

that is what I noticed

would i have to query the ā€œBscTransactionsā€ table?

I donā€™t know, your example was only adding data with .set