Skip to content

Commit

Permalink
feat: PRO-2395 SponsorshipPolicy repository functions and package ver…
Browse files Browse the repository at this point in the history
…sion upgrade for all 3 projects in arka
  • Loading branch information
kanthgithub committed Jun 14, 2024
1 parent 625afd0 commit 06e4617
Show file tree
Hide file tree
Showing 8 changed files with 242 additions and 14 deletions.
2 changes: 1 addition & 1 deletion admin_frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "admin_frontend",
"version": "1.2.6",
"version": "1.2.7",
"private": true,
"dependencies": {
"@emotion/react": "11.11.3",
Expand Down
10 changes: 5 additions & 5 deletions backend/indexer/ponder-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@ declare module "@/generated" {
PonderApp,
} from "@ponder/core";

type Config = typeof import("./ponder.config.ts").default;
type ArkaConfig = typeof import("./ponder.config.ts").default;
type Schema = typeof import("./ponder.schema.ts").default;

export const ponder: PonderApp<Config, Schema>;
export type EventNames = PonderEventNames<Config>;
export const ponder: PonderApp<ArkaConfig, Schema>;
export type EventNames = PonderEventNames<ArkaConfig>;
export type Event<name extends EventNames = EventNames> = PonderEvent<
Config,
ArkaConfig,
name
>;
export type Context<name extends EventNames = EventNames> = PonderContext<
Config,
ArkaConfig,
Schema,
name
>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ async function up({ context: queryInterface }) {
defaultValue: false,
field: 'IS_ENABLED'
},
isUniversal: {
isApplicableToAllNetworks: {
type: Sequelize.BOOLEAN,
defaultValue: false,
field: 'IS_UNIVERSAL'
field: 'IS_APPLICABLE_TO_ALL_NETWORKS'
},
enabledChains: {
type: Sequelize.ARRAY(Sequelize.INTEGER),
Expand Down
2 changes: 1 addition & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "arka",
"version": "1.2.6",
"version": "1.2.7",
"description": "ARKA - (Albanian for Cashier's case) is the first open source Paymaster as a service software",
"type": "module",
"directories": {
Expand Down
7 changes: 4 additions & 3 deletions backend/src/models/sponsorship-policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export class SponsorshipPolicy extends Model {
public description!: string | null;
public isPublic: boolean = false;
public isEnabled: boolean = false;
public isUniversal!: boolean;
public isApplicableToAllNetworks!: boolean;
public enabledChains?: number[];
public isPerpetual!: boolean;
public startTime!: Date | null;
Expand Down Expand Up @@ -53,6 +53,7 @@ export function initializeSponsorshipPolicyModel(sequelize: Sequelize, schema: s
SponsorshipPolicy.init({
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
field: 'ID'
},
Expand Down Expand Up @@ -87,10 +88,10 @@ export function initializeSponsorshipPolicyModel(sequelize: Sequelize, schema: s
defaultValue: false,
field: 'IS_ENABLED'
},
isUniversal: {
isApplicableToAllNetworks: {
type: DataTypes.BOOLEAN,
defaultValue: false,
field: 'IS_UNIVERSAL'
field: 'IS_APPLICABLE_TO_ALL_NETWORKS'
},
enabledChains: {
type: DataTypes.ARRAY(DataTypes.INTEGER),
Expand Down
227 changes: 227 additions & 0 deletions backend/src/repository/sponsorship-policy-repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
import { Sequelize, Op } from 'sequelize';
import { SponsorshipPolicy } from '../models/sponsorship-policy';

export class SponsorshipPolicyRepository {
private sequelize: Sequelize;

constructor(sequelize: Sequelize) {
this.sequelize = sequelize;
}

async findAll(): Promise<SponsorshipPolicy[]> {
const result = await this.sequelize.models.SponsorshipPolicy.findAll();
return result.map(apiKey => apiKey.get() as SponsorshipPolicy);
}

// findAllInADateRange must use the model fields startTime and endTime to filter the results
// user will pass the date range and the query must compare if the startTime and endTime are within the range
// if the policy is perpetual, then it should always be returned
async findAllInADateRange(startDate: Date, endDate: Date): Promise<SponsorshipPolicy[]> {
const result = await this.sequelize.models.SponsorshipPolicy.findAll({
where: {
[Op.or]: [
{
startTime: {
[Op.lte]: endDate
},
endTime: {
[Op.gte]: startDate
}
},
{
isPerpetual: true
}
]
}
});
return result.map(apiKey => apiKey as SponsorshipPolicy);
}

async findAllEnabled(): Promise<SponsorshipPolicy[]> {
const result = await this.sequelize.models.SponsorshipPolicy.findAll({ where: { isEnabled: true } });
return result.map(apiKey => apiKey.get() as SponsorshipPolicy);
}

async findAllEnabledAndApplicable(): Promise<SponsorshipPolicy[]> {
const result = await this.sequelize.models.SponsorshipPolicy.findAll({ where: { isEnabled: true } });
return result.map(apiKey => apiKey.get() as SponsorshipPolicy).filter(apiKey => apiKey.isApplicable);
}

async findLatestEnabledAndApplicable(): Promise<SponsorshipPolicy | null> {
const result = await this.sequelize.models.SponsorshipPolicy.findOne({ where: { isEnabled: true }, order: [['createdAt', 'DESC']] });
return result ? result.get() as SponsorshipPolicy : null;
}

async findOneByWalletAddress(walletAddress: string): Promise<SponsorshipPolicy | null> {
const result = await this.sequelize.models.SponsorshipPolicy.findOne({ where: { walletAddress: walletAddress } });
return result ? result.get() as SponsorshipPolicy : null;
}

async findOneByPolicyName(name: string): Promise<SponsorshipPolicy | null> {
const result = await this.sequelize.models.SponsorshipPolicy.findOne({ where: { name: name } });
return result ? result.get() as SponsorshipPolicy : null;
}

async findOneById(id: number): Promise<SponsorshipPolicy | null> {
const result = await this.sequelize.models.SponsorshipPolicy.findOne({ where: { id: id } });
return result ? result.get() as SponsorshipPolicy : null;
}

async createSponsorshipPolicy(sponsorshipPolicy: SponsorshipPolicy): Promise<SponsorshipPolicy> {
this.validateSponsorshipPolicy(sponsorshipPolicy);

const result = await this.sequelize.models.SponsorshipPolicy.create({
walletAddress: sponsorshipPolicy.walletAddress,
name: sponsorshipPolicy.name,
description: sponsorshipPolicy.description,
isPublic: sponsorshipPolicy.isPublic,
isEnabled: sponsorshipPolicy.isEnabled,
isApplicableToAllNetworks: sponsorshipPolicy.isApplicableToAllNetworks,
enabledChains: sponsorshipPolicy.enabledChains,
isPerpetual: sponsorshipPolicy.isPerpetual,
startTime: sponsorshipPolicy.startTime,
endTime: sponsorshipPolicy.endTime,
globalMaximumApplicable: sponsorshipPolicy.globalMaximumApplicable,
globalMaximumUsd: sponsorshipPolicy.globalMaximumUsd,
globalMaximumNative: sponsorshipPolicy.globalMaximumNative,
globalMaximumOpCount: sponsorshipPolicy.globalMaximumOpCount,
perUserMaximumApplicable: sponsorshipPolicy.perUserMaximumApplicable,
perUserMaximumUsd: sponsorshipPolicy.perUserMaximumUsd,
perUserMaximumNative: sponsorshipPolicy.perUserMaximumNative,
perUserMaximumOpCount: sponsorshipPolicy.perUserMaximumOpCount,
perOpMaximumApplicable: sponsorshipPolicy.perOpMaximumApplicable,
perOpMaximumUsd: sponsorshipPolicy.perOpMaximumUsd,
perOpMaximumNative: sponsorshipPolicy.perOpMaximumNative,
addressAllowList: sponsorshipPolicy.addressAllowList,
addressBlockList: sponsorshipPolicy.addressBlockList
});

return result.get() as SponsorshipPolicy;
}

async updateSponsorshipPolicy(sponsorshipPolicy: SponsorshipPolicy): Promise<SponsorshipPolicy> {

// check if sponsorship policy exists (by primary key id)
const existingSponsorshipPolicy = await this.findOneById(sponsorshipPolicy.id as number);

if (!existingSponsorshipPolicy) {
throw new Error('Sponsorship Policy not found');
}

this.validateSponsorshipPolicy(sponsorshipPolicy);

existingSponsorshipPolicy.name = sponsorshipPolicy.name;
existingSponsorshipPolicy.description = sponsorshipPolicy.description;
existingSponsorshipPolicy.isApplicableToAllNetworks = sponsorshipPolicy.isApplicableToAllNetworks;
existingSponsorshipPolicy.isPerpetual = sponsorshipPolicy.isPerpetual;
// if marked as IsPerpetual, then set startTime and endTime to null
if (sponsorshipPolicy.isPerpetual) {
existingSponsorshipPolicy.startTime = null;
existingSponsorshipPolicy.endTime = null;
} else {
existingSponsorshipPolicy.startTime = sponsorshipPolicy.startTime;
existingSponsorshipPolicy.endTime = sponsorshipPolicy.endTime;
}
existingSponsorshipPolicy.globalMaximumApplicable = sponsorshipPolicy.globalMaximumApplicable;
existingSponsorshipPolicy.globalMaximumUsd = sponsorshipPolicy.globalMaximumUsd;
existingSponsorshipPolicy.globalMaximumNative = sponsorshipPolicy.globalMaximumNative;
existingSponsorshipPolicy.globalMaximumOpCount = sponsorshipPolicy.globalMaximumOpCount;
existingSponsorshipPolicy.perUserMaximumApplicable = sponsorshipPolicy.perUserMaximumApplicable;
existingSponsorshipPolicy.perUserMaximumNative = sponsorshipPolicy.perUserMaximumNative;
existingSponsorshipPolicy.perUserMaximumOpCount = sponsorshipPolicy.perUserMaximumOpCount;
existingSponsorshipPolicy.perUserMaximumUsd = sponsorshipPolicy.perUserMaximumUsd;
existingSponsorshipPolicy.perOpMaximumApplicable = sponsorshipPolicy.perOpMaximumApplicable;
existingSponsorshipPolicy.perOpMaximumNative = sponsorshipPolicy.perOpMaximumNative;
existingSponsorshipPolicy.perOpMaximumUsd = sponsorshipPolicy.perOpMaximumUsd;
existingSponsorshipPolicy.isPublic = sponsorshipPolicy.isPublic;
existingSponsorshipPolicy.addressAllowList = sponsorshipPolicy.addressAllowList;
existingSponsorshipPolicy.addressBlockList = sponsorshipPolicy.addressBlockList;

const result = await existingSponsorshipPolicy.save();
return result.get() as SponsorshipPolicy;
}

validateSponsorshipPolicy(sponsorshipPolicy: SponsorshipPolicy) {
let errors: string[] = [];

if (!sponsorshipPolicy.name || !sponsorshipPolicy.description) {
errors.push('Name and description are required fields');
}

if (!sponsorshipPolicy.isApplicableToAllNetworks) {
if (!sponsorshipPolicy.enabledChains || sponsorshipPolicy.enabledChains.length === 0) {
errors.push('Enabled chains are required');
}
}

if (!sponsorshipPolicy.isPerpetual) {
if (!sponsorshipPolicy.startTime || !sponsorshipPolicy.endTime) {
errors.push('Start and End time are required fields');
}

if (sponsorshipPolicy.startTime && sponsorshipPolicy.endTime) {
if (sponsorshipPolicy.startTime < new Date() || sponsorshipPolicy.endTime < new Date() || sponsorshipPolicy.endTime < sponsorshipPolicy.startTime) {
errors.push('Invalid start and end time');
}
}
}

if (sponsorshipPolicy.globalMaximumApplicable) {
if (!sponsorshipPolicy.globalMaximumUsd && !sponsorshipPolicy.globalMaximumNative && !sponsorshipPolicy.globalMaximumOpCount) {
errors.push('At least 1 Global maximum value is required');
}
}

if (sponsorshipPolicy.perUserMaximumApplicable) {
if (!sponsorshipPolicy.perUserMaximumUsd && !sponsorshipPolicy.perUserMaximumNative && !sponsorshipPolicy.perUserMaximumOpCount) {
errors.push('At least 1 Per User maximum value is required');
}
}

if (sponsorshipPolicy.perOpMaximumApplicable) {
if (!sponsorshipPolicy.perOpMaximumUsd && !sponsorshipPolicy.perOpMaximumNative) {
errors.push('At least 1 Per Op maximum value is required');
}
}

if (errors.length > 0) {
throw new Error(errors.join(', '));
}
}

async disableSponsorshipPolicy(id: number): Promise<void> {
const existingSponsorshipPolicy = await this.findOneById(id);

if (!existingSponsorshipPolicy) {
throw new Error('Sponsorship Policy not found');
}

existingSponsorshipPolicy.isEnabled = false;
await existingSponsorshipPolicy.save();
}

async enableSponsorshipPolicy(id: number): Promise<void> {
const existingSponsorshipPolicy = await this.findOneById(id);

if (!existingSponsorshipPolicy) {
throw new Error('Sponsorship Policy not found');
}

existingSponsorshipPolicy.isEnabled = true;
await existingSponsorshipPolicy.save();
}

async deleteSponsorshipPolicy(id: number): Promise<void> {
const existingSponsorshipPolicy = await this.findOneById(id);

if (!existingSponsorshipPolicy) {
throw new Error('Sponsorship Policy not found');
}

await existingSponsorshipPolicy.destroy();
}

async deleteAllSponsorshipPolicies(): Promise<void> {
await this.sequelize.models.SponsorshipPolicy.destroy({ where: {} });
}
}
2 changes: 1 addition & 1 deletion backend/src/types/sponsorship-policy-dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export interface SponsorshipPolicyDto {
description: string; // Description of the sponsorship policy
isPublic: boolean; // Flag to indicate if the policy is public
isEnabled: boolean; // Flag to indicate if the policy is enabled
isUniversal: boolean; // Flag to indicate if the policy is universal
isApplicableToAllNetworks: boolean; // Flag to indicate if the policy is universal
enabledChains?: number[]; // Array of enabled chain IDs
isPerpetual: boolean; // Flag to indicate if the policy is perpetual
startDate?: string; // Optional start date for the policy
Expand Down
2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "arka_frontend",
"version": "1.2.6",
"version": "1.2.7",
"private": true,
"dependencies": {
"@babel/plugin-proposal-private-property-in-object": "7.21.11",
Expand Down

0 comments on commit 06e4617

Please sign in to comment.