11import type { Job } from 'bullmq'
22import type { Insertable } from 'kysely'
3- import { type Address , erc20Abi , formatUnits } from 'viem'
3+ import { type Address , erc20Abi , erc4626Abi , formatUnits } from 'viem'
44
55import { getViemClient } from '../../chains'
66import { type Tables , db } from '../../db'
@@ -19,12 +19,13 @@ type JobData = {
1919export const erc20Queue = createQueue < JobData > ( 'erc20' )
2020createWorker < JobData > ( erc20Queue , processJob )
2121
22+ // TODO: Refactor this code such that offchain accounts can support ERC-4626. Currently it only supports onchain accounts.
2223async function processJob ( job : Job < JobData > ) {
2324 const client = await getViemClient ( job . data . chainId )
2425
2526 const token = await db
2627 . selectFrom ( 'tokens' )
27- . select ( [ 'id' , 'decimals' ] )
28+ . select ( [ 'id' , 'decimals' , 'erc4626AssetAddress' , 'erc4626AssetDecimals' ] )
2829 . where ( 'address' , '=' , job . data . token )
2930 . where ( 'chain' , '=' , job . data . chainId )
3031 . executeTakeFirst ( )
@@ -36,6 +37,11 @@ async function processJob(job: Job<JobData>) {
3637 // Formatted balance, not the full bigint
3738 let balance : number
3839
40+ // Handle ERC4626
41+ const isErc4626 = ! ! token . erc4626AssetAddress
42+ const effectiveAddress = token . erc4626AssetAddress ?? job . data . token
43+ const effectiveDecimals = token . erc4626AssetDecimals ?? token . decimals
44+
3945 if ( job . data . owner . address ) {
4046 // Handle onchain account
4147 const rawBalance = await client . readContract ( {
@@ -45,7 +51,41 @@ async function processJob(job: Job<JobData>) {
4551 args : [ job . data . owner . address ] ,
4652 } )
4753
48- balance = Number ( formatUnits ( rawBalance , token . decimals ) )
54+ if ( isErc4626 ) {
55+ // Handle ERC-4626 which provides a standard way to get the underlying asset of a yield-bearing token
56+ const underlyingAssetBalance = await client . readContract ( {
57+ abi : erc4626Abi ,
58+ address : job . data . token ,
59+ functionName : 'convertToAssets' ,
60+ args : [ rawBalance ] ,
61+ } )
62+ balance = Number ( formatUnits ( underlyingAssetBalance , effectiveDecimals ) )
63+ } else {
64+ balance = Number ( formatUnits ( rawBalance , effectiveDecimals ) )
65+ }
66+
67+ const rateToEth = await getRateToEth ( {
68+ address : effectiveAddress ,
69+ chainId : job . data . chainId ,
70+ decimals : effectiveDecimals ,
71+ } )
72+
73+ const data : Insertable < Tables [ 'balances' ] > = {
74+ token : token . id ,
75+ owner : job . data . owner . id ,
76+ balance,
77+ ethValue : balance * rateToEth ,
78+ }
79+
80+ await db
81+ . insertInto ( 'balances' )
82+ . values ( data )
83+ . onConflict ( ( oc ) =>
84+ oc
85+ . columns ( [ 'token' , 'owner' ] )
86+ . doUpdateSet ( { ...data , updatedAt : new Date ( ) . toISOString ( ) } )
87+ )
88+ . execute ( )
4989 } else {
5090 // Handle manual account (without an address)
5191 const balanceFromDb = await db
@@ -60,28 +100,28 @@ async function processJob(job: Job<JobData>) {
60100 }
61101
62102 balance = balanceFromDb . balance
63- }
64103
65- const rateToEth = await getRateToEth ( {
66- address : job . data . token ,
67- chainId : job . data . chainId ,
68- decimals : token . decimals ,
69- } )
70-
71- const data : Insertable < Tables [ 'balances' ] > = {
72- token : token . id ,
73- owner : job . data . owner . id ,
74- balance,
75- ethValue : balance * rateToEth ,
76- }
104+ const rateToEth = await getRateToEth ( {
105+ address : job . data . token ,
106+ chainId : job . data . chainId ,
107+ decimals : token . decimals ,
108+ } )
77109
78- await db
79- . insertInto ( 'balances' )
80- . values ( data )
81- . onConflict ( ( oc ) =>
82- oc
83- . columns ( [ 'token' , 'owner' ] )
84- . doUpdateSet ( { ...data , updatedAt : new Date ( ) . toISOString ( ) } )
85- )
86- . execute ( )
110+ const data : Insertable < Tables [ 'balances' ] > = {
111+ token : token . id ,
112+ owner : job . data . owner . id ,
113+ balance,
114+ ethValue : balance * rateToEth ,
115+ }
116+
117+ await db
118+ . insertInto ( 'balances' )
119+ . values ( data )
120+ . onConflict ( ( oc ) =>
121+ oc
122+ . columns ( [ 'token' , 'owner' ] )
123+ . doUpdateSet ( { ...data , updatedAt : new Date ( ) . toISOString ( ) } )
124+ )
125+ . execute ( )
126+ }
87127}
0 commit comments