Skip to content

Commit

Permalink
Merge pull request #287 from zallo-labs/Z-341-responsive-transaction
Browse files Browse the repository at this point in the history
Z 341 responsive transaction
  • Loading branch information
hbriese authored Aug 18, 2024
2 parents 430c98c + 98d7965 commit d0a7afe
Show file tree
Hide file tree
Showing 95 changed files with 2,093 additions and 1,637 deletions.
Binary file not shown.
2 changes: 1 addition & 1 deletion api/dbschema/edgeql-js/__spec__.ts

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions api/dbschema/edgeql-js/modules/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,10 @@ const NonEmptyStr: $.scalarTypeWithConstructor<_std.$str, never> = $.makeType<$.
export type $TransactionStatus = {
"Pending": $.$expr_Literal<$TransactionStatus>;
"Scheduled": $.$expr_Literal<$TransactionStatus>;
"Executing": $.$expr_Literal<$TransactionStatus>;
"Successful": $.$expr_Literal<$TransactionStatus>;
"Failed": $.$expr_Literal<$TransactionStatus>;
"Cancelled": $.$expr_Literal<$TransactionStatus>;
} & $.EnumType<"default::TransactionStatus", ["Pending", "Scheduled", "Executing", "Successful", "Failed", "Cancelled"]>;
} & $.EnumType<"default::TransactionStatus", ["Pending", "Scheduled", "Successful", "Failed", "Cancelled"]>;
const TransactionStatus: $TransactionStatus = $.makeType<$TransactionStatus>(_.spec, "d2eb5a82-5489-11ef-a64b-c5eeac9d34b6", _.syntax.literal);

export type $UAddress = $.ScalarType<"std::str", string>;
Expand Down
4 changes: 1 addition & 3 deletions api/dbschema/events.esdl
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ module default {
default := true;
rewrite insert, update using (exists __subject__.result);
};
required confirmed: bool {
# rewrite insert, update using ((__subject__.result is Confirmed) ?? true);
}
required confirmed: bool; # rewrite insert, update using ((__subject__.result is Confirmed) ?? true);

access policy members_can_select
allow select
Expand Down
2 changes: 1 addition & 1 deletion api/dbschema/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ export namespace $default {
"executable": boolean;
"status": TransactionStatus;
}
export type TransactionStatus = "Pending" | "Scheduled" | "Executing" | "Successful" | "Failed" | "Cancelled";
export type TransactionStatus = "Pending" | "Scheduled" | "Successful" | "Failed" | "Cancelled";
export interface Transferlike extends Event {
"tokenAddress": string;
"token"?: Token | null;
Expand Down
18 changes: 18 additions & 0 deletions api/dbschema/migrations/00005-m174x3t.edgeql
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
CREATE MIGRATION m174x3tft7rqwxzekegp7qad4qlscof7tojfcmw4wielhp7h4mqsya
ONTO m17qamgbzvrq5axhdxhdn526i3wz42l5yt3fjdgog73ihlovmrb2sq
{
ALTER TYPE default::Result {
ALTER TRIGGER update_tx_result WHEN (((__new__.timestamp >= __new__.transaction.result.timestamp) ?? true));
};
ALTER TYPE default::Transaction {
ALTER PROPERTY status {
USING (WITH
result :=
.result
SELECT
std::assert_exists((default::TransactionStatus.Pending IF (((result IS default::SimulatedSuccess) OR (result IS default::SimulatedFailure)) ?? true) ELSE (default::TransactionStatus.Successful IF (((result IS default::OptimisticSuccess) OR (result IS default::ConfirmedSuccess)) ?? false) ELSE (default::TransactionStatus.Failed IF (result IS default::ConfirmedFailure) ELSE (default::TransactionStatus.Scheduled IF NOT (result[IS default::Scheduled].cancelled) ELSE default::TransactionStatus.Cancelled)))))
);
};
};
ALTER SCALAR TYPE default::TransactionStatus EXTENDING enum<Pending, Scheduled, Successful, Failed, Cancelled>;
};
8 changes: 4 additions & 4 deletions api/dbschema/transaction.esdl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module default {
required activation: decimal { constraint min_value(0); default := 0; }
}

scalar type TransactionStatus extending enum<'Pending', 'Scheduled', 'Executing', 'Successful', 'Failed', 'Cancelled'>;
scalar type TransactionStatus extending enum<'Pending', 'Scheduled', 'Successful', 'Failed', 'Cancelled'>;

type Transaction extending Proposal {
required multi unorderedOperations: Operation {
Expand All @@ -37,8 +37,7 @@ module default {
with result := .result
select assert_exists((
TransactionStatus.Pending if ((result is SimulatedSuccess or result is SimulatedFailure) ?? true) else
TransactionStatus.Executing if (result is OptimisticSuccess) else
TransactionStatus.Successful if (result is ConfirmedSuccess) else
TransactionStatus.Successful if ((result is OptimisticSuccess or result is ConfirmedSuccess) ?? false) else
TransactionStatus.Failed if (result is ConfirmedFailure) else
TransactionStatus.Scheduled if (not result[is Scheduled].cancelled) else
TransactionStatus.Cancelled # result is Scheduled
Expand Down Expand Up @@ -81,7 +80,8 @@ module default {
multi link events := .<result[is Event];
multi link transfers := .events[is Transfer];

trigger update_tx_result after insert for each do (
trigger update_tx_result after insert for each
when ((__new__.timestamp >= __new__.transaction.result.timestamp) ?? true) do (
update __new__.transaction set { result := __new__ }
);
}
Expand Down
35 changes: 22 additions & 13 deletions api/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ interface Confirmed implements Node & Result {
id: ID!
networkEthFee: Decimal!
response: Bytes!
systemTx: SystemTx
systx: SystemTx
timestamp: DateTime!
transaction: Transaction!
transfers: [Transfer!]!
Expand All @@ -188,7 +188,7 @@ type ConfirmedFailure implements Confirmed & Failure & Node & Result {
networkEthFee: Decimal!
reason: String
response: Bytes!
systemTx: SystemTx
systx: SystemTx
timestamp: DateTime!
transaction: Transaction!
transfers: [Transfer!]!
Expand All @@ -202,7 +202,7 @@ type ConfirmedSuccess implements Confirmed & Node & Result & Success {
id: ID!
networkEthFee: Decimal!
response: Bytes!
systemTx: SystemTx
systx: SystemTx
timestamp: DateTime!
transaction: Transaction!
transfers: [Transfer!]!
Expand Down Expand Up @@ -282,10 +282,20 @@ input EstimateFeesPerGasInput {
feeToken: UAddress!
}

type EstimatedFeeParts {
maxFeePerGas: Decimal!
maxPriorityFeePerGas: Decimal!
networkFee: Decimal!
paymasterFees: PaymasterFees!
total: Decimal!
}

type EstimatedTransactionFees implements CustomNode {
eth: EstimatedFeeParts!
feeToken: EstimatedFeeParts!
gasLimit: Uint256!
gasPerPubdataLimit: Uint256!
id: ID!
maxNetworkEthFee: Decimal!
paymasterEthFees: PaymasterFees!
}

interface Event implements Node {
Expand All @@ -311,7 +321,7 @@ interface Failure implements Node & Result {
id: ID!
reason: String
response: Bytes!
systemTx: SystemTx
systx: SystemTx
timestamp: DateTime!
transaction: Transaction!
transfers: [Transfer!]!
Expand Down Expand Up @@ -423,7 +433,7 @@ type OptimisticSuccess implements Node & Result & Success {
gasUsed: BigInt!
id: ID!
response: Bytes!
systemTx: SystemTx
systx: SystemTx
timestamp: DateTime!
transaction: Transaction!
transfers: [Transfer!]!
Expand Down Expand Up @@ -705,7 +715,7 @@ interface Result implements Node {
gasUsed: BigInt!
id: ID!
response: Bytes!
systemTx: SystemTx
systx: SystemTx
timestamp: DateTime!
transaction: Transaction!
transfers: [Transfer!]!
Expand All @@ -718,7 +728,7 @@ type Scheduled implements Node & Result {
id: ID!
response: Bytes!
scheduledFor: DateTime!
systemTx: SystemTx
systx: SystemTx
timestamp: DateTime!
transaction: Transaction!
transfers: [Transfer!]!
Expand All @@ -733,7 +743,7 @@ type SimulatedFailure implements Failure & Node & Result {
id: ID!
reason: String
response: Bytes!
systemTx: SystemTx
systx: SystemTx
timestamp: DateTime!
transaction: Transaction!
transfers: [Transfer!]!
Expand All @@ -746,7 +756,7 @@ type SimulatedSuccess implements Node & Result & Success {
id: ID!
reason: String
response: Bytes!
systemTx: SystemTx
systx: SystemTx
timestamp: DateTime!
transaction: Transaction!
transfers: [Transfer!]!
Expand All @@ -771,7 +781,7 @@ interface Success implements Node & Result {
gasUsed: BigInt!
id: ID!
response: Bytes!
systemTx: SystemTx
systx: SystemTx
timestamp: DateTime!
transaction: Transaction!
transfers: [Transfer!]!
Expand Down Expand Up @@ -901,7 +911,6 @@ type TransactionPreparation implements CustomNode {

enum TransactionStatus {
Cancelled
Executing
Failed
Pending
Scheduled
Expand Down
5 changes: 1 addition & 4 deletions api/src/core/networks/networks.module.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import { Global, Module } from '@nestjs/common';
import { NetworksHealthIndicator } from './networks.health';
import { NetworksService } from './networks.service';
import { registerBullQueue } from '../bull/bull.util';
import { NetworkQueue, NetworkWorker } from './networks.worker';

@Global()
@Module({
imports: [...registerBullQueue(NetworkQueue)],
exports: [NetworksService, NetworksHealthIndicator],
providers: [NetworksService, NetworksHealthIndicator, NetworkWorker],
providers: [NetworksService, NetworksHealthIndicator],
})
export class NetworksModule {}
72 changes: 39 additions & 33 deletions api/src/core/networks/networks.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { runExclusively } from '~/util/mutex';
import { ETH } from 'lib/dapps';
import { fromPromise } from 'neverthrow';
import { PartialK } from '~/util/types';
import Decimal from 'decimal.js';

export type Network = ReturnType<typeof create>;

Expand Down Expand Up @@ -90,7 +91,8 @@ function create({ chainKey, redis }: CreateParams) {
...eip712WalletActions()(client),
...walletActions(client, transport, redis),
...blockDetails(client),
...estimatedFeesPerGas(client, redis),
feeParams: feeParams(client, redis),
maxFeePerGas: maxFeePerGas(client, redis),
sendAccountTransaction: sendAccountTransaction(client, redis),
traceCall: traceCall(client),
}));
Expand Down Expand Up @@ -163,41 +165,48 @@ function blockDetails(client: Client) {
};
}

export function estimatedFeesPerGasKey(chain: Chain) {
return `estimatedFeesPerGasKey:${chain}`;
export interface NetworkFeeParams {
maxFeePerGas: Decimal;
maxPriorityFeePerGas: Decimal;
gasPerPubdataLimit: bigint;
}

async function getEstimatedFeesPerGas(client: Client, redis: Redis): Promise<FeeValuesEIP1559> {
const cached = await redis.get(estimatedFeesPerGasKey(client.chain.key));
if (cached) {
const p = JSON.parse(cached) as any;
return {
maxFeePerGas: BigInt(p.maxFeePerGas),
maxPriorityFeePerGas: BigInt(p.maxPriorityFeePerGas),
} satisfies FeeValuesEIP1559;
}

const r = await client.estimateFeesPerGas();
if (r.maxFeePerGas === undefined || r.maxPriorityFeePerGas === undefined)
throw new Error('Gas price estimates non EIP-1559');

return r;
}

function estimatedFeesPerGas(client: Client, redis: Redis) {
const estimatedFeesPerGas = async () => {
const v = await getEstimatedFeesPerGas(client, redis);
function feeParams(client: Client, redis: Redis) {
const { estimateFee } = publicActionsL2()(client);

return async (): Promise<NetworkFeeParams> => {
const r = await (async () => {
const key = `feeParams:${client.chain.key}`;
const cached = await redis.get(key);
if (cached) {
const p = JSON.parse(cached) as any;
return {
maxFeePerGas: BigInt(p.maxFeePerGas),
maxPriorityFeePerGas: BigInt(p.maxPriorityFeePerGas),
gasPerPubdataLimit: p.gasPerPubdataLimit,
};
}

const fresh = await estimateFee({
/* estimateFee required to get gasPerPubdataLimit */
account: '0x1111111111111111111111111111111111111111',
to: '0x1111111111111111111111111111111111111111',
});
redis.set(key, JSON.stringify(fresh), 'EX', 5);
return fresh;
})();

return {
maxFeePerGas: asDecimal(v.maxFeePerGas!, ETH),
maxPriorityFeePerGas: asDecimal(v.maxPriorityFeePerGas!, ETH),
maxFeePerGas: asDecimal(r.maxFeePerGas, ETH),
maxPriorityFeePerGas: asDecimal(r.maxPriorityFeePerGas, ETH),
gasPerPubdataLimit: r.gasPerPubdataLimit,
};
};
}

return {
estimatedFeesPerGas,
estimatedMaxFeePerGas: async () => (await estimatedFeesPerGas()).maxFeePerGas,
};
function maxFeePerGas(client: Client, redis: Redis) {
const getFeeParams = feeParams(client, redis);
return async () => (await getFeeParams()).maxFeePerGas;
}

export type TransactionWithDetailedOutput = {
Expand Down Expand Up @@ -229,14 +238,10 @@ export type SendAccountTransactionReturn = ReturnType<ReturnType<typeof sendAcco
function sendAccountTransaction(client: Client, redis: Redis) {
// Transactions must be submitted sequentially due to requirement for incrementing nonces
return async (tx: SendAccountTransactionParams) => {
const feesPerGas = await getEstimatedFeesPerGas(client, redis);

return await runExclusively(
async () => {
const serialized = client.chain.serializers.transaction({
chainId: client.chain.id,
maxFeePerGas: feesPerGas.maxFeePerGas!,
maxPriorityFeePerGas: feesPerGas.maxPriorityFeePerGas!,
nonce: await client.getTransactionCount({ address: tx.from }),
...tx,
});
Expand Down Expand Up @@ -302,6 +307,7 @@ export interface TraceCallParams {
block?: TraceCallSchema['Parameters'][1];
options?: TraceCallSchema['Parameters'][2];
}
export type TraceCallReturnType = Awaited<ReturnType<ReturnType<typeof traceCall>>>;

function traceCall(client: Client) {
return async (params: TraceCallParams) => {
Expand Down
45 changes: 0 additions & 45 deletions api/src/core/networks/networks.worker.ts

This file was deleted.

Loading

0 comments on commit d0a7afe

Please sign in to comment.