Appearance
Discovering Pools
POE supports an unbounded number of pools. Integrators do not have to hard-code each market: the Factory keeps a registry, so you can enumerate pools or look them up by pair on-chain.
The Factory address is the same on every supported chain (see POE Contracts):
Factory: 0x78120F2C0EBF0cc8B7E7749e62D36e6523dD711DFactory Lookup API
solidity
interface IFactory {
// Total number of pools ever created.
function getPoolsLength() external view returns (uint256);
// Index-based access. `poolId` is in [0, getPoolsLength()).
function getPoolAt(uint256 poolId) external view returns (address);
// Look up a pool by token pair. Returns address(0) if no pool exists.
function getPool(address tokenX, address tokenY) external view returns (address);
event PoolCreated(
uint256 indexed poolId,
address indexed pool,
address tokenX,
address tokenY,
bytes data
);
}getPool sorts the two token addresses internally. getPool(tokenX, tokenY) and getPool(tokenY, tokenX) return the same pool.
WARNING
Do not use getPool lookup order to infer token roles. The pool's canonical ordering still comes from pool.getTokens(), and that ordering matters for oracle keys, swap direction, and amount interpretation.
Listing every pool
Enumerate [0, getPoolsLength()) and resolve each pool to its token pair:
solidity
IFactory factory = IFactory(0x78120F2C0EBF0cc8B7E7749e62D36e6523dD711D);
uint256 n = factory.getPoolsLength();
for (uint256 i = 0; i < n; ++i) {
address pool = factory.getPoolAt(i);
(address tokenX, address tokenY) = IBasePool(pool).getTokens();
// ...
}The pool exposes the pair via getTokens() (returns the same (tokenX, tokenY) tuple the pool was initialized with) and its current balances via getBalances(). See Tracking Liquidity for the full pool read API.
TypeScript
typescript
import { ethers } from 'ethers';
const FACTORY = '0x78120F2C0EBF0cc8B7E7749e62D36e6523dD711D';
const FACTORY_ABI = [
'function getPoolsLength() view returns (uint256)',
'function getPoolAt(uint256 poolId) view returns (address)',
'function getPool(address tokenX, address tokenY) view returns (address)',
];
const POOL_ABI = [
'function getTokens() view returns (address tokenX, address tokenY)',
'function getBalances() view returns (uint256 totalX, uint256 totalY)',
];
async function listPools(provider: ethers.Provider) {
const factory = new ethers.Contract(FACTORY, FACTORY_ABI, provider);
const n: bigint = await factory.getPoolsLength();
const pools = await Promise.all(
Array.from({ length: Number(n) }, async (_, i) => {
const address: string = await factory.getPoolAt(i);
const pool = new ethers.Contract(address, POOL_ABI, provider);
const { tokenX, totalX, tokenY, totalY } =
Object.assign({}, await pool.getTokens(), await pool.getBalances());
return { address, tokenX, tokenY, totalX, totalY };
})
);
return pools;
}Python
python
from web3 import Web3
FACTORY = '0x78120F2C0EBF0cc8B7E7749e62D36e6523dD711D'
factory_abi = [
{"name": "getPoolsLength", "type": "function", "inputs": [],
"outputs": [{"type": "uint256"}], "stateMutability": "view"},
{"name": "getPoolAt", "type": "function",
"inputs": [{"name": "poolId", "type": "uint256"}],
"outputs": [{"type": "address"}], "stateMutability": "view"},
{"name": "getPool", "type": "function",
"inputs": [{"name": "tokenX", "type": "address"},
{"name": "tokenY", "type": "address"}],
"outputs": [{"type": "address"}], "stateMutability": "view"},
]
pool_abi = [
{"name": "getTokens", "type": "function", "inputs": [],
"outputs": [{"name": "tokenX", "type": "address"},
{"name": "tokenY", "type": "address"}],
"stateMutability": "view"},
]
def list_pools(w3: Web3):
factory = w3.eth.contract(address=w3.to_checksum_address(FACTORY), abi=factory_abi)
n = factory.functions.getPoolsLength().call()
pools = []
for i in range(n):
address = factory.functions.getPoolAt(i).call()
pool = w3.eth.contract(address=address, abi=pool_abi)
token_x, token_y = pool.functions.getTokens().call()
pools.append({"address": address, "token_x": token_x, "token_y": token_y})
return poolsLooking up a specific pair
If you already know the pair, skip enumeration:
solidity
address pool = factory.getPool(tokenX, tokenY);
require(pool != address(0), "No pool for this pair");If getPool(a, b) returns address(0), getPool(b, a) will return the same result. A zero result means no pool exists for that token pair.
Reacting to new pools
Pool creation emits a PoolCreated event from the Factory. Indexers and integrators can subscribe to this to discover new markets without polling:
solidity
event PoolCreated(
uint256 indexed poolId,
address indexed pool,
address tokenX,
address tokenY,
bytes data
);poolId is the index used by getPoolAt; data is the per-pool initialization blob (currently maxValue (12 bytes) | oracle (20 bytes)).
