Skip to content

Commit

Permalink
add accept rule
Browse files Browse the repository at this point in the history
  • Loading branch information
luketchang committed Apr 29, 2024
1 parent 1dd243c commit 197aff6
Show file tree
Hide file tree
Showing 7 changed files with 274 additions and 25 deletions.
17 changes: 13 additions & 4 deletions actors/deposit-screener/src/screener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,13 +295,22 @@ export class DepositScreenerScreener {
DepositRequestStatus.FailedScreen
);
} else {
childLogger.info(
"deposit passed first screening stage. pushing to delay queue"
);
let delayCheckResult: Delay;
if (checkResult.type === "Accept") {
delayCheckResult = { type: "Delay", timeSeconds: 0 };
childLogger.info(
`deposit passed labeled accept. pushing to delay queue with delay of 0 sec. spender: ${depositRequest.spender}`
);
} else {
delayCheckResult = checkResult;
childLogger.info(
`deposit passed labeled delay. pushing to delay queue with delay of ${delayCheckResult.timeSeconds} sec. spender: ${depositRequest.spender}`
);
}
await this.scheduleSecondScreeningPhase(
childLogger,
depositRequest,
checkResult
delayCheckResult
);
await this.db.setDepositRequestStatus(
depositRequest,
Expand Down
27 changes: 20 additions & 7 deletions actors/deposit-screener/src/screening/checks/RuleSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ export interface Delay {
timeSeconds: number;
}

export function isRejection(obj: Rejection | Delay): obj is Rejection {
export interface Accept {
type: "Accept";
}

export function isRejection(obj: Rejection | Delay | Accept): obj is Rejection {
return obj.type === "Rejection";
}

Expand Down Expand Up @@ -63,7 +67,7 @@ export interface RuleParams<C extends ApiCallNames> {
name: string;
call: C;
threshold: (data: ApiCallToReturnType[C]) => boolean;
action: Rejection | DelayAction;
action: Rejection | DelayAction | Accept;
}

export type PartialRuleParams<C extends ApiCallNames> = Omit<
Expand All @@ -88,7 +92,7 @@ export interface RuleLike {
check: (
deposit: ScreeningDepositRequest,
cachedFetchOptions: CachedFetchOptions
) => Promise<Rejection | DelayAction | typeof ACTION_NOT_TRIGGERED>;
) => Promise<Rejection | DelayAction | Accept | typeof ACTION_NOT_TRIGGERED>;
}

export class Rule<C extends ApiCallNames> implements RuleLike {
Expand All @@ -113,7 +117,7 @@ export class Rule<C extends ApiCallNames> implements RuleLike {
async check(
deposit: ScreeningDepositRequest,
cachedFetchOptions: CachedFetchOptions
): Promise<Rejection | DelayAction | typeof ACTION_NOT_TRIGGERED> {
): Promise<Rejection | DelayAction | Accept | typeof ACTION_NOT_TRIGGERED> {
const data = (await API_CALL_MAP[this.call](
deposit,
this.cache,
Expand Down Expand Up @@ -147,7 +151,7 @@ export class CompositeRule<T extends ReadonlyArray<ApiCallNames>>
async check(
deposit: ScreeningDepositRequest,
cachedFetchOptions: CachedFetchOptions = {}
): Promise<Rejection | DelayAction | typeof ACTION_NOT_TRIGGERED> {
): Promise<Rejection | DelayAction | Accept | typeof ACTION_NOT_TRIGGERED> {
const results = await Promise.all(
this.partialRules.map(async (partial) => {
const data = (await API_CALL_MAP[partial.call](
Expand Down Expand Up @@ -208,7 +212,7 @@ export class RuleSet {
async check(
deposit: ScreeningDepositRequest,
cachedFetchOptions: CachedFetchOptions = {}
): Promise<Rejection | Delay> {
): Promise<Rejection | Delay | Accept> {
let delaySeconds = this.baseDelaySeconds;
let currRule = this.head;
const rulesLogList: {
Expand All @@ -231,6 +235,12 @@ export class RuleSet {
}
);
return result;
} else if (result.type === "Accept") {
this.logger.info(`Screener accepted addr ${deposit.spender}`, {
deposit: { ...deposit },
results: { ...toLoggable(rulesLogList) },
});
return result;
} else if (result.type === "Delay") {
delaySeconds = APPLY_DELAY_OPERATION[result.operation](
delaySeconds,
Expand All @@ -255,7 +265,10 @@ const toLoggable = (
ruleName: string;
result: Awaited<ReturnType<RuleLike["check"]>>;
}[]
): Record<string, Rejection | DelayAction | typeof ACTION_NOT_TRIGGERED> => {
): Record<
string,
Rejection | DelayAction | Accept | typeof ACTION_NOT_TRIGGERED
> => {
return rulesLogList.reduce((acc, { ruleName, result }) => {
acc[ruleName] = result;
return acc;
Expand Down
36 changes: 26 additions & 10 deletions actors/deposit-screener/src/screening/checks/v1/RULESET_V1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,26 +363,41 @@ const MIXER_USAGE_DELAY: RuleParams<"MISTTRACK_ADDRESS_RISK_SCORE"> = {
},
};

let ENV_BLACKLIST: string[] | undefined;
if (process.env.DEFAULT_BLOCKLIST) {
ENV_BLACKLIST = process.env.DEFAULT_BLOCKLIST.split(",").map(
ethers.utils.getAddress
);
}

const ENV_BLACKLIST_RULE: RuleParams<"IDENTITY"> = {
name: "ENV_BLACKLIST_RULE",
call: "IDENTITY",
threshold: (deposit: ScreeningDepositRequest) =>
ENV_BLACKLIST
threshold: (deposit: ScreeningDepositRequest) => {
const ENV_BLACKLIST = process.env.DEFAULT_BLOCKLIST
? process.env.DEFAULT_BLOCKLIST.split(",").map(ethers.utils.getAddress)
: [];

return ENV_BLACKLIST
? ENV_BLACKLIST.includes(ethers.utils.getAddress(deposit.spender))
: false,
: false;
},
action: {
type: "Rejection",
reason: "ENV_BLACKLIST",
},
};

const ENV_WHITELIST_RULE: RuleParams<"IDENTITY"> = {
name: "ENV_WHITELIST_RULE",
call: "IDENTITY",
threshold: (deposit: ScreeningDepositRequest) => {
const ENV_WHITELIST = process.env.DEFAULT_ALLOWLIST
? process.env.DEFAULT_ALLOWLIST.split(",").map(ethers.utils.getAddress)
: [];

return ENV_WHITELIST
? ENV_WHITELIST.includes(ethers.utils.getAddress(deposit.spender))
: false;
},
action: {
type: "Accept",
},
};

export const US_TIMEZONE_DELAY_RULE: RuleParams<"IDENTITY"> = {
name: "US_TIMEZONE_DELAY_RULE",
call: "IDENTITY",
Expand Down Expand Up @@ -446,6 +461,7 @@ export const RULESET_V1 = (redis: IORedis, logger: Logger): RuleSet => {
logger
)
.add(ENV_BLACKLIST_RULE)
.add(ENV_WHITELIST_RULE)
.add(US_TIMEZONE_DELAY_RULE)
.add(TRM_SEVERE_OWNERSHIP_REJECT)
.add(TRM_HIGH_OWNERSHIP_REJECT)
Expand Down
6 changes: 3 additions & 3 deletions actors/deposit-screener/src/screening/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Address } from "@nocturne-xyz/core";
import { RULESET_V1 } from "./checks/v1/RULESET_V1";
import { Delay, Rejection, RuleSet } from "./checks/RuleSet";
import { Accept, Delay, Rejection, RuleSet } from "./checks/RuleSet";
import IORedis from "ioredis";
import { CachedFetchOptions } from "@nocturne-xyz/offchain-utils";
import { requireApiKeys } from "../utils";
Expand All @@ -16,7 +16,7 @@ export interface ScreeningCheckerApi {
checkDeposit(
depositInfo: ScreeningDepositRequest,
cachedFetchOptions?: CachedFetchOptions
): Promise<Rejection | Delay>;
): Promise<Rejection | Delay | Accept>;
}

export class ConcreteScreeningChecker implements ScreeningCheckerApi {
Expand All @@ -29,7 +29,7 @@ export class ConcreteScreeningChecker implements ScreeningCheckerApi {

async checkDeposit(
depositInfo: ScreeningDepositRequest
): Promise<Rejection | Delay> {
): Promise<Rejection | Delay | Accept> {
return this.ruleset.check(depositInfo);
}
}
Expand Down
5 changes: 4 additions & 1 deletion actors/deposit-screener/src/waitEstimation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export async function estimateSecondsUntilCompletionForProspectiveDeposit(
// ensure passes screen
console.log("in estimateSecondsUntilCompletionForProspectiveDeposit");

const checkResult = await screeningApi.checkDeposit({
let checkResult = await screeningApi.checkDeposit({
spender,
assetAddr,
value,
Expand All @@ -135,6 +135,9 @@ export async function estimateSecondsUntilCompletionForProspectiveDeposit(
throw new Error(
`Prospective deposit request failed screening. reason: ${checkResult.reason} spender: ${spender}. assetAddr: ${assetAddr}, value: ${value}`
);
} else if (checkResult.type === "Accept") {
// If check result is accept treat as if it has no delay
checkResult = { type: "Delay", timeSeconds: 0 };
}

const fulfillerQueue = fulfillerQueues.get(assetAddr);
Expand Down
18 changes: 18 additions & 0 deletions actors/deposit-screener/test/RULESET_V1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ import moment from "moment-timezone";
import * as sinon from "sinon";
import { FIVE_ETHER } from "../src/screening/checks/v1/utils";

const WHITELISTED_ADDRESSES_1 = "0x47794AB20f45Bdc18ef6EcBcB19E1FdF82C6E8Db";
const WHITELISTED_ADDRESSES_2 = "0x52eb4040819bd0f49cb6ea01e5d33d328a25cc3f";

describe("RULESET_V1", () => {
let server: RedisMemoryServer;
let redis: IORedis;
Expand All @@ -35,6 +38,8 @@ describe("RULESET_V1", () => {
});

before(async () => {
process.env.DEFAULT_ALLOWLIST = `${WHITELISTED_ADDRESSES_1},${WHITELISTED_ADDRESSES_2}`;

server = await RedisMemoryServer.create();

const host = await server.getHost();
Expand Down Expand Up @@ -301,4 +306,17 @@ describe("RULESET_V1", () => {
type: "Delay",
});
});

it("should give 0 delay to whitelisted users", async () => {
process.env.DEFAULT_ALLOWLIST = `${WHITELISTED_ADDRESSES_1},${WHITELISTED_ADDRESSES_2}`;

const result1 = await ruleset.check(
formDepositInfo(WHITELISTED_ADDRESSES_1)
);
const result2 = await ruleset.check(
formDepositInfo(WHITELISTED_ADDRESSES_2)
);
expect(result1).to.deep.equal({ type: "Accept" });
expect(result2).to.deep.equal({ type: "Accept" });
});
});
Loading

0 comments on commit 197aff6

Please sign in to comment.