Uniswap V2
Source: examples/uniswap-v2/
The definitive factory + template example. Demonstrates dynamic data sources, counter updates, and the data_source::address_current() API.
What It Does
- A
Factorycontract emitsPairCreatedwhenever a new liquidity pool is deployed. - The factory handler creates a
Poolentity and callsdata_source::create_contract("Pair", pair_address)to start indexing the new pair. - Each pair emits
Swapevents. ThePairtemplate handler records each swap and incrementsPool.swapCount.
Schema
type Pool @entity {
id: ID! # pair address (0x-prefixed hex)
token0: Bytes!
token1: Bytes!
swapCount: BigInt!
}
type Swap @entity {
id: ID!
pool: Pool!
amount0In: BigInt!
amount1In: BigInt!
amount0Out: BigInt!
amount1Out: BigInt!
blockNumber: BigInt!
timestamp: BigInt!
}
Factory Handler
#![allow(unused)] fn main() { #[handler] pub fn handle_pair_created(event: &FactoryPairCreatedEvent, _ctx: &graphite::EventContext) { let pool_id = addr_hex(&event.pair); Pool::new(&pool_id) .set_token0(event.token0.to_vec()) .set_token1(event.token1.to_vec()) .save(); data_source::create_contract("Pair", event.pair); } }
Template Handler
#![allow(unused)] fn main() { #[handler] pub fn handle_swap(event: &PairSwapEvent, ctx: &graphite::EventContext) { let pair_addr = data_source::address_current(); let pool_id = addr_hex(&pair_addr); let swap_id = format!("{}-{}", hex_bytes(&event.tx_hash), hex_bytes(&event.log_index)); Swap::new(&swap_id) .set_pool(pool_id.clone()) .set_amount0_in(event.amount0_in.clone()) .set_amount1_in(event.amount1_in.clone()) .set_amount0_out(event.amount0_out.clone()) .set_amount1_out(event.amount1_out.clone()) .set_block_number(ctx.block_number.clone()) .set_timestamp(ctx.block_timestamp.clone()) .save(); // Increment pool.swapCount if let Some(pool) = Pool::load(&pool_id) { let new_count = le_add_one(pool.swap_count()); pool.set_swap_count(new_count).save(); } } }
Counter Arithmetic
BigInt values are stored as little-endian byte vectors. The le_add_one helper increments them:
#![allow(unused)] fn main() { fn le_add_one(bytes: &[u8]) -> Vec<u8> { let mut result = bytes.to_vec(); let mut carry = 1u16; for byte in result.iter_mut() { let sum = *byte as u16 + carry; *byte = sum as u8; carry = sum >> 8; } if carry > 0 { result.push(carry as u8); } result } }
Tests
The example has four tests:
pair_created_makes_pool_and_data_source— factory handler createsPooland triggers data source creation.swap_creates_entity_and_increments_pool_count— swap handler createsSwapand updatesPool.swapCount.swap_count_increments_per_swap— two swaps produceswapCount = [2].multiple_pairs_independent— two factory events produce two independent pools.
cd examples/uniswap-v2
cargo test
Key Points
data_source::address_current()returns the address of the contract instance that emitted the event — this is how the template handler knows which pool it belongs to.mock::set_current_address(addr)sets this value in tests.mock::assert_contract_data_source_created("Pair", addr)verifies the factory calledcreate_contract.