Skip to content

Commit

Permalink
feat: generalize segmentation rules
Browse files Browse the repository at this point in the history
  • Loading branch information
cdotta committed Nov 4, 2024
1 parent aae1a13 commit 6e043eb
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 115 deletions.
33 changes: 30 additions & 3 deletions apps/api/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ generator client {
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
schemas = ["public", "smart_account"]
schemas = ["public", "smart_account", "treasure_account"]
}

// Partner within the Treasure ecosystem
Expand Down Expand Up @@ -255,16 +255,25 @@ model TransactionErrorLog {

view Transaction {
id String @id
chain String
chainId Int @map("chain_id")
blockNumber Decimal @map("block_number") @db.Decimal
blockTimestamp DateTime @map("block_timestamp")
transactionHash String @map("transaction_hash")
fromAddress String @map("from_address")
toAddress String @map("to_address")
value Decimal? @db.Decimal
value Decimal? @db.Decimal
transactionIndex Decimal? @map("transaction_index") @db.Decimal
receiptEffectiveGasPrice Decimal? @map("receipt_effective_gas_price") @db.Decimal
receiptStatus Decimal? @map("receipt_status") @db.Decimal
receiptGasUsed Decimal? @map("receipt_gas_used") @db.Decimal
gasUsed Decimal? @map("gas_used") @db.Decimal
receiptCumulativeGasUsed Decimal? @map("receipt_cumulative_gas_used") @db.Decimal
gas Decimal? @db.Decimal
@@map("transactions")
@@schema("smart_account")
@@schema("treasure_account")
}

// Ignored models and views added here for diff parity
Expand All @@ -285,3 +294,21 @@ model RawTreasureAccount {
@@ignore
@@schema("smart_account")
}

model RawTransactionTreasureAccount {
key String @id @map("_key")
body Json
@@map("transactions_jsonb")
@@ignore
@@schema("treasure_account")
}

model RawTreasureAccountTreasureAccount {
key String @id @map("_key")
body Json
@@map("treasure_accounts_jsonb")
@@ignore
@@schema("treasure_account")
}
4 changes: 2 additions & 2 deletions apps/api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ import { withCors } from "./middleware/cors";
import { withErrorHandler } from "./middleware/error";
import { withSwagger } from "./middleware/swagger";
import { authRoutes } from "./routes/auth";
import { gasSponsorshipRoutes } from "./routes/gas-sponsorship";
import { harvestersRoutes } from "./routes/harvesters";
import { magicswapRoutes } from "./routes/magicswap";
import { partnersRoutes } from "./routes/partners";
import { projectsRoutes } from "./routes/projects";
import { transactionsRoutes } from "./routes/transactions";
import { usersRoutes } from "./routes/users";
Expand Down Expand Up @@ -126,7 +126,7 @@ const main = async () => {
app.register(transactionsRoutes(ctx));
app.register(harvestersRoutes(ctx));
app.register(magicswapRoutes(ctx));
app.register(gasSponsorshipRoutes(ctx));
app.register(partnersRoutes(ctx));

app.get("/healthcheck", async (_, reply) => {
try {
Expand Down
110 changes: 0 additions & 110 deletions apps/api/src/routes/gas-sponsorship.ts

This file was deleted.

87 changes: 87 additions & 0 deletions apps/api/src/routes/partners.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import type { FastifyPluginAsync } from "fastify";

import type { ErrorReply } from "../schema";
import {
type ValidateBody,
type ValidateParams,
type ValidateReply,
validateBodySchema,
validateReplySchema,
} from "../schema/gas-sponsorship";
import type { TdkApiContext } from "../types";
import { validateSegmentationRules } from "../utils/segmentation";

export const partnersRoutes =
({ db }: TdkApiContext): FastifyPluginAsync =>
async (app) => {
app.post<{
Params: ValidateParams;
Body: ValidateBody;
Reply: ValidateReply | ErrorReply;
}>(
"/partners/:partnerId/gas-sponsorship",
{
schema: {
body: validateBodySchema,
response: {
200: validateReplySchema,
},
},
},
async (req, reply) => {
const { body, params } = req;

const oneMonthAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);

const lastMonthTransactions = await db.transaction.count({
where: {
fromAddress: body.userOp.sender,
blockTimestamp: {
gte: oneMonthAgo,
},
},
});

const {
_sum: { gas: lastMonthGasRaw },
} = await db.transaction.aggregate({
where: {
fromAddress: body.userOp.sender,
blockTimestamp: {
gte: oneMonthAgo,
},
},
_sum: {
gas: true,
},
});

const lastMonthGas =
lastMonthGasRaw?.toNumber() ?? Number.MAX_SAFE_INTEGER;

const { isAllowed, reason } = validateSegmentationRules(
{
partner: {
ids: ["zeeverse", "smols"],
},
user: {
maxTransactions: 100,
maxGas: 1000,
},
},
{
partner: { id: params.partnerId },
user: {
transactionsCount: lastMonthTransactions,
gas: lastMonthGas,
},
},
);

reply.send({
isAllowed,
reason,
});
},
);
};
63 changes: 63 additions & 0 deletions apps/api/src/utils/segmentation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
type Segment = {
partner: {
ids: string[];
};
user: {
maxTransactions: number;
maxGas: number;
};
};

type SegmentData = {
partner: {
id: string;
};
user: {
transactionsCount: number;
gas: number;
};
};

export const validateSegmentationRules = (
segment: Segment,
data: SegmentData,
) => {
const {
partner: { id },
user: { transactionsCount, gas },
} = data;

const isValidPartner = segment.partner.ids.includes(id);
const hasNotExceededTransactions =
transactionsCount <= segment.user.maxTransactions;
const hasNotExceededGas = gas <= segment.user.maxGas;

const rules = [
{
condition: isValidPartner,
reason: "partner is valid",
},
{
condition: hasNotExceededTransactions,
reason: "not exceeded the maximum number of transactions",
},
{
condition: hasNotExceededGas,
reason: "not exceeded the maximum gas",
},
];

const approvedRule = rules.find((rule) => rule.condition);

if (approvedRule) {
return {
isAllowed: true,
reason: approvedRule.reason,
};
}

return {
isAllowed: false,
reason: "does not meet any rule",
};
};

0 comments on commit 6e043eb

Please sign in to comment.