DeFi points integration instructions
This page is dedicated for DeFi protocols utilizing Mellow LRTs and helps to setup points distribution for legitimate users of DeFi protocol.
DeFi Integrations
Get contracts
Async function to load contracts holding LRTs on behalf of users.
getContracts: (params: GetContractsParams) => Promise<DeFiContract[]> | DeFiContract[];
Get user shares
Async function to load user shares between 2 blocks (inclusive).
This function is called for all contracts returned by getContracts
.
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.
Example:
1
0x01
100
0x0
0x0
1
0x02
200
0x0
0x0
1
0x03
50
0x0
0x0
3
0x03
20
0x0
0x0
4
0x01
150
0x0
0x0
5
0x02
0
0x0
0x0
At block 1,
0x01
owns ~28% of the user shares (100 / (100 + 200 + 50)),0x02
owns ~57% and0x03
owns ~14%.At block 3,
0x03
underlying LRT balance changed, he now owns ~6% of the user shares (20 / (100 + 200 + 20)), while0x01
owns ~31% and0x02
owns ~62%.At block 5,
0x02
withdrew all his funds from the protocol, he now owns 0% of the user shares, while0x01
owns ~88.2% (150 / (150 + 20)) and0x03
owns ~11.8% (20 / (150 + 20)).
Get user balances (optional)
Optional async function to load user balances (if they're not 1:1 to user shares).
This function is called for all contracts returned by getContracts
.
getBalances: (params: GetBalancesParams) => Promise<IDeFiUserBalance[]> | IDeFiUserBalance[];
Example
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;
}
}
Interface
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;
}
Last updated