How to Send a Discord Message About a New Uniswap Pool
This tutorial shows you how to use a Web3 Action to send a message to a Discord channel when a new pool is created on Uniswap V3. You can use this Web3 Action to notify your community when something happens on the chain.
The entire code is available in this Github repo. Note that it’s in a separate branch at the moment.
Project Overview
The goal of this project is to teach you how to use Web3 Actions to send a Discord message whenever a Uniswap Pool is made. We’ll show you two approaches - the more convenient one is to use PoolCreated
event as a trigger for our Web3 Action (see the Uniswap docs).
The Plan
- Add contracts to your Tenderly project - UniswapV3ContractFactory. It’s already verified and present in Tenderly, so you just need to add them to your project.
- Store the Discord Webhook URL in your Web3 Action Secrets.
- Create a Web3 Action that will respond to the
[PoolCreated](<https://docs.uniswap.org/protocol/reference/core/interfaces/IUniswapV3Factory#poolcreated>)
event.
1: Add Uniswap Contracts to Your Tenderly Project
Web3 Actions require that smart contracts used to specify triggers are verified in Tenderly.
Web3 Actions require that smart contracts used to specify triggers are verified in Tenderly. Since we’re using existing contracts, you can find them using the search bar by typing in the contract name or address.
Alternatively, go to UniswapV2ContractFactory and click “Add to Project”. Do the same for UniswapV2Pair.
2: Create a Web3 Action for the PoolCreated Event
Below you’ll find the code for our Web3 Action as well as an explanation of how it works:
import { ActionFn, Context, Event, TransactionEvent } from '@tenderly/actions';
import axios from 'axios';
import { ethers } from 'ethers';
import UniswapV3FactoryAbi from './UniswapV3FactoryAbi.json';
export const onPoolCreatedEventEmitted: ActionFn = async (context: Context, event: Event) => {
try {
const txEvent = event as TransactionEvent;
const eventLog = await getPoolCreatedEvent(txEvent);
const tokensData = await getTokensData(eventLog.token0, eventLog.token1);
console.log('Received Tokens Data: ', tokensData);
await notifyDiscord(`${tokensData.token0.name} ↔️ ${tokensData.token1.name}`, context);
} catch (error) {
console.error(error);
}
};
const getPoolCreatedEvent = async (txEvent: TransactionEvent) => {
const ifc = new ethers.utils.Interface(UniswapV3FactoryAbi);
const poolCreatedTopic = ifc.getEventTopic('PoolCreated');
const poolCreatedEventLog = txEvent.logs.find(log => {
return log.topics.find(topic => topic == poolCreatedTopic) !== undefined;
});
if (poolCreatedEventLog == undefined) {
throw Error('PoolCreatedEvent missing');
}
return ifc.decodeEventLog(
'PoolCreated',
poolCreatedEventLog.data,
poolCreatedEventLog.topics,
) as unknown as UniswapPool;
};
const getTokensData = async (token0: string, token1: string) => {
const tokenFields = `id, name, symbol, totalValueLocked, totalSupply, derivedETH`;
const theGraphQuery = `
{
token0: token(id:"${token0.toLowerCase()}"){${tokenFields}},
token1: token(id:"${token1.toLowerCase()}"){${tokenFields}}
}`;
const UNISWAP_THE_GRAPH = 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3';
const resp = await axios.post(UNISWAP_THE_GRAPH, {
query: theGraphQuery,
});
return resp.data.data as unknown as {
token0: UniwswapToken;
token1: UniwswapToken;
};
};
const notifyDiscord = async (text: string, context: Context) => {
console.log('Sending to Discord:', `🐥 ${text}`);
const webhookLink = await context.secrets.get('discord.uniswapChannelWebhook');
await axios.post(
webhookLink,
{
content: `🐥 ${text}`,
},
{
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
},
);
};
type UniswapPool = {
token0: string;
token1: string;
fee: number;
tickSpacing: number;
pool: number;
};
type UniwswapToken = {
id: string;
name: string;
symbol: string;
tokenValueLocked: number;
totalSupply: number;
derivedEth: number;
};
Obtain Tokens in the Newly Created Pool. To get information about two tokens in the new pool, we need to search through the transaction logs in the getEventLog
function. We’re using Ethers and the ABI of the contract: UniswapV3FactoryAbi
. Go to the UniswapV3Factory
contract in Tenderly, click “View ABI” and copy/paste it to a local file in the actions
directory.
Essentially, we’re looking for the log entry in the topic PoolCreate
. First, we’re taking a reference of that topic (poolCreatedTopic = ifc.getEventTopic("PoolCreated")
) and then we’re filtering the actual logs (tx.logs
) until we find the entry referencing that topic. Lastly, we’re decoding the log we found (poolCreatedEventLog
) using Ethers. This is the event we’re looking for, and it contains the addresses of the tokens: token0
and token1
.
Obtain Token Data Using Uniswap’s Graph API on TheGraph. Uniswap exposes access to its data through TheGraph. We’re using the getTokensData
function to query TheGraph and get several fields for the token0
and token1
IDs – the ones present in the PoolCreated
event from the first step.
Notify the Discord Community. Having gathered all the relevant information, we are ready to send a message to our Discord channel. With the notifyDiscord
function, we need to get the Webhook URL from the secrets
. We’re using Axios to make an HTTP post request to run the Webhook and pass the content.
3: Configure Your Web3 Action
In the tenderly.yaml
file, we’re need to specify that we want to have uniswapActions:onPoolCreatedEventEmitted
ran when an event is fired from the UniswapV3Factory
contract (on address 0xd849b2af570ffa3033973ea11be6e01b7ba661d9
) within a transaction that is mined on the Mainnet (network 1).
account_id: ''
actions:
YOUR_USERNAME/YOUR_PROJECT_SLUG:
runtime: v1
sources: actions
specs:
uniswapNewPool:
description: Runs when a new swap is made on Uniswap
function: uniswapActions:onPoolCreatedEventEmitted
trigger:
type: transaction
transaction:
status:
- mined
filters:
- network: 1
eventEmitted:
contract:
address: 0xd849b2af570ffa3033973ea11be6e01b7ba661d9
name: PoolCreated
project_slug: ''
4: Store the Discord Webhook URL in Secrets
Before running our Web3 Action in Tenderly, we have to set the value for the Discord Webhook.
In the Tenderly Dashboard, go to the Actions page of your project and click Secrets. Add a new secret named discord.uniswapChannelWebhook
and paste your Discord channel’s Webhook.
5: Deploy and Try Out Your Web3 Action
Deploy your Web3 Action by running this command:
tenderly actions deploy
To try out if your Web3 Action is working, go to the Actions page in your Tenderly Dashboard. Select your Action and click Manual Trigger.
That’s all! Hopefully, you’ve received a message on your Discord channel 🎉