CTRL/SHIFT - 14 06 2026 PARTHENOPE STAGE - GEORGIOS LOUKOVITIS - THE BRIDGE PROBLEM
NapulETH Meetup·Thu, Jul 9, 2026, 12:00 AM
Transcript
So my name is George. I work at EDOB and we will spend the next few minutes talking about ERC20 tokens. We will provide a definition of what a token is. We will discuss a few security characteristics one needs to be wary of when dealing with tokens. And finally we will discuss ways you can go about checking for the presence of such characteristics in the tokens you are dealing with.
But first, why do we care? I'm not sure that this Oh, yeah, it works. Yeah. Why do we care? After all, tokens are simply, right?
You buy them, you wait for them to go to the moon. Well, not these days anyways. You sell them, and you buy your Lambo, right? Well, not really. You see, no matter what you do in web 3, in some way or form, you're dealing with tokens.
If you're providing liquidity to a decentralized exchange, your position is represented by a token. If you're participating in a governance protocol, your voting power is also a token. If you're liquid staking with Loa, for example, you're still having a token represent your stake. So, what are tokens? Tokens in essence are just smart contracts.
Smart contracts that implement the ERC20 interface as that was outlined in EIP20. That means that they implement certain functions. These are balance of, transfer, transfer from, approve, and total supply. So what do we expect tokens to do? Well, we expect amounts of a token to be able to be held by addresses.
This is what the balance of function returns. And we also expect that these amounts to be able to be transferred either directly through the transfer function or indirectly through the approve and transfer from functions. At least that's what we expect tokens to do. But as we said, tokens are contracts and these functions are implemented by arbitrary code. That means that there is no guarantee that the expectations we have as to the behavior of a token will be met.
It falls on us to check that the token we're about to deal with matches our expectations. So what makes sense for us to be looking for? Well, ideally there will be no transfer restrictions. That means that the tokens you own, you're able to transfer without limitation. Yet, there are many mechanisms that prevent exactly that.
From transfer caps to position caps, blacklists, trading cooldowns to its extreme, uh, honeypotss where you're able to buy a token, but you're not able to sell it. There are mechanisms that can disallow you from dealing with your balance as you might. Then there is balance manipulation. Ideally, you would be solely responsible and capable of altering your balance. Yet, there are many tokens and we will see an example later that fail to meet that standard.
Then there is supply manipulation. You would expect a token you hold to have some value and that value is derived from its scarcity. But if the token is infinitely mintable, there is no scarcity and thus there is no value. There are taxes. Ideally, you would be able to transfer amounts that you own without incurring any fees or at least if there are any fees, there would be capped and not subject to infinite change.
There is centralization. One of the reasons we got into web three is because we didn't want centralized authorities to have power over our wealth. Yet, there are some tokens that provide such privileges to specific addresses. Or even if there are no special privileges, if a large percentage of the total supply of a token that you own is held by very few addresses, these addresses essentially control the value of the token. say that they dump it.
Finally, there is the reputation of the token of the team because all these mechanisms we're talking about are not inherently malicious, but they can be. So, in some cases, they are necessary. Minting is necessary for a stable coin because the reserves need to match the total supply. So with all these in mind, how do we go about checking for such characteristics? First, we can turn to onchain data.
Onchain data can let us know about the the supply distribution of the token, the amount of unique addresses that interact with it, whether the volume of the token is artificial or actually organic. They can also let us know about prior behavior of the creators of the token v via fun flow analysis. Then we can turn to simulation. Tools like foundry allow us to simulate actions we want to take with regards to a token before we actually do so. So for example, we could use an onchain data provider to find a holder of a token and then we can use Foundry to simulate a transfer from that holder to another address and inspect the result.
For example, here we can see that transferring this token would incur a 5% fee. Now the thing is that you're not going to sell a token the moment that you buy it. So, what if the simulation succeeds now, but that's no longer the case in the future? In fact, that's exactly how honeypots work. Honeyots have a specific logic within the code that may disallow certain addresses or all addresses except from the owner from selling a token.
So, how do you go about checking for behavior that is not currently observable but may be enabled in the future? You can inspect the code. As a matter of fact, how many of you currently hold USDT? Cool. Let's have a look.
So, we can see here that you're only able to sell or transfer USDT if you're not blacklisted. We can only we can also see here that once you get blacklisted, your balance might be wiped. And like there is an actual owner to USDT. So it's not a renounced address. This could technically happen.
Mind you, there is a whole social context outside of code that may allow us to trust Tether. But the point I'm trying to make here is that there is more nuance to tokens that one than one might think. We were only able to see that because we have access to the source code of USDT as it was provided to us by its creators. That is not always the case. In fact, more often than not, we do not have access to the source code at least for the first um for the first blocks after the deployment of a token.
Coincidentally, that is also when it sees most of its action. So, what do we do then? Then we can rely on the compilation. You see, we might not have access to the source code of the token, but we do have access to the executable as that is stored on chain in the form of EVM bite code, which looks a bit like this. Or like to make it a bit more readable like this.
Unless you're running the AVM stack machine in your head right now, this should not tell you a lot. But one of the things we do at EDOB is that we provide a decompiler which can take this and turn it into this and then into this. Now this can actually be inspected for all the kinds of mechanisms we were talking about earlier. The thing is that all these ways we have been discussing are time consuming. They also require some technical skill.
What my team tries to do is to automate this process. Now, the funny thing is that I've been giving this talk for a year now and it is baffling to me how much this part has changed. You see, a year ago, if you were to build a tool that automates security checks for ERC20 tokens, you would go a bit like this. You would define the mechanism you're trying to detect and then you would split your effort into two parts. First there is static analysis where you would collect many examples of that mechanism being deployed.
So all the different code occurrences that implement it but you would never find all of them. Then you would try to generalize these examples into a pattern. Uh but the thing is that you will either be too generic and you will drown in false posit positives or you will be too specific and you will miss variations of the mechanism and then you will try to express uh this pattern into a static analysis engine like slither or gigahorse. Then there is the simulation part where you will try to observe the mechanism from a black from a blackbox perspective. You could try to establish a series of actions that can invoke that behavior.
But the thing is that other than the ERC20 standard, you can only guess as for the names of the functions that you need to call and then you would write a script that simulates the series of actions and inspects the behavior. The problem with that is that there is a myriad of different ways that you can go about implementing a specific mechanism and so many names that you can use for it. But you're trying to have a single algorithm to detect it. This can be attempted but it doesn't really work. AI to the rescue.
Actually no before that uh as an example um so blacklists for example this is what a blacklist typically looks like there is a mapping lookup of the message sender whose value controls the flow of the transfer function but what if it's not a mapping what if it's an array or what if it's an external call to an access control uh contract. Now, back to the previous part. AI to the rescue. Is this is this it? Like, do we ask Claude and we're done?
Because like it's a good way to go about it because the thing with AI is that it can make the checks specific to the token. It doesn't matter if the function is called blacklist or like restrict or deny because the check here will be done in the context of the specific contract. So, are we done? Unfortunately, not because LLMs, powerful as they are, they are random, which means that across different runs, the procedure and the results will vary drastically. So, for example, in the first run, the LLM would fetch the source code, but it wouldn't fetch the storage variables, and it would report a mechanism as being active, although it is no longer accessible due to the storage allocations.
And then on the second run, it would fetch the API to do the simulations, but it wouldn't fetch the source code. So it would attempt a simulation and it would be successful, but it would miss the fact that in a week from now, this will no longer be successful because it's a honeypot. I believe that there is a balance to be struck here. There there are things that we still need to be doing ourselves instead of relying on the LLM to do everything on its own. We are the ones that should provide the list and definitions of the mechanisms we're trying to detect because the question is this safe is extremely vague.
What does it mean? Is it safe? Does it mean that if I try to trade it right now, it's going to be successful? Or does it mean that I'm still going to be in control of my balance in a week from now? Then it is up to us to provide the procedure that should be followed.
Checklists have the same effect for LLMs that they do for manual audits. They prevent silly omissions. You can make sure that at least the baseline of stuff that need to happen will happen. The source will be inspected. The storage will be checked.
Uh the past transactions regarding that token will be checked. Then it is up to us to package all the things that can be done. and deterministically as tools for the LLM. There is no reason for the LLM to need to figure out how to fetch the code or read storage or simulate a fun uh transaction. All that can be done by us and provided as tools.
Another benefit we get by providing specific tools is that we can instrument them. This means that we can get logs for past runs of the LLM. Which means that if we get error results, we can debug it. We can actually inspect what happened, what were the calls, what were the results, how it was run. And finally, it is up to us to establish an evaluation pipeline for the tool we're building.
Because to build a tool that is centered around something as random as an LLM and just hope for the best is a recipe for disaster. The thing that we get from the LLM is that all of all the procedure we have established can be tailored to specific tokens instead of us trying to be generic enough to be token agnostic. Thank you very much.
Automatic transcript — names and jargon may be misspelled.