diff --git a/packages/apps/human-app/server/src/modules/oracle-discovery/model/oracle-discovery.model.ts b/packages/apps/human-app/server/src/modules/oracle-discovery/model/oracle-discovery.model.ts index e8914a8d7e..821583c103 100644 --- a/packages/apps/human-app/server/src/modules/oracle-discovery/model/oracle-discovery.model.ts +++ b/packages/apps/human-app/server/src/modules/oracle-discovery/model/oracle-discovery.model.ts @@ -9,10 +9,6 @@ type DiscoveredOracleCreateProps = { address: string; chainId: ChainId; stakedAmount: bigint | null; - lockedAmount: bigint | null; - lockedUntilTimestamp: number | null; - withdrawnAmount: bigint | null; - slashedAmount: bigint | null; amountJobsProcessed: bigint | null; role: string; fee: bigint | null; @@ -41,24 +37,6 @@ export class DiscoveredOracle { @ApiPropertyOptional({ description: 'Amount staked by the operator' }) stakedAmount?: string; - @ApiPropertyOptional({ - description: 'Amount currently locked by the operator', - }) - lockedAmount?: string; - - @ApiPropertyOptional({ description: 'Timestamp until funds are locked' }) - lockedUntilTimestamp?: string; - - @ApiPropertyOptional({ - description: 'Total amount withdrawn by the operator', - }) - withdrawnAmount?: string; - - @ApiPropertyOptional({ - description: 'Total amount slashed from the operator', - }) - slashedAmount?: string; - @ApiPropertyOptional({ description: 'Number of jobs processed by the operator', }) @@ -131,11 +109,7 @@ export class DiscoveredOracle { this.jobTypes = props.jobTypes ?? []; this.reputationNetworks = props.reputationNetworks ?? undefined; this.stakedAmount = props.stakedAmount?.toString(); - this.lockedAmount = props.lockedAmount?.toString(); - this.withdrawnAmount = props.withdrawnAmount?.toString(); - this.slashedAmount = props.slashedAmount?.toString(); this.amountJobsProcessed = props.amountJobsProcessed?.toString(); - this.lockedUntilTimestamp = props.lockedUntilTimestamp?.toString(); } } diff --git a/packages/apps/human-app/server/src/modules/oracle-discovery/oracle-discovery.service.ts b/packages/apps/human-app/server/src/modules/oracle-discovery/oracle-discovery.service.ts index c7e93ee143..d312b23d93 100644 --- a/packages/apps/human-app/server/src/modules/oracle-discovery/oracle-discovery.service.ts +++ b/packages/apps/human-app/server/src/modules/oracle-discovery/oracle-discovery.service.ts @@ -101,11 +101,7 @@ export class OracleDiscoveryService { registrationInstructions: exchangeOracle.registrationInstructions, chainId, stakedAmount: exchangeOracle.stakedAmount, - lockedAmount: exchangeOracle.lockedAmount, - withdrawnAmount: exchangeOracle.withdrawnAmount, - slashedAmount: exchangeOracle.slashedAmount, amountJobsProcessed: exchangeOracle.amountJobsProcessed, - lockedUntilTimestamp: exchangeOracle.lockedUntilTimestamp, fee: exchangeOracle.fee, publicKey: exchangeOracle.publicKey, webhookUrl: exchangeOracle.webhookUrl, diff --git a/packages/apps/human-app/server/src/modules/oracle-discovery/spec/oracle-discovery.fixture.ts b/packages/apps/human-app/server/src/modules/oracle-discovery/spec/oracle-discovery.fixture.ts index de9ca168c8..a91adc975b 100644 --- a/packages/apps/human-app/server/src/modules/oracle-discovery/spec/oracle-discovery.fixture.ts +++ b/packages/apps/human-app/server/src/modules/oracle-discovery/spec/oracle-discovery.fixture.ts @@ -17,10 +17,6 @@ export const response1: DiscoveredOracle = { registrationNeeded: true, registrationInstructions: 'https://instructions.com', stakedAmount: '0', - lockedAmount: '0', - lockedUntilTimestamp: '0', - withdrawnAmount: '0', - slashedAmount: '0', amountJobsProcessed: '0', }; @@ -37,10 +33,6 @@ export const response2: DiscoveredOracle = { registrationNeeded: false, registrationInstructions: undefined, stakedAmount: '0', - lockedAmount: '0', - lockedUntilTimestamp: '0', - withdrawnAmount: '0', - slashedAmount: '0', amountJobsProcessed: '0', }; @@ -57,10 +49,6 @@ export const response3: DiscoveredOracle = { registrationNeeded: false, registrationInstructions: undefined, stakedAmount: '0', - lockedAmount: '0', - lockedUntilTimestamp: '0', - withdrawnAmount: '0', - slashedAmount: '0', amountJobsProcessed: '0', }; @@ -77,10 +65,6 @@ export const response4: DiscoveredOracle = { registrationNeeded: false, registrationInstructions: undefined, stakedAmount: '0', - lockedAmount: '0', - lockedUntilTimestamp: '0', - withdrawnAmount: '0', - slashedAmount: '0', amountJobsProcessed: '0', }; diff --git a/packages/sdk/typescript/human-protocol-sdk/src/constants.ts b/packages/sdk/typescript/human-protocol-sdk/src/constants.ts index 6a9006c802..07a5ad74d3 100644 --- a/packages/sdk/typescript/human-protocol-sdk/src/constants.ts +++ b/packages/sdk/typescript/human-protocol-sdk/src/constants.ts @@ -37,6 +37,10 @@ export const NETWORKS: { 'https://api.studio.thegraph.com/query/74256/ethereum/version/latest', subgraphUrlApiKey: 'https://gateway.thegraph.com/api/deployments/id/QmNhLQEfBJQ46fngBh4YCttk8kNkveFy5uvAeUmyAdX1kD', + stakingSubgraphUrl: + 'https://api.studio.thegraph.com/query/74256/ethereum/version/latest', + stakingSubgraphUrlApiKey: + 'https://gateway.thegraph.com/api/deployments/id/QmNhLQEfBJQ46fngBh4YCttk8kNkveFy5uvAeUmyAdX1kD', oldSubgraphUrl: '', oldFactoryAddress: '', }, @@ -52,6 +56,10 @@ export const NETWORKS: { 'https://api.studio.thegraph.com/query/74256/sepolia/version/latest', subgraphUrlApiKey: 'https://gateway.thegraph.com/api/deployments/id/QmQghdr7hxqrjFde8DN15TzfrJLCfwvzmUH9RzWwH1mKzk', + stakingSubgraphUrl: + 'https://api.studio.thegraph.com/query/74256/sepolia/version/latest', + stakingSubgraphUrlApiKey: + 'https://gateway.thegraph.com/api/deployments/id/QmQghdr7hxqrjFde8DN15TzfrJLCfwvzmUH9RzWwH1mKzk', oldSubgraphUrl: '', oldFactoryAddress: '', }, @@ -67,6 +75,10 @@ export const NETWORKS: { 'https://api.studio.thegraph.com/query/74256/bsc/version/latest', subgraphUrlApiKey: 'https://gateway.thegraph.com/api/deployments/id/QmTioC9Z1HzKSCnEKL3BP9iHqbgZt1ceLU2VE4Mv6sxNkd', + stakingSubgraphUrl: + 'https://api.studio.thegraph.com/query/74256/bsc/version/latest', + stakingSubgraphUrlApiKey: + 'https://gateway.thegraph.com/api/deployments/id/QmTioC9Z1HzKSCnEKL3BP9iHqbgZt1ceLU2VE4Mv6sxNkd', oldSubgraphUrl: 'https://api.thegraph.com/subgraphs/name/humanprotocol/bsc', oldFactoryAddress: '0xc88bC422cAAb2ac8812de03176402dbcA09533f4', }, @@ -82,6 +94,10 @@ export const NETWORKS: { 'https://api.studio.thegraph.com/query/74256/bsc-testnet/version/latest', subgraphUrlApiKey: 'https://gateway.thegraph.com/api/deployments/id/QmPyUYRjAvzDdeenXMGHcCRD2v4qwZbKMEkVkY3Jq6VLwn', + stakingSubgraphUrl: + 'https://api.studio.thegraph.com/query/74256/bsc-testnet/version/latest', + stakingSubgraphUrlApiKey: + 'https://gateway.thegraph.com/api/deployments/id/QmPyUYRjAvzDdeenXMGHcCRD2v4qwZbKMEkVkY3Jq6VLwn', oldSubgraphUrl: 'https://api.thegraph.com/subgraphs/name/humanprotocol/bsctest', oldFactoryAddress: '0xaae6a2646c1f88763e62e0cd08ad050ea66ac46f', @@ -95,9 +111,13 @@ export const NETWORKS: { stakingAddress: '0x01D115E9E8bF0C58318793624CC662a030D07F1D', kvstoreAddress: '0xbcB28672F826a50B03EE91B28145EAbddA73B2eD', subgraphUrl: - 'https://api.studio.thegraph.com/query/74256/polygon/version/latest', + 'https://api.studio.thegraph.com/query/74256/human-polygon/version/latest', subgraphUrlApiKey: 'https://gateway.thegraph.com/api/deployments/id/QmQNkWNE5FPtqbtSkdtR6AEBJz9N7WDz1X7pfvQqYAcUZJ', + stakingSubgraphUrl: + 'https://api.studio.thegraph.com/query/118155/staking/version/latest', + stakingSubgraphUrlApiKey: + 'https://gateway.thegraph.com/api/deployments/id/QmQNkWNE5FPtqbtSkdtR6AEBJz9N7WDz1X7pfvQqYAcUZJ', oldSubgraphUrl: 'https://api.thegraph.com/subgraphs/name/humanprotocol/polygon', oldFactoryAddress: '0x45eBc3eAE6DA485097054ae10BA1A0f8e8c7f794', @@ -114,6 +134,10 @@ export const NETWORKS: { 'https://api.studio.thegraph.com/query/74256/amoy/version/latest', subgraphUrlApiKey: 'https://gateway.thegraph.com/api/deployments/id/QmcLwLMw3UzCSbNbjegrpNu6PB3kAd67xquuyaVWvc5Q7Q', + stakingSubgraphUrl: + 'https://api.studio.thegraph.com/query/74256/amoy/version/latest', + stakingSubgraphUrlApiKey: + 'https://gateway.thegraph.com/api/deployments/id/QmcLwLMw3UzCSbNbjegrpNu6PB3kAd67xquuyaVWvc5Q7Q', oldSubgraphUrl: '', oldFactoryAddress: '', }, @@ -127,6 +151,9 @@ export const NETWORKS: { kvstoreAddress: '0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9', subgraphUrl: 'http://localhost:8000/subgraphs/name/humanprotocol/localhost', subgraphUrlApiKey: '', + stakingSubgraphUrl: + 'http://localhost:8000/subgraphs/name/humanprotocol/staking-localhost', + stakingSubgraphUrlApiKey: '', oldSubgraphUrl: '', oldFactoryAddress: '', }, diff --git a/packages/sdk/typescript/human-protocol-sdk/src/graphql/queries/operator.ts b/packages/sdk/typescript/human-protocol-sdk/src/graphql/queries/operator.ts index e2f4327710..197be116d3 100644 --- a/packages/sdk/typescript/human-protocol-sdk/src/graphql/queries/operator.ts +++ b/packages/sdk/typescript/human-protocol-sdk/src/graphql/queries/operator.ts @@ -5,6 +5,7 @@ const LEADER_FRAGMENT = gql` fragment OperatorFields on Operator { id address + stakedAmount amountJobsProcessed role fee @@ -20,14 +21,6 @@ const LEADER_FRAGMENT = gql` } name category - staker { - stakedAmount - lockedAmount - withdrawnAmount - slashedAmount - lockedUntilTimestamp - lastDepositTimestamp - } } `; @@ -36,7 +29,7 @@ export const GET_LEADERS_QUERY = (filter: IOperatorsFilter) => { const WHERE_CLAUSE = ` where: { - ${minStakedAmount ? `staker_: { stakedAmount_gte: $minStakedAmount }` : ''} + ${minStakedAmount ? `stakedAmount_gte: $minStakedAmount` : ''} ${roles ? `role_in: $roles` : ''} } `; diff --git a/packages/sdk/typescript/human-protocol-sdk/src/graphql/types.ts b/packages/sdk/typescript/human-protocol-sdk/src/graphql/types.ts index fed501218f..762f95c3ae 100644 --- a/packages/sdk/typescript/human-protocol-sdk/src/graphql/types.ts +++ b/packages/sdk/typescript/human-protocol-sdk/src/graphql/types.ts @@ -143,6 +143,7 @@ export type StakerData = { export interface IOperatorSubgraph { id: string; address: string; + stakedAmount: string | null; amountJobsProcessed: string; role: string | null; fee: string | null; @@ -156,14 +157,6 @@ export interface IOperatorSubgraph { category: string | null; jobTypes: string | string[] | null; reputationNetworks: { address: string }[]; - staker: { - stakedAmount: string; - lockedAmount: string; - lockedUntilTimestamp: string; - withdrawnAmount: string; - slashedAmount: string; - lastDepositTimestamp: string; - } | null; } export interface IReputationNetworkSubgraph extends Omit< diff --git a/packages/sdk/typescript/human-protocol-sdk/src/interfaces.ts b/packages/sdk/typescript/human-protocol-sdk/src/interfaces.ts index e9bca41378..3607e7f9a4 100644 --- a/packages/sdk/typescript/human-protocol-sdk/src/interfaces.ts +++ b/packages/sdk/typescript/human-protocol-sdk/src/interfaces.ts @@ -11,10 +11,6 @@ export interface IOperator { chainId: ChainId; address: string; stakedAmount: bigint | null; - lockedAmount: bigint | null; - lockedUntilTimestamp: number | null; - withdrawnAmount: bigint | null; - slashedAmount: bigint | null; amountJobsProcessed: bigint | null; role: string | null; fee: bigint | null; diff --git a/packages/sdk/typescript/human-protocol-sdk/src/operator.ts b/packages/sdk/typescript/human-protocol-sdk/src/operator.ts index ad0dd150d7..cc3e863355 100644 --- a/packages/sdk/typescript/human-protocol-sdk/src/operator.ts +++ b/packages/sdk/typescript/human-protocol-sdk/src/operator.ts @@ -121,15 +121,6 @@ export class OperatorUtils { filter.skip !== undefined && filter.skip >= 0 ? filter.skip : 0; const orderDirection = filter.orderDirection || OrderDirection.DESC; - let orderBy = filter.orderBy; - if (filter.orderBy === 'stakedAmount') orderBy = 'staker__stakedAmount'; - else if (filter.orderBy === 'lockedAmount') - orderBy = 'staker__lockedAmount'; - else if (filter.orderBy === 'withdrawnAmount') - orderBy = 'staker__withdrawnAmount'; - else if (filter.orderBy === 'slashedAmount') - orderBy = 'staker__slashedAmount'; - const networkData = NETWORKS[filter.chainId]; if (!networkData) { @@ -144,7 +135,7 @@ export class OperatorUtils { { minStakedAmount: filter?.minStakedAmount, roles: filter?.roles, - orderBy: orderBy, + orderBy: filter?.orderBy, orderDirection: orderDirection, first: first, skip: skip, @@ -268,7 +259,6 @@ export class OperatorUtils { } function mapOperator(operator: IOperatorSubgraph, chainId: ChainId): IOperator { - const staker = operator?.staker; let jobTypes: string[] = []; let reputationNetworks: string[] = []; @@ -291,15 +281,7 @@ function mapOperator(operator: IOperatorSubgraph, chainId: ChainId): IOperator { id: operator.id, chainId, address: operator.address, - stakedAmount: staker?.stakedAmount ? BigInt(staker?.stakedAmount) : null, - lockedAmount: staker?.lockedAmount ? BigInt(staker?.lockedAmount) : null, - lockedUntilTimestamp: staker?.lockedUntilTimestamp - ? Number(staker.lockedUntilTimestamp) * 1000 - : null, - withdrawnAmount: staker?.withdrawnAmount - ? BigInt(staker?.withdrawnAmount) - : null, - slashedAmount: staker?.slashedAmount ? BigInt(staker?.slashedAmount) : null, + stakedAmount: operator.stakedAmount ? BigInt(operator.stakedAmount) : null, amountJobsProcessed: operator.amountJobsProcessed ? BigInt(operator.amountJobsProcessed) : null, diff --git a/packages/sdk/typescript/human-protocol-sdk/src/staking.ts b/packages/sdk/typescript/human-protocol-sdk/src/staking.ts index b0f0284db6..316e8da263 100644 --- a/packages/sdk/typescript/human-protocol-sdk/src/staking.ts +++ b/packages/sdk/typescript/human-protocol-sdk/src/staking.ts @@ -30,7 +30,7 @@ import { } from './interfaces'; import { StakerData } from './graphql'; import { NetworkData, TransactionOverrides } from './types'; -import { getSubgraphUrl, customGqlFetch, throwError } from './utils'; +import { getStakingSubgraphUrl, customGqlFetch, throwError } from './utils'; import { GET_STAKER_BY_ADDRESS_QUERY, GET_STAKERS_QUERY, @@ -509,7 +509,7 @@ export class StakingUtils { } const { staker } = await customGqlFetch<{ staker: StakerData }>( - getSubgraphUrl(networkData), + getStakingSubgraphUrl(networkData), GET_STAKER_BY_ADDRESS_QUERY, { id: stakerAddress.toLowerCase() }, options @@ -558,7 +558,7 @@ export class StakingUtils { } const { stakers } = await customGqlFetch<{ stakers: StakerData[] }>( - getSubgraphUrl(networkData), + getStakingSubgraphUrl(networkData), GET_STAKERS_QUERY(filter), { minStakedAmount: filter.minStakedAmount diff --git a/packages/sdk/typescript/human-protocol-sdk/src/types.ts b/packages/sdk/typescript/human-protocol-sdk/src/types.ts index 9ced185a32..6fd0814b64 100644 --- a/packages/sdk/typescript/human-protocol-sdk/src/types.ts +++ b/packages/sdk/typescript/human-protocol-sdk/src/types.ts @@ -76,6 +76,14 @@ export type NetworkData = { * Subgraph URL API key */ subgraphUrlApiKey: string; + /** + * Staking subgraph URL + */ + stakingSubgraphUrl?: string; + /** + * Staking subgraph URL API key + */ + stakingSubgraphUrlApiKey?: string; /** * Old subgraph URL */ diff --git a/packages/sdk/typescript/human-protocol-sdk/src/utils.ts b/packages/sdk/typescript/human-protocol-sdk/src/utils.ts index 02ed1bd9aa..52c8d76bfe 100644 --- a/packages/sdk/typescript/human-protocol-sdk/src/utils.ts +++ b/packages/sdk/typescript/human-protocol-sdk/src/utils.ts @@ -96,6 +96,33 @@ export const getSubgraphUrl = (networkData: NetworkData) => { return subgraphUrl; }; +/** + * Gets the staking subgraph URL for the given network, using API key if available. + * Falls back to the default subgraph URL when staking-specific URLs are not configured. + * + * @param networkData - The network data containing subgraph URLs + * @returns The staking subgraph URL with API key if available + */ +export const getStakingSubgraphUrl = (networkData: NetworkData) => { + const stakingSubgraphUrl = networkData.stakingSubgraphUrl + ? networkData.stakingSubgraphUrl + : networkData.subgraphUrl; + const stakingSubgraphUrlApiKey = networkData.stakingSubgraphUrlApiKey + ? networkData.stakingSubgraphUrlApiKey + : networkData.subgraphUrlApiKey; + + if (process.env.SUBGRAPH_API_KEY) { + return stakingSubgraphUrlApiKey; + } + + if (networkData.chainId !== ChainId.LOCALHOST) { + // eslint-disable-next-line no-console + console.warn(WarnSubgraphApiKeyNotProvided); + } + + return stakingSubgraphUrl; +}; + /** * Converts a Date object to Unix timestamp (seconds since epoch). * diff --git a/packages/sdk/typescript/human-protocol-sdk/test/operator.test.ts b/packages/sdk/typescript/human-protocol-sdk/test/operator.test.ts index 454a6bfd48..1b905e25ff 100644 --- a/packages/sdk/typescript/human-protocol-sdk/test/operator.test.ts +++ b/packages/sdk/typescript/human-protocol-sdk/test/operator.test.ts @@ -35,6 +35,7 @@ describe('OperatorUtils', () => { const mockOperatorSubgraph: IOperatorSubgraph = { id: stakerAddress, address: stakerAddress, + stakedAmount: ethers.parseEther('100').toString(), amountJobsProcessed: ethers.parseEther('25').toString(), jobTypes: ['type1', 'type2'], registrationNeeded: true, @@ -45,14 +46,6 @@ describe('OperatorUtils', () => { address: '0x01', }, ], - staker: { - stakedAmount: ethers.parseEther('100').toString(), - lockedAmount: ethers.parseEther('25').toString(), - lockedUntilTimestamp: '0', - withdrawnAmount: ethers.parseEther('25').toString(), - slashedAmount: ethers.parseEther('25').toString(), - lastDepositTimestamp: '0', - }, category: null, fee: null, name: null, @@ -72,10 +65,6 @@ describe('OperatorUtils', () => { reputationNetworks: ['0x01'], chainId: ChainId.LOCALHOST, stakedAmount: ethers.parseEther('100'), - lockedAmount: ethers.parseEther('25'), - lockedUntilTimestamp: 0, - withdrawnAmount: ethers.parseEther('25'), - slashedAmount: ethers.parseEther('25'), role: null, fee: null, publicKey: null, diff --git a/packages/sdk/typescript/human-protocol-sdk/test/staking.test.ts b/packages/sdk/typescript/human-protocol-sdk/test/staking.test.ts index f4bbfbec42..dd9626639e 100644 --- a/packages/sdk/typescript/human-protocol-sdk/test/staking.test.ts +++ b/packages/sdk/typescript/human-protocol-sdk/test/staking.test.ts @@ -694,7 +694,7 @@ describe('StakingUtils', () => { ); expect(gqlFetchSpy).toHaveBeenCalledWith( - NETWORKS[ChainId.LOCALHOST]?.subgraphUrl, + NETWORKS[ChainId.LOCALHOST]?.stakingSubgraphUrl, expect.anything(), { id: stakerAddress.toLowerCase() }, undefined @@ -776,7 +776,7 @@ describe('StakingUtils', () => { const result = await StakingUtils.getStakers(filter); expect(gqlFetchSpy).toHaveBeenCalledWith( - NETWORKS[ChainId.LOCALHOST]?.subgraphUrl, + NETWORKS[ChainId.LOCALHOST]?.stakingSubgraphUrl, expect.anything(), expect.objectContaining({ minStakedAmount: undefined, diff --git a/packages/sdk/typescript/human-protocol-sdk/test/utils.test.ts b/packages/sdk/typescript/human-protocol-sdk/test/utils.test.ts index ac9d4cb0cf..ec62b3ff2c 100644 --- a/packages/sdk/typescript/human-protocol-sdk/test/utils.test.ts +++ b/packages/sdk/typescript/human-protocol-sdk/test/utils.test.ts @@ -22,6 +22,7 @@ import { } from '../src/error'; import { getSubgraphUrl, + getStakingSubgraphUrl, getUnixTimestamp, customGqlFetch, isIndexerError, @@ -104,6 +105,37 @@ describe('getSubgraphUrl', () => { }); }); +describe('getStakingSubgraphUrl', () => { + const networkData = NETWORKS[ChainId.LOCALHOST]!; + + test('returns stakingSubgraphUrl if no API key', () => { + delete process.env.SUBGRAPH_API_KEY; + expect(getStakingSubgraphUrl(networkData)).toBe( + networkData.stakingSubgraphUrl + ); + }); + + test('falls back to subgraphUrl when staking subgraph URL is not set', () => { + delete process.env.SUBGRAPH_API_KEY; + const url = getStakingSubgraphUrl({ + ...networkData, + stakingSubgraphUrl: undefined, + }); + expect(url).toBe(networkData.subgraphUrl); + }); + + test('returns stakingSubgraphUrlApiKey with API key if present', () => { + process.env.SUBGRAPH_API_KEY = 'real-key'; + const url = getStakingSubgraphUrl({ + ...networkData, + stakingSubgraphUrlApiKey: + 'http://localhost:8000/subgraphs/name/humanprotocol/staking-localhost?key=real-key', + }); + expect(url).toContain('real-key'); + delete process.env.SUBGRAPH_API_KEY; + }); +}); + describe('throwError', () => { test.each([ [ diff --git a/packages/sdk/typescript/subgraph/.gitignore b/packages/sdk/typescript/subgraph/.gitignore index 88babc7c01..17073fb19c 100644 --- a/packages/sdk/typescript/subgraph/.gitignore +++ b/packages/sdk/typescript/subgraph/.gitignore @@ -12,5 +12,4 @@ tests/.bin tests/.latest.json src/mapping/HMToken.ts -src/mapping/Staking.ts -src/mapping/Escrow.ts \ No newline at end of file +src/mapping/Escrow.ts diff --git a/packages/sdk/typescript/subgraph/package.json b/packages/sdk/typescript/subgraph/package.json index 50e13ffc6b..0a5488e720 100644 --- a/packages/sdk/typescript/subgraph/package.json +++ b/packages/sdk/typescript/subgraph/package.json @@ -8,10 +8,9 @@ ], "scripts": { "clean": "rm -rf build generated subgraph.yaml", - "generate": "mustache ./config/$NETWORK.json template.yaml > subgraph.yaml && yarn generate-escrow && yarn generate-hmt && yarn generate-staking && graph codegen", - "generate-escrow": "mustache ./config/$NETWORK.json src/mapping/EscrowTemplate.ts > src/mapping/Escrow.ts", - "generate-hmt": "mustache ./config/$NETWORK.json src/mapping/HMTokenTemplate.ts > src/mapping/HMToken.ts", - "generate-staking": "mustache ./config/$NETWORK.json src/mapping/StakingTemplate.ts > src/mapping/Staking.ts", + "generate": "sh -c 'NETWORK=${NETWORK:-polygon}; mustache ./config/$NETWORK.json template.yaml > subgraph.yaml && yarn generate-escrow && yarn generate-hmt && graph codegen'", + "generate-escrow": "sh -c 'NETWORK=${NETWORK:-polygon}; mustache ./config/$NETWORK.json src/mapping/EscrowTemplate.ts > src/mapping/Escrow.ts'", + "generate-hmt": "sh -c 'NETWORK=${NETWORK:-polygon}; mustache ./config/$NETWORK.json src/mapping/HMTokenTemplate.ts > src/mapping/HMToken.ts'", "codegen": "graph codegen", "build": "graph build", "test": "NETWORK=polygon yarn generate && graph test", diff --git a/packages/sdk/typescript/subgraph/schema.graphql b/packages/sdk/typescript/subgraph/schema.graphql index e4d122605c..9eae9ce8b5 100644 --- a/packages/sdk/typescript/subgraph/schema.graphql +++ b/packages/sdk/typescript/subgraph/schema.graphql @@ -32,6 +32,7 @@ type UniqueReceiver @entity(immutable: false) { type Operator @entity(immutable: false) { id: Bytes! address: Bytes! + stakedAmount: BigInt! amountJobsProcessed: BigInt! role: String fee: BigInt @@ -47,8 +48,6 @@ type Operator @entity(immutable: false) { name: String category: String - - staker: Staker @derivedFrom(field: "operator") } type OperatorURL @entity(immutable: false) { @@ -255,63 +254,6 @@ type KVStore @entity(immutable: false) { value: String! } -# Staking -type Staker @entity(immutable: false) { - id: Bytes! - address: Bytes! # address - stakedAmount: BigInt! - lockedAmount: BigInt! - withdrawnAmount: BigInt! - slashedAmount: BigInt! - lockedUntilTimestamp: BigInt! - lastDepositTimestamp: BigInt! - - operator: Operator -} - -type StakeDepositedEvent @entity(immutable: true) { - id: Bytes! - block: BigInt! - timestamp: BigInt! - txHash: Bytes! - - staker: Bytes! # address - amount: BigInt! -} - -type StakeLockedEvent @entity(immutable: true) { - id: Bytes! - block: BigInt! - timestamp: BigInt! - txHash: Bytes! - - staker: Bytes! # address - amount: BigInt! - lockedUntilTimestamp: BigInt! -} - -type StakeWithdrawnEvent @entity(immutable: true) { - id: Bytes! - block: BigInt! - timestamp: BigInt! - txHash: Bytes! - - staker: Bytes! # address - amount: BigInt! -} - -type StakeSlashedEvent @entity(immutable: true) { - id: Bytes! - block: BigInt! - timestamp: BigInt! - txHash: Bytes! - - escrowAddress: Bytes! # address - staker: Bytes! # address - slashRequester: Bytes! # address - amount: BigInt! -} - ################################################## # Statistics # ################################################## diff --git a/packages/sdk/typescript/subgraph/src/mapping/KVStore.ts b/packages/sdk/typescript/subgraph/src/mapping/KVStore.ts index 7cf03a445d..3c0b47bf94 100644 --- a/packages/sdk/typescript/subgraph/src/mapping/KVStore.ts +++ b/packages/sdk/typescript/subgraph/src/mapping/KVStore.ts @@ -12,7 +12,6 @@ import { Operator, OperatorURL, ReputationNetwork, - Staker, } from '../../generated/schema'; import { isValidEthAddress } from './utils/ethAdrress'; import { toEventId } from './utils/event'; @@ -26,13 +25,8 @@ export function createOrLoadOperator(address: Address): Operator { if (!operator) { operator = new Operator(address); operator.address = address; + operator.stakedAmount = ZERO_BI; operator.amountJobsProcessed = ZERO_BI; - - const staker = Staker.load(address); - if (staker) { - staker.operator = operator.id; - staker.save(); - } operator.save(); } diff --git a/packages/sdk/typescript/subgraph/src/mapping/Staking.ts b/packages/sdk/typescript/subgraph/src/mapping/Staking.ts new file mode 100644 index 0000000000..10e390d678 --- /dev/null +++ b/packages/sdk/typescript/subgraph/src/mapping/Staking.ts @@ -0,0 +1,24 @@ +import { + StakeDeposited, + StakeSlashed, + StakeWithdrawn, +} from '../../generated/Staking/Staking'; +import { createOrLoadOperator } from './KVStore'; + +export function handleStakeDeposited(event: StakeDeposited): void { + const operator = createOrLoadOperator(event.params.staker); + operator.stakedAmount = operator.stakedAmount.plus(event.params.tokens); + operator.save(); +} + +export function handleStakeWithdrawn(event: StakeWithdrawn): void { + const operator = createOrLoadOperator(event.params.staker); + operator.stakedAmount = operator.stakedAmount.minus(event.params.tokens); + operator.save(); +} + +export function handleStakeSlashed(event: StakeSlashed): void { + const operator = createOrLoadOperator(event.params.staker); + operator.stakedAmount = operator.stakedAmount.minus(event.params.tokens); + operator.save(); +} diff --git a/packages/sdk/typescript/subgraph/template.yaml b/packages/sdk/typescript/subgraph/template.yaml index fa3b45fd4f..27c24d8568 100644 --- a/packages/sdk/typescript/subgraph/template.yaml +++ b/packages/sdk/typescript/subgraph/template.yaml @@ -109,29 +109,17 @@ dataSources: apiVersion: 0.0.7 language: wasm/assemblyscript entities: - - StakeDepositedEvent - - StakeLockedEvent - - StakeWithdrawnEvent - - StakeSlashedEvent - - FeeWithdrawn - Operator - - OperatorStatistics - - Transaction - - InternalTransaction abis: - name: Staking file: '{{{ Staking.abi }}}' eventHandlers: - event: StakeDeposited(indexed address,uint256) handler: handleStakeDeposited - - event: StakeLocked(indexed address,uint256,uint256) - handler: handleStakeLocked - event: StakeWithdrawn(indexed address,uint256) handler: handleStakeWithdrawn - event: StakeSlashed(indexed address,uint256,indexed address,address) handler: handleStakeSlashed - - event: FeeWithdrawn(uint256) - handler: handleFeeWithdrawn file: ./src/mapping/Staking.ts {{ #LegacyEscrowFactory }} - kind: ethereum @@ -244,4 +232,4 @@ templates: handler: handlePending - event: BulkTransfer(indexed uint256,uint256) handler: handleBulkTransfer -{{ /LegacyEscrow }} \ No newline at end of file +{{ /LegacyEscrow }} diff --git a/packages/sdk/typescript/subgraph/tests/escrow/escrow.test.ts b/packages/sdk/typescript/subgraph/tests/escrow/escrow.test.ts index 9fabb437ba..6df68e0505 100644 --- a/packages/sdk/typescript/subgraph/tests/escrow/escrow.test.ts +++ b/packages/sdk/typescript/subgraph/tests/escrow/escrow.test.ts @@ -115,18 +115,21 @@ describe('Escrow', () => { const reputationOperator = new Operator(reputationOracleAddress); reputationOperator.address = reputationOracleAddress; + reputationOperator.stakedAmount = ZERO_BI; reputationOperator.amountJobsProcessed = ZERO_BI; reputationOperator.fee = BigInt.fromI32(11); reputationOperator.save(); const recordingOperator = new Operator(recordingOracleAddress); recordingOperator.address = recordingOracleAddress; + recordingOperator.stakedAmount = ZERO_BI; recordingOperator.amountJobsProcessed = ZERO_BI; recordingOperator.fee = BigInt.fromI32(22); recordingOperator.save(); const exchangeOperator = new Operator(exchangeOracleAddress); exchangeOperator.address = exchangeOracleAddress; + exchangeOperator.stakedAmount = ZERO_BI; exchangeOperator.amountJobsProcessed = ZERO_BI; exchangeOperator.fee = BigInt.fromI32(33); exchangeOperator.save(); diff --git a/packages/sdk/typescript/subgraph/tests/kvstore/kvstore.test.ts b/packages/sdk/typescript/subgraph/tests/kvstore/kvstore.test.ts index 5a2df8a98d..6ea5886744 100644 --- a/packages/sdk/typescript/subgraph/tests/kvstore/kvstore.test.ts +++ b/packages/sdk/typescript/subgraph/tests/kvstore/kvstore.test.ts @@ -1,4 +1,4 @@ -import { Address, BigInt, DataSourceContext } from '@graphprotocol/graph-ts'; +import { BigInt, DataSourceContext } from '@graphprotocol/graph-ts'; import { afterEach, assert, @@ -11,7 +11,6 @@ import { import { Operator } from '../../generated/schema'; import { handleDataSaved } from '../../src/mapping/KVStore'; -import { createOrLoadStaker } from '../../src/mapping/Staking'; import { toEventId } from '../../src/mapping/utils/event'; import { toBytes } from '../../src/mapping/utils/string'; import { createDataSavedEvent } from './fixtures'; @@ -794,25 +793,4 @@ describe('KVStore', () => { 'market_making' ); }); - - test('Should assign operator to Staker if Staker exists before Operator (KVStore)', () => { - const stakerAddress = '0xD979105297fB0eee83F7433fC09279cb5B94fFC7'; - - const staker = createOrLoadStaker(Address.fromString(stakerAddress)); - - const operatorEvent = createDataSavedEvent( - stakerAddress, - 'role', - 'Operator', - BigInt.fromI32(11) - ); - handleDataSaved(operatorEvent); - - assert.fieldEquals( - 'Staker', - staker.address.toHex(), - 'operator', - staker.address.toHex() - ); - }); }); diff --git a/packages/sdk/typescript/subgraph/tests/staking/fixtures.ts b/packages/sdk/typescript/subgraph/tests/staking/fixtures.ts deleted file mode 100644 index cc77767183..0000000000 --- a/packages/sdk/typescript/subgraph/tests/staking/fixtures.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { Address, BigInt, dataSource, ethereum } from '@graphprotocol/graph-ts'; -import { newMockEvent } from 'matchstick-as/assembly/index'; -import { - FeeWithdrawn, - StakeDeposited, - StakeLocked, - StakeSlashed, - StakeWithdrawn, -} from '../../generated/Staking/Staking'; -import { generateUniqueHash } from '../../tests/utils'; - -export function createStakeDepositedEvent( - staker: string, - tokens: i32, - timestamp: BigInt -): StakeDeposited { - const newStakeDepositedEvent = changetype(newMockEvent()); - newStakeDepositedEvent.transaction.hash = generateUniqueHash( - staker, - timestamp, - newStakeDepositedEvent.transaction.nonce - ); - newStakeDepositedEvent.block.timestamp = timestamp; - newStakeDepositedEvent.transaction.from = Address.fromString(staker); - newStakeDepositedEvent.transaction.to = Address.fromString( - dataSource.address().toHexString() - ); - - newStakeDepositedEvent.parameters = []; - newStakeDepositedEvent.parameters.push( - new ethereum.EventParam( - 'staker', - ethereum.Value.fromAddress(Address.fromString(staker)) - ) - ); - newStakeDepositedEvent.parameters.push( - new ethereum.EventParam('tokens', ethereum.Value.fromI32(tokens)) - ); - - return newStakeDepositedEvent; -} - -export function createStakeLockedEvent( - staker: string, - tokens: i32, - until: i32, - timestamp: BigInt -): StakeLocked { - const newStakeLockedEvent = changetype(newMockEvent()); - newStakeLockedEvent.transaction.hash = generateUniqueHash( - staker, - timestamp, - newStakeLockedEvent.transaction.nonce - ); - newStakeLockedEvent.transaction.from = Address.fromString(staker); - newStakeLockedEvent.transaction.to = Address.fromString( - dataSource.address().toHexString() - ); - newStakeLockedEvent.block.timestamp = timestamp; - - newStakeLockedEvent.parameters = []; - newStakeLockedEvent.parameters.push( - new ethereum.EventParam( - 'staker', - ethereum.Value.fromAddress(Address.fromString(staker)) - ) - ); - newStakeLockedEvent.parameters.push( - new ethereum.EventParam('tokens', ethereum.Value.fromI32(tokens)) - ); - newStakeLockedEvent.parameters.push( - new ethereum.EventParam('until', ethereum.Value.fromI32(until)) - ); - - return newStakeLockedEvent; -} - -export function createStakeWithdrawnEvent( - staker: string, - tokens: i32, - timestamp: BigInt -): StakeWithdrawn { - const newStakeWithdrawnEvent = changetype(newMockEvent()); - newStakeWithdrawnEvent.transaction.hash = generateUniqueHash( - staker, - timestamp, - newStakeWithdrawnEvent.transaction.nonce - ); - - newStakeWithdrawnEvent.transaction.from = Address.fromString(staker); - newStakeWithdrawnEvent.transaction.to = Address.fromString( - dataSource.address().toHexString() - ); - newStakeWithdrawnEvent.block.timestamp = timestamp; - - newStakeWithdrawnEvent.parameters = []; - newStakeWithdrawnEvent.parameters.push( - new ethereum.EventParam( - 'staker', - ethereum.Value.fromAddress(Address.fromString(staker)) - ) - ); - newStakeWithdrawnEvent.parameters.push( - new ethereum.EventParam('tokens', ethereum.Value.fromI32(tokens)) - ); - - return newStakeWithdrawnEvent; -} - -export function createStakeSlashedEvent( - staker: string, - tokens: i32, - escrowAddress: string, - slashRequester: string, - timestamp: BigInt -): StakeSlashed { - const newStakeSlashedEvent = changetype(newMockEvent()); - newStakeSlashedEvent.transaction.hash = generateUniqueHash( - staker, - timestamp, - newStakeSlashedEvent.transaction.nonce - ); - - newStakeSlashedEvent.transaction.from = Address.fromString(staker); - newStakeSlashedEvent.transaction.to = Address.fromString( - dataSource.address().toHexString() - ); - newStakeSlashedEvent.block.timestamp = timestamp; - - newStakeSlashedEvent.parameters = []; - newStakeSlashedEvent.parameters.push( - new ethereum.EventParam( - 'staker', - ethereum.Value.fromAddress(Address.fromString(staker)) - ) - ); - newStakeSlashedEvent.parameters.push( - new ethereum.EventParam('tokens', ethereum.Value.fromI32(tokens)) - ); - newStakeSlashedEvent.parameters.push( - new ethereum.EventParam( - 'escrowAddress', - ethereum.Value.fromAddress(Address.fromString(escrowAddress)) - ) - ); - newStakeSlashedEvent.parameters.push( - new ethereum.EventParam( - 'slashRequester', - ethereum.Value.fromAddress(Address.fromString(slashRequester)) - ) - ); - - return newStakeSlashedEvent; -} - -export function createFeeWithdrawnEvent( - amount: i32, - timestamp: BigInt -): FeeWithdrawn { - const newFeeWithdrawnEvent = changetype(newMockEvent()); - newFeeWithdrawnEvent.transaction.hash = generateUniqueHash( - amount.toString(), - timestamp, - newFeeWithdrawnEvent.transaction.nonce - ); - - newFeeWithdrawnEvent.transaction.to = Address.fromString( - dataSource.address().toHexString() - ); - newFeeWithdrawnEvent.block.timestamp = timestamp; - - newFeeWithdrawnEvent.parameters = []; - newFeeWithdrawnEvent.parameters.push( - new ethereum.EventParam('amount', ethereum.Value.fromI32(amount)) - ); - - return newFeeWithdrawnEvent; -} diff --git a/packages/sdk/typescript/subgraph/tests/staking/staking.test.ts b/packages/sdk/typescript/subgraph/tests/staking/staking.test.ts deleted file mode 100644 index a935e329b6..0000000000 --- a/packages/sdk/typescript/subgraph/tests/staking/staking.test.ts +++ /dev/null @@ -1,797 +0,0 @@ -import { Address, BigInt, DataSourceContext } from '@graphprotocol/graph-ts'; -import { - describe, - test, - assert, - clearStore, - beforeAll, - afterAll, - dataSourceMock, -} from 'matchstick-as/assembly'; - -import { Escrow } from '../../generated/schema'; -import { - handleStakeDeposited, - handleStakeLocked, - handleStakeSlashed, - handleStakeWithdrawn, - handleFeeWithdrawn, - TOKEN_ADDRESS, -} from '../../src/mapping/Staking'; -import { toEventId } from '../../src/mapping/utils/event'; -import { ZERO_BI } from '../../src/mapping/utils/number'; -import { - createFeeWithdrawnEvent, - createStakeDepositedEvent, - createStakeLockedEvent, - createStakeSlashedEvent, - createStakeWithdrawnEvent, -} from './fixtures'; -import { createOrLoadOperator } from '../../src/mapping/KVStore'; - -const stakingAddressString = '0xa16081f360e3847006db660bae1c6d1b2e17ffaa'; -const escrow1AddressString = '0xD979105297fB0eee83F7433fC09279cb5B94fFC7'; -const escrow1Address = Address.fromString(escrow1AddressString); -const escrow2AddressString = '0x92a2eEF7Ff696BCef98957a0189872680600a95A'; -const escrow2Address = Address.fromString(escrow2AddressString); - -describe('Staking', () => { - beforeAll(() => { - dataSourceMock.setReturnValues( - stakingAddressString, - 'rinkeby', - new DataSourceContext() - ); - const escrow1 = new Escrow(escrow1Address); - escrow1.address = escrow1Address; - escrow1.token = Address.zero(); - escrow1.factoryAddress = Address.zero(); - escrow1.launcher = Address.zero(); - escrow1.canceler = Address.zero(); - escrow1.count = ZERO_BI; - escrow1.balance = ZERO_BI; - escrow1.totalFundedAmount = ZERO_BI; - escrow1.amountPaid = ZERO_BI; - escrow1.status = 'Launched'; - escrow1.createdAt = BigInt.fromI32(0); - escrow1.save(); - - const escrow2 = new Escrow(escrow2Address); - escrow2.address = escrow2Address; - escrow2.token = Address.zero(); - escrow2.factoryAddress = Address.zero(); - escrow2.launcher = Address.zero(); - escrow2.canceler = Address.zero(); - escrow2.count = ZERO_BI; - escrow2.balance = ZERO_BI; - escrow2.totalFundedAmount = ZERO_BI; - escrow2.amountPaid = ZERO_BI; - escrow2.status = 'Launched'; - escrow2.createdAt = BigInt.fromI32(0); - escrow2.save(); - }); - - afterAll(() => { - clearStore(); - }); - - test('Should properly index StakingDeposited events', () => { - const data1 = createStakeDepositedEvent( - '0xD979105297fB0eee83F7433fC09279cb5B94fFC6', - 100, - BigInt.fromI32(10) - ); - const data2 = createStakeDepositedEvent( - '0x92a2eEF7Ff696BCef98957a0189872680600a959', - 200, - BigInt.fromI32(11) - ); - - handleStakeDeposited(data1); - handleStakeDeposited(data2); - - const id1 = toEventId(data1).toHex(); - const id2 = toEventId(data2).toHex(); - - // Data 1 - assert.fieldEquals( - 'StakeDepositedEvent', - id1, - 'block', - data1.block.number.toString() - ); - assert.fieldEquals( - 'StakeDepositedEvent', - id1, - 'timestamp', - data1.block.timestamp.toString() - ); - assert.fieldEquals( - 'StakeDepositedEvent', - id1, - 'txHash', - data1.transaction.hash.toHex() - ); - assert.fieldEquals( - 'StakeDepositedEvent', - id1, - 'staker', - data1.params.staker.toHex() - ); - assert.fieldEquals('StakeDepositedEvent', id1, 'amount', '100'); - - // Data 2 - assert.fieldEquals( - 'StakeDepositedEvent', - id2, - 'block', - data2.block.number.toString() - ); - assert.fieldEquals( - 'StakeDepositedEvent', - id2, - 'timestamp', - data2.block.timestamp.toString() - ); - assert.fieldEquals( - 'StakeDepositedEvent', - id2, - 'txHash', - data2.transaction.hash.toHex() - ); - assert.fieldEquals( - 'StakeDepositedEvent', - id2, - 'staker', - data2.params.staker.toHex() - ); - assert.fieldEquals('StakeDepositedEvent', id2, 'amount', '200'); - - // Operator - assert.fieldEquals( - 'Staker', - data1.params.staker.toHex(), - 'stakedAmount', - '100' - ); - assert.fieldEquals( - 'Staker', - data2.params.staker.toHex(), - 'stakedAmount', - '200' - ); - - // Transaction - assert.fieldEquals( - 'Transaction', - data2.transaction.hash.toHex(), - 'txHash', - data2.transaction.hash.toHex() - ); - assert.fieldEquals( - 'Transaction', - data2.transaction.hash.toHex(), - 'method', - 'stake' - ); - assert.fieldEquals( - 'Transaction', - data2.transaction.hash.toHex(), - 'block', - data2.block.number.toString() - ); - assert.fieldEquals( - 'Transaction', - data2.transaction.hash.toHex(), - 'from', - data2.transaction.from.toHex() - ); - assert.fieldEquals( - 'Transaction', - data2.transaction.hash.toHex(), - 'value', - '200' - ); - assert.fieldEquals( - 'Transaction', - data2.transaction.hash.toHex(), - 'token', - TOKEN_ADDRESS.toHexString() - ); - - assert.fieldEquals( - 'Staker', - data1.params.staker.toHex(), - 'address', - data1.params.staker.toHex() - ); - assert.notInStore('Operator', data1.params.staker.toHex()); - }); - - test('Should properly index StakeLocked events', () => { - const data1 = createStakeLockedEvent( - '0xD979105297fB0eee83F7433fC09279cb5B94fFC6', - 50, - 30, - BigInt.fromI32(20) - ); - const data2 = createStakeLockedEvent( - '0x92a2eEF7Ff696BCef98957a0189872680600a959', - 100, - 31, - BigInt.fromI32(21) - ); - - handleStakeLocked(data1); - handleStakeLocked(data2); - - const id1 = toEventId(data1).toHex(); - const id2 = toEventId(data2).toHex(); - - // Data 1 - assert.fieldEquals( - 'StakeLockedEvent', - id1, - 'block', - data1.block.number.toString() - ); - assert.fieldEquals( - 'StakeLockedEvent', - id1, - 'timestamp', - data1.block.timestamp.toString() - ); - assert.fieldEquals( - 'StakeLockedEvent', - id1, - 'txHash', - data1.transaction.hash.toHex() - ); - assert.fieldEquals( - 'StakeLockedEvent', - id1, - 'staker', - data1.params.staker.toHex() - ); - assert.fieldEquals('StakeLockedEvent', id1, 'amount', '50'); - assert.fieldEquals('StakeLockedEvent', id1, 'lockedUntilTimestamp', '30'); - - // Data 2 - assert.fieldEquals( - 'StakeLockedEvent', - id2, - 'block', - data2.block.number.toString() - ); - assert.fieldEquals( - 'StakeLockedEvent', - id2, - 'timestamp', - data2.block.timestamp.toString() - ); - assert.fieldEquals( - 'StakeLockedEvent', - id2, - 'txHash', - data2.transaction.hash.toHex() - ); - assert.fieldEquals( - 'StakeLockedEvent', - id2, - 'staker', - data2.params.staker.toHex() - ); - assert.fieldEquals('StakeLockedEvent', id2, 'amount', '100'); - assert.fieldEquals('StakeLockedEvent', id2, 'lockedUntilTimestamp', '31'); - - // Operator - assert.fieldEquals( - 'Staker', - data1.params.staker.toHex(), - 'stakedAmount', - '100' - ); - assert.fieldEquals( - 'Staker', - data1.params.staker.toHex(), - 'lockedAmount', - '50' - ); - assert.fieldEquals( - 'Staker', - data1.params.staker.toHex(), - 'lockedUntilTimestamp', - '30' - ); - - assert.fieldEquals( - 'Staker', - data2.params.staker.toHex(), - 'stakedAmount', - '200' - ); - assert.fieldEquals( - 'Staker', - data2.params.staker.toHex(), - 'lockedAmount', - '100' - ); - assert.fieldEquals( - 'Staker', - data2.params.staker.toHex(), - 'lockedUntilTimestamp', - '31' - ); - - // Transaction - assert.fieldEquals( - 'Transaction', - data2.transaction.hash.toHex(), - 'txHash', - data2.transaction.hash.toHex() - ); - assert.fieldEquals( - 'Transaction', - data2.transaction.hash.toHex(), - 'method', - 'unstake' - ); - assert.fieldEquals( - 'Transaction', - data2.transaction.hash.toHex(), - 'block', - data2.block.number.toString() - ); - assert.fieldEquals( - 'Transaction', - data2.transaction.hash.toHex(), - 'from', - data2.transaction.from.toHex() - ); - assert.fieldEquals( - 'Transaction', - data2.transaction.hash.toHex(), - 'value', - '100' - ); - assert.fieldEquals( - 'Transaction', - data2.transaction.hash.toHex(), - 'token', - TOKEN_ADDRESS.toHexString() - ); - }); - - test('Should properly index StakeWithdrawn events', () => { - const data1 = createStakeWithdrawnEvent( - '0xD979105297fB0eee83F7433fC09279cb5B94fFC6', - 30, - BigInt.fromI32(40) - ); - const data2 = createStakeWithdrawnEvent( - '0x92a2eEF7Ff696BCef98957a0189872680600a959', - 100, - BigInt.fromI32(41) - ); - - handleStakeWithdrawn(data1); - handleStakeWithdrawn(data2); - - const id1 = toEventId(data1).toHex(); - const id2 = toEventId(data2).toHex(); - - // Data 1 - assert.fieldEquals( - 'StakeWithdrawnEvent', - id1, - 'block', - data1.block.number.toString() - ); - assert.fieldEquals( - 'StakeWithdrawnEvent', - id1, - 'timestamp', - data1.block.timestamp.toString() - ); - assert.fieldEquals( - 'StakeWithdrawnEvent', - id1, - 'txHash', - data1.transaction.hash.toHex() - ); - assert.fieldEquals( - 'StakeWithdrawnEvent', - id1, - 'staker', - data1.params.staker.toHex() - ); - assert.fieldEquals('StakeWithdrawnEvent', id1, 'amount', '30'); - - // Data 2 - assert.fieldEquals( - 'StakeWithdrawnEvent', - id2, - 'block', - data2.block.number.toString() - ); - assert.fieldEquals( - 'StakeWithdrawnEvent', - id2, - 'timestamp', - data2.block.timestamp.toString() - ); - assert.fieldEquals( - 'StakeWithdrawnEvent', - id2, - 'txHash', - data2.transaction.hash.toHex() - ); - assert.fieldEquals( - 'StakeWithdrawnEvent', - id2, - 'staker', - data2.params.staker.toHex() - ); - assert.fieldEquals('StakeWithdrawnEvent', id2, 'amount', '100'); - - // Operator - assert.fieldEquals( - 'Staker', - data1.params.staker.toHex(), - 'stakedAmount', - '70' - ); - assert.fieldEquals( - 'Staker', - data1.params.staker.toHex(), - 'lockedAmount', - '20' - ); - assert.fieldEquals( - 'Staker', - data1.params.staker.toHex(), - 'lockedUntilTimestamp', - '30' - ); - assert.fieldEquals( - 'Staker', - data1.params.staker.toHex(), - 'withdrawnAmount', - '30' - ); - - assert.fieldEquals( - 'Staker', - data2.params.staker.toHex(), - 'stakedAmount', - '100' - ); - assert.fieldEquals( - 'Staker', - data2.params.staker.toHex(), - 'lockedAmount', - '0' - ); - assert.fieldEquals( - 'Staker', - data2.params.staker.toHex(), - 'lockedUntilTimestamp', - '0' - ); - assert.fieldEquals( - 'Staker', - data2.params.staker.toHex(), - 'withdrawnAmount', - '100' - ); - - // Transaction - assert.fieldEquals( - 'Transaction', - data2.transaction.hash.toHex(), - 'txHash', - data2.transaction.hash.toHex() - ); - assert.fieldEquals( - 'Transaction', - data2.transaction.hash.toHex(), - 'method', - 'stakeWithdrawn' - ); - assert.fieldEquals( - 'Transaction', - data2.transaction.hash.toHex(), - 'block', - data2.block.number.toString() - ); - assert.fieldEquals( - 'Transaction', - data2.transaction.hash.toHex(), - 'from', - data2.transaction.from.toHex() - ); - assert.fieldEquals( - 'Transaction', - data2.transaction.hash.toHex(), - 'value', - '100' - ); - assert.fieldEquals( - 'Transaction', - data2.transaction.hash.toHex(), - 'token', - TOKEN_ADDRESS.toHexString() - ); - }); - - test('Should properly index StakeSlashed events', () => { - const data1 = createStakeSlashedEvent( - '0xD979105297fB0eee83F7433fC09279cb5B94fFC6', - 10, - escrow1AddressString, - '0xD979105297fB0eee83F7433fC09279cb5B94fFC8', - BigInt.fromI32(60) - ); - const data2 = createStakeSlashedEvent( - '0x92a2eEF7Ff696BCef98957a0189872680600a959', - 10, - escrow2AddressString, - '0x92a2eEF7Ff696BCef98957a0189872680600a95B', - BigInt.fromI32(61) - ); - - handleStakeSlashed(data1); - handleStakeSlashed(data2); - - const id1 = toEventId(data1).toHex(); - const id2 = toEventId(data2).toHex(); - - // Data 1 - assert.fieldEquals( - 'StakeSlashedEvent', - id1, - 'block', - data1.block.number.toString() - ); - assert.fieldEquals( - 'StakeSlashedEvent', - id1, - 'timestamp', - data1.block.timestamp.toString() - ); - assert.fieldEquals( - 'StakeSlashedEvent', - id1, - 'txHash', - data1.transaction.hash.toHex() - ); - assert.fieldEquals( - 'StakeSlashedEvent', - id1, - 'staker', - data1.params.staker.toHex() - ); - assert.fieldEquals('StakeSlashedEvent', id1, 'amount', '10'); - assert.fieldEquals( - 'StakeSlashedEvent', - id1, - 'escrowAddress', - data1.params.escrowAddress.toHex() - ); - assert.fieldEquals( - 'StakeSlashedEvent', - id1, - 'slashRequester', - data1.params.slashRequester.toHex() - ); - - // Data 2 - assert.fieldEquals( - 'StakeSlashedEvent', - id2, - 'block', - data2.block.number.toString() - ); - assert.fieldEquals( - 'StakeSlashedEvent', - id2, - 'timestamp', - data2.block.timestamp.toString() - ); - assert.fieldEquals( - 'StakeSlashedEvent', - id2, - 'txHash', - data2.transaction.hash.toHex() - ); - assert.fieldEquals( - 'StakeSlashedEvent', - id2, - 'staker', - data2.params.staker.toHex() - ); - assert.fieldEquals('StakeSlashedEvent', id2, 'amount', '10'); - assert.fieldEquals( - 'StakeSlashedEvent', - id2, - 'escrowAddress', - data2.params.escrowAddress.toHex() - ); - assert.fieldEquals( - 'StakeSlashedEvent', - id2, - 'slashRequester', - data2.params.slashRequester.toHex() - ); - - // Operator - assert.fieldEquals( - 'Staker', - data1.params.staker.toHex(), - 'stakedAmount', - '60' - ); - assert.fieldEquals( - 'Staker', - data1.params.staker.toHex(), - 'lockedAmount', - '20' - ); - assert.fieldEquals( - 'Staker', - data1.params.staker.toHex(), - 'lockedUntilTimestamp', - '30' - ); - assert.fieldEquals( - 'Staker', - data1.params.staker.toHex(), - 'withdrawnAmount', - '30' - ); - assert.fieldEquals( - 'Staker', - data1.params.staker.toHex(), - 'slashedAmount', - '10' - ); - - assert.fieldEquals( - 'Staker', - data2.params.staker.toHex(), - 'stakedAmount', - '90' - ); - assert.fieldEquals( - 'Staker', - data2.params.staker.toHex(), - 'lockedAmount', - '0' - ); - assert.fieldEquals( - 'Staker', - data2.params.staker.toHex(), - 'lockedUntilTimestamp', - '0' - ); - assert.fieldEquals( - 'Staker', - data2.params.staker.toHex(), - 'withdrawnAmount', - '100' - ); - assert.fieldEquals( - 'Staker', - data2.params.staker.toHex(), - 'slashedAmount', - '10' - ); - - // Transaction - assert.fieldEquals( - 'Transaction', - data2.transaction.hash.toHex(), - 'txHash', - data2.transaction.hash.toHex() - ); - assert.fieldEquals( - 'Transaction', - data2.transaction.hash.toHex(), - 'method', - 'slash' - ); - assert.fieldEquals( - 'Transaction', - data2.transaction.hash.toHex(), - 'block', - data2.block.number.toString() - ); - assert.fieldEquals( - 'Transaction', - data2.transaction.hash.toHex(), - 'from', - data2.transaction.from.toHex() - ); - assert.fieldEquals( - 'Transaction', - data2.transaction.hash.toHex(), - 'value', - '10' - ); - assert.fieldEquals( - 'Transaction', - data2.transaction.hash.toHex(), - 'token', - TOKEN_ADDRESS.toHexString() - ); - assert.fieldEquals( - 'Transaction', - data2.transaction.hash.toHex(), - 'escrow', - escrow2Address.toHexString() - ); - }); - - test('Should properly create transactions for FeeWithdrawn events', () => { - const data = createFeeWithdrawnEvent(10, BigInt.fromI32(60)); - - handleFeeWithdrawn(data); - - // Transaction - assert.fieldEquals( - 'Transaction', - data.transaction.hash.toHex(), - 'txHash', - data.transaction.hash.toHex() - ); - assert.fieldEquals( - 'Transaction', - data.transaction.hash.toHex(), - 'method', - 'withdrawFees' - ); - assert.fieldEquals( - 'Transaction', - data.transaction.hash.toHex(), - 'block', - data.block.number.toString() - ); - assert.fieldEquals( - 'Transaction', - data.transaction.hash.toHex(), - 'to', - stakingAddressString - ); - assert.fieldEquals( - 'Transaction', - data.transaction.hash.toHex(), - 'value', - '10' - ); - assert.fieldEquals( - 'Transaction', - data.transaction.hash.toHex(), - 'token', - TOKEN_ADDRESS.toHexString() - ); - }); - - test('Should associate Staker with Operator if Operator exists before staking', () => { - const stakerAddress = '0xD979105297fB0eee83F7433fC09279cb5B94fFC8'; - const data = createStakeDepositedEvent( - stakerAddress, - 100, - BigInt.fromI32(12) - ); - createOrLoadOperator(Address.fromString(stakerAddress)); - - handleStakeDeposited(data); - - assert.fieldEquals( - 'Staker', - data.params.staker.toHex(), - 'operator', - data.params.staker.toHex() - ); - }); -}); diff --git a/packages/sdk/typescript/subgraphs/staking/.gitignore b/packages/sdk/typescript/subgraphs/staking/.gitignore new file mode 100644 index 0000000000..5f5d92e13e --- /dev/null +++ b/packages/sdk/typescript/subgraphs/staking/.gitignore @@ -0,0 +1,14 @@ +# Graph build +build/ + +# Graph generated +generated/ + +# Mustache generated +subgraph.yaml + +# Graph test generated +tests/.bin +tests/.latest.json + +src/mapping/Staking.ts diff --git a/packages/sdk/typescript/subgraphs/staking/.prettierignore b/packages/sdk/typescript/subgraphs/staking/.prettierignore new file mode 100644 index 0000000000..6610ae1e5e --- /dev/null +++ b/packages/sdk/typescript/subgraphs/staking/.prettierignore @@ -0,0 +1,3 @@ +subgraph.yaml +build +generated diff --git a/packages/sdk/typescript/subgraphs/staking/.prettierrc b/packages/sdk/typescript/subgraphs/staking/.prettierrc new file mode 100644 index 0000000000..c1a6f66713 --- /dev/null +++ b/packages/sdk/typescript/subgraphs/staking/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "es5" +} diff --git a/packages/sdk/typescript/subgraphs/staking/README.md b/packages/sdk/typescript/subgraphs/staking/README.md new file mode 100644 index 0000000000..c2f2fb4b94 --- /dev/null +++ b/packages/sdk/typescript/subgraphs/staking/README.md @@ -0,0 +1,22 @@ +

Human staking subgraph

+ +This package indexes staking entities and staking events only. + +## Generate + +```bash +NETWORK=polygon yarn generate +``` + +## Build + +```bash +yarn build +``` + +## Local deploy + +```bash +yarn create-local +yarn deploy-local +``` diff --git a/packages/sdk/typescript/subgraphs/staking/config/amoy.json b/packages/sdk/typescript/subgraphs/staking/config/amoy.json new file mode 100644 index 0000000000..f97b8172ad --- /dev/null +++ b/packages/sdk/typescript/subgraphs/staking/config/amoy.json @@ -0,0 +1,9 @@ +{ + "network": "polygon-amoy", + "description": "HUMAN staking subgraph on Amoy Testnet", + "Staking": { + "address": "0xffE496683F842a923110415b7278ded3F265f2C5", + "startBlock": 14983952, + "abi": "../../../../../node_modules/@human-protocol/core/abis/Staking.json" + } +} diff --git a/packages/sdk/typescript/subgraphs/staking/config/bsc-testnet.json b/packages/sdk/typescript/subgraphs/staking/config/bsc-testnet.json new file mode 100644 index 0000000000..a0f7021314 --- /dev/null +++ b/packages/sdk/typescript/subgraphs/staking/config/bsc-testnet.json @@ -0,0 +1,9 @@ +{ + "network": "chapel", + "description": "Human staking subgraph on Binance Smart Chain testnet", + "Staking": { + "address": "0xD6D347ba6987519B4e42EcED43dF98eFf5465a23", + "startBlock": 45938762, + "abi": "../../../../../node_modules/@human-protocol/core/abis/Staking.json" + } +} diff --git a/packages/sdk/typescript/subgraphs/staking/config/bsc.json b/packages/sdk/typescript/subgraphs/staking/config/bsc.json new file mode 100644 index 0000000000..dc12210bcb --- /dev/null +++ b/packages/sdk/typescript/subgraphs/staking/config/bsc.json @@ -0,0 +1,9 @@ +{ + "network": "bsc", + "description": "Human staking subgraph on BSC mainnet", + "Staking": { + "address": "0xE24e5C08E28331D24758b69A5E9f383D2bDD1c98", + "startBlock": 45120420, + "abi": "../../../../../node_modules/@human-protocol/core/abis/Staking.json" + } +} diff --git a/packages/sdk/typescript/subgraphs/staking/config/ethereum.json b/packages/sdk/typescript/subgraphs/staking/config/ethereum.json new file mode 100644 index 0000000000..250b52e78c --- /dev/null +++ b/packages/sdk/typescript/subgraphs/staking/config/ethereum.json @@ -0,0 +1,9 @@ +{ + "network": "mainnet", + "description": "Human staking subgraph on Ethereum Mainnet", + "Staking": { + "address": "0xEf6Da3aB52c33925Be3F84038193a7e1331F51E6", + "startBlock": 21464165, + "abi": "../../../../../node_modules/@human-protocol/core/abis/Staking.json" + } +} diff --git a/packages/sdk/typescript/subgraphs/staking/config/localhost.json b/packages/sdk/typescript/subgraphs/staking/config/localhost.json new file mode 100644 index 0000000000..da3109c6f7 --- /dev/null +++ b/packages/sdk/typescript/subgraphs/staking/config/localhost.json @@ -0,0 +1,9 @@ +{ + "network": "localhost", + "description": "Human staking subgraph on localhost", + "Staking": { + "address": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + "startBlock": 3, + "abi": "../../../../../node_modules/@human-protocol/core/abis/Staking.json" + } +} diff --git a/packages/sdk/typescript/subgraphs/staking/config/polygon.json b/packages/sdk/typescript/subgraphs/staking/config/polygon.json new file mode 100644 index 0000000000..7f0b5bafb6 --- /dev/null +++ b/packages/sdk/typescript/subgraphs/staking/config/polygon.json @@ -0,0 +1,9 @@ +{ + "network": "matic", + "description": "Human staking subgraph on Polygon mainnet", + "Staking": { + "address": "0x01D115E9E8bF0C58318793624CC662a030D07F1D", + "startBlock": 65832028, + "abi": "../../../../../node_modules/@human-protocol/core/abis/Staking.json" + } +} diff --git a/packages/sdk/typescript/subgraphs/staking/config/sepolia.json b/packages/sdk/typescript/subgraphs/staking/config/sepolia.json new file mode 100644 index 0000000000..4755d7e895 --- /dev/null +++ b/packages/sdk/typescript/subgraphs/staking/config/sepolia.json @@ -0,0 +1,9 @@ +{ + "network": "sepolia", + "description": "HUMAN staking subgraph on Sepolia Ethereum Testnet", + "Staking": { + "address": "0x2163e3A40032Af1C359ac731deaB48258b317890", + "startBlock": 7062708, + "abi": "../../../../../node_modules/@human-protocol/core/abis/Staking.json" + } +} diff --git a/packages/sdk/typescript/subgraphs/staking/eslint.config.mjs b/packages/sdk/typescript/subgraphs/staking/eslint.config.mjs new file mode 100644 index 0000000000..e3b3ba3aa9 --- /dev/null +++ b/packages/sdk/typescript/subgraphs/staking/eslint.config.mjs @@ -0,0 +1,57 @@ +import eslint from '@eslint/js'; +import globals from 'globals'; +import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; +import tseslint from 'typescript-eslint'; +import { flatConfigs as graphqlFlatConfigs } from '@graphql-eslint/eslint-plugin'; +const graphqlOperations = graphqlFlatConfigs['operations-recommended']; + +export default tseslint.config( + { + ignores: ['build', 'generated', 'schema.graphql'], + }, + eslint.configs.recommended, + tseslint.configs.recommended, + eslintPluginPrettierRecommended, + { + files: ['**/*.ts', '**/*.js'], + languageOptions: { + globals: { + ...globals.node, + ...globals.es2022, + }, + ecmaVersion: 2022, + sourceType: 'module', + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + rules: { + 'no-console': 'warn', + '@/quotes': [ + 'error', + 'single', + { avoidEscape: true, allowTemplateLiterals: true }, + ], + }, + }, + { + files: ['**/*.graphql'], + languageOptions: { + ...(graphqlOperations.languageOptions ?? {}), + parserOptions: { + ...(graphqlOperations.languageOptions?.parserOptions ?? {}), + schema: './schema.graphql', + }, + }, + rules: graphqlOperations.rules, + }, + { + files: ['**/*.spec.ts', '**/*.spec.tsx', '**/*.test.ts', '**/*.test.tsx'], + languageOptions: { + globals: { + ...globals.jest, + }, + }, + } +); diff --git a/packages/sdk/typescript/subgraphs/staking/local-graph-status.sh b/packages/sdk/typescript/subgraphs/staking/local-graph-status.sh new file mode 100755 index 0000000000..4025b2cfa7 --- /dev/null +++ b/packages/sdk/typescript/subgraphs/staking/local-graph-status.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +GRAPHQL_URL="http://localhost:8030/graphql" + + +node_health() { + RESPONSE_BODY=$(curl -s -X POST "$GRAPHQL_URL" -H 'Content-Type: application/json' --data '{"query": "{ version { version } }"}') + VERSION=$(echo "$RESPONSE_BODY" | grep -o '"version":"[^"]*"' | awk -F'"' '{print $4}') + + if [ -n "$VERSION" ]; then + echo "Graph node is healthy. Version is $VERSION" + exit 0 + else + echo "Graph node is not healthy" + exit 1 + fi +} + +subgraph_health() { + if [ -z "$SUBGRAPH_NAME" ]; then + echo "Error: SUBGRAPH_NAME environment variable is not set" + exit 1 + fi + + RESPONSE_BODY=$(curl -s -X POST "$GRAPHQL_URL" -H 'Content-Type: application/json' --data "{ \"query\": \"{indexingStatusForCurrentVersion(subgraphName: \\\"$SUBGRAPH_NAME\\\") { health } }\" }") + STATUS=$(echo "$RESPONSE_BODY" | grep -o '"health":"[^"]*"' | awk -F'"' '{print $4}') + + if [[ "$STATUS" == "healthy" ]]; then + echo "Subgraph is healthy" + exit 0 + else + echo "Subgraph is not healthy: $STATUS" + exit 1 + fi +} + +# Call the function passed as the first argument +"$@" diff --git a/packages/sdk/typescript/subgraphs/staking/matchstick.yaml b/packages/sdk/typescript/subgraphs/staking/matchstick.yaml new file mode 100644 index 0000000000..b6dd695aba --- /dev/null +++ b/packages/sdk/typescript/subgraphs/staking/matchstick.yaml @@ -0,0 +1 @@ +libsFolder: ../../../../node_modules diff --git a/packages/sdk/typescript/subgraphs/staking/package.json b/packages/sdk/typescript/subgraphs/staking/package.json new file mode 100644 index 0000000000..3d61e31d8d --- /dev/null +++ b/packages/sdk/typescript/subgraphs/staking/package.json @@ -0,0 +1,58 @@ +{ + "name": "@tools/staking-subgraph", + "private": true, + "description": "Human Protocol Staking Subgraph", + "version": "1.0.0", + "files": [ + "generated" + ], + "scripts": { + "clean": "rm -rf build generated subgraph.yaml", + "generate": "sh -c 'NETWORK=${NETWORK:-polygon}; mustache ./config/$NETWORK.json template.yaml > subgraph.yaml && yarn generate-staking && graph codegen'", + "generate-staking": "sh -c 'NETWORK=${NETWORK:-polygon}; mustache ./config/$NETWORK.json src/mapping/StakingTemplate.ts > src/mapping/Staking.ts'", + "codegen": "graph codegen", + "build": "graph build", + "test": "NETWORK=polygon yarn generate && graph test", + "create-local": "graph create --node http://localhost:8020/ humanprotocol/staking-localhost", + "remove-local": "graph remove --node http://localhost:8020/ humanprotocol/staking-localhost", + "deploy-local": "graph deploy --node http://localhost:8020/ --ipfs http://localhost:5010 humanprotocol/staking-localhost -l 0", + "health-local:node": "sh ./local-graph-status.sh node_health", + "health-local:subgraph": "SUBGRAPH_NAME=humanprotocol/staking-localhost sh ./local-graph-status.sh subgraph_health", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "format": "prettier --write '**/*.{ts,json,graphql}'" + }, + "repository": { + "type": "git", + "url": "https://github.com/humanprotocol/human-protocol.git", + "directory": "packages/sdk/typescript/subgraphs/staking" + }, + "keywords": [ + "human-protocol", + "sdk", + "subgraph", + "staking", + "ethereum" + ], + "license": "MIT", + "devDependencies": { + "@graphprotocol/graph-cli": "^0.97.1", + "@graphprotocol/graph-ts": "^0.38.0", + "@graphql-eslint/eslint-plugin": "^3.19.1", + "@human-protocol/core": "workspace:*", + "eslint": "^9.39.1", + "graphql": "^16.6.0", + "matchstick-as": "^0.6.0", + "mustache": "^4.2.0", + "prettier": "^3.7.4" + }, + "lint-staged": { + "*.{ts,graphql}": [ + "prettier --write", + "eslint --fix" + ], + "*.{yaml,json}": [ + "prettier --write" + ] + } +} diff --git a/packages/sdk/typescript/subgraphs/staking/schema.graphql b/packages/sdk/typescript/subgraphs/staking/schema.graphql new file mode 100644 index 0000000000..bd2956d435 --- /dev/null +++ b/packages/sdk/typescript/subgraphs/staking/schema.graphql @@ -0,0 +1,49 @@ +type Staker @entity(immutable: false) { + id: Bytes! + address: Bytes! + stakedAmount: BigInt! + lockedAmount: BigInt! + withdrawnAmount: BigInt! + slashedAmount: BigInt! + lockedUntilTimestamp: BigInt! + lastDepositTimestamp: BigInt! +} + +type StakeDepositedEvent @entity(immutable: true) { + id: Bytes! + block: BigInt! + timestamp: BigInt! + txHash: Bytes! + staker: Bytes! + amount: BigInt! +} + +type StakeLockedEvent @entity(immutable: true) { + id: Bytes! + block: BigInt! + timestamp: BigInt! + txHash: Bytes! + staker: Bytes! + amount: BigInt! + lockedUntilTimestamp: BigInt! +} + +type StakeWithdrawnEvent @entity(immutable: true) { + id: Bytes! + block: BigInt! + timestamp: BigInt! + txHash: Bytes! + staker: Bytes! + amount: BigInt! +} + +type StakeSlashedEvent @entity(immutable: true) { + id: Bytes! + block: BigInt! + timestamp: BigInt! + txHash: Bytes! + escrowAddress: Bytes! + staker: Bytes! + slashRequester: Bytes! + amount: BigInt! +} diff --git a/packages/sdk/typescript/subgraph/src/mapping/StakingTemplate.ts b/packages/sdk/typescript/subgraphs/staking/src/mapping/StakingTemplate.ts similarity index 70% rename from packages/sdk/typescript/subgraph/src/mapping/StakingTemplate.ts rename to packages/sdk/typescript/subgraphs/staking/src/mapping/StakingTemplate.ts index 30cc8a395f..312721b5d2 100644 --- a/packages/sdk/typescript/subgraph/src/mapping/StakingTemplate.ts +++ b/packages/sdk/typescript/subgraphs/staking/src/mapping/StakingTemplate.ts @@ -1,24 +1,19 @@ import { - FeeWithdrawn, StakeDeposited, StakeLocked, StakeSlashed, StakeWithdrawn, } from '../../generated/Staking/Staking'; import { - Operator, StakeDepositedEvent, StakeLockedEvent, Staker, StakeSlashedEvent, StakeWithdrawnEvent, } from '../../generated/schema'; -import { Address, dataSource } from '@graphprotocol/graph-ts'; +import { Address } from '@graphprotocol/graph-ts'; import { ZERO_BI } from './utils/number'; import { toEventId } from './utils/event'; -import { createTransaction } from './utils/transaction'; - -export const TOKEN_ADDRESS = Address.fromString('{{ HMToken.address }}'); export function createOrLoadStaker(address: Address): Staker { let staker = Staker.load(address); @@ -34,10 +29,6 @@ export function createOrLoadStaker(address: Address): Staker { staker.lockedUntilTimestamp = ZERO_BI; staker.lastDepositTimestamp = ZERO_BI; - const operator = Operator.load(address); - if (operator) { - staker.operator = operator.id; - } staker.save(); } @@ -45,17 +36,6 @@ export function createOrLoadStaker(address: Address): Staker { } export function handleStakeDeposited(event: StakeDeposited): void { - createTransaction( - event, - 'stake', - event.params.staker, - dataSource.address(), - null, - null, - event.params.tokens, - TOKEN_ADDRESS - ); - // Create StakeDepostiedEvent entity const eventEntity = new StakeDepositedEvent(toEventId(event)); eventEntity.block = event.block.number; eventEntity.timestamp = event.block.timestamp; @@ -64,27 +44,13 @@ export function handleStakeDeposited(event: StakeDeposited): void { eventEntity.amount = event.params.tokens; eventEntity.save(); - // Update staker const staker = createOrLoadStaker(event.params.staker); - staker.stakedAmount = staker.stakedAmount.plus(eventEntity.amount); staker.lastDepositTimestamp = event.block.timestamp; - staker.save(); } export function handleStakeLocked(event: StakeLocked): void { - createTransaction( - event, - 'unstake', - event.params.staker, - dataSource.address(), - null, - null, - event.params.tokens, - TOKEN_ADDRESS - ); - // Create StakeLockedEvent entity const eventEntity = new StakeLockedEvent(toEventId(event)); eventEntity.block = event.block.number; eventEntity.timestamp = event.block.timestamp; @@ -94,7 +60,6 @@ export function handleStakeLocked(event: StakeLocked): void { eventEntity.lockedUntilTimestamp = event.params.until; eventEntity.save(); - // Update staker const staker = createOrLoadStaker(event.params.staker); staker.lockedAmount = eventEntity.amount; staker.lockedUntilTimestamp = eventEntity.lockedUntilTimestamp; @@ -102,17 +67,6 @@ export function handleStakeLocked(event: StakeLocked): void { } export function handleStakeWithdrawn(event: StakeWithdrawn): void { - createTransaction( - event, - 'stakeWithdrawn', - event.params.staker, - dataSource.address(), - null, - null, - event.params.tokens, - TOKEN_ADDRESS - ); - // Create StakeWithdrawnEvent entity const eventEntity = new StakeWithdrawnEvent(toEventId(event)); eventEntity.block = event.block.number; eventEntity.timestamp = event.block.timestamp; @@ -121,7 +75,6 @@ export function handleStakeWithdrawn(event: StakeWithdrawn): void { eventEntity.amount = event.params.tokens; eventEntity.save(); - // Update staker const staker = createOrLoadStaker(event.params.staker); staker.lockedAmount = staker.lockedAmount.minus(eventEntity.amount); if (staker.lockedAmount.equals(ZERO_BI)) { @@ -133,17 +86,6 @@ export function handleStakeWithdrawn(event: StakeWithdrawn): void { } export function handleStakeSlashed(event: StakeSlashed): void { - createTransaction( - event, - 'slash', - event.params.staker, - dataSource.address(), - null, - event.params.escrowAddress, - event.params.tokens, - TOKEN_ADDRESS - ); - // Create StakeSlashedEvent entity const eventEntity = new StakeSlashedEvent(toEventId(event)); eventEntity.block = event.block.number; eventEntity.timestamp = event.block.timestamp; @@ -154,22 +96,8 @@ export function handleStakeSlashed(event: StakeSlashed): void { eventEntity.slashRequester = event.params.slashRequester; eventEntity.save(); - // Update staker const staker = createOrLoadStaker(event.params.staker); staker.slashedAmount = staker.slashedAmount.plus(eventEntity.amount); staker.stakedAmount = staker.stakedAmount.minus(eventEntity.amount); staker.save(); } - -export function handleFeeWithdrawn(event: FeeWithdrawn): void { - createTransaction( - event, - 'withdrawFees', - event.transaction.from, - dataSource.address(), - null, - null, - event.params.amount, - TOKEN_ADDRESS - ); -} diff --git a/packages/sdk/typescript/subgraphs/staking/src/mapping/utils/event.ts b/packages/sdk/typescript/subgraphs/staking/src/mapping/utils/event.ts new file mode 100644 index 0000000000..26a83cacce --- /dev/null +++ b/packages/sdk/typescript/subgraphs/staking/src/mapping/utils/event.ts @@ -0,0 +1,7 @@ +import { Bytes, ethereum } from '@graphprotocol/graph-ts'; + +export function toEventId(event: ethereum.Event): Bytes { + return event.transaction.hash + .concatI32(event.logIndex.toI32()) + .concatI32(event.block.timestamp.toI32()); +} diff --git a/packages/sdk/typescript/subgraphs/staking/src/mapping/utils/number.ts b/packages/sdk/typescript/subgraphs/staking/src/mapping/utils/number.ts new file mode 100644 index 0000000000..9a4c0d5909 --- /dev/null +++ b/packages/sdk/typescript/subgraphs/staking/src/mapping/utils/number.ts @@ -0,0 +1,3 @@ +import { BigInt } from '@graphprotocol/graph-ts'; + +export const ZERO_BI = BigInt.fromI32(0); diff --git a/packages/sdk/typescript/subgraphs/staking/template.yaml b/packages/sdk/typescript/subgraphs/staking/template.yaml new file mode 100644 index 0000000000..69dcbe4a82 --- /dev/null +++ b/packages/sdk/typescript/subgraphs/staking/template.yaml @@ -0,0 +1,37 @@ +specVersion: 1.0.0 +description: '{{ description }}' +schema: + file: ./schema.graphql +indexerHints: + prune: auto +dataSources: + - kind: ethereum + name: Staking + network: '{{ network }}' + source: + abi: Staking + address: '{{ Staking.address }}' + startBlock: { { Staking.startBlock } } + mapping: + kind: ethereum/events + apiVersion: 0.0.7 + language: wasm/assemblyscript + entities: + - Staker + - StakeDepositedEvent + - StakeLockedEvent + - StakeWithdrawnEvent + - StakeSlashedEvent + abis: + - name: Staking + file: '{{{ Staking.abi }}}' + eventHandlers: + - event: StakeDeposited(indexed address,uint256) + handler: handleStakeDeposited + - event: StakeLocked(indexed address,uint256,uint256) + handler: handleStakeLocked + - event: StakeWithdrawn(indexed address,uint256) + handler: handleStakeWithdrawn + - event: StakeSlashed(indexed address,uint256,indexed address,address) + handler: handleStakeSlashed + file: ./src/mapping/Staking.ts diff --git a/packages/sdk/typescript/subgraphs/staking/tsconfig.json b/packages/sdk/typescript/subgraphs/staking/tsconfig.json new file mode 100644 index 0000000000..f9e6cb7174 --- /dev/null +++ b/packages/sdk/typescript/subgraphs/staking/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "baseUrl": ".", + "types": ["@graphprotocol/graph-ts", "node"] + }, + "include": ["src", "tests"] +} diff --git a/yarn.lock b/yarn.lock index 0db0c9afc7..438f643480 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11164,6 +11164,22 @@ __metadata: languageName: node linkType: hard +"@tools/staking-subgraph@workspace:packages/sdk/typescript/subgraphs/staking": + version: 0.0.0-use.local + resolution: "@tools/staking-subgraph@workspace:packages/sdk/typescript/subgraphs/staking" + dependencies: + "@graphprotocol/graph-cli": "npm:^0.97.1" + "@graphprotocol/graph-ts": "npm:^0.38.0" + "@graphql-eslint/eslint-plugin": "npm:^3.19.1" + "@human-protocol/core": "workspace:*" + eslint: "npm:^9.39.1" + graphql: "npm:^16.6.0" + matchstick-as: "npm:^0.6.0" + mustache: "npm:^4.2.0" + prettier: "npm:^3.7.4" + languageName: unknown + linkType: soft + "@tools/subgraph@workspace:packages/sdk/typescript/subgraph": version: 0.0.0-use.local resolution: "@tools/subgraph@workspace:packages/sdk/typescript/subgraph"