Skip to content

Commit

Permalink
address @pbio comments; expand on multi-protocol support + minor cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
nbayindirli committed May 1, 2024
1 parent 1ba8804 commit e2e4877
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 101 deletions.
7 changes: 7 additions & 0 deletions typescript/sdk/src/providers/transactions/TransactionTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {
EthersV5Transaction,
EthersV5TransactionReceipt,
} from '../ProviderType.js';

export type EV5Tx = EthersV5Transaction['transaction'];
export type EV5Receipt = EthersV5TransactionReceipt['receipt'];
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
import { ProtocolType } from '@hyperlane-xyz/utils';

import { ChainName } from '../../../types.js';
import { MultiProvider } from '../../MultiProvider.js';
import {
TypedTransaction,
TypedTransactionReceipt,
ProtocolTypedProvider,
ProtocolTypedReceipt,
ProtocolTypedTransaction,
} from '../../ProviderType.js';

import { TxSubmitterType } from './TxSubmitterTypes.js';

export interface TxSubmitterInterface<
TX extends TypedTransaction,
TR extends TypedTransactionReceipt,
> {
export interface TxSubmitterInterface<TProtocol extends ProtocolType> {
/**
* Defines the type of tx submitter.
*/
txSubmitterType: TxSubmitterType;
multiProvider: MultiProvider;
/**
* The chain to submit transactions on.
*/
chain: ChainName;
/**
* The provider to use for transaction submission.
*/
provider?: ProtocolTypedProvider<TProtocol>['provider'];
/**
* Should execute all transactions and return their receipts.
* @param txs The array of transactions to execute
*/
submit(...txs: TX[]): Promise<TR[] | void>;
submit(
...txs: ProtocolTypedTransaction<TProtocol>['transaction'][]
): Promise<ProtocolTypedReceipt<TProtocol>['receipt'][] | void>;
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Logger } from 'pino';

import { rootLogger } from '@hyperlane-xyz/utils';
import { ProtocolType } from '@hyperlane-xyz/utils';

import {
TypedTransaction,
TypedTransactionReceipt,
ProtocolTypedReceipt,
ProtocolTypedTransaction,
} from '../../../ProviderType.js';
import { TxTransformerInterface } from '../../transformer/TxTransformerInterface.js';
import { TxSubmitterInterface } from '../TxSubmitterInterface.js';
Expand All @@ -15,39 +16,36 @@ import { TxSubmitterInterface } from '../TxSubmitterInterface.js';
* Example use-cases:
* const eV5builder = new TxSubmitterBuilder<EV5Transaction, EV5TransactionReceipt>();
* let txReceipts = eV5builder.for(
* new GnosisSafeTxSubmitter(chainA)
* new EV5GnosisSafeTxSubmitter(chainA)
* ).transform(
* InterchainAccountTxTransformer(chainB)
* EV5InterchainAccountTxTransformer(chainB)
* ).submit(
* txs
* );
* txReceipts = eV5builder.for(
* new ImpersonatedAccountTxSubmitter(chainA)
* new EV5ImpersonatedAccountTxSubmitter(chainA)
* ).submit(txs);
* txReceipts = eV5builder.for(
* new JsonRpcTxSubmitter(chainC)
* new EV5JsonRpcTxSubmitter(chainC)
* ).submit(txs);
*/
export class TxSubmitterBuilder<
TX extends TypedTransaction,
TR extends TypedTransactionReceipt,
> {
export class TxSubmitterBuilder<TProtocol extends ProtocolType> {
protected readonly logger: Logger = rootLogger.child({
module: 'submitter-builder',
});

constructor(
private currentSubmitter: TxSubmitterInterface<TX, TR>,
private readonly currentTransformers: TxTransformerInterface<TX>[] = [],
private currentSubmitter: TxSubmitterInterface<TProtocol>,
private readonly currentTransformers: TxTransformerInterface<TProtocol>[] = [],
) {}

/**
* Sets the current submitter for the builder.
* @param txSubmitterOrType The submitter to add to the builder
*/
public for(
txSubmitter: TxSubmitterInterface<TX, TR>,
): TxSubmitterBuilder<TX, TR> {
txSubmitter: TxSubmitterInterface<TProtocol>,
): TxSubmitterBuilder<TProtocol> {
this.currentSubmitter = txSubmitter;
return this;
}
Expand All @@ -57,8 +55,8 @@ export class TxSubmitterBuilder<
* @param txTransformerOrType The transformer to add to the builder
*/
public transform(
txTransformer: TxTransformerInterface<TX>,
): TxSubmitterBuilder<TX, TR> {
txTransformer: TxTransformerInterface<TProtocol>,
): TxSubmitterBuilder<TProtocol> {
this.currentTransformers.push(txTransformer);
return this;
}
Expand All @@ -67,14 +65,16 @@ export class TxSubmitterBuilder<
* Submits a set of transactions to the builder.
* @param txs The transactions to submit
*/
public async submit(...txs: TX[]): Promise<TR[]> {
public async submit(
...txs: ProtocolTypedTransaction<TProtocol>['transaction'][]
): Promise<ProtocolTypedReceipt<TProtocol>['receipt'][] | void> {
this.logger.info(
`Submitting ${txs.length} transactions to the ${this.currentSubmitter.txSubmitterType} submitter...`,
);

let transformedTxs = txs;
while (this.currentTransformers.length > 0) {
const currentTransformer: TxTransformerInterface<TX> =
const currentTransformer: TxTransformerInterface<TProtocol> =
this.currentTransformers.pop()!;
transformedTxs = await currentTransformer.transform(...transformedTxs);
this.logger.info(
Expand All @@ -87,6 +87,6 @@ export class TxSubmitterBuilder<
`✅ Successfully submitted ${transformedTxs.length} transactions to the ${this.currentSubmitter.txSubmitterType} submitter.`,
);

return txReceipts ?? [];
return txReceipts;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,25 @@ import {
SafeTransactionData,
} from '@safe-global/safe-core-sdk-types';
import assert from 'assert';
import { PopulatedTransaction } from 'ethers';
import { Logger } from 'pino';

import { Address, rootLogger } from '@hyperlane-xyz/utils';

import { ChainName } from '../../../../types.js';
import { getSafe, getSafeService } from '../../../../utils/gnosisSafe.js';
import { MultiProvider } from '../../../MultiProvider.js';
import {
EthersV5Transaction,
EthersV5TransactionReceipt,
} from '../../../ProviderType.js';
import { TxSubmitterInterface } from '../TxSubmitterInterface.js';
import { EV5Tx } from '../../TransactionTypes.js';
import { TxSubmitterType } from '../TxSubmitterTypes.js';

interface GnosisSafeTxSubmitterProps {
import { EV5TxSubmitterInterface } from './EV5TxSubmitterInterface.js';

interface EV5GnosisSafeTxSubmitterProps {
safeAddress: Address;
signerAddress?: Address;
}

export class GnosisSafeTxSubmitter
implements
TxSubmitterInterface<EthersV5Transaction, EthersV5TransactionReceipt>
{
export class EV5GnosisSafeTxSubmitter implements EV5TxSubmitterInterface {
public readonly txSubmitterType: TxSubmitterType =
TxSubmitterType.GNOSIS_SAFE;

Expand All @@ -38,10 +34,10 @@ export class GnosisSafeTxSubmitter
constructor(
public readonly multiProvider: MultiProvider,
public readonly chain: ChainName,
public readonly props: GnosisSafeTxSubmitterProps,
public readonly props: EV5GnosisSafeTxSubmitterProps,
) {}

public async submit(...txs: EthersV5Transaction[]): Promise<void> {
public async submit(...txs: EV5Tx[]): Promise<void> {
const safe: Safe.default = await getSafe(
this.chain,
this.multiProvider,
Expand All @@ -55,11 +51,10 @@ export class GnosisSafeTxSubmitter
this.props.safeAddress,
);
const safeTransactionBatch: MetaTransactionData[] = txs.map(
({ transaction }: EthersV5Transaction) => {
const { to, data, value } = transaction;
({ to, data, value }: PopulatedTransaction) => {
assert(
to && data,
'Invalid EthersV5Transaction: Missing required field to or data.',
'Invalid PopulatedTransaction: Missing required field to or data.',
);
return { to, data, value: value?.toString() ?? '0' };
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,17 @@ import { Address } from '@hyperlane-xyz/utils';
import { ChainName } from '../../../../types.js';
import { impersonateAccount } from '../../../../utils/fork.js';
import { MultiProvider } from '../../../MultiProvider.js';
import {
EthersV5Transaction,
EthersV5TransactionReceipt,
ProviderType,
} from '../../../ProviderType.js';
import { TxSubmitterInterface } from '../TxSubmitterInterface.js';
import { EV5Receipt, EV5Tx } from '../../TransactionTypes.js';
import { TxSubmitterType } from '../TxSubmitterTypes.js';

interface ImpersonatedAccountTxSubmitterProps {
import { EV5TxSubmitterInterface } from './EV5TxSubmitterInterface.js';

interface EV5ImpersonatedAccountTxSubmitterProps {
address: Address;
}

export class ImpersonatedAccountTxSubmitter
implements
TxSubmitterInterface<EthersV5Transaction, EthersV5TransactionReceipt>
export class EV5ImpersonatedAccountTxSubmitter
implements EV5TxSubmitterInterface
{
public readonly txSubmitterType: TxSubmitterType =
TxSubmitterType.IMPERSONATED_ACCOUNT;
Expand All @@ -33,26 +29,24 @@ export class ImpersonatedAccountTxSubmitter
constructor(
public readonly multiProvider: MultiProvider,
public readonly chain: ChainName,
public readonly props: ImpersonatedAccountTxSubmitterProps,
public readonly props: EV5ImpersonatedAccountTxSubmitterProps,
) {}

public async submit(
...txs: EthersV5Transaction[]
): Promise<EthersV5TransactionReceipt[]> {
const receipts: EthersV5TransactionReceipt[] = [];
public async submit(...txs: EV5Tx[]): Promise<EV5Receipt[]> {
const receipts: EV5Receipt[] = [];
for (const tx of txs) {
const signer = await impersonateAccount(this.props.address);
this.multiProvider.setSigner(this.chain, signer);
const receipt: ContractReceipt = await this.multiProvider.sendTransaction(
this.chain,
tx.transaction,
tx,
);

this.logger.debug(
`Submitted EthersV5Transaction on ${this.chain}: ${receipt.transactionHash}`,
);

receipts.push({ type: ProviderType.EthersV5, receipt });
receipts.push(receipt);
}
return receipts;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,12 @@ import { rootLogger } from '@hyperlane-xyz/utils';

import { ChainName } from '../../../../types.js';
import { MultiProvider } from '../../../MultiProvider.js';
import {
EthersV5Transaction,
EthersV5TransactionReceipt,
ProviderType,
} from '../../../ProviderType.js';
import { TxSubmitterInterface } from '../TxSubmitterInterface.js';
import { EV5Receipt, EV5Tx } from '../../TransactionTypes.js';
import { TxSubmitterType } from '../TxSubmitterTypes.js';

export class JsonRpcTxSubmitter
implements
TxSubmitterInterface<EthersV5Transaction, EthersV5TransactionReceipt>
{
import { EV5TxSubmitterInterface } from './EV5TxSubmitterInterface.js';

export class EV5JsonRpcTxSubmitter implements EV5TxSubmitterInterface {
public readonly txSubmitterType: TxSubmitterType = TxSubmitterType.JSON_RPC;

protected readonly logger: Logger = rootLogger.child({
Expand All @@ -28,19 +22,17 @@ export class JsonRpcTxSubmitter
public readonly chain: ChainName,
) {}

public async submit(
...txs: EthersV5Transaction[]
): Promise<EthersV5TransactionReceipt[]> {
const receipts: EthersV5TransactionReceipt[] = [];
public async submit(...txs: EV5Tx[]): Promise<EV5Receipt[]> {
const receipts: EV5Receipt[] = [];
for (const tx of txs) {
const receipt: ContractReceipt = await this.multiProvider.sendTransaction(
this.chain,
tx.transaction,
tx,
);
this.logger.debug(
`Submitted EthersV5Transaction on ${this.chain}: ${receipt.transactionHash}`,
);
receipts.push({ type: ProviderType.EthersV5, receipt });
receipts.push(receipt);
}
return receipts;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ProtocolType } from '@hyperlane-xyz/utils';

import { MultiProvider } from '../../../MultiProvider.js';
import { TxSubmitterInterface } from '../TxSubmitterInterface.js';

export interface EV5TxSubmitterInterface
extends TxSubmitterInterface<ProtocolType.Ethereum> {
/**
* The EV5 multi-provider to use for transaction submission.
*/
multiProvider: MultiProvider;
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import { TypedTransaction } from '../../ProviderType.js';
import { ProtocolType } from '@hyperlane-xyz/utils';

import { ProtocolTypedTransaction } from '../../ProviderType.js';

import { TxTransformerType } from './TxTransformerTypes.js';

export interface TxTransformerInterface<TX extends TypedTransaction> {
export interface TxTransformerInterface<TProtocol extends ProtocolType> {
/**
* Defines the type of tx transformer.
*/
txTransformerType: TxTransformerType;

/**
* Should transform all transactions of type TX into transactions of type TX.
* @param txs The array of transactions to transform
*/
transform(...txs: TX[]): Promise<TX[]>;
transform(
...txs: ProtocolTypedTransaction<TProtocol>['transaction'][]
): Promise<ProtocolTypedTransaction<TProtocol>['transaction'][]>;
}
Loading

0 comments on commit e2e4877

Please sign in to comment.