diff --git a/claim_contracts/Makefile b/claim_contracts/Makefile
index d1f5ab3517..c1ae58d0bc 100644
--- a/claim_contracts/Makefile
+++ b/claim_contracts/Makefile
@@ -43,7 +43,6 @@ deploy-token-testnet: ## 🚀 Deploy the token contract
--private-key $(DEPLOYER_PRIVATE_KEY) \
--rpc-url $(RPC_URL) \
--broadcast \
- --verbosity 3 \
--verify \
--etherscan-api-key $(ETHERSCAN_API_KEY)
diff --git a/claim_contracts/base/.env.example b/claim_contracts/base/.env.example
new file mode 100644
index 0000000000..eb55b16a46
--- /dev/null
+++ b/claim_contracts/base/.env.example
@@ -0,0 +1,25 @@
+# Base L2 ALIGN Token Deployment
+# Copy this file to .env and fill in the values
+
+# Keys
+DEPLOYER_PRIVATE_KEY=
+USER_PRIVATE_KEY=
+
+# RPC URLs
+BASE_SEPOLIA_RPC_URL=https://sepolia.base.org
+BASE_MAINNET_RPC_URL=https://mainnet.base.org
+L1_SEPOLIA_RPC_URL=https://ethereum-sepolia-rpc.publicnode.com
+L1_MAINNET_RPC_URL=https://ethereum-rpc.publicnode.com
+
+# Token addresses
+L1_TOKEN_SEPOLIA=0xd2Fd114f098b355321cB3424400f3CC6a0d75C9A
+L2_TOKEN_SEPOLIA=0x4AAcFbc2C31598a560b285dB20966E00B73F9F81
+L1_TOKEN_MAINNET=
+L2_TOKEN_MAINNET=
+
+# Bridge addresses (source: https://docs.base.org/chain/base-contracts)
+L1_BRIDGE_SEPOLIA=0xfd0Bf71F60660E2f608ed56e1659C450eB113120
+L1_BRIDGE_MAINNET=0x3154Cf16ccdb4C6d922629664174b904d80F2C35
+
+# Bridge amount in wei (1e18 = 1 ALIGN)
+AMOUNT=1000000000000000000
diff --git a/claim_contracts/base/.gitignore b/claim_contracts/base/.gitignore
new file mode 100644
index 0000000000..35116f49a1
--- /dev/null
+++ b/claim_contracts/base/.gitignore
@@ -0,0 +1,17 @@
+# Compiler files
+cache/
+out/
+
+# Ignores development broadcast logs
+!/broadcast
+/broadcast/*/31337/
+/broadcast/**/dry-run/
+
+# Docs
+docs/
+
+# Dotenv file
+.env
+
+# Node
+node_modules/
diff --git a/claim_contracts/base/Makefile b/claim_contracts/base/Makefile
new file mode 100644
index 0000000000..c1732ef494
--- /dev/null
+++ b/claim_contracts/base/Makefile
@@ -0,0 +1,110 @@
+-include .env
+export
+
+.PHONY: help deploy-base-sepolia deploy-base-mainnet verify bridge-l1-to-base-sepolia bridge-l1-to-base-mainnet withdraw-base-to-l1-sepolia withdraw-base-to-l1-mainnet prove-withdrawal-sepolia prove-withdrawal-mainnet finalize-withdrawal-sepolia finalize-withdrawal-mainnet withdraw-full-sepolia withdraw-full-mainnet
+
+help: ## Show help
+ @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
+
+# Factory predeploy (same on all OP Stack chains)
+FACTORY=0x4200000000000000000000000000000000000012
+
+# --- Deployment ---
+
+deploy-base-sepolia: ## Deploy ALIGN on BaseSepolia
+ @L2_TOKEN=$$(cast send $(FACTORY) \
+ "createOptimismMintableERC20(address,string,string)" \
+ $(L1_TOKEN_SEPOLIA) "Aligned Token" "ALIGN" \
+ --private-key $(DEPLOYER_PRIVATE_KEY) \
+ --rpc-url $(BASE_SEPOLIA_RPC_URL) \
+ --json | jq -r '.logs[0].topics[2]' | cast parse-bytes32-address) && \
+ echo "L2 Token deployed at: $$L2_TOKEN"
+
+deploy-base-mainnet: ## Deploy ALIGN on BaseMainnet
+ @L2_TOKEN=$$(cast send $(FACTORY) \
+ "createOptimismMintableERC20(address,string,string)" \
+ $(L1_TOKEN_MAINNET) "Aligned Token" "ALIGN" \
+ --private-key $(DEPLOYER_PRIVATE_KEY) \
+ --rpc-url $(BASE_MAINNET_RPC_URL) \
+ --json | jq -r '.logs[0].topics[2]' | cast parse-bytes32-address) && \
+ echo "L2 Token deployed at: $$L2_TOKEN"
+
+# --- Verification ---
+
+verify: ## Verify L2 token (requires L2_TOKEN, RPC_URL)
+ cast call $(L2_TOKEN) "name()(string)" --rpc-url $(RPC_URL)
+ cast call $(L2_TOKEN) "symbol()(string)" --rpc-url $(RPC_URL)
+ cast call $(L2_TOKEN) "decimals()(uint8)" --rpc-url $(RPC_URL)
+ cast call $(L2_TOKEN) "REMOTE_TOKEN()(address)" --rpc-url $(RPC_URL)
+ cast call $(L2_TOKEN) "BRIDGE()(address)" --rpc-url $(RPC_URL)
+ cast call $(L2_TOKEN) "totalSupply()(uint256)" --rpc-url $(RPC_URL)
+
+# --- Bridging L1 -> Base ---
+
+bridge-l1-to-base-sepolia: ## Bridge ALIGN from Sepolia to BaseSepolia (requires AMOUNT)
+ cast send $(L1_TOKEN_SEPOLIA) "approve(address,uint256)" $(L1_BRIDGE_SEPOLIA) $(AMOUNT) \
+ --private-key $(USER_PRIVATE_KEY) --rpc-url $(L1_SEPOLIA_RPC_URL)
+ cast send $(L1_BRIDGE_SEPOLIA) "depositERC20(address,address,uint256,uint32,bytes)" \
+ $(L1_TOKEN_SEPOLIA) $(L2_TOKEN_SEPOLIA) $(AMOUNT) 200000 0x \
+ --private-key $(USER_PRIVATE_KEY) --rpc-url $(L1_SEPOLIA_RPC_URL)
+
+bridge-l1-to-base-mainnet: ## Bridge ALIGN from Ethereum to Base (requires AMOUNT)
+ cast send $(L1_TOKEN_MAINNET) "approve(address,uint256)" $(L1_BRIDGE_MAINNET) $(AMOUNT) \
+ --private-key $(USER_PRIVATE_KEY) --rpc-url $(L1_MAINNET_RPC_URL)
+ cast send $(L1_BRIDGE_MAINNET) "depositERC20(address,address,uint256,uint32,bytes)" \
+ $(L1_TOKEN_MAINNET) $(L2_TOKEN_MAINNET) $(AMOUNT) 200000 0x \
+ --private-key $(USER_PRIVATE_KEY) --rpc-url $(L1_MAINNET_RPC_URL)
+
+bridge-l1-to-base-sepolia-to: ## Bridge ALIGN from Sepolia to BaseSepolia to a different address (requires AMOUNT, TO)
+ cast send $(L1_TOKEN_SEPOLIA) "approve(address,uint256)" $(L1_BRIDGE_SEPOLIA) $(AMOUNT) \
+ --private-key $(USER_PRIVATE_KEY) --rpc-url $(L1_SEPOLIA_RPC_URL)
+ cast send $(L1_BRIDGE_SEPOLIA) "depositERC20To(address,address,address,uint256,uint32,bytes)" \
+ $(L1_TOKEN_SEPOLIA) $(L2_TOKEN_SEPOLIA) $(TO) $(AMOUNT) 200000 0x \
+ --private-key $(USER_PRIVATE_KEY) --rpc-url $(L1_SEPOLIA_RPC_URL)
+
+bridge-l1-to-base-mainnet-to: ## Bridge ALIGN from Ethereum to Base to a different address (requires AMOUNT, TO)
+ cast send $(L1_TOKEN_MAINNET) "approve(address,uint256)" $(L1_BRIDGE_MAINNET) $(AMOUNT) \
+ --private-key $(USER_PRIVATE_KEY) --rpc-url $(L1_MAINNET_RPC_URL)
+ cast send $(L1_BRIDGE_MAINNET) "depositERC20To(address,address,address,uint256,uint32,bytes)" \
+ $(L1_TOKEN_MAINNET) $(L2_TOKEN_MAINNET) $(TO) $(AMOUNT) 200000 0x \
+ --private-key $(USER_PRIVATE_KEY) --rpc-url $(L1_MAINNET_RPC_URL)
+
+# --- Bridging Base -> L1 (withdrawal) ---
+# This initiates the withdrawal on L2. After this, you must:
+# 1. Wait ~1 hour for the L2 output to be proposed
+# 2. Prove the withdrawal on L1 (requires Optimism SDK or Base Bridge UI)
+# 3. Wait 7 days (challenge period)
+# 4. Finalize the withdrawal on L1
+# L2StandardBridge predeploy: 0x4200000000000000000000000000000000000010
+
+withdraw-base-to-l1-sepolia: ## Initiate ALIGN withdrawal from BaseSepolia to Sepolia (requires AMOUNT)
+ cast send 0x4200000000000000000000000000000000000010 \
+ "withdraw(address,uint256,uint32,bytes)" \
+ $(L2_TOKEN_SEPOLIA) $(AMOUNT) 200000 0x \
+ --private-key $(USER_PRIVATE_KEY) --rpc-url $(BASE_SEPOLIA_RPC_URL)
+
+withdraw-base-to-l1-mainnet: ## Initiate ALIGN withdrawal from Base to Ethereum (requires AMOUNT)
+ cast send 0x4200000000000000000000000000000000000010 \
+ "withdraw(address,uint256,uint32,bytes)" \
+ $(L2_TOKEN_MAINNET) $(AMOUNT) 200000 0x \
+ --private-key $(USER_PRIVATE_KEY) --rpc-url $(BASE_MAINNET_RPC_URL)
+
+# --- Prove & Finalize (requires npm install) ---
+
+prove-withdrawal-sepolia: ## Prove withdrawal on L1 Sepolia (requires TX_HASH)
+ npx tsx scripts/withdraw.ts prove --tx-hash $(TX_HASH) --network sepolia
+
+prove-withdrawal-mainnet: ## Prove withdrawal on L1 Mainnet (requires TX_HASH)
+ npx tsx scripts/withdraw.ts prove --tx-hash $(TX_HASH) --network mainnet
+
+finalize-withdrawal-sepolia: ## Finalize withdrawal on L1 Sepolia (requires TX_HASH)
+ npx tsx scripts/withdraw.ts finalize --tx-hash $(TX_HASH) --network sepolia
+
+finalize-withdrawal-mainnet: ## Finalize withdrawal on L1 Mainnet (requires TX_HASH)
+ npx tsx scripts/withdraw.ts finalize --tx-hash $(TX_HASH) --network mainnet
+
+withdraw-full-sepolia: ## Full withdrawal flow: prove + finalize on Sepolia (requires TX_HASH)
+ npx tsx scripts/withdraw.ts full --tx-hash $(TX_HASH) --network sepolia
+
+withdraw-full-mainnet: ## Full withdrawal flow: prove + finalize on Mainnet (requires TX_HASH)
+ npx tsx scripts/withdraw.ts full --tx-hash $(TX_HASH) --network mainnet
diff --git a/claim_contracts/base/README.md b/claim_contracts/base/README.md
new file mode 100644
index 0000000000..748f6b7c69
--- /dev/null
+++ b/claim_contracts/base/README.md
@@ -0,0 +1,87 @@
+# ALIGN Token on Base L2
+
+Deployment and bridging of the Aligned Token (ALIGN) on Base using the [OP Standard Bridge](https://docs.optimism.io/app-developers/tutorials/bridging/standard-bridge-standard-token).
+
+## Setup
+
+```bash
+cp .env.example .env
+# Fill in DEPLOYER_PRIVATE_KEY and USER_PRIVATE_KEY
+```
+
+## Deploy
+
+The L2 token is created via the `OptimismMintableERC20Factory` predeploy at `0x4200000000000000000000000000000000000012`. No custom contract is needed.
+
+```bash
+make deploy-base-sepolia # BaseSepolia
+make deploy-base-mainnet # BaseMainnet
+```
+
+## Verify
+
+```bash
+make verify L2_TOKEN=
RPC_URL=https://sepolia.base.org
+```
+
+## Bridge (L1 -> Base)
+
+Approve + deposit in one command. `AMOUNT` is in wei (1e18 = 1 ALIGN).
+
+```bash
+make bridge-l1-to-base-sepolia AMOUNT=1000000000000000000
+make bridge-l1-to-base-mainnet AMOUNT=1000000000000000000
+```
+
+Tokens appear on Base after ~20 minutes.
+
+## Withdraw (Base -> L1)
+
+Withdrawals are a [multi-step process](https://docs.optimism.io/app-developers/tutorials/bridging/cross-dom-bridge-erc20#withdraw-tokens). No approval is needed. All three steps use the same `TX_HASH` — the **L2 initiation tx hash** from step 1.
+
+1. **Initiate** on L2 (burns tokens on Base):
+
+ ```bash
+ make withdraw-base-to-l1-sepolia AMOUNT=1000000000000000000
+ make withdraw-base-to-l1-mainnet AMOUNT=1000000000000000000
+ ```
+
+ Save the tx hash from this step — it's needed for prove and finalize.
+
+2. **Prove** on L1 — wait ~1 hour for the L2 output to be proposed, then prove:
+
+ ```bash
+ make prove-withdrawal-sepolia TX_HASH=
+ make prove-withdrawal-mainnet TX_HASH=
+ ```
+
+3. **Finalize** on L1 — wait 7 days challenge period (shorter on testnet), then finalize:
+
+ ```bash
+ make finalize-withdrawal-sepolia TX_HASH=
+ make finalize-withdrawal-mainnet TX_HASH=
+ ```
+
+ > **Note:** Prove and finalize use `viem` + `viem/op-stack`. Run `npm install` first.
+
+## Bridge Addresses
+
+Source: [Base Contracts](https://docs.base.org/chain/base-contracts)
+
+| Network | L1StandardBridge | L2StandardBridge |
+|---------|------------------|------------------|
+| Sepolia | [`0xfd0Bf71F60660E2f608ed56e1659C450eB113120`](https://sepolia.etherscan.io/address/0xfd0Bf71F60660E2f608ed56e1659C450eB113120) | [`0x4200000000000000000000000000000000000010`](https://sepolia.basescan.org/address/0x4200000000000000000000000000000000000010) |
+| Mainnet | [`0x3154Cf16ccdb4C6d922629664174b904d80F2C35`](https://etherscan.io/address/0x3154Cf16ccdb4C6d922629664174b904d80F2C35) | [`0x4200000000000000000000000000000000000010`](https://basescan.org/address/0x4200000000000000000000000000000000000010) |
+
+## Deployed Addresses
+
+| Network | L1 Token (Ethereum) | L2 Token (Base) |
+|---------|---------------------|-----------------|
+| Sepolia | `0xd2Fd114f098b355321cB3424400f3CC6a0d75C9A` | `0x4AAcFbc2C31598a560b285dB20966E00B73F9F81` |
+| Mainnet | TBD | TBD |
+
+## References
+
+- [OP Standard Bridge Standard Token Tutorial](https://docs.optimism.io/app-developers/tutorials/bridging/standard-bridge-standard-token)
+- [OP Bridge ERC-20 Tutorial (withdraw flow)](https://docs.optimism.io/app-developers/tutorials/bridging/cross-dom-bridge-erc20)
+- [Base Contracts](https://docs.base.org/chain/base-contracts)
diff --git a/claim_contracts/base/package-lock.json b/claim_contracts/base/package-lock.json
new file mode 100644
index 0000000000..33731e661a
--- /dev/null
+++ b/claim_contracts/base/package-lock.json
@@ -0,0 +1,777 @@
+{
+ "name": "aligned-base-bridge",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "aligned-base-bridge",
+ "dependencies": {
+ "viem": "^2"
+ },
+ "devDependencies": {
+ "tsx": "^4",
+ "typescript": "^5"
+ }
+ },
+ "node_modules/@adraffy/ens-normalize": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.1.tgz",
+ "integrity": "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==",
+ "license": "MIT"
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz",
+ "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz",
+ "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz",
+ "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz",
+ "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz",
+ "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz",
+ "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz",
+ "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz",
+ "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz",
+ "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz",
+ "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz",
+ "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz",
+ "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz",
+ "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz",
+ "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz",
+ "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz",
+ "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz",
+ "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz",
+ "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz",
+ "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz",
+ "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz",
+ "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz",
+ "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz",
+ "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz",
+ "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz",
+ "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz",
+ "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@noble/ciphers": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz",
+ "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==",
+ "license": "MIT",
+ "engines": {
+ "node": "^14.21.3 || >=16"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@noble/curves": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz",
+ "integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==",
+ "license": "MIT",
+ "dependencies": {
+ "@noble/hashes": "1.8.0"
+ },
+ "engines": {
+ "node": "^14.21.3 || >=16"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@noble/hashes": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
+ "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
+ "license": "MIT",
+ "engines": {
+ "node": "^14.21.3 || >=16"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@scure/base": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz",
+ "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@scure/bip32": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz",
+ "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==",
+ "license": "MIT",
+ "dependencies": {
+ "@noble/curves": "~1.9.0",
+ "@noble/hashes": "~1.8.0",
+ "@scure/base": "~1.2.5"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@scure/bip39": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz",
+ "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==",
+ "license": "MIT",
+ "dependencies": {
+ "@noble/hashes": "~1.8.0",
+ "@scure/base": "~1.2.5"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/abitype": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.2.3.tgz",
+ "integrity": "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/wevm"
+ },
+ "peerDependencies": {
+ "typescript": ">=5.0.4",
+ "zod": "^3.22.0 || ^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ },
+ "zod": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz",
+ "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.27.4",
+ "@esbuild/android-arm": "0.27.4",
+ "@esbuild/android-arm64": "0.27.4",
+ "@esbuild/android-x64": "0.27.4",
+ "@esbuild/darwin-arm64": "0.27.4",
+ "@esbuild/darwin-x64": "0.27.4",
+ "@esbuild/freebsd-arm64": "0.27.4",
+ "@esbuild/freebsd-x64": "0.27.4",
+ "@esbuild/linux-arm": "0.27.4",
+ "@esbuild/linux-arm64": "0.27.4",
+ "@esbuild/linux-ia32": "0.27.4",
+ "@esbuild/linux-loong64": "0.27.4",
+ "@esbuild/linux-mips64el": "0.27.4",
+ "@esbuild/linux-ppc64": "0.27.4",
+ "@esbuild/linux-riscv64": "0.27.4",
+ "@esbuild/linux-s390x": "0.27.4",
+ "@esbuild/linux-x64": "0.27.4",
+ "@esbuild/netbsd-arm64": "0.27.4",
+ "@esbuild/netbsd-x64": "0.27.4",
+ "@esbuild/openbsd-arm64": "0.27.4",
+ "@esbuild/openbsd-x64": "0.27.4",
+ "@esbuild/openharmony-arm64": "0.27.4",
+ "@esbuild/sunos-x64": "0.27.4",
+ "@esbuild/win32-arm64": "0.27.4",
+ "@esbuild/win32-ia32": "0.27.4",
+ "@esbuild/win32-x64": "0.27.4"
+ }
+ },
+ "node_modules/eventemitter3": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
+ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
+ "license": "MIT"
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "4.13.6",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz",
+ "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/isows": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.7.tgz",
+ "integrity": "sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/wevm"
+ }
+ ],
+ "license": "MIT",
+ "peerDependencies": {
+ "ws": "*"
+ }
+ },
+ "node_modules/ox": {
+ "version": "0.14.0",
+ "resolved": "https://registry.npmjs.org/ox/-/ox-0.14.0.tgz",
+ "integrity": "sha512-WLOB7IKnmI3Ol6RAqY7CJdZKl8QaI44LN91OGF1061YIeN6bL5IsFcdp7+oQShRyamE/8fW/CBRWhJAOzI35Dw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/wevm"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@adraffy/ens-normalize": "^1.11.0",
+ "@noble/ciphers": "^1.3.0",
+ "@noble/curves": "1.9.1",
+ "@noble/hashes": "^1.8.0",
+ "@scure/bip32": "^1.7.0",
+ "@scure/bip39": "^1.6.0",
+ "abitype": "^1.2.3",
+ "eventemitter3": "5.0.1"
+ },
+ "peerDependencies": {
+ "typescript": ">=5.4.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
+ "node_modules/tsx": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
+ "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "~0.27.0",
+ "get-tsconfig": "^4.7.5"
+ },
+ "bin": {
+ "tsx": "dist/cli.mjs"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "devOptional": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/viem": {
+ "version": "2.47.2",
+ "resolved": "https://registry.npmjs.org/viem/-/viem-2.47.2.tgz",
+ "integrity": "sha512-etDIwDgmDiGaPg8rUbJtUFuC3/nAJCbhMYyfh5dOcqNNkzBWTNcS2VluPSM5JVo+9U3b2hle2RkBEq3+xyvlvg==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/wevm"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@noble/curves": "1.9.1",
+ "@noble/hashes": "1.8.0",
+ "@scure/bip32": "1.7.0",
+ "@scure/bip39": "1.6.0",
+ "abitype": "1.2.3",
+ "isows": "1.0.7",
+ "ox": "0.14.0",
+ "ws": "8.18.3"
+ },
+ "peerDependencies": {
+ "typescript": ">=5.0.4"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ws": {
+ "version": "8.18.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
+ "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ }
+ }
+}
diff --git a/claim_contracts/base/package.json b/claim_contracts/base/package.json
new file mode 100644
index 0000000000..cb2184d9d6
--- /dev/null
+++ b/claim_contracts/base/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "aligned-base-bridge",
+ "private": true,
+ "type": "module",
+ "dependencies": {
+ "viem": "^2"
+ },
+ "devDependencies": {
+ "tsx": "^4",
+ "typescript": "^5"
+ }
+}
diff --git a/claim_contracts/base/scripts/withdraw.ts b/claim_contracts/base/scripts/withdraw.ts
new file mode 100644
index 0000000000..87745ec0e1
--- /dev/null
+++ b/claim_contracts/base/scripts/withdraw.ts
@@ -0,0 +1,240 @@
+import {
+ createPublicClient,
+ createWalletClient,
+ http,
+ type Hash,
+ type Chain,
+} from "viem";
+import { privateKeyToAccount } from "viem/accounts";
+import { sepolia, baseSepolia, mainnet, base } from "viem/chains";
+import {
+ publicActionsL1,
+ walletActionsL1,
+ publicActionsL2,
+} from "viem/op-stack";
+
+// --- Config ---
+
+type Network = "sepolia" | "mainnet";
+
+function getChains(network: Network): { l1: Chain; l2: Chain } {
+ return network === "sepolia"
+ ? { l1: sepolia, l2: baseSepolia }
+ : { l1: mainnet, l2: base };
+}
+
+function getRpcUrls(network: Network) {
+ if (network === "sepolia") {
+ return {
+ l1: process.env.L1_SEPOLIA_RPC_URL || "https://ethereum-sepolia-rpc.publicnode.com",
+ l2: process.env.BASE_SEPOLIA_RPC_URL || "https://sepolia.base.org",
+ };
+ }
+ return {
+ l1: process.env.L1_MAINNET_RPC_URL || "https://ethereum-rpc.publicnode.com",
+ l2: process.env.BASE_MAINNET_RPC_URL || "https://mainnet.base.org",
+ };
+}
+
+function getPrivateKey(): Hash {
+ const key = process.env.USER_PRIVATE_KEY;
+ if (!key) {
+ console.error("Error: USER_PRIVATE_KEY not set");
+ process.exit(1);
+ }
+ return key as Hash;
+}
+
+function createPublicClients(network: Network) {
+ const chains = getChains(network);
+ const rpc = getRpcUrls(network);
+
+ const publicClientL1 = createPublicClient({
+ chain: chains.l1,
+ transport: http(rpc.l1),
+ }).extend(publicActionsL1());
+
+ const publicClientL2 = createPublicClient({
+ chain: chains.l2,
+ transport: http(rpc.l2),
+ }).extend(publicActionsL2());
+
+ return { publicClientL1, publicClientL2, chains };
+}
+
+function createClients(network: Network) {
+ const { publicClientL1, publicClientL2, chains } = createPublicClients(network);
+ const rpc = getRpcUrls(network);
+ const account = privateKeyToAccount(getPrivateKey());
+
+ const walletClientL1 = createWalletClient({
+ account,
+ chain: chains.l1,
+ transport: http(rpc.l1),
+ }).extend(walletActionsL1());
+
+ return { publicClientL1, walletClientL1, publicClientL2, chains };
+}
+
+// --- Commands ---
+
+async function prove(txHash: Hash, network: Network) {
+ const { publicClientL1, walletClientL1, publicClientL2, chains } =
+ createClients(network);
+
+ console.log(`Getting withdrawal receipt for ${txHash}...`);
+ const receipt = await publicClientL2.getTransactionReceipt({ hash: txHash });
+
+ console.log("Waiting for withdrawal to be provable (~1 hour)...");
+ const { output, withdrawal } = await publicClientL1.waitToProve({
+ receipt,
+ targetChain: chains.l2,
+ });
+
+ console.log("Building prove withdrawal args...");
+ const proveArgs = await publicClientL2.buildProveWithdrawal({
+ output,
+ withdrawal,
+ });
+
+ console.log("Proving withdrawal on L1...");
+ const proveHash = await walletClientL1.proveWithdrawal(proveArgs);
+
+ console.log("Waiting for prove tx confirmation...");
+ await publicClientL1.waitForTransactionReceipt({ hash: proveHash });
+
+ console.log(`Withdrawal proved: ${proveHash}`);
+ return proveHash;
+}
+
+async function finalize(txHash: Hash, network: Network) {
+ const { publicClientL1, walletClientL1, publicClientL2, chains } =
+ createClients(network);
+
+ console.log(`Getting withdrawal receipt for ${txHash}...`);
+ const receipt = await publicClientL2.getTransactionReceipt({ hash: txHash });
+
+ console.log("Waiting for finalization period (7 days on mainnet)...");
+ // Use getWithdrawalStatus instead of waitToFinalize due to a viem bug
+ // with Portal v3+ (Fault Proof System) where waitToFinalize incorrectly
+ // throws "Withdrawal has not been proven on L1".
+ while (true) {
+ const withdrawalStatus = await publicClientL1.getWithdrawalStatus({
+ receipt,
+ targetChain: chains.l2,
+ });
+ console.log(` Status: ${withdrawalStatus}`);
+ if (withdrawalStatus === "ready-to-finalize") break;
+ if (withdrawalStatus === "finalized") {
+ console.log("Withdrawal already finalized.");
+ return;
+ }
+ await new Promise((resolve) => setTimeout(resolve, 10_000));
+ }
+
+ console.log("Finalizing withdrawal on L1...");
+ const finalizeHash = await walletClientL1.finalizeWithdrawal({
+ receipt,
+ targetChain: chains.l2,
+ });
+
+ console.log("Waiting for finalize tx confirmation...");
+ await publicClientL1.waitForTransactionReceipt({ hash: finalizeHash });
+
+ console.log(`Withdrawal finalized: ${finalizeHash}`);
+ return finalizeHash;
+}
+
+async function status(txHash: Hash, network: Network) {
+ const { publicClientL1, publicClientL2, chains } = createPublicClients(network);
+
+ console.log(`Getting withdrawal receipt for ${txHash}...`);
+ const receipt = await publicClientL2.getTransactionReceipt({ hash: txHash });
+
+ const withdrawalStatus = await publicClientL1.getWithdrawalStatus({
+ receipt,
+ targetChain: chains.l2,
+ });
+
+ console.log(`Withdrawal status: ${withdrawalStatus}`);
+}
+
+async function full(txHash: Hash, network: Network) {
+ await prove(txHash, network);
+ await finalize(txHash, network);
+ console.log("Withdrawal complete.");
+}
+
+// --- CLI ---
+
+function usage() {
+ console.log(`Usage: npx tsx scripts/withdraw.ts --tx-hash --network
+
+Commands:
+ prove Prove a withdrawal on L1 (wait ~1 hour after initiation)
+ finalize Finalize a withdrawal on L1 (wait 7 days after proving)
+ full Run prove + finalize sequentially
+ status Check withdrawal status`);
+ process.exit(1);
+}
+
+function parseArgs() {
+ const args = process.argv.slice(2);
+ const command = args[0];
+
+ if (!command || !["prove", "finalize", "full", "status"].includes(command)) {
+ usage();
+ }
+
+ let txHash: string | undefined;
+ let network: Network = "sepolia";
+
+ for (let i = 1; i < args.length; i++) {
+ if (args[i] === "--tx-hash" && args[i + 1]) {
+ txHash = args[++i];
+ } else if (args[i] === "--network" && args[i + 1]) {
+ network = args[++i] as Network;
+ }
+ }
+
+ if (!txHash) {
+ console.error("Error: --tx-hash is required");
+ usage();
+ }
+
+ if (!["sepolia", "mainnet"].includes(network)) {
+ console.error("Error: --network must be sepolia or mainnet");
+ usage();
+ }
+
+ return { command: command!, txHash: txHash as Hash, network };
+}
+
+async function main() {
+ const { command, txHash, network } = parseArgs();
+
+ console.log(`Network: ${network}`);
+ console.log(`Command: ${command}`);
+ console.log(`TX Hash: ${txHash}`);
+ console.log("---");
+
+ switch (command) {
+ case "prove":
+ await prove(txHash, network);
+ break;
+ case "finalize":
+ await finalize(txHash, network);
+ break;
+ case "full":
+ await full(txHash, network);
+ break;
+ case "status":
+ await status(txHash, network);
+ break;
+ }
+}
+
+main().catch((err) => {
+ console.error(err);
+ process.exit(1);
+});
diff --git a/claim_contracts/base/tsconfig.json b/claim_contracts/base/tsconfig.json
new file mode 100644
index 0000000000..3b933a4fc0
--- /dev/null
+++ b/claim_contracts/base/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "module": "NodeNext",
+ "moduleResolution": "NodeNext",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true
+ }
+}
diff --git a/docker/batcher.Dockerfile b/docker/batcher.Dockerfile
index 7feff7c010..66917350e7 100644
--- a/docker/batcher.Dockerfile
+++ b/docker/batcher.Dockerfile
@@ -11,6 +11,9 @@ FROM lukemathwalker/cargo-chef:latest-rust-1 AS chef
FROM chef AS planner
+COPY crates/sdk/Cargo.toml /aligned_layer/crates/sdk/Cargo.toml
+COPY crates/sdk/src/lib.rs /aligned_layer/crates/sdk/src/lib.rs
+
COPY crates/batcher/Cargo.toml /aligned_layer/crates/batcher/Cargo.toml
COPY crates/batcher/src/main.rs /aligned_layer/crates/batcher/src/main.rs
WORKDIR /aligned_layer/crates/batcher/