diff --git a/src/clients/MultiCallerClient.ts b/src/clients/MultiCallerClient.ts index 7f2ba00ec..033e93ec8 100644 --- a/src/clients/MultiCallerClient.ts +++ b/src/clients/MultiCallerClient.ts @@ -16,6 +16,7 @@ import { getProvider, Multicall2Call, assert, + getPermissionedMultisender, } from "../utils"; import { AugmentedTransaction, TransactionClient } from "./TransactionClient"; import lodash from "lodash"; @@ -260,10 +261,21 @@ export class MultiCallerClient { return this.baseSigner ? getMultisender(chainId, this.baseSigner.connect(await getProvider(chainId))) : undefined; } + async _getPermissionedMultisender(chainId: number): Promise { + return this.baseSigner + ? getPermissionedMultisender(chainId, this.baseSigner.connect(await getProvider(chainId))) + : undefined; + } + async buildMultiSenderBundle(transactions: AugmentedTransaction[]): Promise { // Validate all transactions have the same chainId and can be sent from multisender. const { chainId } = transactions[0]; - const multisender = await this._getMultisender(chainId); + // If any transactions are to be sent through the permissioned multisender, then we should use that instead + // of the default multisender. This means that the caller must only batch transactions to this chain that can + // be executed from the permissioned multisender. + const multisender = transactions.some((t) => t.sendThroughPermissionedMulticall) + ? await this._getPermissionedMultisender(chainId) + : await this._getMultisender(chainId); if (!multisender) { throw new Error("Multisender not available for this chain"); } @@ -272,12 +284,17 @@ export class MultiCallerClient { const callData: Multicall2Call[] = []; let gasLimit: BigNumber | undefined = bnZero; transactions.forEach((txn, idx) => { - if (!txn.unpermissioned || txn.chainId !== chainId) { + if (!txn.unpermissioned || !txn.sendThroughPermissionedMulticall || txn.chainId !== chainId) { this.logger.error({ at: "MultiCallerClient#buildMultiSenderBundle", message: "Some transactions in the queue contain different target chain or are permissioned", transactions: transactions.map(({ contract, chainId, unpermissioned }) => { - return { target: getTarget(contract.address), unpermissioned: Boolean(unpermissioned), chainId }; + return { + target: getTarget(contract.address), + unpermissioned: Boolean(unpermissioned), + sendThroughPermissionedMulticall: Boolean(txn.sendThroughPermissionedMulticall), + chainId, + }; }), notificationPath: "across-error", }); @@ -380,7 +397,7 @@ export class MultiCallerClient { multisenderTxns = [], unsendableTxns = [], } = lodash.groupBy(txns, (txn) => { - if (txn.unpermissioned) { + if (txn.unpermissioned || txn.sendThroughPermissionedMulticall) { return "multisenderTxns"; } else if (txn.contract.multicall) { return "multicallerTxns"; diff --git a/src/clients/TransactionClient.ts b/src/clients/TransactionClient.ts index 84a1e092d..50c08ca79 100644 --- a/src/clients/TransactionClient.ts +++ b/src/clients/TransactionClient.ts @@ -26,6 +26,8 @@ export interface AugmentedTransaction { value?: BigNumber; unpermissioned?: boolean; // If false, the transaction must be sent from the enqueuer of the method. // If true, then can be sent from the MakerDAO multisender contract. + sendThroughPermissionedMulticall?: boolean; + // If true, then should be sent through the permissioned multisender contract. canFailInSimulation?: boolean; // Optional batch ID to use to group transactions groupId?: string; diff --git a/src/finalizer/index.ts b/src/finalizer/index.ts index 296afdc34..49a265075 100644 --- a/src/finalizer/index.ts +++ b/src/finalizer/index.ts @@ -294,8 +294,8 @@ export async function finalize( hubChainId, ...configuredChainIds, ]).map( - async (chainId) => - [chainId, await getMultisender(chainId, spokePoolClients[chainId].spokePool.signer)] as [number, Contract] + (chainId) => + [chainId, getMultisender(chainId, spokePoolClients[chainId].spokePool.signer)] as [number, Contract] ) ) ); diff --git a/src/utils/TransactionUtils.ts b/src/utils/TransactionUtils.ts index a0bb308b4..56b16a72a 100644 --- a/src/utils/TransactionUtils.ts +++ b/src/utils/TransactionUtils.ts @@ -53,10 +53,14 @@ export function getNetworkError(err: unknown): string { return isEthersError(err) ? err.reason : isError(err) ? err.message : "unknown error"; } -export async function getMultisender(chainId: number, baseSigner: Signer): Promise { +export function getMultisender(chainId: number, baseSigner: Signer): Contract | undefined { return sdkUtils.getMulticall3(chainId, baseSigner); } +export function getPermissionedMultisender(chainId: number, baseSigner: Signer): Contract | undefined { + return sdkUtils.getPermissionedMulticall3(chainId, baseSigner); +} + // Note that this function will throw if the call to the contract on method for given args reverts. Implementers // of this method should be considerate of this and catch the response to deal with the error accordingly. export async function runTransaction(