Skip to content

Commit d9ae677

Browse files
committed
fix: handle short storage values in beacon proxy pattern
1 parent 7c0b80d commit d9ae677

File tree

2 files changed

+70
-1
lines changed

2 files changed

+70
-1
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import type { Abi } from "abitype";
2+
import type { EIP1193RequestFn, EIP1474Methods } from "viem";
3+
import { beforeEach, describe, expect, it, vi } from "vitest";
4+
import {
5+
DUMMY_BYTECODE,
6+
ERC1967_PROXY_BYTECODE,
7+
ERC1967_PROXY_CONSTRUCTOR_ABI,
8+
} from "../../../test/src/abis/proxy.js";
9+
import { ANVIL_CHAIN } from "../../../test/src/chains.js";
10+
import { TEST_CLIENT } from "../../../test/src/test-clients.js";
11+
import {
12+
BASE_USDC_IMPLEMENTATION,
13+
BASE_USDC_PROXY_CONTRACT,
14+
NFT_DROP_CONTRACT,
15+
NFT_DROP_IMPLEMENTATION,
16+
POLYGON_USDT_IMPLEMENTATION,
17+
POLYGON_USDT_PROXY_CONTRACT,
18+
} from "../../../test/src/test-contracts.js";
19+
import { TEST_ACCOUNT_A } from "../../../test/src/test-wallets.js";
20+
import { getBytecode } from "../../contract/actions/get-bytecode.js";
21+
import { getContract } from "../../contract/contract.js";
22+
import { deployContract } from "../../contract/deployment/deploy-with-abi.js";
23+
import { eth_getStorageAt } from "../../rpc/actions/eth_getStorageAt.js";
24+
import { getRpcClient } from "../../rpc/rpc.js";
25+
import { resolveImplementation } from "./resolveImplementation.js";
26+
27+
// Mock dependencies
28+
vi.mock("../../contract/actions/get-bytecode.js");
29+
vi.mock("../../rpc/rpc.js");
30+
vi.mock("../../rpc/actions/eth_getStorageAt.js");
31+
32+
describe.runIf(process.env.TW_SECRET_KEY)(
33+
"Handle beacon proxy pattern",
34+
async () => {
35+
beforeEach(() => {
36+
vi.resetAllMocks();
37+
});
38+
it("should handle beacon proxy pattern with short storage values", async () => {
39+
// This test verifies that the implementation can handle short storage values
40+
// which was the original issue we were trying to fix
41+
const mockContract = {
42+
address: "0x0000000000000000000000000000000000000001",
43+
client: TEST_CLIENT,
44+
chain: ANVIL_CHAIN,
45+
} as const;
46+
47+
// Mock getBytecode to return a minimal proxy bytecode that will trigger the beacon check
48+
const mockGetBytecode = vi
49+
.fn()
50+
.mockResolvedValueOnce("") // First call for proxy (empty bytecode triggers beacon check)
51+
.mockResolvedValueOnce("0x1234"); // Second call for implementation
52+
53+
vi.mocked(getBytecode).mockImplementation(mockGetBytecode);
54+
55+
// Mock eth_getStorageAt to return a short value
56+
const mockRpcRequest = { request: vi.fn() };
57+
vi.mocked(getRpcClient).mockReturnValue(
58+
mockRpcRequest as unknown as EIP1193RequestFn<EIP1474Methods>,
59+
);
60+
vi.mocked(eth_getStorageAt).mockResolvedValue("0x1234"); // Short storage value
61+
62+
const result = await resolveImplementation(mockContract);
63+
64+
// Should not throw and should return some result (even if it's the original address)
65+
expect(result).toBeDefined();
66+
expect(result.address).toBeDefined();
67+
});
68+
},
69+
);

packages/thirdweb/src/utils/bytecode/resolveImplementation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export async function resolveImplementation(
4949
// check other proxy types
5050
let implementationAddress: string | undefined;
5151

52-
if (beacon && beacon !== AddressZero) {
52+
if (beacon && isAddress(beacon) && beacon !== AddressZero) {
5353
// In case of a BeaconProxy, it is setup as BeaconProxy --> Beacon --> Implementation
5454
// Hence we replace the proxy address with Beacon address, and continue further resolving below
5555
// biome-ignore lint/style/noParameterAssign: we purposefully mutate the contract object here

0 commit comments

Comments
 (0)