Async function to load contracts holding LRTs on behalf of users.
Copy getContracts: (params: GetContractsParams) => Promise<DeFiContract[]> | DeFiContract[];
Async function to load user shares between 2 blocks (inclusive).
Copy getUserShares: (params: GetUserSharesParams) => Promise<IDeFiUserShare[]> | IDeFiUserShare[];
A user share represents the amount of LRT owned by an address pro-rata to others.
User shares are sorted by block and a new entry must be added each time LRT ratios change.
Get user balances (optional)
Optional async function to load user balances (if they're not 1:1 to user shares).
Copy getBalances: (params: GetBalancesParams) => Promise<IDeFiUserBalance[]> | IDeFiUserBalance[];
Copy import { GetContractsParams, GetDeFiPoolsParams, GetUserSharesParams, IDeFiUserShare } from "../types";
export class ZircuitDeFiCollector implements IDeFiCollector {
public getContracts({ chainId }: GetContractsParams) {
if (chainId === 1) {
return [
{
chainId,
vault: "0x7a4EffD87C2f3C55CA251080b1343b605f327E3a",
address: "0xF047ab4c75cebf0eB9ed34Ae2c186f3611aEAfa6"
},
// ...
];
}
return [];
}
public getDeFiPools({ chainId }: GetDeFiPoolsParams) {
if (chainId === 1) {
return [
{
id: "pool-zircuit-rstETH",
chainId: 1,
name: "rstETH",
protocol: "Zircuit",
vault: "0x7a4EffD87C2f3C55CA251080b1343b605f327E3a",
protocol_address: "0xF047ab4c75cebf0eB9ed34Ae2c186f3611aEAfa6",
url: "https://stake.zircuit.com",
boost: 2,
}
// ...
];
}
return [];
}
public async getUserShares({
chainId,
contract, // ZtakingPool
fromBlock,
toBlock,
}: GetUserSharesParams) {
if (chainId !== 1) {
return [];
}
const userShares: IDeFiUserShare[] = [];
const vaults = ["0x7a4EffD87C2f3C55CA251080b1343b605f327E3a"];
const depositEvents = await getDepositEvents(contract);
const withdrawalEvents = await getWithdrawalEvents(contract);
// balance changes events
const events = [...depositEvents, ...withdrawalEvents].sort(
(a, b) => a.block - b.block
);
for (const event of events) {
// get user vaults balances at this block
const balances = await getMulticallBalance(
event.block,
event.user,
vaults
);
for (const balance of balances) {
userShares.push({
block: event.block,
user: event.user,
vault: balance.vault,
protocol_address: contract,
});
}
}
return userShares;
}
}
Copy export type ChainId = 1 | 1923 | 9889 | 17000 | 48900;
export interface IDeFiUserShare {
chainId: ChainId;
block: number;
/**
* UNIX timestamp
*/
timestamp: number;
/**
* User address (checksummed)
*/
user: string;
/**
* Share amount
*/
amount: bigint;
/**
* Vault address (checksummed)
*/
vault: string;
/**
* Protocol address (checksummed) e.g. LP, SY, Credit Account
*/
protocol_address: string;
}
export interface IDeFiUserBalance {
chainId: ChainId;
block: number;
timestamp: number;
user: string;
amount: bigint;
vault: string;
protocol_address: string;
}
/**
* Pool metadata
*/
export interface IDefiPool {
/**
* Unique pool identifier
*/
id: string;
/**
* Pool display name
*/
name: string;
/**
* Protocol display name
*/
protocol: string;
/**
* Vault address (checksummed)
*/
vault: string;
/**
* Protocol address (checksummed) e.g. LP, SY, Credit Account
*/
protocol_address: string;
/**
* Link to protocol pool page
*/
url: string;
/**
* Mellow points multiplier e.g. 2, 3 for 2x or 3x boost
*/
boost: number;
}
export interface IPointsFee {
chainId: ChainId;
contract: string;
vault: string;
/**
* Recipient address
*/
recipient: string;
/**
* Ex: 3n for 3% fee
*/
amount: bigint;
}
export type GetContractsParams = {
chainId: ChainId;
toBlock: number;
toTimestamp: number;
};
export type GetDeFiPoolsParams = GetContractsParams;
export type GetUserSharesParams = {
chainId: ChainId;
contract: string;
fromBlock: number;
toBlock: number;
fromTimestamp: number;
toTimestamp: number;
/**
* Currently loaded user shares
*/
userShares: IDeFiUserShare[];
};
export type GetBalancesParams = {
chainId: ChainId;
contract: string;
fromBlock: number;
toBlock: number;
fromTimestamp: number;
toTimestamp: number;
/**
* All user shares (includes shares just loaded)
*/
allUserShares: IDeFiUserShare[];
/**
* Currently loaded user balances
*/
userBalances: IDeFiUserBalance[];
};
export type GetCurrentBalancesParams = {
chainId: ChainId;
contract: string;
userBalances: IDeFiUserBalance[];
};
export type ProcessUserSharesParams = {
chainId: ChainId;
contract: string;
userShares: Map<string, IDeFiUserShare>;
block: number;
timestamp: number;
};
export type DeFiContract = {
vault: string;
chainId: ChainId;
address: `0x${string}`;
/**
* To fallback points if the contract owns LRTs but there's no shareholder
* ex: Pendle leftover points for expired pools
* ex: LRTs deposited in SY but SY not used in YT / LP
*/
fallbackAddress?: `0x${string}`;
startBlock?: number;
startTimestamp?: number;
};
export interface IDeFiCollector {
/**
* Get LRT holders e.g. UniswapV3 Pool, Pendle SY
*/
getContracts: (
params: GetContractsParams
) => Promise<DeFiContract[]> | DeFiContract[];
/**
* Get DeFi pools metadata
*/
getDeFiPools: (
params: GetDeFiPoolsParams
) => Promise<IDefiPool[]> | IDefiPool[];
/**
* Get historical user shares
*/
getUserShares: (
params: GetUserSharesParams
) => Promise<IDeFiUserShare[]> | IDeFiUserShare[];
/**
* (Optional) Get historical user balances
*/
getBalances?: (
params: GetBalancesParams
) => Promise<IDeFiUserBalance[]> | IDeFiUserBalance[];
/**
* (Optional) Get protocol fees
*/
getPointsFees?: () => IPointsFee[];
/**
* (Optional) Hook to post process current user shares
*/
processUserShares?: (params: ProcessUserSharesParams) => void;
}