Skip to content

Commit

Permalink
improve: interpret depositIds as BigNumbers (#830)
Browse files Browse the repository at this point in the history
Signed-off-by: bennett <[email protected]>
Co-authored-by: Dong-Ha Kim <[email protected]>
  • Loading branch information
bmzig and dohaki authored Jan 24, 2025
1 parent e80a9eb commit 96139ff
Show file tree
Hide file tree
Showing 14 changed files with 112 additions and 74 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@across-protocol/sdk",
"author": "UMA Team",
"version": "3.4.12",
"version": "3.4.13",
"license": "AGPL-3.0",
"homepage": "https://docs.across.to/reference/sdk",
"files": [
Expand Down
6 changes: 4 additions & 2 deletions src/clients/BundleDataClient/utils/SuperstructUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ import {
pattern,
boolean,
defaulted,
union,
type,
} from "superstruct";
import { BigNumber } from "../../../utils";

const PositiveIntegerStringSS = pattern(string(), /\d+/);
const Web3AddressSS = pattern(string(), /^0x[a-fA-F0-9]{40}$/);

const BigNumberType = coerce(instance(BigNumber), string(), (value) => {
const BigNumberType = coerce(instance(BigNumber), union([string(), number()]), (value) => {
try {
// Attempt to convert the string to a BigNumber
return BigNumber.from(value);
Expand All @@ -41,7 +42,7 @@ const V3RelayDataSS = {
originChainId: number(),
depositor: string(),
recipient: string(),
depositId: number(),
depositId: BigNumberType,
message: string(),
};

Expand Down Expand Up @@ -91,6 +92,7 @@ const V3FillSS = {
repaymentChainId: number(),
relayExecutionInfo: V3RelayExecutionEventInfoSS,
quoteTimestamp: number(),
messageHash: optional(string()),
};

const V3FillWithBlockSS = {
Expand Down
50 changes: 27 additions & 23 deletions src/clients/SpokePoolClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
getRelayDataHash,
isDefined,
toBN,
bnOne,
isUnsafeDepositId,
} from "../utils";
import {
paginatedEventQuery,
Expand Down Expand Up @@ -46,8 +48,8 @@ type SpokePoolUpdateSuccess = {
success: true;
currentTime: number;
oldestTime: number;
firstDepositId: number;
latestDepositId: number;
firstDepositId: BigNumber;
latestDepositId: BigNumber;
events: Log[][];
searchEndBlock: number;
};
Expand All @@ -66,18 +68,18 @@ export class SpokePoolClient extends BaseAbstractClient {
protected oldestTime = 0;
protected depositHashes: { [depositHash: string]: DepositWithBlock } = {};
protected depositHashesToFills: { [depositHash: string]: FillWithBlock[] } = {};
protected speedUps: { [depositorAddress: string]: { [depositId: number]: SpeedUpWithBlock[] } } = {};
protected speedUps: { [depositorAddress: string]: { [depositId: string]: SpeedUpWithBlock[] } } = {};
protected slowFillRequests: { [relayDataHash: string]: SlowFillRequestWithBlock } = {};
protected depositRoutes: { [originToken: string]: { [DestinationChainId: number]: boolean } } = {};
protected tokensBridged: TokensBridged[] = [];
protected rootBundleRelays: RootBundleRelayWithBlock[] = [];
protected relayerRefundExecutions: RelayerRefundExecutionWithBlock[] = [];
protected queryableEventNames: string[] = [];
protected configStoreClient: AcrossConfigStoreClient | undefined;
public earliestDepositIdQueried = Number.MAX_SAFE_INTEGER;
public latestDepositIdQueried = 0;
public firstDepositIdForSpokePool = Number.MAX_SAFE_INTEGER;
public lastDepositIdForSpokePool = Number.MAX_SAFE_INTEGER;
public earliestDepositIdQueried = MAX_BIG_INT;
public latestDepositIdQueried = bnZero;
public firstDepositIdForSpokePool = MAX_BIG_INT;
public lastDepositIdForSpokePool = MAX_BIG_INT;
public fills: { [OriginChainId: number]: FillWithBlock[] } = {};

/**
Expand Down Expand Up @@ -232,7 +234,7 @@ export class SpokePoolClient extends BaseAbstractClient {
*/
public appendMaxSpeedUpSignatureToDeposit(deposit: DepositWithBlock): DepositWithBlock {
const { depositId, depositor } = deposit;
const speedups = this.speedUps[depositor]?.[depositId];
const speedups = this.speedUps[depositor]?.[depositId.toString()];
if (!isDefined(speedups) || speedups.length === 0) {
return deposit;
}
Expand Down Expand Up @@ -265,7 +267,7 @@ export class SpokePoolClient extends BaseAbstractClient {
* @param depositId The unique ID of the deposit being queried.
* @returns The corresponding deposit if found, undefined otherwise.
*/
public getDeposit(depositId: number): DepositWithBlock | undefined {
public getDeposit(depositId: BigNumber): DepositWithBlock | undefined {
const depositHash = this.getDepositHash({ depositId, originChainId: this.chainId });
return this.depositHashes[depositHash];
}
Expand Down Expand Up @@ -295,7 +297,7 @@ export class SpokePoolClient extends BaseAbstractClient {
* Retrieves speed up requests grouped by depositor and depositId.
* @returns A mapping of depositor addresses to deposit ids with their corresponding speed up requests.
*/
public getSpeedUps(): { [depositorAddress: string]: { [depositId: number]: SpeedUpWithBlock[] } } {
public getSpeedUps(): { [depositorAddress: string]: { [depositId: string]: SpeedUpWithBlock[] } } {
return this.speedUps;
}

Expand Down Expand Up @@ -365,7 +367,7 @@ export class SpokePoolClient extends BaseAbstractClient {
);

// Log any invalid deposits with same deposit id but different params.
const invalidFillsForDeposit = invalidFills.filter((x) => x.depositId === deposit.depositId);
const invalidFillsForDeposit = invalidFills.filter((x) => x.depositId.eq(deposit.depositId));
if (invalidFillsForDeposit.length > 0) {
this.logger.warn({
at: "SpokePoolClient",
Expand Down Expand Up @@ -396,7 +398,7 @@ export class SpokePoolClient extends BaseAbstractClient {
* @note This hash is used to match deposits and fills together.
* @note This hash takes the form of: `${depositId}-${originChainId}`.
*/
public getDepositHash(event: { depositId: number; originChainId: number }): string {
public getDepositHash(event: { depositId: BigNumber; originChainId: number }): string {
return `${event.depositId}-${event.originChainId}`;
}

Expand All @@ -405,7 +407,7 @@ export class SpokePoolClient extends BaseAbstractClient {
* @param blockTag The block number to search for the deposit ID at.
* @returns The deposit ID.
*/
public _getDepositIdAtBlock(blockTag: number): Promise<number> {
public _getDepositIdAtBlock(blockTag: number): Promise<BigNumber> {
return getDepositIdAtBlock(this.spokePool as SpokePool, blockTag);
}

Expand Down Expand Up @@ -438,10 +440,11 @@ export class SpokePoolClient extends BaseAbstractClient {
*/
protected async _update(eventsToQuery: string[]): Promise<SpokePoolUpdate> {
// Find the earliest known depositId. This assumes no deposits were placed in the deployment block.
let firstDepositId: number = this.firstDepositIdForSpokePool;
if (firstDepositId === Number.MAX_SAFE_INTEGER) {
let firstDepositId = this.firstDepositIdForSpokePool;
if (firstDepositId.eq(MAX_BIG_INT)) {
firstDepositId = await this.spokePool.numberOfDeposits({ blockTag: this.deploymentBlock });
if (isNaN(firstDepositId) || firstDepositId < 0) {
firstDepositId = BigNumber.from(firstDepositId); // Cast input to a big number.
if (!BigNumber.isBigNumber(firstDepositId) || firstDepositId.lt(bnZero)) {
throw new Error(`SpokePoolClient::update: Invalid first deposit id (${firstDepositId})`);
}
}
Expand Down Expand Up @@ -491,9 +494,10 @@ export class SpokePoolClient extends BaseAbstractClient {
]);
this.log("debug", `Time to query new events from RPC for ${this.chainId}: ${Date.now() - timerStart} ms`);

const [currentTime, numberOfDeposits] = multicallFunctions.map(
const [currentTime, _numberOfDeposits] = multicallFunctions.map(
(fn, idx) => spokePool.interface.decodeFunctionResult(fn, multicallOutput[idx])[0]
);
const _latestDepositId = BigNumber.from(_numberOfDeposits).sub(bnOne);

if (!BigNumber.isBigNumber(currentTime) || currentTime.lt(this.currentTime)) {
const errMsg = BigNumber.isBigNumber(currentTime)
Expand All @@ -510,7 +514,7 @@ export class SpokePoolClient extends BaseAbstractClient {
currentTime: currentTime.toNumber(), // uint32
oldestTime: oldestTime.toNumber(),
firstDepositId,
latestDepositId: Math.max(numberOfDeposits - 1, 0),
latestDepositId: _latestDepositId.gt(bnZero) ? _latestDepositId : bnZero,
searchEndBlock: searchConfig.toBlock,
events,
};
Expand Down Expand Up @@ -579,10 +583,10 @@ export class SpokePoolClient extends BaseAbstractClient {
}
assign(this.depositHashes, [this.getDepositHash(deposit)], deposit);

if (deposit.depositId < this.earliestDepositIdQueried) {
if (deposit.depositId.lt(this.earliestDepositIdQueried) && !isUnsafeDepositId(deposit.depositId)) {
this.earliestDepositIdQueried = deposit.depositId;
}
if (deposit.depositId > this.latestDepositIdQueried) {
if (deposit.depositId.gt(this.latestDepositIdQueried) && !isUnsafeDepositId(deposit.depositId)) {
this.latestDepositIdQueried = deposit.depositId;
}
}
Expand All @@ -594,7 +598,7 @@ export class SpokePoolClient extends BaseAbstractClient {

for (const event of speedUpEvents) {
const speedUp = { ...spreadEventWithBlockNumber(event), originChainId: this.chainId } as SpeedUpWithBlock;
assign(this.speedUps, [speedUp.depositor, speedUp.depositId], [speedUp]);
assign(this.speedUps, [speedUp.depositor, speedUp.depositId.toString()], [speedUp]);

// Find deposit hash matching this speed up event and update the deposit data associated with the hash,
// if the hash+data exists.
Expand Down Expand Up @@ -778,7 +782,7 @@ export class SpokePoolClient extends BaseAbstractClient {
return this.oldestTime;
}

async findDeposit(depositId: number, destinationChainId: number): Promise<DepositWithBlock> {
async findDeposit(depositId: BigNumber, destinationChainId: number): Promise<DepositWithBlock> {
// Binary search for event search bounds. This way we can get the blocks before and after the deposit with
// deposit ID = fill.depositId and use those blocks to optimize the search for that deposit.
// Stop searches after a maximum # of searches to limit number of eth_call requests. Make an
Expand Down Expand Up @@ -807,7 +811,7 @@ export class SpokePoolClient extends BaseAbstractClient {
);
const tStop = Date.now();

const event = query.find(({ args }) => args["depositId"] === depositId);
const event = query.find(({ args }) => args["depositId"].eq(depositId));
if (event === undefined) {
const srcChain = getNetworkName(this.chainId);
const dstChain = getNetworkName(destinationChainId);
Expand Down
22 changes: 11 additions & 11 deletions src/clients/mocks/MockSpokePoolClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
SlowFillLeaf,
SpeedUp,
} from "../../interfaces";
import { toBN, toBNWei, getCurrentTime, randomAddress } from "../../utils";
import { toBN, toBNWei, getCurrentTime, randomAddress, BigNumber, bnZero, bnOne, bnMax } from "../../utils";
import { SpokePoolClient, SpokePoolUpdate } from "../SpokePoolClient";
import { HubPoolClient } from "../HubPoolClient";
import { EventManager, EventOverrides, getEventManager } from "./MockEvents";
Expand All @@ -26,8 +26,8 @@ export class MockSpokePoolClient extends SpokePoolClient {
public eventManager: EventManager;
private destinationTokenForChainOverride: Record<number, string> = {};
// Allow tester to set the numberOfDeposits() returned by SpokePool at a block height.
public depositIdAtBlock: number[] = [];
public numberOfDeposits = 0;
public depositIdAtBlock: BigNumber[] = [];
public numberOfDeposits = bnZero;

constructor(
logger: winston.Logger,
Expand Down Expand Up @@ -57,21 +57,21 @@ export class MockSpokePoolClient extends SpokePoolClient {
this.latestBlockSearched = blockNumber;
}

setDepositIds(_depositIds: number[]): void {
setDepositIds(_depositIds: BigNumber[]): void {
this.depositIdAtBlock = [];
if (_depositIds.length === 0) {
return;
}
let lastDepositId = _depositIds[0];
for (let i = 0; i < _depositIds.length; i++) {
if (_depositIds[i] < lastDepositId) {
if (_depositIds[i].lt(lastDepositId)) {
throw new Error("deposit ID must be equal to or greater than previous");
}
this.depositIdAtBlock[i] = _depositIds[i];
lastDepositId = _depositIds[i];
}
}
_getDepositIdAtBlock(blockTag: number): Promise<number> {
_getDepositIdAtBlock(blockTag: number): Promise<BigNumber> {
return Promise.resolve(this.depositIdAtBlock[blockTag]);
}

Expand All @@ -96,13 +96,13 @@ export class MockSpokePoolClient extends SpokePoolClient {
// Update latestDepositIdQueried.
const idx = eventsToQuery.indexOf("V3FundsDeposited");
const latestDepositId = (events[idx] ?? []).reduce(
(depositId, event) => Math.max(depositId, (event.args["depositId"] ?? 0) as number),
(depositId, event) => bnMax(depositId, event.args["depositId"] ?? bnZero),
this.latestDepositIdQueried
);

return Promise.resolve({
success: true,
firstDepositId: 0,
firstDepositId: bnZero,
latestDepositId,
currentTime,
oldestTime: 0,
Expand All @@ -122,8 +122,8 @@ export class MockSpokePoolClient extends SpokePoolClient {
const { blockNumber, transactionIndex } = deposit;
let { depositId, depositor, destinationChainId, inputToken, inputAmount, outputToken, outputAmount } = deposit;
depositId ??= this.numberOfDeposits;
assert(depositId >= this.numberOfDeposits, `${depositId} < ${this.numberOfDeposits}`);
this.numberOfDeposits = depositId + 1;
assert(depositId.gte(this.numberOfDeposits), `${depositId} < ${this.numberOfDeposits}`);
this.numberOfDeposits = depositId.add(bnOne);

destinationChainId ??= random(1, 42161, false);
depositor ??= randomAddress();
Expand Down Expand Up @@ -168,7 +168,7 @@ export class MockSpokePoolClient extends SpokePoolClient {
const { blockNumber, transactionIndex } = fill;
let { originChainId, depositId, inputToken, inputAmount, outputAmount, fillDeadline, relayer } = fill;
originChainId ??= random(1, 42161, false);
depositId ??= random(1, 100_000, false);
depositId ??= BigNumber.from(random(1, 100_000, false));
inputToken ??= randomAddress();
inputAmount ??= toBNWei(random(1, 1000, false));
outputAmount ??= inputAmount;
Expand Down
4 changes: 2 additions & 2 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export {
TOKEN_SYMBOLS_MAP,
} from "@across-protocol/constants";

export const { AddressZero: ZERO_ADDRESS } = ethersConstants;
export const { AddressZero: ZERO_ADDRESS, HashZero: ZERO_BYTES } = ethersConstants;

// 2^96 - 1 is a conservative erc20 max allowance.
export const MAX_SAFE_ALLOWANCE = "79228162514264337593543950335";
Expand All @@ -33,7 +33,7 @@ export const TRANSFER_THRESHOLD_MAX_CONFIG_STORE_VERSION = 1;
export const ARWEAVE_TAG_APP_NAME = "across-protocol";

// A hardcoded version number used, by default, to tag all Arweave records.
export const ARWEAVE_TAG_APP_VERSION = 2;
export const ARWEAVE_TAG_APP_VERSION = 3;

/**
* A default list of chain Ids that the protocol supports. This is outlined
Expand Down
4 changes: 2 additions & 2 deletions src/interfaces/SpokePool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export interface RelayData {
originChainId: number;
depositor: string;
recipient: string;
depositId: number;
depositId: BigNumber;
inputToken: string;
inputAmount: BigNumber;
outputToken: string;
Expand Down Expand Up @@ -67,7 +67,7 @@ export interface FillWithBlock extends Fill, SortableEvent {}
export interface SpeedUp {
depositor: string;
depositorSignature: string;
depositId: number;
depositId: BigNumber;
originChainId: number;
updatedRecipient: string;
updatedOutputAmount: BigNumber;
Expand Down
11 changes: 11 additions & 0 deletions src/utils/BigNumberUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,14 @@ export const toBN = (num: BigNumberish, rounding: "floor" | "round" | "ceil" = "
// Otherwise, it is a string int and we can parse it directly.
return BigNumber.from(num.toString());
};

/**
* Compares two BigNumbers and returns the maximum. Order does not matter.
*
* @param val The first BigNumber to compare.
* @param cmp The second BigNumber to compare.
* @returns The greater of the two BigNumbers.
*/
export const bnMax = (val: BigNumber, cmp: BigNumber) => {
return val.gt(cmp) ? val : cmp;
};
4 changes: 2 additions & 2 deletions src/utils/DepositUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export async function queryHistoricalDepositForFill(

const { depositId } = fill;
let { firstDepositIdForSpokePool: lowId, lastDepositIdForSpokePool: highId } = spokePoolClient;
if (depositId < lowId || depositId > highId) {
if (depositId.lt(lowId) || depositId.gt(highId)) {
return {
found: false,
code: InvalidFill.DepositIdInvalid,
Expand All @@ -61,7 +61,7 @@ export async function queryHistoricalDepositForFill(
}

({ earliestDepositIdQueried: lowId, latestDepositIdQueried: highId } = spokePoolClient);
if (depositId >= lowId && depositId <= highId) {
if (depositId.gte(lowId) && depositId.lte(highId)) {
const originChain = getNetworkName(fill.originChainId);
const deposit = spokePoolClient.getDeposit(depositId);
if (isDefined(deposit)) {
Expand Down
8 changes: 8 additions & 0 deletions src/utils/EventUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Result } from "@ethersproject/abi";
import { Contract, Event, EventFilter } from "ethers";
import { Log, SortableEvent } from "../interfaces";
import { delay } from "./common";
import { isDefined, toBN, BigNumberish } from "./";

const maxRetries = 3;
const retrySleepTime = 10;
Expand Down Expand Up @@ -58,6 +59,13 @@ export function spreadEvent(args: Result | Record<string, unknown>): { [key: str
if (returnedObject.rootBundleId) {
returnedObject.rootBundleId = Number(returnedObject.rootBundleId);
}
// If depositId is included in the event, cast it to a BigNumber. Need to check if it is defined since the deposit ID can
// be 0, which would still make this evaluate as false.
if (isDefined(returnedObject.depositId)) {
// Assuming a numeric output, we can safely cast the unknown to BigNumberish since the depositId will either be a uint32 (and therefore a TypeScript `number`),
// or a uint256 (and therefore an ethers BigNumber).
returnedObject.depositId = toBN(returnedObject.depositId as BigNumberish);
}

return returnedObject;
}
Expand Down
Loading

0 comments on commit 96139ff

Please sign in to comment.