Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .changeset/smart-account-package.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
"@towns-protocol/smart-account": patch
---

Introduced ERC-4337 modular smart account SDK with viem integration, account type detection, batch operations support, and ReplaySafeHash signature protection.

```typescript
import { createBundlerClient } from "viem/account-abstraction";
import { toModularSmartAccount } from "@towns-protocol/smart-account/create2";

const account = await toModularSmartAccount({
client: publicClient,
owner: localAccount,
});

const bundlerClient = createBundlerClient({
client: publicClient,
transport: http("https://your-bundler-url.com"),
});

const userOpHash = await bundlerClient.sendUserOperation({
account,
calls: [{ to: recipient, value: parseEther("0.1") }],
});
```
262 changes: 122 additions & 140 deletions bun.lock

Large diffs are not rendered by default.

78 changes: 77 additions & 1 deletion core/justfile
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ _get-docker-build-hash:
git ls-tree HEAD ../packages/contracts/scripts 2>/dev/null
git ls-tree HEAD ../packages/contracts/src 2>/dev/null
git ls-tree HEAD ../scripts/deploy-contracts.sh 2>/dev/null
git ls-tree HEAD ../scripts/deploy-base-contracts.sh 2>/dev/null
git ls-tree HEAD ../scripts/start-local-basechain.sh 2>/dev/null
git ls-tree HEAD ../scripts/start-local-riverchain.sh 2>/dev/null
git ls-tree HEAD ../core/justfile 2>/dev/null
Expand Down Expand Up @@ -109,14 +110,27 @@ _anvil-start $NAME $PORT $CHAIN_ID:
echo "Starting Docker ${NAME} chain on port ${PORT}"
IMAGE=$(just _get-docker-image)

# Expose Alto bundler port (4337) for base chain
ALTO_PORT_ARG=""
if [ "${NAME}" = "base" ]; then
ALTO_PORT_ARG="-p 4337:4337"
fi

docker run -d \
--name towns-${NAME}-chain \
-p ${PORT}:${PORT} \
${ALTO_PORT_ARG} \
-e CHAIN=${NAME} \
-e RIVER_BLOCK_TIME \
$IMAGE

../scripts/wait-for-port.sh ${PORT} "Docker_anvil_${NAME}"

# Wait for Alto bundler if base chain
if [ "${NAME}" = "base" ]; then
echo "Waiting for Alto bundler on port 4337..."
../scripts/wait-for-port.sh 4337 "Docker_alto"
fi
else
echo "Starting ${NAME} on port ${PORT}"
mkdir -p ./run_files/anvil
Expand All @@ -134,16 +148,78 @@ _anvil-start $NAME $PORT $CHAIN_ID:
fi
fi

# Alto bundler for ERC-4337 account abstraction
_alto-start:
#!/usr/bin/env -S bash {{BASH_OPTS}}
if nc -z localhost 4337 > /dev/null 2>&1; then
echo "Alto bundler already running on port 4337"
else
echo "Starting Alto bundler on port 4337..."
mkdir -p ./run_files/anvil
ANVIL_PRIVATE_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
ENTRYPOINT_V06="0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"
ENTRYPOINT_V07="0x0000000071727De22E5E9d8BAf0edAc6f37da032"
( nohup bunx @pimlico/alto@0.0.20 \
--entrypoints "${ENTRYPOINT_V06},${ENTRYPOINT_V07}" \
--rpc-url "http://127.0.0.1:8545" \
--executor-private-keys "${ANVIL_PRIVATE_KEY}" \
--utility-private-key "${ANVIL_PRIVATE_KEY}" \
--min-balance "0" \
--safe-mode false \
--deploy-simulations-contract \
--port 4337 \
</dev/null > ./run_files/anvil/alto.log 2>&1 & )
../scripts/wait-for-port.sh 4337 alto_bundler
fi
Comment thread
miguel-nascimento marked this conversation as resolved.

_alto-stop:
#!/usr/bin/env -S bash {{BASH_OPTS}}
../scripts/kill-on-port.sh 4337 alto_bundler

# Run Alto bundler in foreground (for VSCode task)
alto-run:
#!/usr/bin/env -S bash {{BASH_OPTS}}
if nc -z localhost 4337 > /dev/null 2>&1; then
echo "Alto bundler already running on port 4337"
exit 0
fi
echo "Starting Alto bundler on port 4337..."
ANVIL_PRIVATE_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
ENTRYPOINT_V06="0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"
ENTRYPOINT_V07="0x0000000071727De22E5E9d8BAf0edAc6f37da032"
bunx @pimlico/alto@0.0.20 \
--entrypoints "${ENTRYPOINT_V06},${ENTRYPOINT_V07}" \
--rpc-url "http://127.0.0.1:8545" \
--executor-private-keys "${ANVIL_PRIVATE_KEY}" \
--utility-private-key "${ANVIL_PRIVATE_KEY}" \
--min-balance "0" \
--safe-mode false \
--deploy-simulations-contract \
--port 4337
Comment thread
miguel-nascimento marked this conversation as resolved.

anvil-base:
#!/usr/bin/env -S bash {{BASH_OPTS}}
just _anvil-start "base" "8545" "31337"

# Start base anvil chain with Alto bundler (native mode - runs in foreground)
anvil-base-with-alto:
#!/usr/bin/env -S bash {{BASH_OPTS}}
just _anvil-start "base" "8545" "31337"
echo "Waiting for EntryPoint contracts to be deployed..."
# Wait for EntryPoint v0.7 to be deployed (happens during 'Configure Nodes' step)
while ! cast code 0x0000000071727De22E5E9d8BAf0edAc6f37da032 --rpc-url http://127.0.0.1:8545 2>/dev/null | grep -q "^0x6080"; do
sleep 1
done
echo "EntryPoint deployed. Starting Alto bundler..."
just alto-run
Comment thread
miguel-nascimento marked this conversation as resolved.

anvil-base-stop:
#!/usr/bin/env -S bash {{BASH_OPTS}}
if [ "${USE_DOCKER_CHAINS}" = "1" ]; then
docker stop towns-base-chain 2>/dev/null || true
docker rm towns-base-chain 2>/dev/null || true
else
just _alto-stop
../scripts/kill-on-port.sh 8545 anvil_base
fi

Expand Down Expand Up @@ -527,7 +603,7 @@ just-register-nodes-river:

register-nodes-river: config-river-chain config-instances just-register-nodes-river

config: config-root config-app-registry config-notifications register-nodes-base register-nodes-river storage-start
config: config-root config-app-registry config-notifications register-nodes-base register-nodes-river storage-start _alto-start

build:
#!/usr/bin/env -S bash {{BASH_OPTS}}
Expand Down
15 changes: 14 additions & 1 deletion packages/anvil-docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ RUN curl -fsSL https://bun.sh/install | bash && \
ln -s /root/.bun/bin/bun /usr/local/bin/bun && \
ln -s /root/.bun/bin/bunx /usr/local/bin/bunx

# Install Alto bundler globally using bun (for deploying ERC-4337 contracts)
RUN bun install -g @pimlico/alto@0.0.20
Comment thread
miguel-nascimento marked this conversation as resolved.

# Install just from precompiled binary
RUN curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/local/bin

Expand Down Expand Up @@ -94,10 +97,20 @@ RUN apt-get update && \
apt-get install -y --no-install-recommends \
bash \
curl \
netcat-openbsd && \
netcat-openbsd \
ca-certificates \
unzip && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

# Install Bun for Alto bundler
RUN curl -fsSL https://bun.sh/install | bash && \
ln -s /root/.bun/bin/bun /usr/local/bin/bun && \
ln -s /root/.bun/bin/bunx /usr/local/bin/bunx

# Install Alto bundler globally using bun
RUN bun install -g @pimlico/alto@0.0.20

# Create app directory
WORKDIR /app

Expand Down
122 changes: 54 additions & 68 deletions packages/anvil-docker/scripts/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,93 +5,79 @@ set -e

export RIVER_BLOCK_TIME="${RIVER_BLOCK_TIME:-1}"

# Parse command line arguments
parse_args() {
SHOW_HELP=false

while [ $# -gt 0 ]; do
case $1 in
--help)
SHOW_HELP=true
shift
;;
*)
echo "Unknown option: $1"
show_help
exit 1
;;
esac
done
}

show_help() {
cat << EOF
Usage: $0 [OPTIONS]

OPTIONS:
--help Show this help message

DESCRIPTION:
This script starts local blockchain networks (base chain and river chain)
and optionally runs tests against them.
# Anvil's first default private key
ANVIL_PRIVATE_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"

EXAMPLES:
$0 # Start both chains in the background and exit
$0 --help # Show this help message

EOF
}
# ERC-4337 EntryPoint addresses
ENTRYPOINT_V06="0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"
ENTRYPOINT_V07="0x0000000071727De22E5E9d8BAf0edAc6f37da032"
ENTRYPOINT_SIMULATIONS="0x74Cb5e4eE81b86e70f9045036a1C5477de69eE87"
Comment thread
miguel-nascimento marked this conversation as resolved.

main() {
# Parse command line arguments
parse_args "$@"

# Show help if requested
if [ "$SHOW_HELP" = true ]; then
show_help
if [ "$1" = "--help" ]; then
echo "Usage: CHAIN=base|river $0"
echo "Starts Anvil chain from pre-deployed state. Base chain includes Alto bundler."
exit 0
fi

if [ "$CHAIN" = "base" ]; then
start_base_chain
elif [ "$CHAIN" = "river" ]; then
start_river_chain
else
echo "Run with CHAIN=base or CHAIN=river."
exit 1
fi
}

setup_trap() {
trap 'cleanup $?' SIGINT SIGTERM EXIT
case "$CHAIN" in
base) start_base_chain ;;
river) start_river_chain ;;
*)
echo "Error: Set CHAIN=base or CHAIN=river"
exit 1
;;
esac
}

start_base_chain() {
echo "Starting base chain..."
RIVER_ANVIL_OPTS="--load-state base-anvil-state.json --host 0.0.0.0" bash ./scripts/start-local-basechain.sh
sleep 1 # Give it a moment to start
BASE_PID=$(pgrep -f "anvil.*base-anvil-state.json" | head -n 1)
echo "Base chain started with PID: $BASE_PID"
RIVER_ANVIL_OPTS="--load-state base-anvil-state.json --host 0.0.0.0" bash ./scripts/start-local-basechain.sh &
BASE_PID=$!

# Wait for Anvil to be ready
while ! nc -z localhost 8545 2>/dev/null; do
echo "Waiting for base chain..."
sleep 1
done
echo "Base chain ready on port 8545"

# Start Alto bundler (ERC-4337 contracts are pre-deployed in the Anvil state)
echo "Starting Alto bundler..."
/root/.bun/bin/bunx @pimlico/alto@0.0.20 \
--entrypoints "$ENTRYPOINT_V06,$ENTRYPOINT_V07" \
--rpc-url "http://127.0.0.1:8545" \
--executor-private-keys "$ANVIL_PRIVATE_KEY" \
--utility-private-key "$ANVIL_PRIVATE_KEY" \
--min-balance "0" \
--safe-mode false \
--entrypoint-simulation-contract "$ENTRYPOINT_SIMULATIONS" \
--port 4337 &
ALTO_PID=$!

while ! nc -z localhost 4337 2>/dev/null; do
echo "Waiting for Alto bundler..."
sleep 1
done
echo "Alto bundler ready on port 4337"
Comment thread
miguel-nascimento marked this conversation as resolved.

# Keep container alive
wait $BASE_PID
}

start_river_chain() {
echo "Starting river chain..."
RIVER_ANVIL_OPTS="--load-state river-anvil-state.json --host 0.0.0.0" bash ./scripts/start-local-riverchain.sh
sleep 1 # Give it a moment to start
RIVER_PID=$(pgrep -f "anvil.*river-anvil-state.json" | head -n 1)
echo "River chain started with PID: $RIVER_PID"
RIVER_ANVIL_OPTS="--load-state river-anvil-state.json --host 0.0.0.0" bash ./scripts/start-local-riverchain.sh
}

# Function to cleanup on exit
cleanup() {
local exit_code=$1
echo "Cleaning up..."
echo "Killing base chain (PID: $BASE_PID)"
kill -9 $BASE_PID 2>/dev/null || true
echo "Killing river chain (PID: $RIVER_PID)"
kill -9 $RIVER_PID 2>/dev/null || true
exit $exit_code
[ -n "$ALTO_PID" ] && kill -9 $ALTO_PID 2>/dev/null || true
[ -n "$BASE_PID" ] && kill -9 $BASE_PID 2>/dev/null || true
exit "${1:-0}"
}

trap 'cleanup $?' SIGINT SIGTERM EXIT

# Call main with all arguments
main "$@"
1 change: 1 addition & 0 deletions packages/bot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"@towns-protocol/sdk-crypto": "workspace:^",
"@towns-protocol/utils": "workspace:^",
"@towns-protocol/web3": "workspace:^",
"@towns-protocol/smart-account": "workspace:^",
"ethers": "^5.8.0",
"image-size": "^2.0.2",
"jsonwebtoken": "^9.0.2",
Expand Down
Loading
Loading