Skip to content
This repository has been archived by the owner on May 7, 2024. It is now read-only.

183 fix collect tracking for evm lp investors #195

Merged
merged 13 commits into from
Jan 29, 2024
1 change: 1 addition & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export COMPOSE_PROFILES=subql
export DB_USER=postgres
export DB_PASS=postgres
export DB_DATABASE=postgres
Expand Down
File renamed without changes.
15 changes: 15 additions & 0 deletions abi/investmentManager.abi.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[
{
"inputs": [],
"name": "userEscrow",
"outputs": [
{
"internalType": "contract UserEscrowLike",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
}
]
File renamed without changes.
2 changes: 1 addition & 1 deletion chains-cfg/centrifuge.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ network:
file: ./dist/chaintypes.js
dataSources:
- kind: substrate/Runtime
startBlock: 3824709 # block first pool was created at
startBlock: 3858140 # block first pool was created at
2 changes: 1 addition & 1 deletion chains-cfg/development-embrio.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: 'centrifuge-subql'
repository: 'https://github.com/embrio-tech/centrifuge-subql'
network:
endpoint: wss://fullnode.demo.k-f.dev/public-ws
chainId: '0x27d6bdae3ea8fc7021792f3ccea5ee62fee37641c6f69d6e8530cfb45ef57a64'
chainId: '0xe0e40f62affe742eb92d75c07830671ef3e0cc6efc1ecaf81cf34a28148e91f0'
chaintypes:
file: ./dist/chaintypes.js
dataSources:
Expand Down
6 changes: 4 additions & 2 deletions chains-evm/_root.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ dataSources:
abi: poolManager
assets:
poolManager:
file: './poolManager.abi.json'
file: './abi/poolManager.abi.json'
investmentManager:
file: './abi/investmentManager.abi.json'
mapping:
file: './dist/index.js'
handlers:
Expand All @@ -34,7 +36,7 @@ templates:
abi: erc20
assets:
erc20:
file: './erc20.abi.json'
file: './abi/erc20.abi.json'
mapping:
file: ./dist/index.js
handlers:
Expand Down
6 changes: 5 additions & 1 deletion chains-evm/eth/centrifuge.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ network:
chainId: '1' # Ethereum Mainnet
endpoint: "https://mainnet.infura.io/v3/a4ba76cd4be643618572e7467a444e3a"
dictionary: "https://gx.api.subquery.network/sq/subquery/eth-dictionary"
bypassBlocks:
- "18721040-18735450"
- "18735460-18747630"
- "18747640-18763940"
dataSources:
- kind: ethereum/Runtime
startBlock: 18721000
startBlock: 18721030
options:
address: '0x78E9e622A57f70F1E0Ec652A4931E4e278e58142'
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"@jest/globals": "^29.2.0",
"@polkadot/api": "^10",
"@polkadot/typegen": "^10",
"@subql/cli": "latest",
"@subql/cli": "^4.2.5",
"@subql/testing": "latest",
"@subql/types": "latest",
"@types/jest": "^29.1.2",
Expand Down
9 changes: 6 additions & 3 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ type Tranche @entity {
interestRatePerSec: BigInt
minRiskBuffer: BigInt

isActive: Boolean @index
isActive: Boolean! @index

tokenSupply: BigInt
tokenPrice: BigInt
Expand Down Expand Up @@ -194,6 +194,8 @@ enum InvestorTransactionType {
TRANSFER_OUT
INVEST_COLLECT
REDEEM_COLLECT
INVEST_LP_COLLECT
REDEEM_LP_COLLECT
}

type OutstandingOrder @entity {
Expand All @@ -210,7 +212,7 @@ type OutstandingOrder @entity {
}

type InvestorTransaction @entity {
id: ID! # extrinsic hash - epoch number - transaction type
id: ID! # extrinsic hash - epoch number ( or 0 for EVM ) - transaction type
hash: String!
account: Account! @index #Account @index
pool: Pool! @index
Expand Down Expand Up @@ -366,12 +368,13 @@ type Proxy @entity {
}

type Currency @entity {
id: ID! #TODO: chainId - currencyType - [currencySpec]
id: ID! # chainId - currencySpec - [currencySpec]
chain: Blockchain!
decimals: Int!

tokenAddress: String
escrowAddress: String
userEscrowAddress: String

pool: Pool
tranche: Tranche
Expand Down
4 changes: 4 additions & 0 deletions src/@types/gobal.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export {}
declare global {
function getNodeChainId(): Promise<string>
}
10 changes: 10 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import '@polkadot/api-augment'
import { atob } from 'abab'
import type { u64 } from '@polkadot/types'
import type { Provider } from '@ethersproject/providers'

const cfgChainIdProm = 'query' in api ? (api.query.evmChainId.chainId() as Promise<u64>) : null
const ethNetworkProm =
typeof (api as unknown as Provider).getNetwork === 'function' ? (api as unknown as Provider).getNetwork() : null

global.atob = atob
global.getNodeChainId = async function () {
if(cfgChainIdProm) return (await cfgChainIdProm).toString(10)
if(ethNetworkProm) return (await ethNetworkProm).chainId.toString(10)
}

export * from './mappings/handlers/blockHandlers'
export * from './mappings/handlers/poolsHandlers'
Expand Down
80 changes: 61 additions & 19 deletions src/mappings/handlers/evmHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,18 @@ import { InvestorTransactionData, InvestorTransactionService } from '../services
import { CurrencyService } from '../services/currencyService'
import { BlockchainService } from '../services/blockchainService'
import { CurrencyBalanceService } from '../services/currencyBalanceService'
import { PoolManagerAbi__factory } from '../../types/contracts'
import { InvestmentManagerAbi__factory, PoolManagerAbi__factory } from '../../types/contracts'
import type { Provider } from '@ethersproject/abstract-provider'
import { TrancheBalanceService } from '../services/trancheBalanceService'

const ethApi = api as unknown as Provider
//const networkPromise = typeof ethApi.getNetwork === 'function' ? ethApi.getNetwork() : null

export const handleEvmDeployTranche = errorHandler(_handleEvmDeployTranche)
async function _handleEvmDeployTranche(event: DeployTrancheLog): Promise<void> {
const [_poolId, _trancheId, tokenAddress] = event.args

const chainId = parseInt(event.transaction.chainId, 16).toString(10)
const chainId = await getNodeChainId() //(await networkPromise).chainId.toString(10)
const blockchain = await BlockchainService.getOrInit(chainId)

const poolId = _poolId.toString()
Expand All @@ -29,10 +34,14 @@ async function _handleEvmDeployTranche(event: DeployTrancheLog): Promise<void> {
const tranche = await TrancheService.getOrSeed(pool.id, trancheId)

const currency = await CurrencyService.getOrInitEvm(blockchain.id, tokenAddress)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const poolManager = PoolManagerAbi__factory.connect(event.address, api as any)
const poolManager = PoolManagerAbi__factory.connect(event.address, ethApi)
const escrowAddress = await poolManager.escrow()
await currency.initEvmDetails(tokenAddress, escrowAddress, tranche.poolId, tranche.trancheId)

const investmentManagerAddress = await poolManager.investmentManager()
const investmentManager = InvestmentManagerAbi__factory.connect(investmentManagerAddress, ethApi)
const userEscrowAddress = await investmentManager.userEscrow()

await currency.initEvmDetails(tokenAddress, escrowAddress, userEscrowAddress, tranche.poolId, tranche.trancheId)
await currency.save()

await createTrancheTrackerDatasource({ address: tokenAddress })
Expand All @@ -46,41 +55,74 @@ async function _handleEvmTransfer(event: TransferLog): Promise<void> {
logger.info(`Transfer ${fromEvmAddress}-${toEvmAddress} of ${amount.toString()} at block: ${event.blockNumber}`)

const evmTokenAddress = event.address
const chainId = parseInt(event.transaction.chainId, 16).toString(10)
const chainId = await getNodeChainId() //(await networkPromise).chainId.toString(10)
const blockchain = await BlockchainService.getOrInit(chainId)
const evmToken = await CurrencyService.getOrInitEvm(blockchain.id, evmTokenAddress)
const escrowAddress = evmToken.escrowAddress
const { escrowAddress, userEscrowAddress } = evmToken
const serviceAddresses = [evmTokenAddress, escrowAddress, userEscrowAddress, nullAddress]

const isFromUserAddress = !serviceAddresses.includes(fromEvmAddress)
const isToUserAddress = !serviceAddresses.includes(toEvmAddress)
const isFromEscrow = fromEvmAddress === escrowAddress
const _isFromUserEscrow = fromEvmAddress === userEscrowAddress

const orderData: Omit<InvestorTransactionData, 'address'> = {
poolId: evmToken.poolId,
trancheId: evmToken.trancheId,
trancheId: evmToken.trancheId.split('-')[1],
//epochNumber: pool.currentEpoch,
hash: event.transactionHash,
timestamp: new Date(Number(event.block.timestamp) * 1000),
//price: tranche.tokenPrice,
amount: amount.toBigInt(),
}

if (fromEvmAddress !== evmTokenAddress && fromEvmAddress !== escrowAddress && fromEvmAddress !== nullAddress) {
const fromAddress = AccountService.evmToSubstrate(fromEvmAddress, blockchain.id)
const fromAccount = await AccountService.getOrInit(fromAddress)
let fromAddress: string = null,
fromAccount: AccountService = null
if (isFromUserAddress) {
fromAddress = AccountService.evmToSubstrate(fromEvmAddress, blockchain.id)
fromAccount = await AccountService.getOrInit(fromAddress)
}

const txOut = InvestorTransactionService.transferOut({ ...orderData, address: fromAccount.id })
await txOut.save()
let toAddress: string = null,
toAccount: AccountService = null
if (isToUserAddress) {
toAddress = AccountService.evmToSubstrate(toEvmAddress, blockchain.id)
toAccount = await AccountService.getOrInit(toAddress)
}

// Handle Currency Balance Updates
if (isToUserAddress) {
const toBalance = await CurrencyBalanceService.getOrInitEvm(toAddress, evmToken.id)
await toBalance.credit(amount.toBigInt())
await toBalance.save()
}

if (isFromUserAddress) {
const fromBalance = await CurrencyBalanceService.getOrInitEvm(fromAddress, evmToken.id)
await fromBalance.debit(amount.toBigInt())
await fromBalance.save()
}

if (toEvmAddress !== evmTokenAddress && toEvmAddress !== escrowAddress && toEvmAddress !== nullAddress) {
const toAddress = AccountService.evmToSubstrate(toEvmAddress, blockchain.id)
const toAccount = await AccountService.getOrInit(toAddress)
// Handle INVEST_LP_COLLECT
if (isFromEscrow && isToUserAddress) {
const investLpCollect = InvestorTransactionService.collectLpInvestOrder({ ...orderData, address: toAccount.id })
await investLpCollect.save()

const trancheBalance = await TrancheBalanceService.getOrInit(toAccount.id, orderData.poolId, orderData.trancheId)
await trancheBalance.investCollect(orderData.amount)
await trancheBalance.save()
}
// TODO: Handle REDEEM_LP_COLLECT
// if (isFromUserEscrow && isToUserAddress) {
// const redeemLpCollect = InvestorTransactionService.collectLpRedeemOrder()
// }

// Handle Transfer In and Out
if (isFromUserAddress && isToUserAddress) {
const txIn = InvestorTransactionService.transferIn({ ...orderData, address: toAccount.id })
await txIn.save()

const toBalance = await CurrencyBalanceService.getOrInitEvm(toAddress, blockchain.id)
await toBalance.credit(amount.toBigInt())
await toBalance.save()
const txOut = InvestorTransactionService.transferOut({ ...orderData, address: fromAccount.id })
await txOut.save()
}
}
17 changes: 12 additions & 5 deletions src/mappings/handlers/investmentsHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,14 +139,17 @@ async function _handleInvestOrdersCollected(event: SubstrateEvent<InvestOrdersCo
`Address: ${address.toHex()} at ` +
`block ${event.block.block.header.number.toString()} hash:${event.extrinsic.extrinsic.hash.toString()}`
)
const account = await AccountService.getOrInit(address.toHex())
if (await account.isForeignEvm()) {
logger.info('Skipping Address as collection is from another EVM Chain')
return
}

const pool = await PoolService.getById(poolId.toString())
if (pool === undefined) throw missingPool
if (!pool) throw missingPool
const endEpochId = pool.lastEpochClosed
logger.info(`Collection for ending epoch: ${endEpochId}`)

const account = await AccountService.getOrInit(address.toHex())

const tranche = await TrancheService.getById(poolId.toString(), trancheId.toHex())

// Update tranche price
Expand Down Expand Up @@ -186,13 +189,17 @@ async function _handleRedeemOrdersCollected(event: SubstrateEvent<RedeemOrdersCo
`block ${event.block.block.header.number.toString()} hash:${event.extrinsic.extrinsic.hash.toString()}`
)

const account = await AccountService.getOrInit(address.toHex())
if (await account.isForeignEvm()) {
logger.info('Skipping as Address is EVM')
return
}

const pool = await PoolService.getById(poolId.toString())
if (pool === undefined) throw missingPool
const endEpochId = pool.lastEpochClosed
logger.info(`Collection for ending epoch: ${endEpochId}`)

const account = await AccountService.getOrInit(address.toHex())

const tranche = await TrancheService.getById(poolId.toString(), trancheId.toHex())

// Update tranche price
Expand Down
1 change: 1 addition & 0 deletions src/mappings/services/accountService.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { AccountService } from './accountService'

global.getNodeChainId = () => Promise.resolve('2030')
// eslint-disable-next-line @typescript-eslint/no-explicit-any
api.query['evmChainId'] = { chainId: jest.fn(() => ({ toString: () => '2030' })) } as any

Expand Down
11 changes: 8 additions & 3 deletions src/mappings/services/accountService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@ import { BlockchainService } from './blockchainService'
const EVM_SUFFIX = '45564d00'

export class AccountService extends Account {
static async init(address: string, blockchainService = BlockchainService) {
static async init(address: string) {
logger.info(`Initialising new account: ${address}`)
if (this.isEvm(address)) {
const chainId = this.readEvmChainId(address)
const account = new this(address, chainId)
account.evmAddress = address.substring(0, 42)
return account
} else {
return new this(address, await blockchainService.getThisChainId())
return new this(address, await getNodeChainId())
}
}

static async getOrInit(address: string, blockchainService = BlockchainService): Promise<AccountService> {
let account = (await this.get(address)) as AccountService
if (account === undefined) {
if (!account) {
account = await this.init(address)
await blockchainService.getOrInit(account.chainId)
await account.save()
Expand All @@ -39,6 +39,11 @@ export class AccountService extends Account {
return address.length === 66 && address.endsWith(EVM_SUFFIX)
}

public async isForeignEvm() {
const nodeChainId = await getNodeChainId()
return this.chainId !== nodeChainId
}

public isEvm() {
return AccountService.isEvm(this.id)
}
Expand Down
10 changes: 5 additions & 5 deletions src/mappings/services/blockchainService.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Blockchain } from '../../types/models/Blockchain'
import { u64 } from '@polkadot/types'
//import { u64 } from '@polkadot/types'
export class BlockchainService extends Blockchain {
static init(chainId: string) {
logger.info(`Initialising new blockchain with evm Id ${chainId}`)
return new this(chainId)
}

static async getOrInit(_chainId?: string) {
const chainId = _chainId ?? await this.getThisChainId()
const chainId = _chainId ?? await getNodeChainId()//(await this.getThisChainId())
let blockchain = await this.get(chainId)
if (!blockchain) {
blockchain = this.init(chainId)
Expand All @@ -16,7 +16,7 @@ export class BlockchainService extends Blockchain {
return blockchain as BlockchainService
}

static async getThisChainId() {
return ((await api.query.evmChainId.chainId()) as u64).toString(10)
}
// static async getThisChainId() {
// return ((await api.query.evmChainId.chainId()) as u64).toString(10)
// }
}
Loading
Loading