Skip to content

feat: solana balances #19

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,647 changes: 2,964 additions & 683 deletions package-lock.json

Large diffs are not rendered by default.

5,706 changes: 0 additions & 5,706 deletions packages/aleph-holders/package-lock.json

This file was deleted.

4 changes: 3 additions & 1 deletion packages/aleph-holders/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
"@aleph-indexer/bsc": "^1.1.11",
"@aleph-indexer/core": "^1.1.10",
"@aleph-indexer/ethereum": "^1.1.11",
"@aleph-indexer/framework": "^1.1.11"
"@aleph-indexer/solana": "^1.1.11",
"@aleph-indexer/framework": "^1.1.11",
"@solana/spl-token": "^0.4.1"
}
}
8 changes: 7 additions & 1 deletion packages/aleph-holders/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ async function main() {

const projectId = config.INDEXER_NAMESPACE || 'aleph-holders'
const supportedBlockchains = (
config.INDEXER_BLOCKCHAINS || 'ethereum,bsc'
config.INDEXER_BLOCKCHAINS || 'ethereum,bsc,solana'
).split(',') as Blockchain[]
const dataPath = config.INDEXER_DATA_PATH || undefined // 'data'
const transport =
Expand All @@ -35,6 +35,12 @@ async function main() {
transport,
transportConfig,
apiPort,
parser: {
instances: 1,
},
fetcher: {
instances: 1,
},
indexer: {
dataPath,
main: {
Expand Down
4 changes: 2 additions & 2 deletions packages/aleph-holders/src/api/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
} from 'graphql'
import { GraphQLBlockchain } from '@aleph-indexer/framework'

export const ERC20TransferEventQueryArgs = {
export const TransferEventQueryArgs = {
blockchain: { type: new GraphQLNonNull(GraphQLBlockchain) },
account: { type: GraphQLString },
startDate: { type: GraphQLFloat },
Expand All @@ -19,7 +19,7 @@ export const ERC20TransferEventQueryArgs = {
reverse: { type: GraphQLBoolean },
}

export const ERC20BalanceQueryArgs = {
export const BalanceQueryArgs = {
blockchain: { type: new GraphQLNonNull(GraphQLBlockchain) },
account: { type: GraphQLString },
limit: { type: GraphQLInt },
Expand Down
8 changes: 3 additions & 5 deletions packages/aleph-holders/src/api/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@ import MainDomain from '../domain/main.js'
import {
Balance,
BalanceQueryArgs,
ERC20TransferEvent,
ERC20TransferEventQueryArgs,
TransferEvent,
TransferEventQueryArgs,
} from '../types.js'

export class APIResolver {
constructor(protected domain: MainDomain) {}

async getEvents(
args: ERC20TransferEventQueryArgs,
): Promise<ERC20TransferEvent[]> {
async getEvents(args: TransferEventQueryArgs): Promise<TransferEvent[]> {
return this.domain.getEvents(args)
}

Expand Down
15 changes: 8 additions & 7 deletions packages/aleph-holders/src/api/schema.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { GraphQLObjectType } from 'graphql'
import { GraphQLInt, GraphQLObjectType } from 'graphql'
import { IndexerAPISchema } from '@aleph-indexer/framework'
import * as Types from './types.js'
import * as Args from './args.js'
import { APIResolver } from './resolvers.js'
import MainDomain from '../domain/main.js'
import { BalanceQueryArgs, ERC20TransferEventQueryArgs } from '../types.js'
import { BalanceQueryArgs, TransferEventQueryArgs } from '../types.js'
import { GraphQLLong } from '@aleph-indexer/core'

export default class APISchema extends IndexerAPISchema {
constructor(
Expand All @@ -18,14 +19,14 @@ export default class APISchema extends IndexerAPISchema {
name: 'Query',
fields: {
events: {
type: Types.ERC20TransferEventList,
args: Args.ERC20TransferEventQueryArgs,
type: Types.TransferEventList,
args: Args.TransferEventQueryArgs,
resolve: (_, args) =>
this.resolver.getEvents(args as ERC20TransferEventQueryArgs),
this.resolver.getEvents(args as TransferEventQueryArgs),
},
balances: {
type: Types.ERC20BalanceList,
args: Args.ERC20BalanceQueryArgs,
type: Types.BalanceList,
args: Args.BalanceQueryArgs,
resolve: (_, args) =>
this.resolver.getBalances(args as BalanceQueryArgs),
},
Expand Down
14 changes: 7 additions & 7 deletions packages/aleph-holders/src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ const commonFields = {
transaction: { type: new GraphQLNonNull(GraphQLString) },
}

export const ERC20TransferEvent = new GraphQLObjectType({
name: 'ERC20TransferEvent',
export const TransferEvent = new GraphQLObjectType({
name: 'TransferEvent',
fields: {
...commonFields,
from: { type: new GraphQLNonNull(GraphQLString) },
Expand All @@ -27,17 +27,17 @@ export const ERC20TransferEvent = new GraphQLObjectType({
},
})

export const ERC20TransferEventList = new GraphQLList(ERC20TransferEvent)
export const TransferEventList = new GraphQLList(TransferEvent)

export const ERC20Balance = new GraphQLObjectType({
name: 'ERC20Balance',
export const Balance = new GraphQLObjectType({
name: 'Balance',
Comment on lines -32 to +33
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be still a ERC20Balance, as we have a Solana specific implementation down below

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

balance query also can return solana balances from snapshot db

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see now where the problem in this discussion lies.

What you can do is make Balance a union of two types, ERC20Balance and SolanaBalance:

const erc20BalanceFields = {
  account: { type: new GraphQLNonNull(GraphQLString) },
  balance: { type: new GraphQLNonNull(GraphQLBigNumber) },
  balanceNum: { type: GraphQLFloat },
}

const solanaBalanceFields = {
  ...erc20BalanceFields,
  tokenAccount: { type: new GraphQLNonNull(GraphQLString) },
}

export const ERC20Balance = new GraphQLObjectType({
  name: 'ERC20Balance',
  fields: erc20BalanceFields,
})

export const SolanaBalance = new GraphQLObjectType({
  name: 'SolanaBalance',
  fields: solanaBalanceFields,
})

export const Balance = new GraphQLUnionType({
  name: 'Balance',
  types: [ ERC20Balance, SolanaBalance],
});

This will allow you to keep all the relevant info for Solana-related balances, while not having to deal with undefined values for ERC20Balances. This style also comes in handy later, when you might want to filter balances based on which type they are.

fields: {
account: { type: new GraphQLNonNull(GraphQLString) },
balance: { type: new GraphQLNonNull(GraphQLBigNumber) },
balanceNum: { type: GraphQLFloat },
},
})

export const ERC20BalanceList = new GraphQLList(ERC20Balance)
export const BalanceList = new GraphQLList(Balance)

export const types = [ERC20TransferEvent, ERC20Balance]
export const types = [TransferEvent, Balance]
4 changes: 2 additions & 2 deletions packages/aleph-holders/src/dal/balance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export enum BalanceDALIndex {

const accountKey = {
get: (e: Balance) => e.account,
length: EntityStorage.EthereumAddressLength,
length: EntityStorage.AddressLength,
}

const blockchainKey = {
Expand Down Expand Up @@ -49,7 +49,7 @@ const mapValueFn = async (value: any) => {

export function createBalanceDAL(path: string): BalanceStorage {
return new EntityStorage<Balance>({
name: 'erc20_balance',
name: 'balance',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we are storing Balance (previously ERC20Balance) in this database. What about Solana balances and their additional field then? Are they not stored?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The snapshot taken is stored on the snapshot dal

path,
key: [blockchainKey, accountKey],
indexes: [
Expand Down
21 changes: 21 additions & 0 deletions packages/aleph-holders/src/dal/tokenAccount.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { EntityStorage } from '@aleph-indexer/core'

export type TokenAccountStorage = EntityStorage<TokenAccount>

export type TokenAccount = {
address: string
owner: string
}

const accountKey = {
get: (e: TokenAccount) => e.address,
length: EntityStorage.AddressLength,
}

export function createTokenAccounttDAL(path: string): TokenAccountStorage {
return new EntityStorage<TokenAccount>({
name: 'account_mint',
path,
key: [accountKey],
})
}
Original file line number Diff line number Diff line change
@@ -1,43 +1,44 @@
import { EntityStorage } from '@aleph-indexer/core'
import { ERC20TransferEvent } from '../types.js'
import { TransferEvent } from '../types.js'
import {
blockchainDecimals,
uint256ToBigNumber,
uint256ToNumber,
} from '../utils/index.js'

export type ERC20TransferEventStorage = EntityStorage<ERC20TransferEvent>
export type TransferEventStorage = EntityStorage<TransferEvent>

export enum ERC20TransferEventDALIndex {
export enum TransferEventDALIndex {
BlockchainTimestamp = 'blockchain_timestamp',
BlockchainHeight = 'blockchain_height',
BlockchainAccountTimestamp = 'blockchain_account_timestamp',
BlockchainAccountHeight = 'blockchain_account_height',
}

const idKey = {
get: (e: ERC20TransferEvent) => e.id,
get: (e: TransferEvent) => e.id,
length: EntityStorage.VariableLength,
}

const accountKey = {
get: (e: ERC20TransferEvent) => [e.from, e.to],
length: EntityStorage.EthereumAddressLength,
get: (e: TransferEvent) => [e.from, e.to],
length: EntityStorage.AddressLength,
}

const blockchainKey = {
get: (e: ERC20TransferEvent) => e.blockchain,
get: (e: TransferEvent) => e.blockchain,
length: EntityStorage.VariableLength,
}

const timestampKey = {
get: (e: ERC20TransferEvent) => e.timestamp,
get: (e: TransferEvent) => e.timestamp,
length: EntityStorage.TimestampLength,
}

const heightKey = {
get: (e: ERC20TransferEvent) => e.height,
get: (e: TransferEvent) => e.height,
// @note: up to 10**9 [9 digits] enough for 300 years in ethereum
// on solana 14 years aprox, block gen each 400ms
length: 8,
}

Expand All @@ -60,28 +61,26 @@ const mapValueFn = async (value: any) => {
return value
}

export function createERC20TransferEventDAL(
path: string,
): ERC20TransferEventStorage {
return new EntityStorage<ERC20TransferEvent>({
name: 'erc20_transfer_event',
export function createTransferEventDAL(path: string): TransferEventStorage {
return new EntityStorage<TransferEvent>({
name: 'transfer_event',
path,
key: [idKey],
indexes: [
{
name: ERC20TransferEventDALIndex.BlockchainTimestamp,
name: TransferEventDALIndex.BlockchainTimestamp,
key: [blockchainKey, timestampKey],
},
{
name: ERC20TransferEventDALIndex.BlockchainHeight,
name: TransferEventDALIndex.BlockchainHeight,
key: [blockchainKey, heightKey],
},
{
name: ERC20TransferEventDALIndex.BlockchainAccountTimestamp,
name: TransferEventDALIndex.BlockchainAccountTimestamp,
key: [blockchainKey, accountKey, timestampKey],
},
{
name: ERC20TransferEventDALIndex.BlockchainAccountHeight,
name: TransferEventDALIndex.BlockchainAccountHeight,
key: [blockchainKey, accountKey, heightKey],
},
],
Expand Down
28 changes: 20 additions & 8 deletions packages/aleph-holders/src/domain/main.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import { BlockchainChain } from '@aleph-indexer/framework'
import { IndexerMainDomain } from '@aleph-indexer/framework'
import {
IndexerMainDomain,
BlockchainChain,
IndexerMainDomainContext,
} from '@aleph-indexer/framework'
import {
Balance,
BalanceQueryArgs,
ERC20TransferEvent,
ERC20TransferEventQueryArgs,
TransferEvent,
TransferEventQueryArgs,
} from '../types.js'
import { blockchainTokenContract } from '../utils/index.js'

export default class MainDomain extends IndexerMainDomain {
constructor(
protected context: IndexerMainDomainContext,
) {
super(context)
}

async init(): Promise<void> {
await super.init()

Expand All @@ -18,6 +27,11 @@ export default class MainDomain extends IndexerMainDomain {
account: blockchainTokenContract[BlockchainChain.Ethereum],
index: { logs: true },
},
{
blockchainId: BlockchainChain.Solana,
account: blockchainTokenContract[BlockchainChain.Solana],
index: { transactions: true },
},
// {
// blockchainId: BlockchainChain.Bsc,
// account: blockchainTokenContract[BlockchainChain.Bsc],
Expand All @@ -26,9 +40,7 @@ export default class MainDomain extends IndexerMainDomain {
])
}

async getEvents(
args: ERC20TransferEventQueryArgs,
): Promise<ERC20TransferEvent[]> {
async getEvents(args: TransferEventQueryArgs): Promise<TransferEvent[]> {
const { blockchain } = args
const [alephTokenSC] = this.accounts[blockchain].values()

Expand All @@ -40,7 +52,7 @@ export default class MainDomain extends IndexerMainDomain {
args: [args],
})

return response as ERC20TransferEvent[]
return response as TransferEvent[]
}

async getBalances(args: BalanceQueryArgs): Promise<Balance[]> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { BlockchainId } from '@aleph-indexer/framework'
import { EthereumParsedLog } from '@aleph-indexer/ethereum'
import { AlephEvent, Balance, ERC20TransferEvent } from '../types.js'
import { AlephEvent, Balance, TransferEvent } from '../../types.js'
import {
bigNumberToString,
uint256ToBigNumber,
uint256ToString,
} from '../utils/index.js'
} from '../../utils/index.js'

export class EventParser {
export class EthereumEventParser {
parseERC20TransferEvent(
blockchain: BlockchainId,
entity: EthereumParsedLog,
): ERC20TransferEvent {
): TransferEvent {
const parsedEvent = this.parseCommonScheme(blockchain, entity)

const [rawFrom, rawTo, rawValue] = entity.parsed?.args || []
Expand Down
Loading