Links

Workers

Execute arbitrary script for all your entities on schedule

Use Cases

  • Regularly calculate health factor of all user positions, and notify if is in danger.
  • Calculate TVL across all your DeFi pools to keep track in real-time.

Example

manifest.yml
borrowing-limit/input.sql
borrowing-limit/handler.ts
You define a custom enricher script that is executed for every single row return by the "inputSql" query.
manifest: 1.0.0
# ... rest of your manifest.yml
enrichers:
- id: calculate-borrowing-limit
inputSql: ./borrowing-limit/input.sql
handler: ./borrowing-limit/handler.ts
workers:
# Every 30 minutes, run the SQL query in input.sql, and for every single row
# execute the script defined as "calculate-borrowing-limit" enricher.
- schedule: rate(30 minutes)
enricher: calculate-borrowing-limit
This input query means we want to run the enricher script for every single Position entity that exists in our protocol, this can return millions of rows, the scheduler will properly distribute and schedule the workload:
SELECT
*
FROM
entities
WHERE
namespace = 'fuji-finance'
AND entityType = 'Position'
This is a sample script built for a real-world DeFi protocol, that helps calculate current borrowing limit for each user, ready to be queried (from backend, other jobs, frontend dashboards etc.)
import { BigNumber } from 'ethers';
exports.handleInput = async function ({ data }) {
if (
!data ||
!data.entityId ||
!data.entityType ||
data.entityType !== 'Position'
) {
console.log(`Skipping enriching input data: ${JSOn.stringify({ data })}`);
return false;
}
if (!data.poolAddress || !data.userAddress) {
console.log(
`Skipping enriching position without poolAddress or userAddress: `,
data,
);
return false;
}
const poolContract = await blockchain.getContract(
data.chainId,
data.poolAddress,
[
'function oracle() view returns (address)',
'function getAssetsIn(address) view returns (address[])',
'function markets(address) view returns (bool,uint256,bool)',
],
);
const oracleAddress = await poolContract.oracle();
const oracleContract = await blockchain.getContract(
data.chainId,
oracleAddress,
['function getUnderlyingPrice(address) view returns (uint256)'],
);
const marketAddresses = await poolContract.getAssetsIn(data.userAddress);
let totalCollateralValue = BigNumber.from(0);
let totalBorrowValue = BigNumber.from(0);
for (const marketAddress of marketAddresses) {
const marketContract = await blockchain.getContract(
data.chainId,
marketAddress,
[
'function getAccountSnapshot(address) view returns (uint256,uint256,uint256,uint256)',
],
);
const underlyingPrice = await oracleContract.getUnderlyingPrice(
marketAddress,
);
const [, collateralFactor] = await poolContract.markets(marketAddress);
const [errNo, collateralBalance, borrowBalance, exchangeRate] =
await marketContract.getAccountSnapshot(data.userAddress);
const collateralValue =
collateralFactor && collateralFactor.gt(0)
? collateralBalance
.mul(exchangeRate)
.mul(underlyingPrice)
.div(collateralFactor)
: BigNumber.from(0);
totalCollateralValue = totalCollateralValue.add(collateralValue);
const borrowValue = borrowBalance.mul(exchangeRate).mul(underlyingPrice);
totalBorrowValue = totalBorrowValue.add(borrowValue);
}
const borrowingLimit = totalCollateralValue.gt(0)
? totalBorrowValue.div(totalCollateralValue)
: BigNumber.from(0);
const provider = await blockchain.getProvider(data.chainId);
await database.upsert({
entityType: 'Position',
entityId: data.entityId,
horizon: {
blockNumber: await provider.getBlockNumber(),
},
data: {
totalCollateralValue,
totalBorrowValue,
borrowingLimit,
},
});
return true;
};

Early Access

This feature is available for select customers, if you need this feature now ask our engineers 🙂
Last modified 1mo ago