Events
ClipFi emits minimal, indexer-friendly events for building feeds, analytics, and bots.
All events are emitted from individual ClipFiToken contracts (proxies).
Trade Events
Buy
Emitted when a user buys tokens on the bonding curve.
event Buy(
address indexed user,
bytes32 indexed clipperId,
bytes32 indexed contentId,
uint256 ethIn,
uint256 netEth,
uint256 tokensOut
);
| Field |
Type |
Description |
user |
address |
Buyer's wallet address |
clipperId |
bytes32 |
Keccak256 hash of clipper's username |
contentId |
bytes32 |
Keccak256 hash of normalized clip URL |
ethIn |
uint256 |
Total ETH sent by user (in wei) |
netEth |
uint256 |
ETH after fees (in wei) |
tokensOut |
uint256 |
Tokens received by user |
Fee calculation: ethIn - netEth = total fees paid
Sell
Emitted when a user sells tokens back to the bonding curve.
event Sell(
address indexed user,
bytes32 indexed clipperId,
bytes32 indexed contentId,
uint256 tokensIn,
uint256 ethOutGross,
uint256 netEth
);
| Field |
Type |
Description |
user |
address |
Seller's wallet address |
clipperId |
bytes32 |
Keccak256 hash of clipper's username |
contentId |
bytes32 |
Keccak256 hash of normalized clip URL |
tokensIn |
uint256 |
Tokens sold by user |
ethOutGross |
uint256 |
ETH value before fees (in wei) |
netEth |
uint256 |
ETH received by user after fees (in wei) |
Fee calculation: ethOutGross - netEth = total fees paid
IntegratorFeePaid
Emitted when an integrator earns fees from a trade.
event IntegratorFeePaid(
address indexed integrator,
uint256 amount
);
| Field |
Type |
Description |
integrator |
address |
Integrator's fee recipient address |
amount |
uint256 |
Fee amount paid to integrator (in wei) |
Graduation Events
ReadyToGraduate
Emitted when a token reaches its ETH graduation threshold.
event ReadyToGraduate(
bytes32 indexed contentId,
address indexed token,
uint256 ethReserves,
uint256 ethTarget
);
| Field |
Type |
Description |
contentId |
bytes32 |
Keccak256 hash of normalized clip URL |
token |
address |
Token contract address |
ethReserves |
uint256 |
Current ETH in bonding curve (in wei) |
ethTarget |
uint256 |
Graduation threshold (in wei) |
This event signals the token is ready for graduation. The ClipFi backend automatically calls graduate() when detected.
Graduated
Emitted when migration to Uniswap V2 is complete.
event Graduated(
bytes32 indexed contentId,
address indexed token,
address indexed dexPair
);
| Field |
Type |
Description |
contentId |
bytes32 |
Keccak256 hash of normalized clip URL |
token |
address |
Token contract address |
dexPair |
address |
Uniswap V2 pair address |
After graduation, trading continues on Uniswap V2. The 2% fee still applies to swaps involving the main pair.
Example: Filtering Events
import { ethers } from 'ethers';
const provider = new ethers.JsonRpcProvider('https://mainnet.base.org');
// ClipFi token ABI (events only)
const ABI = [
'event Buy(address indexed user, bytes32 indexed clipperId, bytes32 indexed contentId, uint256 ethIn, uint256 netEth, uint256 tokensOut)',
'event Sell(address indexed user, bytes32 indexed clipperId, bytes32 indexed contentId, uint256 tokensIn, uint256 ethOutGross, uint256 netEth)',
];
const token = new ethers.Contract(TOKEN_ADDRESS, ABI, provider);
// --- Filter by user wallet ---
const userFilter = token.filters.Buy(userAddress);
const userBuys = await token.queryFilter(userFilter, fromBlock, toBlock);
// --- Filter by clipper username ---
// Hash the username to get clipperId
const clipperId = ethers.keccak256(ethers.toUtf8Bytes("username"));
const clipperFilter = token.filters.Buy(null, clipperId);
const clipperTrades = await token.queryFilter(clipperFilter, fromBlock, toBlock);
// --- Filter by clip URL ---
// Normalize URL: lowercase, no protocol, no query params
const normalizedUrl = "tiktok.com/@user/video/123456789";
const contentId = ethers.keccak256(ethers.toUtf8Bytes(normalizedUrl));
const clipFilter = token.filters.Buy(null, null, contentId);
const clipTrades = await token.queryFilter(clipFilter, fromBlock, toBlock);
// --- Calculate price from event ---
userBuys.forEach(event => {
const ethIn = event.args.ethIn;
const tokensOut = event.args.tokensOut;
// Price = ETH per token
const price = Number(ethIn) / Number(tokensOut);
console.log(`Bought at ${price} ETH per token`);
});
from web3 import Web3
w3 = Web3(Web3.HTTPProvider('https://mainnet.base.org'))
# ClipFi token ABI (events only)
ABI = [
{"anonymous": False, "name": "Buy", "type": "event",
"inputs": [{"indexed": True, "name": "user", "type": "address"},
{"indexed": True, "name": "clipperId", "type": "bytes32"},
{"indexed": True, "name": "contentId", "type": "bytes32"},
{"indexed": False, "name": "ethIn", "type": "uint256"},
{"indexed": False, "name": "netEth", "type": "uint256"},
{"indexed": False, "name": "tokensOut", "type": "uint256"}]},
]
token = w3.eth.contract(address=TOKEN_ADDRESS, abi=ABI)
# --- Filter by user wallet ---
user_filter = token.events.Buy.create_filter(
fromBlock=from_block,
argument_filters={'user': user_address}
)
user_buys = user_filter.get_all_entries()
# --- Filter by clipper username ---
# Hash the username to get clipperId
clipper_id = w3.keccak(text="username")
clipper_filter = token.events.Buy.create_filter(
fromBlock=from_block,
argument_filters={'clipperId': clipper_id}
)
clipper_trades = clipper_filter.get_all_entries()
# --- Filter by clip URL ---
# Normalize URL: lowercase, no protocol, no query params
normalized_url = "tiktok.com/@user/video/123456789"
content_id = w3.keccak(text=normalized_url)
clip_filter = token.events.Buy.create_filter(
fromBlock=from_block,
argument_filters={'contentId': content_id}
)
clip_trades = clip_filter.get_all_entries()
# --- Calculate price from event ---
for event in user_buys:
eth_in = event['args']['ethIn']
tokens_out = event['args']['tokensOut']
# Price = ETH per token
price = eth_in / tokens_out
print(f"Bought at {price} ETH per token")
package main
import (
"context"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
)
func queryEvents() {
client, _ := ethclient.Dial("https://mainnet.base.org")
// Buy event signature
buyEventSig := crypto.Keccak256Hash([]byte(
"Buy(address,bytes32,bytes32,uint256,uint256,uint256)",
))
// --- Filter by clipper username ---
// Hash the username to get clipperId
clipperId := crypto.Keccak256Hash([]byte("username"))
query := ethereum.FilterQuery{
Addresses: []common.Address{tokenAddress},
Topics: [][]common.Hash{
{buyEventSig}, // Event signature
nil, // user (any)
{clipperId}, // clipperId filter
},
FromBlock: big.NewInt(fromBlock),
ToBlock: big.NewInt(toBlock),
}
logs, _ := client.FilterLogs(context.Background(), query)
// --- Calculate price from event ---
for _, log := range logs {
ethIn := new(big.Int).SetBytes(log.Data[:32])
tokensOut := new(big.Int).SetBytes(log.Data[64:96])
fmt.Printf("ETH in: %s, Tokens out: %s\n", ethIn, tokensOut)
}
}
use ethers::{
prelude::*,
providers::{Http, Provider},
};
async fn query_events() -> Result<(), Box<dyn std::error::Error>> {
let provider = Provider::<Http>::try_from("https://mainnet.base.org")?;
// Buy event signature
let buy_event_sig = H256::from(keccak256(
"Buy(address,bytes32,bytes32,uint256,uint256,uint256)"
));
// --- Filter by clipper username ---
// Hash the username to get clipperId
let clipper_id = H256::from(keccak256("username".as_bytes()));
let filter = Filter::new()
.address(token_address)
.topic0(buy_event_sig) // Event signature
.topic2(clipper_id) // clipperId filter (topic index 2)
.from_block(from_block)
.to_block(to_block);
let logs = provider.get_logs(&filter).await?;
// --- Calculate price from event ---
for log in logs {
let eth_in = U256::from_big_endian(&log.data[0..32]);
let tokens_out = U256::from_big_endian(&log.data[64..96]);
println!("ETH in: {}, Tokens out: {}", eth_in, tokens_out);
}
Ok(())
}
# Query Buy events filtered by clipper username
# First, compute the keccak256 hash of the username
CLIPPER_ID=$(cast keccak "username")
# Query events with topic filter
cast logs \
--from-block 1000000 \
--to-block latest \
--address TOKEN_ADDRESS \
"Buy(address indexed user, bytes32 indexed clipperId, bytes32 indexed contentId, uint256 ethIn, uint256 netEth, uint256 tokensOut)" \
--topic2 $CLIPPER_ID \
--rpc-url https://mainnet.base.org
Indexing Notes
- All
indexed fields are filterable via standard RPC methods
contentId = keccak256(normalizedClipUrl) — URL is lowercase, no protocol, no query params
clipperId = keccak256(username) — Username as UTF-8 bytes
- Events are emitted from individual token proxies, not the factory
- For a global feed, index all tokens from
ClipRegistry
Use Cases
| Use Case |
Events to Index |
| Activity feed |
Buy, Sell |
| Price charts |
Buy, Sell (calculate price from amounts) |
| Volume tracking |
Buy, Sell |
| Graduation alerts |
ReadyToGraduate, Graduated |
| Integrator dashboard |
IntegratorFeePaid |
| Clipper earnings |
Buy, Sell (filter by clipperId) |