Skip to content
This repository was archived by the owner on Feb 8, 2025. It is now read-only.

fix(api): inserting simulation of self-transfer #272

Merged
merged 1 commit into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 70 additions & 70 deletions api/dbschema/edgeql-js/__spec__.ts

Large diffs are not rendered by default.

202 changes: 101 additions & 101 deletions api/dbschema/edgeql-js/modules/default.ts

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions api/dbschema/edgeql-js/modules/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -700,14 +700,14 @@ export type $ObjectTypeλShape = $.typeutil.flatten<$SourceλShape & Omit<$Consi
"<__type__[is Result]": $.LinkDesc<_default.$Result, $.Cardinality.Many, {}, false, false, false, false>;
"<__type__[is ReceiptResult]": $.LinkDesc<_default.$ReceiptResult, $.Cardinality.Many, {}, false, false, false, false>;
"<__type__[is Successful]": $.LinkDesc<_default.$Successful, $.Cardinality.Many, {}, false, false, false, false>;
"<__type__[is Approver]": $.LinkDesc<_default.$Approver, $.Cardinality.Many, {}, false, false, false, false>;
"<__type__[is current_approver]": $.LinkDesc<_default.$current_approver, $.Cardinality.Many, {}, false, false, false, false>;
"<__type__[is Rejection]": $.LinkDesc<_default.$Rejection, $.Cardinality.Many, {}, false, false, false, false>;
"<__type__[is ApproverDetails]": $.LinkDesc<_default.$ApproverDetails, $.Cardinality.Many, {}, false, false, false, false>;
"<__type__[is Message]": $.LinkDesc<_default.$Message, $.Cardinality.Many, {}, false, false, false, false>;
"<__type__[is Operation]": $.LinkDesc<_default.$Operation, $.Cardinality.Many, {}, false, false, false, false>;
"<__type__[is Transaction]": $.LinkDesc<_default.$Transaction, $.Cardinality.Many, {}, false, false, false, false>;
"<__type__[is UserLabelled]": $.LinkDesc<_default.$UserLabelled, $.Cardinality.Many, {}, false, false, false, false>;
"<__type__[is Token]": $.LinkDesc<_default.$Token, $.Cardinality.Many, {}, false, false, false, false>;
"<__type__[is Approver]": $.LinkDesc<_default.$Approver, $.Cardinality.Many, {}, false, false, false, false>;
"<__type__[is current_approver]": $.LinkDesc<_default.$current_approver, $.Cardinality.Many, {}, false, false, false, false>;
"<__type__[is User]": $.LinkDesc<_default.$User, $.Cardinality.Many, {}, false, false, false, false>;
"<__type__[is current_user]": $.LinkDesc<_default.$current_user, $.Cardinality.Many, {}, false, false, false, false>;
"<__type__[is Contact]": $.LinkDesc<_default.$Contact, $.Cardinality.Many, {}, false, false, false, false>;
Expand All @@ -717,6 +717,7 @@ export type $ObjectTypeλShape = $.typeutil.flatten<$SourceλShape & Omit<$Consi
"<__type__[is Transferlike]": $.LinkDesc<_default.$Transferlike, $.Cardinality.Many, {}, false, false, false, false>;
"<__type__[is Transfer]": $.LinkDesc<_default.$Transfer, $.Cardinality.Many, {}, false, false, false, false>;
"<__type__[is TransferApproval]": $.LinkDesc<_default.$TransferApproval, $.Cardinality.Many, {}, false, false, false, false>;
"<__type__[is Rejection]": $.LinkDesc<_default.$Rejection, $.Cardinality.Many, {}, false, false, false, false>;
"<__type__[is ActionFunction]": $.LinkDesc<_default.$ActionFunction, $.Cardinality.Many, {}, false, false, false, false>;
"<__type__[is Action]": $.LinkDesc<_default.$Action, $.Cardinality.Many, {}, false, false, false, false>;
"<__type__[is TransferLimit]": $.LinkDesc<_default.$TransferLimit, $.Cardinality.Many, {}, false, false, false, false>;
Expand All @@ -730,7 +731,6 @@ export type $ObjectTypeλShape = $.typeutil.flatten<$SourceλShape & Omit<$Consi
"<__type__[is PaymasterFees]": $.LinkDesc<_default.$PaymasterFees, $.Cardinality.Many, {}, false, false, false, false>;
"<__type__[is Function]": $.LinkDesc<_default.$Function, $.Cardinality.Many, {}, false, false, false, false>;
"<__type__[is Contract]": $.LinkDesc<_default.$Contract, $.Cardinality.Many, {}, false, false, false, false>;
"<__type__[is ApproverDetails]": $.LinkDesc<_default.$ApproverDetails, $.Cardinality.Many, {}, false, false, false, false>;
"<__type__": $.LinkDesc<$.ObjectType, $.Cardinality.Many, {}, false, false, false, false>;
"<intersection_of": $.LinkDesc<$.ObjectType, $.Cardinality.Many, {}, false, false, false, false>;
"<subject": $.LinkDesc<$.ObjectType, $.Cardinality.Many, {}, false, false, false, false>;
Expand Down
26 changes: 13 additions & 13 deletions api/dbschema/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,12 @@ export namespace $default {
"implementation": string;
"photo"?: string | null;
"salt": string;
"policies": Policy[];
"approvers": Approver[];
"messages": Message[];
"proposals": Proposal[];
"transactions": Transaction[];
"transfers": Transfer[];
"policies": Policy[];
}
export interface Action extends std.$Object {
"functions": ActionFunction[];
Expand All @@ -119,18 +119,18 @@ export namespace $default {
export type ApprovalIssue = "HashMismatch" | "Expired";
export interface Approver extends std.$Object {
"address": string;
"user": User;
"labelled"?: Labelled | null;
"accounts": Account[];
"details"?: ApproverDetails | null;
"label"?: string | null;
"user": User;
"accounts": Account[];
}
export interface ApproverDetails extends std.$Object {
"approver": Approver;
"name"?: string | null;
"bluetoothDevices"?: string[] | null;
"name"?: string | null;
"cloud"?: {provider: CloudProvider, subject: string} | null;
"pushToken"?: string | null;
"approver": Approver;
}
export type CloudProvider = "Apple" | "Google";
export interface UserLabelled extends Labelled {}
Expand All @@ -145,11 +145,11 @@ export namespace $default {
export interface Event extends std.$Object {
"account": Account;
"block": bigint;
"internal": boolean;
"logIndex": number;
"systxHash": string;
"timestamp": Date;
"systx"?: SystemTx | null;
"internal": boolean;
}
export interface Result extends std.$Object {
"timestamp": Date;
Expand Down Expand Up @@ -185,9 +185,9 @@ export namespace $default {
"timestamp": Date;
"validationErrors": {reason: string, operation: number}[];
"approvals": Approval[];
"policy": Policy;
"proposedBy": Approver;
"rejections": Rejection[];
"policy": Policy;
}
export interface Message extends Proposal {
"signature"?: string | null;
Expand All @@ -214,9 +214,9 @@ export namespace $default {
"draft"?: PolicyState | null;
"proposal"?: Transaction | null;
"initState": boolean;
"isActive": boolean;
"isDraft": boolean;
"latest"?: PolicyState | null;
"isActive": boolean;
"isLatest": boolean;
}
export interface Policy extends PolicyState {
Expand Down Expand Up @@ -256,16 +256,16 @@ export namespace $default {
"result"?: Result | null;
}
export interface Token extends UserLabelled {
"units"?: {symbol: string, decimals: number}[] | null;
"address": string;
"symbol": string;
"name": string;
"decimals": number;
"isFeeToken": boolean;
"icon"?: string | null;
"pythUsdPriceId"?: string | null;
"user"?: User | null;
"isSystem": boolean;
"units"?: {symbol: string, decimals: number}[] | null;
"name": string;
"symbol": string;
}
export interface Transaction extends Proposal {
"maxAmount": string;
Expand All @@ -291,10 +291,10 @@ export namespace $default {
"token"?: Token | null;
"amount": string;
"from": string;
"isFeeTransfer": boolean;
"to": string;
"incoming": boolean;
"isFeeTransfer": boolean;
"outgoing": boolean;
"to": string;
}
export interface Transferlike extends Event, TransferDetails {
"spentBy"?: Policy | null;
Expand Down
28 changes: 28 additions & 0 deletions api/src/feat/simulations/insert-simulation.edgeql
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
with tx := (select Transaction filter .id = <uuid>$transaction),
transfers := <array<json>>$transfers,
account := tx.account
select {
prevSimulation := (delete tx.simulation),
tx := (
update tx set {
executable := <bool>$executable,
simulation := (insert Simulation {
success := <bool>$success,
responses := <array<Bytes>>$responses,
transfers := assert_distinct((
for transfer in array_unpack(transfers) union (
insert TransferDetails {
account := account,
from := <Address>transfer['from'],
to := <Address>transfer['to'],
tokenAddress := <UAddress>transfer['tokenAddress'],
amount := <decimal><str>transfer['amount'],
incoming := <bool>transfer['incoming'],
outgoing := <bool>transfer['outgoing']
}
) if count(transfers) > 0 else {}
))
})
}
)
}
53 changes: 53 additions & 0 deletions api/src/feat/simulations/insert-simulation.query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// GENERATED by @edgedb/generate v0.5.3

import type {Executor} from "edgedb";

export type InsertSimulationArgs = {
readonly "transaction": string;
readonly "transfers": ReadonlyArray<unknown>;
readonly "executable": boolean;
readonly "success": boolean;
readonly "responses": ReadonlyArray<string>;
};

export type InsertSimulationReturns = {
"prevSimulation": {
"id": string;
} | null;
"tx": {
"id": string;
} | null;
};

export function insertSimulation(client: Executor, args: InsertSimulationArgs): Promise<InsertSimulationReturns> {
return client.queryRequiredSingle(`\
with tx := (select Transaction filter .id = <uuid>$transaction),
transfers := <array<json>>$transfers,
account := tx.account
select {
prevSimulation := (delete tx.simulation),
tx := (
update tx set {
executable := <bool>$executable,
simulation := (insert Simulation {
success := <bool>$success,
responses := <array<Bytes>>$responses,
transfers := assert_distinct((
for transfer in array_unpack(transfers) union (
insert TransferDetails {
account := account,
from := <Address>transfer['from'],
to := <Address>transfer['to'],
tokenAddress := <UAddress>transfer['tokenAddress'],
amount := <decimal><str>transfer['amount'],
incoming := <bool>transfer['incoming'],
outgoing := <bool>transfer['outgoing']
}
) if count(transfers) > 0 else {}
))
})
}
)
}`, args);

}
60 changes: 18 additions & 42 deletions api/src/feat/simulations/simulations.worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import { DatabaseService } from '~/core/database';
import e, { $infer } from '~/edgeql-js';
import { and } from '~/core/database';
import { OperationsService } from '../operations/operations.service';
import { selectAccount } from '../accounts/accounts.util';
import { SwapOp, TransferFromOp, TransferOp } from '../operations/operations.model';
import { RUNNING_JOB_STATUSES, TypedJob, createQueue } from '~/core/bull/bull.util';
import { Worker } from '~/core/bull/Worker';
Expand All @@ -33,6 +32,7 @@ import { selectTransaction } from '../transactions/transactions.util';
import { SelectedPolicies, selectPolicy } from '../policies/policies.util';
import { ProposalsService } from '../proposals/proposals.service';
import { ProposalEvent } from '../proposals/proposals.input';
import { insertSimulation } from './insert-simulation.query';

export const SimulationsQueue = createQueue<{ transaction: UUID | Hex }>('Simulations');
export type SimulationsQueue = typeof SimulationsQueue;
Expand Down Expand Up @@ -66,27 +66,26 @@ export class SimulationsWorker extends Worker<SimulationsQueue> {

async process(job: TypedJob<SimulationsQueue>) {
const proposal = selectTransaction(job.data.transaction);
const p = await this.db.query(
const t = await this.db.query(
e.select(proposal, () => ({
id: true,
...TransactionExecutableShape,
})),
);
if (!p) return 'Transaction not found';
if (!t) return 'Transaction not found';

const promisedExecutable = this.isExecutable(p);
const account = asUAddress(p.account.address);
const promisedExecutable = this.isExecutable(t);
const account = asUAddress(t.account.address);
const localAccount = asAddress(account);
const chain = asChain(account);
const selectedAccount = selectAccount(account);

const simulations = await Promise.all(
p.operations.map(async (op) =>
t.operations.map(async (op) =>
(
await simulate({
network: this.networks.get(chain),
account: localAccount,
gas: p.gasLimit,
gas: t.gasLimit,
type: 'eip712',
to: asAddress(op.to),
value: op.value ?? 0n,
Expand Down Expand Up @@ -117,7 +116,7 @@ export class SimulationsWorker extends Worker<SimulationsQueue> {
.filter(isTruthy);

const transfers: Omit<TransferDetails, 'account'>[] = [];
for (const op of p.operations) {
for (const op of t.operations) {
if (op.value) {
transfers.push({
from: localAccount,
Expand All @@ -142,7 +141,7 @@ export class SimulationsWorker extends Worker<SimulationsQueue> {
from: localAccount,
to: f.to,
tokenAddress: asUAddress(f.token, chain),
amount: f.to === localAccount ? e.decimal('0') : f.amount.negated().toString(),
amount: f.to === localAccount ? '0' : f.amount.negated().toString(),
incoming: localAccount === f.to,
outgoing: true,
});
Expand All @@ -168,38 +167,15 @@ export class SimulationsWorker extends Worker<SimulationsQueue> {
}

const executable = await promisedExecutable;
await this.db.query(
e.select({
prevSimulation: e.delete(proposal.simulation, () => ({})),
proposal: e.update(proposal, () => ({
set: {
executable,
simulation: e.insert(e.Simulation, {
success,
responses,
...(transfers.length && {
transfers: e.with(
[selectedAccount],
e.for(e.set(...transfers.map((t) => e.json(t))), (t) =>
e.insert(e.TransferDetails, {
account: selectedAccount,
from: e.cast(e.Address, t.from),
to: e.cast(e.Address, t.to),
tokenAddress: e.cast(e.UAddress, t.tokenAddress),
amount: e.cast(e.decimal, e.cast(e.str, t.amount)),
incoming: e.cast(e.bool, t.incoming),
outgoing: e.cast(e.bool, t.outgoing),
}),
),
),
}),
}),
},
})),
}),
);

this.proposals.event({ id: asUUID(p.id), account }, ProposalEvent.simulated);
await this.db.exec(insertSimulation, {
transaction: t.id,
executable,
success,
responses,
transfers,
});

this.proposals.event({ id: asUUID(t.id), account }, ProposalEvent.simulated);

return { executable };
}
Expand Down
Loading