Skip to content

Commit

Permalink
add Bundle model and processor for handling bundle events
Browse files Browse the repository at this point in the history
  • Loading branch information
doerfli committed Nov 22, 2024
1 parent bdee577 commit 0b595fc
Show file tree
Hide file tree
Showing 6 changed files with 254 additions and 5 deletions.
20 changes: 20 additions & 0 deletions prisma/migrations/20241122151152_init/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
-- CreateTable
CREATE TABLE "Bundle" (
"bundleNftId" BIGINT NOT NULL,
"poolNftId" BIGINT NOT NULL,
"lifetime" BIGINT NOT NULL,
"locked" BOOLEAN NOT NULL DEFAULT false,
"closed" BOOLEAN NOT NULL DEFAULT false,
"balance" BIGINT NOT NULL,
"lockedAmount" BIGINT NOT NULL,
"created_blockNumber" INTEGER NOT NULL,
"created_timestamp" BIGINT NOT NULL,
"created_txHash" TEXT NOT NULL,
"created_from" TEXT NOT NULL,
"modified_blockNumber" INTEGER NOT NULL,
"modified_timestamp" BIGINT NOT NULL,
"modified_txHash" TEXT NOT NULL,
"modified_from" TEXT NOT NULL,

CONSTRAINT "Bundle_pkey" PRIMARY KEY ("bundleNftId")
);
20 changes: 19 additions & 1 deletion prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,22 @@ model OracleRequest {
modified_timestamp BigInt
modified_txHash String
modified_from String
}
}

model Bundle {
bundleNftId BigInt @id
poolNftId BigInt
lifetime BigInt
locked Boolean @default(false)
closed Boolean @default(false)
balance BigInt
lockedAmount BigInt
created_blockNumber Int
created_timestamp BigInt
created_txHash String
created_from String
modified_blockNumber Int
modified_timestamp BigInt
modified_txHash String
modified_from String
}
143 changes: 143 additions & 0 deletions src/bundle_processor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { PrismaClient } from "@prisma/client";
import { IBundleService__factory } from "./generated/contracts/gif";
import { logger } from "./logger";
import { Bundle } from "./types/bundle";
import { DecodedLogEntry } from "./types/logdata";

export default class BundleProcessor {
private prisma: PrismaClient;

constructor(prisma: PrismaClient) {
this.prisma = prisma;
}

async persistBundles(bundles: Bundle[]): Promise<void> {
for (const bundle of bundles) {
await this.prisma.bundle.upsert({
where: { bundleNftId: bundle.bundleNftId },
update: {
poolNftId: bundle.poolNftId,
lifetime: bundle.lifetime,
locked: bundle.locked,
closed: bundle.closed,
balance: bundle.balance,
lockedAmount: bundle.lockedAmount,
modified_blockNumber: bundle.modified.blockNumber,
modified_timestamp: bundle.modified.timestamp,
modified_txHash: bundle.modified.txHash,
modified_from: bundle.modified.from
},
create: {
bundleNftId: bundle.bundleNftId,
poolNftId: bundle.poolNftId,
lifetime: bundle.lifetime,
locked: bundle.locked,
closed: bundle.closed,
balance: bundle.balance,
lockedAmount: bundle.lockedAmount,
created_blockNumber: bundle.created.blockNumber,
created_timestamp: bundle.created.timestamp,
created_txHash: bundle.created.txHash,
created_from: bundle.created.from,
modified_blockNumber: bundle.modified.blockNumber,
modified_timestamp: bundle.modified.timestamp,
modified_txHash: bundle.modified.txHash,
modified_from: bundle.modified.from
}
});
}
}

async processBundleCreatedEvent(event: DecodedLogEntry, bundles: Map<BigInt, Bundle>): Promise<Map<BigInt, Bundle>> {
if (event.event_name !== 'LogBundleServiceBundleCreated') {
throw new Error(`Invalid event type ${event.event_name}`);
}

logger.info(`Processing bundle created event ${event.tx_hash} - ${event.event_name} - ${event.data}`);
const data = this.decodeBundleServiceEvent(event);
if (data === null || data === undefined) {
logger.error(`Failed to decode event ${event.tx_hash} - ${event.event_name} - ${event.data}`);
return bundles;
}
if (data.name !== 'LogBundleServiceBundleCreated') {
throw new Error(`Invalid event name ${data.name}`);
}

const bundleNftId = data.args[0] as BigInt;
const poolNftId = data.args[1] as BigInt;
const lifetime = data.args[2] as BigInt;

// TODO: validate poolNftId

const bundle = {
bundleNftId,
poolNftId,
lifetime,
locked: false,
closed: false,
balance: BigInt(0),
lockedAmount: BigInt(0),
created: {
blockNumber: event.block_number,
timestamp:BigInt(new Date(event.block_time).getTime()),
txHash: event.tx_hash,
from: event.tx_from
},
modified: {
blockNumber: event.block_number,
timestamp:BigInt(new Date(event.block_time).getTime()),
txHash: event.tx_hash,
from: event.tx_from
}
} as Bundle;
bundles.set(bundleNftId, bundle);
return bundles;
}

async processBundleClosedEvent(event: DecodedLogEntry, bundles: Map<BigInt, Bundle>): Promise<Map<BigInt, Bundle>> {
if (event.event_name !== 'LogBundleServiceBundleClosed') {
throw new Error(`Invalid event type ${event.event_name}`);
}

logger.info(`Processing bundle closed event ${event.tx_hash} - ${event.event_name} - ${event.data}`);
const data = this.decodeBundleServiceEvent(event);
if (data === null || data === undefined) {
logger.error(`Failed to decode event ${event.tx_hash} - ${event.event_name} - ${event.data}`);
return bundles;
}
if (data.name !== 'LogBundleServiceBundleClosed') {
throw new Error(`Invalid event name ${data.name}`);
}

const bundleNftId = data.args[0] as BigInt;
const bundle = bundles.get(bundleNftId);
if (bundle === undefined) {
throw new Error(`Bundle not found ${bundleNftId}`);
}
bundle.closed = true;
bundle.modified = {
blockNumber: event.block_number,
timestamp:BigInt(new Date(event.block_time).getTime()),
txHash: event.tx_hash,
from: event.tx_from
};
return bundles;
}

decodeBundleServiceEvent(event: DecodedLogEntry) {
const topic0 = event.topic0;
let topic1 = event.topic1;
if (topic1 === null || topic1 === undefined || topic1 === '') {
topic1 = '0x';
}
let topic2 = event.topic2;
if (topic2 === null || topic2 === undefined || topic2 === '') {
topic2 = '0x';
}
let topic3 = event.topic3;
if (topic3 === null || topic3 === undefined || topic3 === '') {
topic3 = '0x';
}
return IBundleService__factory.createInterface().parseLog({ topics: [topic0, topic1, topic2, topic3], data: event.data });
}
}
52 changes: 49 additions & 3 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import { Claim } from './types/claim';
import { Payout } from './types/payout';
import { OracleRequest } from './types/oracle_request';
import OracleProcessor from './oracle_processor';
import BundleProcessor from './bundle_processor';
import { Bundle } from './types/bundle';

dotenv.config();

Expand All @@ -37,6 +39,7 @@ class Main {
private componentProcessor: ComponentProcessor;
private riskProcessor: RiskProcessor;
private oracleProcessor: OracleProcessor;
private bundleProcessor: BundleProcessor;

constructor(prisma: PrismaClient) {
this.dune = new DuneApi();
Expand All @@ -46,11 +49,15 @@ class Main {
this.policyProcessor = new PolicyProcessor(prisma);
this.riskProcessor = new RiskProcessor(prisma);
this.oracleProcessor = new OracleProcessor(prisma);
this.bundleProcessor = new BundleProcessor(prisma);
}

public async main(): Promise<void> {
const gifEvents = await this.dune.getLatestResult(DUNE_QUERY_ID_GIF_EVENTS, 0);
const { nfts, instances, policies, components, risks, claims, payouts, requests } = await this.parseGifEvents(gifEvents);
const {
nfts, instances, policies, components,
risks, claims, payouts, requests, bundles
} = await this.parseGifEvents(gifEvents);

await this.nftProcessor.persistNfts(Array.from(nfts.values()));
await this.instanceProcessor.persistInstances(Array.from(instances.values()));
Expand All @@ -60,6 +67,7 @@ class Main {
await this.policyProcessor.persistClaims(Array.from(claims.values()));
await this.policyProcessor.persistPayouts(Array.from(payouts.values()));
await this.oracleProcessor.persistOracleRequests(Array.from(requests.values()));
await this.bundleProcessor.persistBundles(Array.from(bundles.values()));

for (const nft of nfts.values()) {
logger.info(`NFT: ${nft.nftId} - ${ObjectType[nft.objectType]} - ${nft.objectAddress} - ${nft.owner}`);
Expand All @@ -84,6 +92,10 @@ class Main {
for (const payout of payouts.values()) {
logger.info(`Payout: ${payout.policyNftId} ${payout.payoutId} - ${payout.payoutAmount}`);
}

for (const bundle of bundles.values()) {
logger.info(`Bundle: ${bundle.bundleNftId} - ${bundle.balance} - ${bundle.lockedAmount}`);
}
}

async parseGifEvents(gifEvents: Array<DecodedLogEntry>)
Expand All @@ -95,7 +107,8 @@ class Main {
risks: Map<string, Risk>,
claims: Map<string, Claim>,
payouts: Map<string, Payout>,
requests: Map<BigInt, OracleRequest>
requests: Map<BigInt, OracleRequest>,
bundles: Map<BigInt, Bundle>
}>
{
const nfts = new Map<BigInt, Nft>();
Expand All @@ -106,6 +119,7 @@ class Main {
const claims = new Map<string, Claim>();
const payouts = new Map<string, Payout>();
const requests = new Map<BigInt, OracleRequest>();
const bundles = new Map<BigInt, Bundle>();

for (const event of gifEvents) {
// logger.debug(`Processing gif event ${event.tx_hash} - ${event.block_number} - ${event.event_name}`);
Expand Down Expand Up @@ -200,12 +214,44 @@ class Main {
await this.oracleProcessor.processOracleRequestCancelledEvent(event, requests);
break;

case 'LogBundleServiceBundleCreated':
await this.bundleProcessor.processBundleCreatedEvent(event, bundles);
break;
case 'LogBundleServiceBundleClosed':
await this.bundleProcessor.processBundleClosedEvent(event, bundles);
break;

// event LogBundleServiceBundleLocked(NftId bundleNftId);
// event LogBundleServiceBundleUnlocked(NftId bundleNftId);
// event LogBundleServiceBundleExtended(NftId bundleNftId, Seconds lifetimeExtension, Timestamp extendedExpiredAt);
// event LogBundleServiceBundleFeeUpdated(NftId bundleNftId, Amount fixedFee, UFixed fractionalFee);
// event LogBundleServiceCollateralLocked(NftId bundleNftId, NftId policyNftId, Amount collateralAmount);
// event LogBundleServiceCollateralReleased(NftId bundleNftId, NftId policyNftId, Amount collateralAmount);
// event LogBundleServiceBundleStaked(NftId bundleNftId, Amount amount);
// event LogBundleServiceBundleUnstaked(NftId bundleNftId, Amount amount);

// event LogPoolServiceMaxBalanceAmountUpdated(NftId poolNftId, Amount previousMaxCapitalAmount, Amount currentMaxCapitalAmount);
// event LogPoolServiceWalletFunded(NftId poolNftId, address poolOwner, Amount amount);
// event LogPoolServiceWalletDefunded(NftId poolNftId, address poolOwner, Amount amount);
// event LogPoolServiceBundleCreated(NftId instanceNftId, NftId poolNftId, NftId bundleNftId);
// event LogPoolServiceBundleClosed(NftId instanceNftId, NftId poolNftId, NftId bundleNftId);
// event LogPoolServiceBundleStaked(NftId instanceNftId, NftId poolNftId, NftId bundleNftId, Amount amount, Amount netAmount);
// event LogPoolServiceBundleUnstaked(NftId instanceNftId, NftId poolNftId, NftId bundleNftId, Amount amount, Amount netAmount);
// event LogPoolServiceFeesWithdrawn(NftId bundleNftId, address recipient, address tokenAddress, Amount amount);
// event LogPoolServiceProcessFundedClaim(NftId policyNftId, ClaimId claimId, Amount availableAmount);
// event LogPoolServiceApplicationVerified(NftId poolNftId, NftId bundleNftId, NftId applicationNftId, Amount totalCollateralAmount);
// event LogPoolServiceCollateralLocked(NftId poolNftId, NftId bundleNftId, NftId applicationNftId, Amount totalCollateralAmount, Amount lockedCollateralAmount);
// event LogPoolServiceCollateralReleased(NftId bundleNftId, NftId policyNftId, Amount remainingCollateralAmount);
// event LogPoolServiceSaleProcessed(NftId poolNftId, NftId bundleNftId, Amount bundleNetAmount, Amount bundleFeeAmount, Amount poolFeeAmount);
// event LogPoolServicePayoutProcessed(NftId poolNftId, NftId bundleNftId, NftId policyNftId, PayoutId payoutId, Amount netPayoutAmount, Amount processingFeeAmount, address payoutBeneficiary);


default:
logger.info('Unhandeled event: ' + event.event_name);
}
}

return { nfts, instances, policies, components, risks, claims, payouts, requests };
return { nfts, instances, policies, components, risks, claims, payouts, requests, bundles };
}
}

Expand Down
23 changes: 23 additions & 0 deletions src/types/bundle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

export interface Bundle {
bundleNftId: bigint;
poolNftId: bigint;
lifetime: bigint;
locked: boolean;
closed: boolean;
balance: bigint;
lockedAmount: bigint;
created: {
blockNumber: number;
timestamp: bigint;
txHash: string;
from: string;
}
modified: {
blockNumber: number;
timestamp: bigint;
txHash: string;
from: string;
}
}

1 change: 0 additions & 1 deletion src/types/oracle_request.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { ObjectType } from "./objecttype";

export interface OracleRequest {
oracleNftId: bigint;
Expand Down

0 comments on commit 0b595fc

Please sign in to comment.