Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Z 341 responsive transaction #287

Merged
merged 21 commits into from
Aug 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
bbf6225
fix(app): always require password on open when secure store is encryp…
hbriese Aug 11, 2024
cca7c5d
fix(app): multiple nested panes
hbriese Aug 12, 2024
ecf8dd4
fix(api): confirmed events timestamp
hbriese Aug 12, 2024
402e6dc
fix(app): only show the latest pane of a given route
hbriese Aug 12, 2024
0c024ed
fix(api): exclude transfer events intended for another account
hbriese Aug 13, 2024
bf72391
feat(api,app): responsive transaction screen
hbriese Aug 15, 2024
fd7ed96
fix(api): unconfirmed transfers showing in query
hbriese Aug 15, 2024
91d115d
perf(api): reduce network job frequency
hbriese Aug 15, 2024
a853df8
refactor(app): remove unused components
hbriese Aug 15, 2024
78b6a99
refactor(app): use react-call for confirmation modals
hbriese Aug 15, 2024
a855c7e
feat(app): remove transacton
hbriese Aug 15, 2024
6bc50b5
fix(app): policy upsert transaction path
hbriese Aug 15, 2024
1b55b29
feat(app): cancel scheduled transaction button
hbriese Aug 15, 2024
b450e84
feat(api): use only necessary fee amount when executing transaction -…
hbriese Aug 16, 2024
300a1d6
perf(app): use cached proposals for store update when removing transa…
hbriese Aug 16, 2024
c56ada7
feat(api): replay most recent subscription event
hbriese Aug 16, 2024
85008cc
fix(api): correctly attach events to confirmed result
hbriese Aug 16, 2024
d059d65
feat(api): emit executed event after processing events
hbriese Aug 18, 2024
21b8555
fix(app): exclude upgrade approver from policy presets
hbriese Aug 18, 2024
c21708c
feat(api): simulate fee transfers
hbriese Aug 18, 2024
98d7965
test(api): mock maxFeePerGas
hbriese Aug 18, 2024
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
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
Loading