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

improve(BundleDataClient): Add comment about not running binary search on "unsafe" deposit ID's #796

Merged
merged 1 commit into from
Dec 20, 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
7 changes: 7 additions & 0 deletions src/clients/BundleDataClient/BundleDataClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -899,6 +899,9 @@ export class BundleDataClient {
bundleInvalidFillsV3.push(fill);
return;
}
// If deposit is using the deterministic relay hash feature, then the following binary search-based
// algorithm will not work. However, it is impossible to emit an infinite fill deadline using
// the unsafeDepositV3 function so there is no need to catch the special case.
const historicalDeposit = await queryHistoricalDepositForFill(originClient, fill);
if (!historicalDeposit.found) {
bundleInvalidFillsV3.push(fill);
Expand Down Expand Up @@ -1003,6 +1006,10 @@ export class BundleDataClient {
// older deposit in case the spoke pool client's lookback isn't old enough to find the matching deposit.
// We can skip this step if the deposit's fill deadline is not infinite, because we can assume that the
// spoke pool clients have loaded deposits old enough to cover all fills with a non-infinite fill deadline.
// We do not need to handle the case where the deposit ID is > uint32 (in which case we wouldn't
// want to perform a binary search lookup for it because the deposit ID is "unsafe" and cannot be
// found using such a method) because infinite fill deadlines cannot be produced from the unsafeDepositV3()
// function.
if (
INFINITE_FILL_DEADLINE.eq(slowFillRequest.fillDeadline) &&
slowFillRequest.blockNumber >= destinationChainBlockRange[0]
Expand Down
4 changes: 4 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export const { AddressZero: ZERO_ADDRESS } = ethersConstants;
// 2^96 - 1 is a conservative erc20 max allowance.
export const MAX_SAFE_ALLOWANCE = "79228162514264337593543950335";

// The maximum depositId that can be emitted in a depositV3 method is the maximum uint32 value, so
// 2^32 - 1.
export const MAX_SAFE_DEPOSIT_ID = "4294967295";

export const SECONDS_PER_YEAR = 31557600; // 365.25 days per year.

/**
Expand Down
12 changes: 11 additions & 1 deletion src/utils/SpokeUtils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import assert from "assert";
import { BytesLike, Contract, PopulatedTransaction, providers, utils as ethersUtils } from "ethers";
import { CHAIN_IDs, ZERO_ADDRESS } from "../constants";
import { CHAIN_IDs, MAX_SAFE_DEPOSIT_ID, ZERO_ADDRESS } from "../constants";
import { Deposit, Fill, FillStatus, RelayData, SlowFillRequest } from "../interfaces";
import { SpokePoolClient } from "../clients";
import { chunk } from "./ArrayUtils";
Expand Down Expand Up @@ -244,6 +244,16 @@ export function getRelayHashFromEvent(e: Deposit | Fill | SlowFillRequest): stri
return getRelayDataHash(e, e.destinationChainId);
}

export function isUnsafeDepositId(depositId: number): boolean {
// SpokePool.unsafeDepositV3() produces a uint256 depositId by hashing the msg.sender, depositor and input
// uint256 depositNonce. There is a possibility that this resultant uint256 is less than the maxSafeDepositId (i.e.
// the maximum uint32 value) which makes it possible that an unsafeDepositV3's depositId can collide with a safe
// depositV3's depositId, but the chances of a collision are 1 in 2^(256 - 32), so we'll ignore this
// possibility.
const maxSafeDepositId = BigNumber.from(MAX_SAFE_DEPOSIT_ID);
return maxSafeDepositId.lt(depositId);
}

/**
* Find the amount filled for a deposit at a particular block.
* @param spokePool SpokePool contract instance.
Expand Down
101 changes: 0 additions & 101 deletions src/utils/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import assert from "assert";
import Decimal from "decimal.js";
import { ethers, PopulatedTransaction, providers, VoidSigner } from "ethers";
import { getGasPriceEstimate } from "../gasPriceOracle";
import { TypedMessage } from "../interfaces/TypedData";
import { BigNumber, BigNumberish, BN, formatUnits, parseUnits, toBN } from "./BigNumberUtils";
import { ConvertDecimals } from "./FormattingUtils";
import { chainIsOPStack } from "./NetworkUtils";
Expand Down Expand Up @@ -323,106 +322,6 @@ async function getLineaGasFees(chainId: number, transport: Transport | undefined
};
}

export type UpdateDepositDetailsMessageType = {
UpdateDepositDetails: [
{
name: "depositId";
type: "uint32";
},
{ name: "originChainId"; type: "uint256" },
{ name: "updatedRelayerFeePct"; type: "int64" },
{ name: "updatedRecipient"; type: "address" },
{ name: "updatedMessage"; type: "bytes" },
];
};

export type UpdateV3DepositDetailsMessageType = {
UpdateDepositDetails: [
{ name: "depositId"; type: "uint32" },
{ name: "originChainId"; type: "uint256" },
{ name: "updatedOutputAmount"; type: "uint256" },
{ name: "updatedRecipient"; type: "address" },
{ name: "updatedMessage"; type: "bytes" },
];
};

/**
* Utility function to get EIP-712 compliant typed data that can be signed with the JSON-RPC method
* `eth_signedTypedDataV4` in MetaMask (https://docs.metamask.io/guide/signing-data.html). The resulting signature
* can then be used to call the method `speedUpDeposit` of a `SpokePool.sol` contract.
* @param depositId The deposit ID to speed up.
* @param originChainId The chain ID of the origin chain.
* @param updatedRelayerFeePct The new relayer fee percentage.
* @param updatedRecipient The new recipient address.
* @param updatedMessage The new message that should be provided to the recipient.
* @return EIP-712 compliant typed data.
*/
export function getUpdateDepositTypedData(
depositId: number,
originChainId: number,
updatedRelayerFeePct: BigNumber,
updatedRecipient: string,
updatedMessage: string
): TypedMessage<UpdateDepositDetailsMessageType> {
return {
types: {
UpdateDepositDetails: [
{ name: "depositId", type: "uint32" },
{ name: "originChainId", type: "uint256" },
{ name: "updatedRelayerFeePct", type: "int64" },
{ name: "updatedRecipient", type: "address" },
{ name: "updatedMessage", type: "bytes" },
],
},
primaryType: "UpdateDepositDetails",
domain: {
name: "ACROSS-V2",
version: "1.0.0",
chainId: originChainId,
},
message: {
depositId,
originChainId,
updatedRelayerFeePct,
updatedRecipient,
updatedMessage,
},
};
}

export function getUpdateV3DepositTypedData(
nicholaspai marked this conversation as resolved.
Show resolved Hide resolved
depositId: number,
originChainId: number,
updatedOutputAmount: BigNumber,
updatedRecipient: string,
updatedMessage: string
): TypedMessage<UpdateV3DepositDetailsMessageType> {
return {
types: {
UpdateDepositDetails: [
{ name: "depositId", type: "uint32" },
{ name: "originChainId", type: "uint256" },
{ name: "updatedOutputAmount", type: "uint256" },
{ name: "updatedRecipient", type: "address" },
{ name: "updatedMessage", type: "bytes" },
],
},
primaryType: "UpdateDepositDetails",
domain: {
name: "ACROSS-V2",
version: "1.0.0",
chainId: originChainId,
},
message: {
depositId,
originChainId,
updatedOutputAmount,
updatedRecipient,
updatedMessage,
},
};
}

export function randomAddress() {
return ethers.utils.getAddress(ethers.utils.hexlify(ethers.utils.randomBytes(20)));
}
Loading