Skip to content

Commit 1c3cdbe

Browse files
authored
feat: truncate select bytes32 values in events to bytes20 addresses (#848)
Signed-off-by: bennett <bennett@umaproject.org>
1 parent cf85522 commit 1c3cdbe

File tree

5 files changed

+146
-3
lines changed

5 files changed

+146
-3
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@across-protocol/sdk",
33
"author": "UMA Team",
4-
"version": "3.4.18",
4+
"version": "3.4.19",
55
"license": "AGPL-3.0",
66
"homepage": "https://docs.across.to/reference/sdk",
77
"files": [

src/clients/mocks/MockSpokePoolClient.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
FillWithBlock,
1414
SlowFillLeaf,
1515
SpeedUp,
16+
TokensBridged,
1617
} from "../../interfaces";
1718
import { toBN, toBNWei, getCurrentTime, randomAddress, BigNumber, bnZero, bnOne, bnMax } from "../../utils";
1819
import { SpokePoolClient, SpokePoolUpdate } from "../SpokePoolClient";
@@ -225,6 +226,19 @@ export class MockSpokePoolClient extends SpokePoolClient {
225226
});
226227
}
227228

229+
setTokensBridged(tokensBridged: TokensBridged): Log {
230+
const event = "TokensBridged";
231+
const topics = [tokensBridged.chainId, tokensBridged.leafId, tokensBridged.l2TokenAddress];
232+
const args = { ...tokensBridged };
233+
234+
return this.eventManager.generateEvent({
235+
event,
236+
address: this.spokePool.address,
237+
topics: topics.map((topic) => topic.toString()),
238+
args,
239+
});
240+
}
241+
228242
requestV3SlowFill(request: SlowFillRequestWithBlock): Log {
229243
const event = "RequestedV3SlowFill";
230244

src/utils/AddressUtils.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@ export function compareAddressesSimple(addressA?: string, addressB?: string): bo
3939
return addressA.toLowerCase() === addressB.toLowerCase();
4040
}
4141

42+
// Converts an input (assumed to be) bytes32 string into a bytes20 string.
43+
// If the input is not a bytes32 but is less than type(uint160).max, then this function
44+
// will still succeed.
45+
// Throws an error if the string as an unsigned integer is greater than type(uint160).max.
46+
export function toAddress(hexString: string): string {
47+
// rawAddress is the address which is not properly checksummed.
48+
const rawAddress = utils.hexZeroPad(utils.hexStripZeros(hexString), 20);
49+
return utils.getAddress(rawAddress);
50+
}
51+
4252
export function isValidEvmAddress(address: string): boolean {
4353
if (utils.isAddress(address)) {
4454
return true;

src/utils/EventUtils.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,27 @@ import { Result } from "@ethersproject/abi";
33
import { Contract, Event, EventFilter } from "ethers";
44
import { Log, SortableEvent } from "../interfaces";
55
import { delay } from "./common";
6-
import { isDefined, toBN, BigNumberish } from "./";
6+
import { isDefined, toBN, BigNumberish, toAddress } from "./";
77

88
const maxRetries = 3;
99
const retrySleepTime = 10;
1010

11+
// Event fields which changed from an `address` to `bytes32` after the SVM contract upgrade.
12+
const knownExtendedAddressFields = [
13+
// TokensBridged
14+
"l2TokenAddress",
15+
// FundsDeposited/FilledRelay/RequestedSlowFill
16+
"inputToken",
17+
"outputToken",
18+
"depositor",
19+
"recipient",
20+
"exclusiveRelayer",
21+
// FilledRelay
22+
"relayer",
23+
// RequestedSpeedUpDeposit
24+
"updatedRecipient",
25+
];
26+
1127
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1228
export function spreadEvent(args: Result | Record<string, unknown>): { [key: string]: any } {
1329
const keys = Object.keys(args).filter((key: string) => isNaN(+key)); // Extract non-numeric keys.
@@ -67,6 +83,13 @@ export function spreadEvent(args: Result | Record<string, unknown>): { [key: str
6783
returnedObject.depositId = toBN(returnedObject.depositId as BigNumberish);
6884
}
6985

86+
// Truncate all fields which may be bytes32 into a bytes20 string.
87+
for (const field of knownExtendedAddressFields) {
88+
if (isDefined(returnedObject[field])) {
89+
returnedObject[field] = toAddress(String(returnedObject[field]));
90+
}
91+
}
92+
7093
return returnedObject;
7194
}
7295

test/SpokePoolClient.v3Events.ts

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { DEFAULT_CONFIG_STORE_VERSION, GLOBAL_CONFIG_STORE_KEYS } from "../src/c
55
import { MockConfigStoreClient, MockHubPoolClient, MockSpokePoolClient } from "../src/clients/mocks";
66
import { EMPTY_MESSAGE, ZERO_ADDRESS } from "../src/constants";
77
import { DepositWithBlock, FillWithBlock, Log, SlowFillRequest, SlowFillRequestWithBlock } from "../src/interfaces";
8-
import { getCurrentTime, isDefined, randomAddress } from "../src/utils";
8+
import { getCurrentTime, isDefined, randomAddress, toAddress, toBN } from "../src/utils";
99
import {
1010
SignerWithAddress,
1111
createSpyLogger,
@@ -338,4 +338,100 @@ describe("SpokePoolClient: Event Filtering", function () {
338338
expect(fillEvent.outputToken).to.equal(expectedFill.args!.outputToken);
339339
});
340340
});
341+
342+
it("Correctly truncates events with bytes32 address fields: TokensBridged", async function () {
343+
for (let i = 0; i < 10; ++i) {
344+
const l2TokenAddress = ethers.utils.hexZeroPad(randomAddress(), 32);
345+
originSpokePoolClient.setTokensBridged({ l2TokenAddress, chainId: i, leafId: i + 1 } as TokensBridged);
346+
await originSpokePoolClient.update(["TokensBridged"]);
347+
const tokensBridged = originSpokePoolClient.getTokensBridged().at(-1);
348+
expect(tokensBridged.l2TokenAddress).to.equal(toAddress(l2TokenAddress));
349+
}
350+
});
351+
352+
it("Correctly truncates events with bytes32 address fields: FundsDeposited", async function () {
353+
for (let _i = 0; _i < 10; ++_i) {
354+
const [depositor, recipient, inputToken, outputToken, exclusiveRelayer] = Array(5)
355+
.fill(0)
356+
.map((_) => ethers.utils.hexZeroPad(randomAddress(), 32));
357+
originSpokePoolClient.depositV3({
358+
depositor,
359+
recipient,
360+
inputToken,
361+
outputToken,
362+
exclusiveRelayer,
363+
} as DepositWithBlock);
364+
await originSpokePoolClient.update(["V3FundsDeposited"]);
365+
const deposit = originSpokePoolClient.getDeposits().at(-1);
366+
expect(deposit.depositor).to.equal(toAddress(depositor));
367+
expect(deposit.recipient).to.equal(toAddress(recipient));
368+
expect(deposit.inputToken).to.equal(toAddress(inputToken));
369+
expect(deposit.outputToken).to.equal(toAddress(outputToken));
370+
expect(deposit.exclusiveRelayer).to.equal(toAddress(exclusiveRelayer));
371+
}
372+
});
373+
it("Correctly truncates events with bytes32 address fields: RequestedSpeedUpDeposit", async function () {
374+
for (let i = 0; i < 10; ++i) {
375+
const [depositor, updatedRecipient] = Array(2)
376+
.fill(0)
377+
.map((_) => ethers.utils.hexZeroPad(randomAddress(), 32));
378+
originSpokePoolClient.speedUpV3Deposit({ depositor, updatedRecipient, depositId: toBN(i) } as SpeedUp);
379+
await originSpokePoolClient.update(["RequestedSpeedUpV3Deposit"]);
380+
const speedUp = originSpokePoolClient.getSpeedUps()[toAddress(depositor)][toBN(i)].at(-1);
381+
expect(speedUp.depositor).to.equal(toAddress(depositor));
382+
expect(speedUp.updatedRecipient).to.equal(toAddress(updatedRecipient));
383+
}
384+
});
385+
it("Correctly truncates events with bytes32 address fields: FilledRelay", async function () {
386+
for (let i = 0; i < 10; ++i) {
387+
const [depositor, recipient, inputToken, outputToken, exclusiveRelayer, relayer] = Array(6)
388+
.fill(0)
389+
.map((_) => ethers.utils.hexZeroPad(randomAddress(), 32));
390+
originSpokePoolClient.fillV3Relay({
391+
depositor,
392+
recipient,
393+
inputToken,
394+
outputToken,
395+
exclusiveRelayer,
396+
relayer,
397+
depositId: toBN(i),
398+
} as FillWithBlock);
399+
await originSpokePoolClient.update(["FilledV3Relay"]);
400+
const relay = originSpokePoolClient.getFills().at(-1);
401+
expect(relay.depositor).to.equal(toAddress(depositor));
402+
expect(relay.recipient).to.equal(toAddress(recipient));
403+
expect(relay.inputToken).to.equal(toAddress(inputToken));
404+
expect(relay.outputToken).to.equal(toAddress(outputToken));
405+
expect(relay.exclusiveRelayer).to.equal(toAddress(exclusiveRelayer));
406+
expect(relay.relayer).to.equal(toAddress(relayer));
407+
}
408+
});
409+
it("Correctly truncates events with bytes32 address fields: RequestedSlowFill", async function () {
410+
for (let i = 0; i < 10; ++i) {
411+
const [depositor, recipient, inputToken, outputToken, exclusiveRelayer] = Array(5)
412+
.fill(0)
413+
.map((_) => ethers.utils.hexZeroPad(randomAddress(), 32));
414+
originSpokePoolClient.requestV3SlowFill({
415+
depositor,
416+
recipient,
417+
inputToken,
418+
outputToken,
419+
exclusiveRelayer,
420+
depositId: toBN(i),
421+
originChainId: 1,
422+
inputAmount: toBN(i),
423+
outputAmount: toBN(i),
424+
message: "0x",
425+
fillDeadline: 0,
426+
exclusivityDeadline: 0,
427+
} as SlowFillRequestWithBlock);
428+
await originSpokePoolClient.update(["RequestedV3SlowFill"]);
429+
const slowFill = originSpokePoolClient.getSlowFillRequestsForOriginChain(1).at(-1);
430+
expect(slowFill.depositor).to.equal(toAddress(depositor));
431+
expect(slowFill.recipient).to.equal(toAddress(recipient));
432+
expect(slowFill.inputToken).to.equal(toAddress(inputToken));
433+
expect(slowFill.outputToken).to.equal(toAddress(outputToken));
434+
expect(slowFill.exclusiveRelayer).to.equal(toAddress(exclusiveRelayer));
435+
}
436+
});
341437
});

0 commit comments

Comments
 (0)