Skip to content

Commit

Permalink
Merge pull request #154 from mich3lang3lo/feat/zeko-integration
Browse files Browse the repository at this point in the history
feat: zeko providers
  • Loading branch information
mrcnk authored Mar 28, 2024
2 parents 13d7f0e + c62f100 commit 41897bb
Show file tree
Hide file tree
Showing 40 changed files with 1,290 additions and 19 deletions.
1 change: 1 addition & 0 deletions packages/providers/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * as MinaExplorer from './mina-explorer'
export * as MinaNode from './mina-node'
export * as Obscura from './obscura-provider'
export * from './unified-providers'
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {
AccountInfo,
AccountInfoArgs,
AccountInfoProvider
} from '@palladxyz/mina-core'

import { createGraphQLRequest } from '../utils/fetch-utils'
import { healthCheck } from '../utils/health-check-utils'
import { getTokenAccountInfoQuery } from './queries'

export const createAccountInfoProvider = (url: string): AccountInfoProvider => {
const getAccountInfo = async (
args: AccountInfoArgs
): Promise<Record<string, AccountInfo>> => {
const variables = { publicKey: args.publicKey }
const query = getTokenAccountInfoQuery(args.tokenMap || { MINA: '1' })
const fetchGraphQL = createGraphQLRequest(url)
const result = await fetchGraphQL(query, variables)

if (!result.ok) {
throw new Error(result.message)
}

const accountsData = result.data
const accountsInfo: Record<string, AccountInfo> = {}

for (const [key, account] of Object.entries(accountsData)) {
if (account === null) {
accountsInfo[key] = {
balance: { total: 0 },
nonce: 0,
inferredNonce: 0,
delegate: '',
publicKey: args.publicKey
}
} else {
accountsInfo[key] = account as AccountInfo
}
}

return accountsInfo
}

return {
healthCheck: () => healthCheck(url),
getAccountInfo
}
}
1 change: 1 addition & 0 deletions packages/providers/src/mina-node/account-info/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './account-info-provider'
35 changes: 35 additions & 0 deletions packages/providers/src/mina-node/account-info/queries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export const healthCheckQuery = `
{
syncStatus
}
`

export const getAccountBalance = `
query accountBalance($publicKey: PublicKey!) {
account(publicKey: $publicKey) {
balance {
total
},
nonce
inferredNonce
delegate
publicKey
}
}
`
import { TokenIdMap } from '@palladxyz/mina-core'

export function getTokenAccountInfoQuery(tokenIds: TokenIdMap): string {
// Start with the base part of the query
let queryString = `query tokenQuery($publicKey: PublicKey!) {\n`

// Dynamically add account queries based on tokenIds
Object.entries(tokenIds).forEach(([alias, tokenId]) => {
queryString += ` ${alias}: account(token: "${tokenId}", publicKey: $publicKey) {\n ...AccountFields\n }\n`
})

// Add the fragment definition
queryString += `}\n\nfragment AccountFields on Account {\n balance {\n total\n }\n tokenSymbol\n tokenId\n nonce\n inferredNonce\n publicKey\n delegate\n}`

return queryString
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { AccountInfo, AccountInfoArgs } from '@palladxyz/mina-core'
import { ExecutionResult } from 'graphql'
import { gql, GraphQLClient } from 'graphql-request'
import { SubscriptionClient } from 'subscriptions-transport-ws'

import {
AccountData,
AccountInfoGraphQLProvider
} from '../account-info/AccountInfoProvider'
import { getAccountBalance } from '../account-info/queries'

interface BlockData {
newBlock?: {
creator?: string
stateHash?: string
protocolState?: {
consensusState?: {
blockHeight?: number
}
previousStateHash?: string
}
}
}

export class BlockListenerProvider {
private accountInfoProvider: AccountInfoGraphQLProvider
private subscriptionClient: SubscriptionClient
private gqlClient: GraphQLClient

constructor(minaGql: string, wsEndpoint: string) {
this.accountInfoProvider = new AccountInfoGraphQLProvider(minaGql)
this.subscriptionClient = new SubscriptionClient(wsEndpoint, {
reconnect: true
})
this.gqlClient = new GraphQLClient(minaGql)
}

listenToNewBlocks(publicKey: string) {
const subscription = gql`
subscription {
newBlock(publicKey: "${publicKey}") {
creator
stateHash
protocolState {
consensusState {
blockHeight
}
previousStateHash
}
}
}
`

return this.subscriptionClient
.request({
query: subscription
})
.subscribe({
next: async (result: ExecutionResult) => {
// Type guard
if ('data' in result && 'newBlock' in result.data) {
const data: BlockData = result.data
if (data.newBlock) {
console.log('Received block data:', data)
const accountInfo = await this.getAccountInfo({ publicKey })
console.log('Received updated account info:', accountInfo)
} else {
console.error('No new block data in result:', result)
}
} else {
console.error('Unexpected result:', result)
}
}
})
}

private async getAccountInfo(args: AccountInfoArgs): Promise<AccountInfo> {
console.log('Initiating getAccountInfo with args:', args)
const query = gql`
${getAccountBalance}
`
try {
console.log('Sending request for account info...')
const data = (await this.gqlClient.request(query, {
publicKey: args.publicKey
})) as AccountData
console.log('Received response for account info:', data)

if (!data || !data.account) {
throw new Error('Invalid account data response')
}
return data.account
} catch (error: unknown) {
console.error('Error in getAccountInfo:', error)
// this can fail if the account doesn't exist yet on the chain & if the node is not available
// perform health check to see if the node is available
const healthCheckResponse = await this.accountInfoProvider.healthCheck()
if (!healthCheckResponse.ok) {
throw new Error('Node is not available')
}
// if the node is available, then the account doesn't exist yet
// return an empty account
console.log('Error in getAccountInfo, account does not exist yet!')
return {
balance: { total: 0 },
nonce: 0,
inferredNonce: 0,
delegate: '',
publicKey: args.publicKey
}
}
}
}
1 change: 1 addition & 0 deletions packages/providers/src/mina-node/block-listener/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './BlockListenerProvider'
9 changes: 9 additions & 0 deletions packages/providers/src/mina-node/block-listener/queries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const healthCheckQuery = `
{
__schema {
types {
name
}
}
}
`
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {
ChainHistoryProvider,
Mina,
TransactionsByAddressesArgs,
TransactionsByIdsArgs
} from '@palladxyz/mina-core'

import { createGraphQLRequest } from '../utils/fetch-utils'
import {
healthCheck,
healthCheckQueryArchive
} from '../utils/health-check-utils'
import { transactionsByAddressesQuery } from './queries'

export const createChainHistoryProvider = (
url: string
): ChainHistoryProvider => {
const transactionsByAddresses = async (
args: TransactionsByAddressesArgs
): Promise<Mina.TransactionBody[]> => {
const { startAt, limit } = args.pagination || { startAt: 0, limit: 10 }
// TODO: remove array of addresses from TransactionsByAddressesArgs
const variables = { address: args.addresses[0], limit, offset: startAt }
const query = transactionsByAddressesQuery
const fetchGraphQL = createGraphQLRequest(url)
const result = await fetchGraphQL(query, variables)

if (!result.ok) {
throw new Error(result.message)
}

const transactions = result.data.transactions

return transactions
}

const transactionsByHashes = async (
args: TransactionsByIdsArgs
): Promise<Mina.TransactionBody[]> => {
const variables = { ids: args.ids }
const query = transactionsByAddressesQuery
const fetchGraphQL = createGraphQLRequest(url)
const result = await fetchGraphQL(query, variables)

if (!result.ok) {
throw new Error(result.message)
}

const transactions = result.data

return transactions
}

return {
healthCheck: () => healthCheck(url, healthCheckQueryArchive),
transactionsByAddresses,
transactionsByHashes
}
}
1 change: 1 addition & 0 deletions packages/providers/src/mina-node/chain-history/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './chain-history-provider'
42 changes: 42 additions & 0 deletions packages/providers/src/mina-node/chain-history/queries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
export const transactionsByAddressesQuery = `
query Transactions($address: String!, $limit: Int) {
transactions(
query: { canonical: true, OR: [{ to: $address }, { from: $address }] }
limit: $limit
sortBy: DATETIME_DESC
) {
amount
to
token
kind
isDelegation
hash
from
fee
failureReason
dateTime
blockHeight
}
}
`

export const transactionsByHashesQuery = `
query Transaction($hash: String!) {
transaction(query: { hash: $hash }) {
amount
blockHeight
dateTime
failureReason
fee
from
hash
id
isDelegation
kind
memo
nonce
to
token
}
}
`
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { DaemonStatus, DaemonStatusProvider } from '@palladxyz/mina-core'

import { createGraphQLRequest } from '../utils/fetch-utils'
import { healthCheck } from '../utils/health-check-utils'
import { getDaemonStatusQuery } from './queries'

export const createDaemonStatusProvider = (
url: string
): DaemonStatusProvider => {
const getDaemonStatus = async (): Promise<DaemonStatus> => {
const fetchGraphQL = createGraphQLRequest(url)
const result = await fetchGraphQL(getDaemonStatusQuery)

if (!result.ok) {
throw new Error(result.message)
}

const daemonStatus = result.data

return daemonStatus
}

return {
healthCheck: () => healthCheck(url),
getDaemonStatus
}
}
1 change: 1 addition & 0 deletions packages/providers/src/mina-node/daemon-status/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './daemon-status-provider'
7 changes: 7 additions & 0 deletions packages/providers/src/mina-node/daemon-status/queries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const getDaemonStatusQuery = `
query {
daemonStatus {
chainId
}
}
`
5 changes: 5 additions & 0 deletions packages/providers/src/mina-node/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './account-info'
export * from './chain-history'
export * from './daemon-status'
export * from './tx-submit'
export * from './types'
1 change: 1 addition & 0 deletions packages/providers/src/mina-node/tx-submit/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './tx-submit-provider'
Loading

0 comments on commit 41897bb

Please sign in to comment.