Skip to content

Commit

Permalink
Merge branch 'main' into patch-1
Browse files Browse the repository at this point in the history
  • Loading branch information
Rufusemmanuel authored Jan 14, 2025
2 parents a5b9c5d + 6e15740 commit 347b4c7
Show file tree
Hide file tree
Showing 14 changed files with 985 additions and 530 deletions.
11 changes: 6 additions & 5 deletions bridge-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@
"dependencies": {
"@consensys/linea-sdk": "0.3.0",
"@headlessui/react": "2.1.9",
"@tanstack/react-query": "5.59.3",
"@tanstack/react-query": "5.62.16",
"@wagmi/connectors": "5.1.15",
"@wagmi/core": "2.13.8",
"@web3modal/wagmi": "5.1.11",
"@wagmi/core": "2.16.3",
"@reown/appkit": "1.6.3",
"@reown/appkit-adapter-wagmi": "1.6.3",
"clsx": "^2.1.1",
"compare-versions": "6.1.1",
"date-fns": "4.1.0",
Expand All @@ -44,8 +45,8 @@
"sharp": "0.33.5",
"swiper": "11.1.14",
"tailwind-merge": "^2.5.3",
"viem": "2.21.19",
"wagmi": "2.12.17",
"viem": "2.22.4",
"wagmi": "2.14.6",
"zustand": "4.5.4"
},
"devDependencies": {
Expand Down
4 changes: 2 additions & 2 deletions bridge-ui/src/components/ConnectButton.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useWeb3Modal } from "@web3modal/wagmi/react";
import { useAppKit } from "@reown/appkit/react";
import { cn } from "@/utils/cn";
import { Button } from "./ui";

Expand All @@ -7,7 +7,7 @@ type ConnectButtonProps = {
};

export default function ConnectButton({ fullWidth }: ConnectButtonProps) {
const { open } = useWeb3Modal();
const { open } = useAppKit();
return (
<Button
id="wallet-connect-btn"
Expand Down
35 changes: 9 additions & 26 deletions bridge-ui/src/config/wagmi.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,28 @@
import { defaultWagmiConfig } from "@web3modal/wagmi/react/config";
import { http, injected } from "@wagmi/core";
import { http } from "@wagmi/core";
import { mainnet, sepolia, linea, lineaSepolia } from "@wagmi/core/chains";
import { walletConnect, coinbaseWallet } from "@wagmi/connectors";
import { config } from "./config";
import { WagmiAdapter } from "@reown/appkit-adapter-wagmi";
import { AppKitNetwork } from "@reown/appkit/networks";

if (!config.walletConnectId) throw new Error("Project ID is not defined");

const metadata = {
name: "Linea Bridge",
description: `Linea Bridge is a bridge solution, providing secure and efficient cross-chain transactions between Layer 1 and Linea networks.
Discover the future of blockchain interaction with Linea Bridge.`,
url: "https://bridge.linea.build",
icons: [],
};
export const chains: [AppKitNetwork, ...AppKitNetwork[]] = [mainnet, sepolia, linea, lineaSepolia];

const chains = [mainnet, sepolia, linea, lineaSepolia] as const;

export const wagmiConfig = defaultWagmiConfig({
chains,
export const wagmiAdapter = new WagmiAdapter({
networks: chains,
projectId: config.walletConnectId,
metadata,
multiInjectedProviderDiscovery: true,
ssr: true,
enableEIP6963: true,

batch: {
multicall: true,
},
connectors: [
walletConnect({
projectId: config.walletConnectId,
showQrModal: false,
}),
injected({ shimDisconnect: true }),
coinbaseWallet({
appName: "Linea Bridge",
}),
],
transports: {
[mainnet.id]: http(`https://mainnet.infura.io/v3/${process.env.NEXT_PUBLIC_INFURA_ID}`, { batch: true }),
[sepolia.id]: http(`https://sepolia.infura.io/v3/${process.env.NEXT_PUBLIC_INFURA_ID}`, { batch: true }),
[linea.id]: http(`https://linea-mainnet.infura.io/v3/${process.env.NEXT_PUBLIC_INFURA_ID}`, { batch: true }),
[lineaSepolia.id]: http(`https://linea-sepolia.infura.io/v3/${process.env.NEXT_PUBLIC_INFURA_ID}`, { batch: true }),
},
});

export const wagmiConfig = wagmiAdapter.wagmiConfig;
28 changes: 26 additions & 2 deletions bridge-ui/src/contexts/web3.context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,39 @@

import { ReactNode } from "react";
import { WagmiProvider } from "wagmi";
import { createWeb3Modal } from "@web3modal/wagmi/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { config, wagmiConfig } from "@/config";
import { createAppKit } from "@reown/appkit/react";
import { chains, wagmiAdapter } from "@/config/wagmi";

const queryClient = new QueryClient();

if (!config.walletConnectId) throw new Error("Project ID is not defined");

createWeb3Modal({ wagmiConfig, projectId: config.walletConnectId });
const metadata = {
name: "Linea Bridge",
description: `The Linea Bridge is a bridge solution, providing secure and efficient cross-chain transactions between Ethereum Layer 1 and Linea networks.
Discover the future of blockchain interaction with Linea Bridge.`,
url: "https://bridge.linea.build",
icons: [],
};

createAppKit({
adapters: [wagmiAdapter],
networks: chains,
projectId: config.walletConnectId,
metadata,
features: {
analytics: true,
email: false,
socials: false,
swaps: false,
onramp: false,
history: false,
},
enableEIP6963: true,
coinbasePreference: "eoaOnly",
});

type Web3ProviderProps = {
children: ReactNode;
Expand Down
1 change: 1 addition & 0 deletions contracts/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ coverage.json
typechain
typechain-types
cache
cache_forge
build
.openzeppelin

2 changes: 1 addition & 1 deletion contracts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,4 +167,4 @@ pnpm run test
pnpm run test:reportgas
pnpm run coverage
```
```
2 changes: 1 addition & 1 deletion contracts/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ additional_compiler_profiles = [ { name = "london", evm_version = "london" } ]
compilation_restrictions = [
{ paths = "./**/L2MessageService.sol", evm_version = "london" },
{ paths = "./**/TokenBridge.sol", evm_version = "london" },
]
]
1 change: 1 addition & 0 deletions contracts/hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import "@nomicfoundation/hardhat-toolbox";
import "@nomicfoundation/hardhat-foundry";
import "@openzeppelin/hardhat-upgrades";
import "@nomicfoundation/hardhat-foundry";
import * as dotenv from "dotenv";
import "hardhat-deploy";
import "hardhat-storage-layout";
Expand Down
2 changes: 1 addition & 1 deletion contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"lint": "pnpm run lint:sol && npm run lint:ts && npm run prettier",
"lint:fix": "pnpm run lint:sol:fix && npm run lint:ts:fix && npm run prettier:fix",
"clean": "rimraf .openzeppelin build cache node_modules typechain-types coverage coverage.json",
"solidity:docgen": "npx hardhat docgen"
"solidity:docgen": "command -v forge && npx hardhat docgen || echo 'Foundry installation required for Solidity docgen, skipping...'"
},
"devDependencies": {
"@ethereumjs/util": "9.0.3",
Expand Down
211 changes: 211 additions & 0 deletions contracts/test/foundry/LineaRollup.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.26;

import "forge-std/Test.sol";
import "../../contracts/LineaRollup.sol";
import "../../contracts/interfaces/l1/ILineaRollup.sol";
import "../../contracts/lib/Utils.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

contract LineaRollupTestHelper is LineaRollup {
function calculateY(bytes calldata data, bytes32 dataEvaluationPoint) external pure returns (bytes32) {
return _calculateY(data, dataEvaluationPoint);
}
}

contract LineaRollupTest is Test {
LineaRollupTestHelper lineaRollup;
LineaRollupTestHelper implementation;
address operator;
address defaultAdmin;
address verifier;
address nonAuthorizedAccount;
address securityCouncil;
address fallbackOperator;

bytes32 VERIFIER_SETTER_ROLE;
bytes32 VERIFIER_UNSETTER_ROLE;
bytes32 OPERATOR_ROLE;
bytes32 DEFAULT_ADMIN_ROLE;

function setUp() public {
operator = address(0x1);
defaultAdmin = address(0x2);
verifier = address(0x3);
securityCouncil = defaultAdmin;
fallbackOperator = address(0x4);
nonAuthorizedAccount = address(0x5);

implementation = new LineaRollupTestHelper();

ILineaRollup.InitializationData memory initData;
initData.initialStateRootHash = bytes32(0x0);
initData.initialL2BlockNumber = 0;
initData.genesisTimestamp = block.timestamp;
initData.defaultVerifier = verifier;
initData.rateLimitPeriodInSeconds = 86400; // 1 day
initData.rateLimitAmountInWei = 100 ether;

initData.roleAddresses = new IPermissionsManager.RoleAddress[](1);
initData.roleAddresses[0] = IPermissionsManager.RoleAddress({
addressWithRole: operator,
role: implementation.OPERATOR_ROLE()
});

initData.pauseTypeRoles = new IPauseManager.PauseTypeRole[](0);
initData.unpauseTypeRoles = new IPauseManager.PauseTypeRole[](0);
initData.fallbackOperator = fallbackOperator;
initData.defaultAdmin = defaultAdmin;

bytes memory initializer = abi.encodeWithSelector(
LineaRollup.initialize.selector,
initData
);

ERC1967Proxy proxy = new ERC1967Proxy(address(implementation), initializer);

lineaRollup = LineaRollupTestHelper(address(proxy));

VERIFIER_SETTER_ROLE = lineaRollup.VERIFIER_SETTER_ROLE();
VERIFIER_UNSETTER_ROLE = lineaRollup.VERIFIER_UNSETTER_ROLE();
OPERATOR_ROLE = lineaRollup.OPERATOR_ROLE();
DEFAULT_ADMIN_ROLE = lineaRollup.DEFAULT_ADMIN_ROLE();

assertEq(lineaRollup.hasRole(DEFAULT_ADMIN_ROLE, defaultAdmin), true, "Default admin not set");
assertEq(lineaRollup.hasRole(OPERATOR_ROLE, operator), true, "Operator not set");
}

function testSubmitDataAsCalldata() public {
ILineaRollup.CompressedCalldataSubmission memory submission;
submission.finalStateRootHash = keccak256(abi.encodePacked("finalStateRootHash"));
submission.snarkHash = keccak256(abi.encodePacked("snarkHash"));

// Adjust compressedData to start with 0x00
submission.compressedData = hex"00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff";

bytes32 dataEvaluationPoint = Utils._efficientKeccak(
submission.snarkHash,
keccak256(submission.compressedData)
);

bytes32 dataEvaluationClaim = lineaRollup.calculateY(submission.compressedData, dataEvaluationPoint);

bytes32 parentShnarf = lineaRollup.GENESIS_SHNARF();

bytes32 expectedShnarf = keccak256(
abi.encodePacked(
parentShnarf,
submission.snarkHash,
submission.finalStateRootHash,
dataEvaluationPoint,
dataEvaluationClaim
)
);

vm.prank(operator);
lineaRollup.submitDataAsCalldata(submission, parentShnarf, expectedShnarf);

uint256 exists = lineaRollup.blobShnarfExists(expectedShnarf);
assertEq(exists, 1, "Blob shnarf should exist after submission");
}

function testChangeVerifierNotAuthorized() public {
address newVerifier = address(0x1234);

vm.prank(nonAuthorizedAccount);
vm.expectRevert(
abi.encodePacked(
"AccessControl: account ",
_toAsciiString(nonAuthorizedAccount),
" is missing role ",
_toHexString(VERIFIER_SETTER_ROLE)
)
);
lineaRollup.setVerifierAddress(newVerifier, 2);
}

function testSetVerifierAddressSuccess() public {
vm.startPrank(defaultAdmin);
lineaRollup.grantRole(VERIFIER_SETTER_ROLE, defaultAdmin);
vm.stopPrank();

address newVerifier = address(0x1234);

vm.prank(defaultAdmin);
lineaRollup.setVerifierAddress(newVerifier, 2);

assertEq(lineaRollup.verifiers(2), newVerifier, "Verifier address not updated");
}

function testUnsetVerifierAddress() public {
vm.startPrank(defaultAdmin);
lineaRollup.grantRole(VERIFIER_UNSETTER_ROLE, defaultAdmin);

lineaRollup.grantRole(VERIFIER_SETTER_ROLE, defaultAdmin);

address newVerifier = address(0x1234);
lineaRollup.setVerifierAddress(newVerifier, 0);
vm.stopPrank();

vm.prank(defaultAdmin);
lineaRollup.unsetVerifierAddress(0);

assertEq(lineaRollup.verifiers(0), address(0), "Verifier address not unset");
}

function testUnsetVerifierNotAuthorized() public {
vm.prank(nonAuthorizedAccount);
vm.expectRevert(
abi.encodePacked(
"AccessControl: account ",
_toAsciiString(nonAuthorizedAccount),
" is missing role ",
_toHexString(VERIFIER_UNSETTER_ROLE)
)
);
lineaRollup.unsetVerifierAddress(0);
}

// Helper function to convert address to ascii string
function _toAsciiString(address x) internal pure returns (string memory) {
bytes memory s = new bytes(42);
s[0] = "0";
s[1] = "x";
for (uint256 i = 0; i < 20; i++) {
uint8 b = uint8(uint256(uint160(x)) / (2**(8 * (19 - i))));
uint8 hi = b / 16;
uint8 lo = b - 16 * hi;
s[2 + 2 * i] = _char(hi);
s[3 + 2 * i] = _char(lo);
}
return string(s);
}

// Helper function to convert byte to char
function _char(uint8 b) internal pure returns (bytes1 c) {
if (b < 10) {
return bytes1(b + 0x30);
} else {
return bytes1(b + 0x57);
}
}

// Helper function to convert bytes32 to hex string
function _toHexString(bytes32 data) internal pure returns (string memory) {
return _toHexString(abi.encodePacked(data));
}

// Helper function to convert bytes to hex string
function _toHexString(bytes memory data) internal pure returns (string memory) {
bytes memory hexString = new bytes(data.length * 2 + 2);
hexString[0] = "0";
hexString[1] = "x";
bytes memory hexChars = "0123456789abcdef";
for (uint256 i = 0; i < data.length; i++) {
hexString[2 + i * 2] = hexChars[uint8(data[i] >> 4)];
hexString[3 + i * 2] = hexChars[uint8(data[i] & 0x0f)];
}
return string(hexString);
}
}
2 changes: 1 addition & 1 deletion docker/compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ services:
sequencer:
hostname: sequencer
container_name: sequencer
image: consensys/linea-besu-package:${SEQUENCER_TAG:-mainnet-811f30b}
image: consensys/linea-besu-package:${SEQUENCER_TAG:-mainnet-b297345}
profiles: [ "l2", "l2-bc", "debug", "external-to-monorepo" ]
ports:
- "8545:8545"
Expand Down
Loading

0 comments on commit 347b4c7

Please sign in to comment.