-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Integrate with moralis for top token holder finding (#94)
* feat: add token holder moralis repository * refactor: substitute goldrush to moralis
- Loading branch information
1 parent
fcfbcbb
commit 615821e
Showing
7 changed files
with
128 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -77,4 +77,7 @@ | |
# TENDERLY_PROJECT_NAME= | ||
|
||
# ETHPLORER | ||
# ETHPLORER_API_KEY= | ||
# ETHPLORER_API_KEY= | ||
|
||
# MORALIS | ||
# MORALIS_API_KEY= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 45 additions & 0 deletions
45
libs/repositories/src/TokenHolderRepository/TokenHolderRepositoryMoralis.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { Container } from 'inversify'; | ||
import { TokenHolderRepositoryMoralis } from './TokenHolderRepositoryMoralis'; | ||
import { SupportedChainId } from '@cowprotocol/shared'; | ||
import { WETH, NULL_ADDRESS } from '../../test/mock'; | ||
import { MORALIS_API_KEY } from '../datasources/moralis'; | ||
|
||
describe('TokenHolderRepositoryMoralis', () => { | ||
let tokenHolderRepositoryMoralis: TokenHolderRepositoryMoralis; | ||
|
||
beforeAll(() => { | ||
const container = new Container(); | ||
container | ||
.bind<TokenHolderRepositoryMoralis>(TokenHolderRepositoryMoralis) | ||
.to(TokenHolderRepositoryMoralis); | ||
tokenHolderRepositoryMoralis = container.get(TokenHolderRepositoryMoralis); | ||
expect(MORALIS_API_KEY).toBeDefined(); | ||
}); | ||
|
||
describe('getTopTokenHolders', () => { | ||
it('should return the top token holders of WETH', async () => { | ||
const tokenHolders = | ||
await tokenHolderRepositoryMoralis.getTopTokenHolders( | ||
SupportedChainId.MAINNET, | ||
WETH | ||
); | ||
|
||
expect(tokenHolders?.length).toBeGreaterThan(0); | ||
expect(tokenHolders?.[0].address).toBeDefined(); | ||
expect(Number(tokenHolders?.[0].balance)).toBeGreaterThan(0); | ||
expect(Number(tokenHolders?.[0].balance)).toBeGreaterThan( | ||
Number(tokenHolders?.[1].balance) | ||
); | ||
}, 100000); | ||
|
||
it('should return null for an unknown token', async () => { | ||
const tokenHolders = | ||
await tokenHolderRepositoryMoralis.getTopTokenHolders( | ||
SupportedChainId.MAINNET, | ||
NULL_ADDRESS | ||
); | ||
|
||
expect(tokenHolders).toBeNull(); | ||
}, 100000); | ||
}); | ||
}); |
58 changes: 58 additions & 0 deletions
58
libs/repositories/src/TokenHolderRepository/TokenHolderRepositoryMoralis.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { injectable } from 'inversify'; | ||
import { | ||
TokenHolderPoint, | ||
TokenHolderRepository, | ||
} from './TokenHolderRepository'; | ||
import { SupportedChainId } from '@cowprotocol/shared'; | ||
import { | ||
MORALIS_API_BASE_URL, | ||
MORALIS_API_KEY, | ||
MORALIS_CLIENT_NETWORK_MAPPING, | ||
} from '../datasources/moralis'; | ||
|
||
interface MoralisTokenHolderItem { | ||
balance: string; | ||
balance_formated: string; | ||
is_contract: boolean; | ||
owner_address: string; | ||
owner_address_label: string; | ||
entity: string; | ||
entity_logo: string; | ||
usd_value: string; | ||
percentage_relative_to_total_supply: number; | ||
} | ||
|
||
interface MoralisTokenHoldersResponse { | ||
result: MoralisTokenHolderItem[]; | ||
cursor: string; | ||
page: number; | ||
page_size: number; | ||
} | ||
|
||
@injectable() | ||
export class TokenHolderRepositoryMoralis implements TokenHolderRepository { | ||
async getTopTokenHolders( | ||
chainId: SupportedChainId, | ||
tokenAddress: string | ||
): Promise<TokenHolderPoint[] | null> { | ||
const response = (await fetch( | ||
`${MORALIS_API_BASE_URL}/v2.2/erc20/${tokenAddress}/owners?chain=${MORALIS_CLIENT_NETWORK_MAPPING[chainId]}&order=DESC`, | ||
{ | ||
method: 'GET', | ||
headers: { | ||
accept: 'application/json', | ||
'X-API-Key': `${MORALIS_API_KEY}`, | ||
}, | ||
} | ||
).then((res) => res.json())) as MoralisTokenHoldersResponse; | ||
|
||
if (response.result.length === 0) { | ||
return null; | ||
} | ||
|
||
return response.result.map((item) => ({ | ||
address: item.owner_address, | ||
balance: item.balance, | ||
})); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { SupportedChainId } from '@cowprotocol/shared'; | ||
|
||
export const MORALIS_API_KEY = process.env.MORALIS_API_KEY; | ||
export const MORALIS_API_BASE_URL = 'https://deep-index.moralis.io/api'; | ||
|
||
export const MORALIS_CLIENT_NETWORK_MAPPING: Record<SupportedChainId, string> = | ||
{ | ||
[SupportedChainId.MAINNET]: 'eth', | ||
[SupportedChainId.SEPOLIA]: 'sepolia', | ||
[SupportedChainId.GNOSIS_CHAIN]: 'gnosis', | ||
[SupportedChainId.ARBITRUM_ONE]: 'arbitrum', | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters