If you're here, you likely understand the power and potential of evm-translator to create human readable interpretations of ethereum transactions. We have the infrastructure in place to elegantly interpret transactions from nearly every contract deployed to the chain. However, we still need lots of contract-specific interpretations to improve coverage. That's where you come in.
We've set up a Wonder bounty board with ~$5k USDC of bounties across ~50 contracts waiting to be interpreted.
Note: Once you've claimed a task, you'll have 24 hours to submit your work for it. This makes sure other folks have a chance to claim and do the task if it's not an immediate priority for you.
Do you have a protocol or contract you interact with frequently that you'd like to see detailed, concise interpretations for? Have you deployed your own contract that deserves recognition by evm-translator? Contribute to evm-translator!
You don't need to be an advanced web3 software developer to contribute a contract interpretation to evm-translator.
If you need any help, hop in our discord! There is a help channel.
At the end, you will submit 3 files with your Wonder Task:
- The Interpreter Map in JSON format (into a .json file)
- a list of tx hashes, 1 per function you interpreted. These will be added to our test suite
- An example of each function's interpreted output in JSON format. These can all be in 1 file. (into a .json file)
- pick a contract from Wonder's dashboard (we suggest starting with an easy one)
- go to https://evm-translator-demo.themetagame.xyz/contribute
- enter in the contract address (on the right)
- click "generate template"
- take note of the writeFunctions in the template
- check out how to interpret a contract
- In a new tab, go to https://bloxy.info. Search for the contract's address
- For each of the write functions, click on the link in the "Smart Contract Function Calls" section in the "Calls Count" column
- Click on one of the hashes in the "Tx Hash" column. You can now copy and paste the tx hash to Etherscan to get a better idea of what happens in the tx.
- Go back to the evm-translator demo site, and paste the tx hash in.
- Click "interpret". This will use the interpreter map that's been generated to interpret the tx hash you just pasted in.
- Update the interpreter map so the interpretation makes sense for this transaction
- note down the txHash and the interpretation output
- repeat steps 7-12 for each function
- Once you're done with the whole interpreter map, copy and paste it into a .json file
- Submit the 3 files to your Wonder Task!
Note: For any contract that is a proxy contract, the writeFunctions may not be correct. You'll need to do a bit of extra work to get implementation contract's functions that you want to actually interpret.
The difference here is submitting a PR with the relevant file changes, rather than submitting files via Wonder. This gives us an easier way to give you a contributor credential in the future! (and not to mention easier on the Metagame team).
A complete PR includes:
- adding 1 file
- updating 2 files
- running the tests so a new snapshot gets generated.
First, go to the Metagame discord and request api keys for your .env file in the #ask-for-api-key channel.
Once you've completed an interpreter map...
In your terminal
gh repo clone metagame-xyz/evm-translator
yarn install
-
Add the
.JSON
file you downloaded from https://evm-translator.xyz/contribute to this folder -
Add a link to the file in the index.ts
-
Add your test txs to testTxHashes
something like:
AaveV2: { repay: '0xdf2f782ab0296121318cca140ef069f9f074c51ff4b11f0c677bcb01126f81de', deposit: '0x24ee705da17a6061091880f47335d92950c72398980e271cdb9c69e8502827f4', withdraw: '0x8df7e436048d687edfaf351e913783729eeaa9ece741391b2a8428d6b7762fe1', borrow: '0x564544c9aef01836615254504677b91a9ef96d5ae15eac50d98e08774ed1096c', },
where each hash is a txHash of an example of that function being called. There should be one for each function you've interpreted.
If you implemented a method that has different interpretations depending on the perspective of the user, such as an NFT sale, add an entry to the
multiSidedTxMap
object. The key should be an example multi-sided transaction and the value should be an array of the different addresses that should be tested. -
Testing
yarn test
This should generate new snapshots! Check them out and make sure they're what you think they're supposed to be!
-
Add, commit, and push your changes to your branch. See this tutorial if you don't know how.
-
Open an pull request. See this tutorial if you don't know how.
Please spend time detailing your proposed interpretation using the title and description of the PR. The only files that you should have edited are your own JSON file,
contractInterpreters/index.ts
,__tests__/testTxHashes.ts
, and maybeutils/constants.ts
if you have a multicall method. We will not accept proposals that edit aditional files -
Check tests in GitHub actions
Right after you opened the PR, a GitHub action started up to make sure everything is kosher with your interpretation. You can see the actions here. If your action succeeds, you're all good! We will review the PR and merge it if meets our standards. Thank you for your contribution!
If your action fails, examine the error by clicking into it and then fix it. The test will automatically rerun when you push to a branch with an open PR. If you believe the failure isn't your fault, please open an issue on GitHub.
You made it to the fun part, congrats. Here, I'll walk through an example interpretation of a the Uniswap V2 contract to explain the process.
-
Create a new branch
Please title the branch after the contract you plan to interpret. If you plan on interpreting multiple contracts, please make different branches for each contract. You can create a new branch by typing
git checkout -b {your-name/contract-name}
within the evm-translator folder in your terminal. -
Grab a contract interpretation template.
Go to https://evm-translator-demo.themetagame.xyz/templateGenerator and input your contract address into the text field. Select "Get Template". Once the JSON loads and appears on the screen, select "Download JSON file". Then, move this file within the
evm-translator/src/core/contractInterpreters
.Note that sometimes, your contract can have a single interpretation but be deployed in multiple places (see an example of this). In this case, you can rename the JSON file to remove the underscore and everything after it. We will cover how to handle this case in a minute.
-
Customize your JSON file.
First, let's walk through the structure of this JSON file, as it will be the basis for your contract interpretation. See the complete UniswapV2Router02_0x7a25.json file as reference.
{ "contractAddress": "0x7a250d5630b4cf539739df2c5dacb4c659f2488d", "contractOfficialName": "UniswapV2Router02", "contractName": "Uniswap V2", "entityName": "Uniswap", "writeFunctions": { "addedLiquidity": { "action": "added liquidity", ... } } }
-
contractAddress: the address of the contract you're interpreting. If your interpretation applies to multiple contract addresses, feel free to leave off this field.
-
contractOfficialName: A specific but still readable name for the contract(s) you're interpreting.
-
contractName: A highly readable name for the contract(s) you're interpreting. This name can appear in the
exampleDescription
field of the interpretation so choose it wisely. -
entityName: The name of the entity associated with the contract
-
writeFunctions: this is the meat of your interpretation. Each entry has a key which is the method name and a value which is an inner object with the following fields...
-
(required!) action: A very short (1-2 words ideally) description of what the user did in the transaction. This should include a past tense verb. See the
Action
enum from this file for a bunch of action names. If at all possible, pick an action from this enum.Some actions will depend on the perspective of the user, like an NFT sale. One user "bought" and the other "sold". If your action is an NFT sale, use the special
"__NFTSALE__"
action that will resolve to either "bought" or "sold" depending on the user's address.If your contract's task is labeled as multisided on Wonder, and it is not a basic NFT Sale, see the section on multisided transactions below. This will require writing code.
-
There are a few other optional fields that will be detailed in step 4.
-
Gathering your contract's methods
We recommend using Bloxy.info to identify your contract's methods. Enter the contract address in the search box, press "enter", and then scroll down to the "Smart Contract Function Calls" section. Here, you can see all of the contract's methods in order of popularity––a feature Etherscan.io does not have.
Note that some methods are only called internally and do not need to be included in the
writeFunctions
object. Cross reference this list with your contract's documentation or source code to get a full understanding of which methods users interact with directly.You can also select the number under "Calls Count" to get examples of transactions with this method. At this point, we encourage you to copy the transaction hash into Etherscan.io to get a better understanding of what is happening in a given transaction.
Dealing with multisided transactions
Multisided transactions are transactions that have multiple parties involved, each with a different perspective on what happened. For example, in an NFT sale transaction, one address "bought" and the other "sold. Interpreting contracts with multisided transactions earns higher compensation because it is more difficult. Let's look at how this is done.
- Decide what your special action keyword will be. It must start and end with a double underscore like
"__NFTSALE__"
. Then, add that keyword to theAction
enum insrc/core/interfaces/interpreted.ts
. - Go to
/src/core/MultiSidedActionInterpreters.ts
and add a new key-value pair for your multisided action. There's a chance that an existing key-value fits your needs but this is unlikely. The key should be your new action wrapped in brackets like[Action.__NFTSALE__]
. The function will take in an interpretation and spit out a basic action. - Write the function body. This is the tricky part. You need to take the interpretation, understanding who is sending and receiving what, and return the appropriate action based on the perspective of the address. The
assetsSent
andassetsReceived
fields already take the address into account so these will likely be your best tools here. - Once you're all done with that, you can use your keyword in your JSON contract interpretation file. It would go in the
action
field within the value of a key-value pair inwriteFunctions
.
Good luck!
Dealing with "multicall" methodsSome contracts use methods like "multicall", which bundle multiple method calls into one. If your contract does this, have no fear! Simply use
"executed multiple actions"
for youraction
field. Here is an example fromUniswapV3NonFungiblePositionManager.json
..."multicall": { "action": "executed multiple actions", "exampleDescriptionTemplate": "{userAddress} {action} on {contractName}", "exampleDescription": "leotheprocess.eth executed multiple actions on Uniswap V3", "keywords": {} }
Make sure you implement all the important methods within
writeFunctions
that the multicall method could call. You can analyze the trace method calls of a multicall transaction on Bloxy.info in the "Execution trace" section. Only concern yourself with the first level of idented method calls.Lastly, navigate to
evm-translator/src/utils/constants.ts
. Add the name of your multicall-style method tomulticallFunctionNames
if it does not already exist in that array. Add your contract address tomulticallContractAddresses
.When you test it, the final interpretation should have an
actions
field with an array of all the "sub-actions" that the multicall method initiates. -
-
Filtering for keywords
If you'd prefer to skip this step, add the following blank fields to each of the
writeFunctions.[methodName]
objects..."exampleDescriptionTemplate": "", "exampleDescription": "", "keywords": {}
This step involves generating the
"exampleDescription"
field for the interpretation, which is a one-sentence, human-readable description of the transaction. Although this is cool, it is not required to complete the contract interpretation.If you're game to tackle this challenge then let's get into it...
First, let's examine how this would be done for Uniswap V2's
swapExactTokensForTokens
method. This method is called when a Uniswap user wants to swap one non-ETH token for another non-ETH token."exampleDescriptionTemplate": "{userName} {action} {tokenAmount0} {tokenName0} for {tokenAmount1} {tokenName1} on {contractName}", "exampleDescription": "leotheprocess.eth swapped 246 DAI for 247 USDC on Uniswap V2", "keywords": { "tokenName0": { "key": "contractSymbol", "filters": { "eventName": "Transfer", "from": "{userAddress}" }, "defaultValue": "an unknown token" }, "tokenAmount0": { "key": "value", "filters": { "eventName": "Transfer", "from": "{userAddress}" }, "defaultValue": "an unknown amount" }, "tokenName1": { "key": "contractSymbol", "filters": { "eventName": "Transfer", "to": "{userAddress}" }, "defaultValue": "an unknown token" }, "tokenAmount1": { "key": "value", "filters": { "eventName": "Transfer", "to": "{userAddress}" }, "defaultValue": "an unknown amount" } }
Let's walk through these fields and what they mean.
-
exampleDescriptionTemplate: Creates a human readable one-sentence description of the transaction. You can use variables in this description by wrapping their names in curly braces. I will cover how to create your variables in the "keywords" section, but here are the ones that you can use with no extra setup...
- {userName}: The ENS (or address if no ENS is present) of the user interacting with the contract
- {contractName}: The value of the
contractName
field from above - {action}: The value of the
action
field from above - {userAddress} (userName is recommended): The address of the user interacting with the contract
- {__NATIVEVALUETRANSFERRED__}: The amount of ETH transferred in a NFT sale.
-
exampleDescription: Just like
exampleDescriptionTemplate
but with example values plugged in for the variables. These values can be whatever you like and are just meant to show what theexampleDescriptionTemplate
field could look like when it gets filled in. -
keywords: An object with each key that matches a variable wrapped in curly braces from
exampleDescriptionTemplate
and each value being an object with "instructions" on how to isolate the variable's value from the transaction's event logs. This inner object has the following keys...- key: The name of the field from the event. For example, if you want the value of a Transfer, this would likely be "value" or "amount". If you want an ERC20 token's symbol, use your filters to isolate an event emitted from the address of the token's contract, and use
"key": "contractSymbol"
. See the Uniswap interpretation for an example of this. - filters: An object with each key/value pair being a criteria that the event must meet to be used to populate the keyword's value. For example, if you want to get the value of a Transfer from the user's address to someone else, you could use something like
"eventName": "Transfer"
,"from": "{userAddress}"
for filters. - index: Sometimes you must manually specify the event's position you want to isolate when you cannot isolate it with your filters. Defaults to 0. See the
removeLiquidityWithPermit
method within the Uniswap interpretation for an example of how this field is used (example tx: 0xe1147990da44812918868c75c795ae91592a59277692e9050f7e5ab89dc143da).
- key: The name of the field from the event. For example, if you want the value of a Transfer, this would likely be "value" or "amount". If you want an ERC20 token's symbol, use your filters to isolate an event emitted from the address of the token's contract, and use
How to parse event logs
If you skipped "Filtering for keywords", skip this step as well.
First, find an example transaction calling the specific method you're working on. You can do this by browsing your contract's transactions on Etherscan.io (make sure to avoid pending transactions) or by using Bloxy.info (see above).
Then, enter the transaction hash into Etherscan.io and navigate into the "Logs" section. Here, you can see all the pieces of data you can include as keywords. Let's look at an example of a swapExactTokensForTokens transaction. In this transaction, user (0x4090) is swapping 900 USDC for 202,899 KINGDOM. The four pieces of information we need are...
- Amount of tokens sent
- Symbol of tokens sent
- Amount of tokens received
- Symbol of tokens sent
It may not look like it at first, but these two event logs have everything we need. The
value
field from the first event log has the amount of tokens sent. (Don't worry about the extra zeros. We handle those.) The field at index 1 ("from") is the user. Therefore, the keyword object will look like this..."tokenAmount0": { "key": "value", "filters": { "eventName": "Transfer", "from": "{userAddress}" }, "defaultValue": "an unknown amount" },
And we can use
"{tokenAmount0}"
withinexampleDescriptionTemplate
The
Address
field from the first event log has the contract address of the ERC20 token being sent (USDC). The field at index 2 ("from") is the user. Therefore, the keyword object will look like this..."tokenName0": { "key": "contractSymbol", "filters": { "eventName": "Transfer", "to": "{userAddress}" }, "defaultValue": "an unknown amount" },
The
"contractSymbol"
key takes the contract address that emits the event and gets the symbol of the ERC20 token it represents. We can use"{tokenName0}"
withinexampleDescriptionTemplate
.We can repeat these steps for the tokens being received, except we use
"from"
instead of"to"
. -
-
Enter the interpretation into the main directory.
Go to
evm-translator/src/core/contractInterpreters/index.ts
. Add a new entry in thecontractInterpreters
object with the key being the contract's address and the value beingrequire('./{name of your JSON file})
. If you are using the same JSON interpreation file for multiple contract addresses, you can add multiple entries all with the same value.
My contract interpretation isn't being acknowledged by the interpreter!
- Make sure that you have followed part 3 step 5 and added the interpretation to the
contractInterpreters/index.ts
file. - Make sure that the transaction you inputting into your local testing console is a transaction from the contract interpreted.