Skip to content

Commit

Permalink
fix: add validation to check bridge init log producer (#98)
Browse files Browse the repository at this point in the history
# What ❔

- validate bridge init log to be produced by bridge contract

## Why ❔

- if token is bridged outside of bridge it can't rely on provided token
l1 address
  • Loading branch information
Romsters committed Nov 23, 2023
1 parent c5783b5 commit c1acec5
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 6 deletions.
110 changes: 109 additions & 1 deletion packages/worker/src/token/token.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ describe("TokenService", () => {
let stopGetTokenInfoDurationMetricMock: jest.Mock;

beforeEach(async () => {
blockchainServiceMock = mock<BlockchainService>();
blockchainServiceMock = mock<BlockchainService>({
bridgeAddresses: {
l2Erc20DefaultBridge: "0x0000000000000000000000000000000000001111",
},
});
tokenRepositoryMock = mock<TokenRepository>();

stopGetTokenInfoDurationMetricMock = jest.fn();
Expand Down Expand Up @@ -60,6 +64,7 @@ describe("TokenService", () => {

transactionReceipt = mock<types.TransactionReceipt>({
logs: [],
to: "0x0000000000000000000000000000000000001111",
});

deployedContractAddress = mock<ContractAddress>({
Expand Down Expand Up @@ -255,6 +260,54 @@ describe("TokenService", () => {
});
});

describe("when there is a bridge initialization log in transaction receipt which is not produced by the bridge contract", () => {
beforeEach(() => {
transactionReceipt.to = "0x0000000000000000000000000000000000001112";
transactionReceipt.logs = [
mock<types.Log>({
topics: [
"0x290afdae231a3fc0bbae8b1af63698b0a1d79b21ad17df0342dfb952fe74f8e5",
"0x000000000000000000000000c7e0220d02d549c4846a6ec31d89c3b670ebe35c",
"0x0100014340e955cbf39159da998b3374bee8f3c0b3c75a7a9e3df6b85052379d",
"0x000000000000000000000000dc187378edd8ed1585fb47549cc5fe633295d571",
],
}),
mock<types.Log>({
address: "0xdc187378edD8Ed1585fb47549Cc5fe633295d571",
topics: [
"0xe6b2ac4004ee4493db8844da5db69722d2128345671818c3c41928655a83fb2c",
"0x0000000000000000000000000db321efaa9e380d0b37b55b530cdaa62728b9a3",
],
data: "0x000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000441444c3100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000441444c3100000000000000000000000000000000000000000000000000000000",
}),
];
});

it("starts the get token info duration metric", async () => {
await tokenService.saveERC20Token(deployedContractAddress, transactionReceipt);
expect(startGetTokenInfoDurationMetricMock).toHaveBeenCalledTimes(1);
});

it("gets token data by the contract address", async () => {
await tokenService.saveERC20Token(deployedContractAddress, transactionReceipt);
expect(blockchainServiceMock.getERC20TokenData).toHaveBeenCalledTimes(1);
expect(blockchainServiceMock.getERC20TokenData).toHaveBeenCalledWith(deployedContractAddress.address);
});

it("upserts the token without l1Address", async () => {
await tokenService.saveERC20Token(deployedContractAddress, transactionReceipt);
expect(tokenRepositoryMock.upsert).toHaveBeenCalledTimes(1);
expect(tokenRepositoryMock.upsert).toHaveBeenCalledWith({
...tokenData,
blockNumber: deployedContractAddress.blockNumber,
transactionHash: deployedContractAddress.transactionHash,
l2Address: deployedContractAddress.address,
l1Address: undefined,
logIndex: deployedContractAddress.logIndex,
});
});
});

describe("when there is a bridge initialize log in transaction receipt for the current token address", () => {
let bridgedToken;

Expand Down Expand Up @@ -311,6 +364,61 @@ describe("TokenService", () => {
});
});

describe("when there is a bridge initialize log in transaction receipt which is not produced by the bridge contract", () => {
beforeEach(() => {
transactionReceipt.to = "0x0000000000000000000000000000000000001112";
transactionReceipt.logs = [
mock<types.Log>({
topics: [
"0x290afdae231a3fc0bbae8b1af63698b0a1d79b21ad17df0342dfb952fe74f8e5",
"0x000000000000000000000000913389f49358cb49a8e9e984a5871df43f80eb96",
"0x01000125c745537b5254be2ca086aee7fbd5d91789ed15790a942f9422d36447",
"0x0000000000000000000000005a393c95e7bddd0281650023d8c746fb1f596b7b",
],
}),
mock<types.Log>({
address: "0x5a393c95e7Bddd0281650023D8C746fB1F596B7b",
topics: [
"0x81e8e92e5873539605a102eddae7ed06d19bea042099a437cbc3644415eb7404",
"0x000000000000000000000000c8f8ce6491227a6a2ab92e67a64011a4eba1c6cf",
],
data: "0x000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000134c313131206465706c6f79656420746f204c310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044c31313100000000000000000000000000000000000000000000000000000000",
}),
];

deployedContractAddress = mock<ContractAddress>({
address: "0x5a393c95e7bddd0281650023d8c746fb1f596b7b",
blockNumber: 10,
transactionHash: "transactionHash",
logIndex: 20,
});
});

it("starts the get token info duration metric", async () => {
await tokenService.saveERC20Token(deployedContractAddress, transactionReceipt);
expect(startGetTokenInfoDurationMetricMock).toHaveBeenCalledTimes(1);
});

it("gets token data by the contract address", async () => {
await tokenService.saveERC20Token(deployedContractAddress, transactionReceipt);
expect(blockchainServiceMock.getERC20TokenData).toHaveBeenCalledTimes(1);
expect(blockchainServiceMock.getERC20TokenData).toHaveBeenCalledWith(deployedContractAddress.address);
});

it("upserts the token without l1Address", async () => {
await tokenService.saveERC20Token(deployedContractAddress, transactionReceipt);
expect(tokenRepositoryMock.upsert).toHaveBeenCalledTimes(1);
expect(tokenRepositoryMock.upsert).toHaveBeenCalledWith({
...tokenData,
blockNumber: deployedContractAddress.blockNumber,
transactionHash: deployedContractAddress.transactionHash,
l2Address: deployedContractAddress.address,
l1Address: undefined,
logIndex: deployedContractAddress.logIndex,
});
});
});

describe("if the token symbol is empty", () => {
beforeEach(() => {
jest.spyOn(blockchainServiceMock, "getERC20TokenData").mockResolvedValueOnce({
Expand Down
12 changes: 7 additions & 5 deletions packages/worker/src/token/token.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,13 @@ export class TokenService {
l1Address?: string;
};

const bridgeLog = transactionReceipt.logs?.find(
(log) =>
isLogOfType(log, [LogType.BridgeInitialization, LogType.BridgeInitialize]) &&
log.address.toLowerCase() === contractAddress.address.toLowerCase()
);
const bridgeLog =
transactionReceipt.to.toLowerCase() === this.blockchainService.bridgeAddresses.l2Erc20DefaultBridge &&
transactionReceipt.logs?.find(
(log) =>
isLogOfType(log, [LogType.BridgeInitialization, LogType.BridgeInitialize]) &&
log.address.toLowerCase() === contractAddress.address.toLowerCase()
);

if (bridgeLog) {
const parsedLog = parseLog(CONTRACT_INTERFACES.L2_STANDARD_ERC20, bridgeLog);
Expand Down

0 comments on commit c1acec5

Please sign in to comment.