Skip to main content

Lineth SDK

Bridge ETH and tokens, and send and claim messages between Ethereum (L1) and Linea (L2), using a set of composable TypeScript functions. The Lineth SDK is built on viem: you create standard viem clients and call SDK actions on them, or extend a client with SDK decorators.

The SDK is published as two packages:

  • @lfdt-lineth/sdk-viem: the actions and decorators most applications use.
  • @lfdt-lineth/sdk-core: framework-agnostic utilities (Merkle tree, message types, chain and contract helpers). Most users get it automatically through @lfdt-lineth/sdk-viem and do not install it directly.

It supports Ethereum Mainnet, Linea Mainnet, Sepolia, and Linea Sepolia. You select the network by passing the matching chain from viem/chains, and the SDK resolves the correct contract addresses for you.

Installation​

Install the SDK alongside viem, which is a required peer dependency (version 2.22.0 or later):

npm install @lfdt-lineth/sdk-viem viem

Set up your clients​

Most actions take a viem client. Read actions use a public client, and state-changing actions (deposits, withdrawals, claims) use a wallet client. Use mainnet and linea for production, or sepolia and lineaSepolia for testing:

import { createPublicClient, http } from 'viem';
import { sepolia, lineaSepolia } from 'viem/chains';

const l1Client = createPublicClient({ chain: sepolia, transport: http() });
const l2Client = createPublicClient({ chain: lineaSepolia, transport: http() });

Bridge ETH and tokens​

Use deposit to bridge from L1 to L2, and withdraw to bridge from L2 to L1. Set token to zeroAddress to bridge native ETH. Both actions take a wallet client and return a transaction hash.

import { createWalletClient, createPublicClient, http, zeroAddress } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { sepolia, lineaSepolia } from 'viem/chains';
import { deposit } from '@lfdt-lineth/sdk-viem';

const client = createWalletClient({ chain: sepolia, transport: http() });
const l2Client = createPublicClient({ chain: lineaSepolia, transport: http() });

const hash = await deposit(client, {
l2Client,
account: privateKeyToAccount('0x...'),
amount: 1_000_000_000_000n,
token: zeroAddress, // Use zeroAddress for ETH
to: '0xRecipientAddress',
data: '0x', // Optional data
fee: 100_000_000n, // Optional fee
});
import { createWalletClient, http, zeroAddress } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { lineaSepolia } from 'viem/chains';
import { withdraw } from '@lfdt-lineth/sdk-viem';

const client = createWalletClient({ chain: lineaSepolia, transport: http() });

const hash = await withdraw(client, {
account: privateKeyToAccount('0x...'),
amount: 1_000_000_000_000n,
token: zeroAddress, // Use zeroAddress for ETH
to: '0xRecipientAddress',
data: '0x', // Optional data
});

Claim messages​

A message sent across the bridge is claimed on the destination layer. Messages to L2 are usually delivered automatically by the Postman service, so manual claiming is mainly needed on L1. Use claimOnL1 to finalize an L2-to-L1 message, and claimOnL2 to finalize an L1-to-L2 message.

Claiming on L1 requires a Merkle proof. You can fetch it with getMessageProof and pass it in, as shown below.

import { createWalletClient, http, zeroAddress } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { sepolia } from 'viem/chains';
import { claimOnL1 } from '@lfdt-lineth/sdk-viem';

const client = createWalletClient({ chain: sepolia, transport: http() });

const hash = await claimOnL1(client, {
account: privateKeyToAccount('0x...'),
from: '0xSenderAddress',
to: '0xRecipientAddress',
fee: 100_000_000n,
value: 1_000_000_000_000n,
messageNonce: 1n,
calldata: '0x',
feeRecipient: zeroAddress,
messageProof: {
root: '0x...',
proof: ['0x...'],
leafIndex: 0,
},
});

Read message data​

These actions run on a public client and read data from the chain. To check whether a message is ready to claim, query its status, and to claim on L1, fetch its proof:

import { createPublicClient, http } from 'viem';
import { sepolia, lineaSepolia } from 'viem/chains';
import { getL2ToL1MessageStatus, getMessageProof } from '@lfdt-lineth/sdk-viem';

const l1Client = createPublicClient({ chain: sepolia, transport: http() });
const l2Client = createPublicClient({ chain: lineaSepolia, transport: http() });

const status = await getL2ToL1MessageStatus(l1Client, {
l2Client,
messageHash: '0x...',
});

const messageProof = await getMessageProof(l1Client, {
l2Client,
messageHash: '0x...',
});

The full set of read actions:

ActionReturns
getL1ToL2MessageStatusWhether an L1-to-L2 message is unknown, claimable, or claimed.
getL2ToL1MessageStatusWhether an L2-to-L1 message is unknown, claimable, or claimed, including finalization checks.
getMessageProofThe Merkle proof needed to claim an L2-to-L1 message on L1.
getMessageByMessageHashFull message details from onchain MessageSent events.
getMessagesByTransactionHashAll messages emitted in a given transaction.
getTransactionReceiptByMessageHashThe transaction receipt that contains a specific message.
getMessageSentEventsMessageSent logs for a given block range.
getBlockExtraDataDecoded Linea block extraData (version and fee parameters).
computeMessageHashDeterministically hashes message parameters (utility).

Decorators​

Instead of calling actions as standalone functions, you can extend a viem client so the actions become methods on the client. Use publicActionsL1 and publicActionsL2 for read actions, and walletActionsL1 and walletActionsL2 for write actions:

import { createPublicClient, http } from 'viem';
import { sepolia, lineaSepolia } from 'viem/chains';
import { publicActionsL1, publicActionsL2 } from '@lfdt-lineth/sdk-viem';

const l1Client = createPublicClient({ chain: sepolia, transport: http() }).extend(publicActionsL1());
const l2Client = createPublicClient({ chain: lineaSepolia, transport: http() }).extend(publicActionsL2());

Each decorator accepts optional contract addresses (lineaRollupAddress, l2MessageServiceAddress, and for wallet decorators the token bridge addresses) for non-standard deployments. On the supported networks you can omit them, and the SDK resolves the correct addresses automatically.

Core utilities​

For advanced or framework-agnostic use, @lfdt-lineth/sdk-core exposes the building blocks the viem package is built on: SparseMerkleTree for proof generation and verification, parseBlockExtraData, formatMessageStatus, getContractsAddressesByChainId, the OnChainMessageStatus enum, chain helpers, and the message types. Install it directly only if you need these without viem:

npm install @lfdt-lineth/sdk-core

Resources​

note

This SDK was previously published as @consensys/linea-sdk. The package was renamed to @lfdt-lineth/sdk-viem and rebuilt on viem. The previous @consensys/linea-sdk API (the LineaSDK class and L1ClaimingService) is no longer the documented interface.

Was this page helpful?