Complete setup for the LI.FI smart-contracts repo: the local toolchain (everyone), plus — for LI.FI developers — access to internal resources (the Safe proposal MongoDB and deploy infrastructure). The README's Getting Started has only the quick-start; this is the full guide.
- Node.js 22 or newer (
nvm usepicks it from.nvmrc).bunx tsxruns the scripts on whatevernodeis first onPATH, and nothing checks its version. - Bun (latest version)
- Foundry (pinned via
.foundry-version) - Git
- Cursor IDE (recommended) or VSCode
-
Clone the repository:
git clone https://github.com/lifinance/contracts.git cd contracts -
Install dependencies:
bun i forge install
-
Install the pinned Foundry version:
foundryup --install "$(cat .foundry-version)"CI and the shell deploy scripts verify that your installed
forgematches.foundry-versionand refuse to run on mismatch — a deploy built with a drifted compiler produces bytecode that cannot be reproduced from the recorded commit. The pre-commit hook runs the same check but only warns, so a drifted localforgecan still commit. To bump the pin, change.foundry-versionin a PR — every workflow picks it up automatically, but your machine does not: rerunfoundryup --install "$(cat .foundry-version)"after the pin changes, or deploys will refuse.The zkEVM toolchain is pinned separately, in
foundry.toml[external.zksync](foundry_zksyncfor the binary release,zksolcfor the compiler it drives).install_foundry_zksyncinstalls that release and then refuses unless both pins hold, soFOUNDRY_ZKSYNC_VERSIONcannot be used to build a deployment against a release the repository does not pin. The Tron deploy scripts deploy pre-built Forge artifacts and check.foundry-versionbefore loading any of them. -
Set up environment variables:
cp .env.example .env # Edit .env with your configuration
For optimal AI assistance in Cursor IDE:
-
Copy
.cursorrules.exampleto.cursorrules:cp .cursorrules.example .cursorrules
-
The
.cursorrulesfile provides context for AI interactions with our codebase. It helps the AI understand:- Project structure and conventions
- Development environment and tools
- Key files and their purposes
- Testing and deployment requirements
-
You can customize
.cursorrulesbased on your needs, but we recommend keeping the core context intact.
Deploy and Safe tooling in this repo reads and writes LI.FI infrastructure — most
importantly the Safe proposal MongoDB (sc_private.pendingTransactions, via
SC_MONGODB_URI) used by propose-to-safe, confirm-safe-tx,
list-pending-proposals, and the rollout skills.
As of July 2026 the legacy OpenVPN has been retired. These resources are no
longer reachable by IP whitelisting; access now goes through the lifi-connect
port-forwarding CLI, authenticated via AWS SSO (Okta). This section gets a fresh
machine — or an agent session — from zero to a working connection.
Canonical, always-current source (DevOps-owned): Accessing Resources with Port Forwarding. This section is the repo-local summary; if the two disagree, the Notion doc wins — please open a PR to reconcile.
All available via Homebrew:
brew install awscli kubectl jq yq goawscli— AWS SSO login and the EKS token helperkubectl—lifi-connectport-forwards through the EKS clustersjq,yq— used bygenerate-kubeconfig.sh(needs the mikefarahyq, i.e.brew install yq)go— only needed iflifi-connectbuilds from source (no prebuilt release for your platform)
git clone git@github.com:lifinance/lifi-ops.git
cd lifi-ops
./generate-aws-config.sh DeveloperAccess # opens a browser → log in with OktaPick any account/role when prompted — the script configures all your profiles. Add the session-renewal alias (sessions last 8 hours):
echo 'alias awslogin="aws sso login --sso-session LIFI"' >> ~/.zshrc && source ~/.zshrcRenew any time with awslogin. Verify:
aws sts get-caller-identity --profile shared-services-devlifi-connect needs one kubectl context per cluster in ~/.kube/config:
cd lifi-ops
./generate-kubeconfig.sh # backs up the existing kubeconfig, then rebuilds itcd lifi-ops/lifi-connect
./install.sh # installs to ~/.local/bin/lifi-connect
lifi-connect update # keep it current when DevOps ship fixesFor work that touches the Safe proposal database (production):
awslogin # if the 8h AWS session has expired
lifi-connect prod smart-contracts # foreground; leave it running in its own tablifi-connect prints a static localhost:<port> for the smart-contracts
MongoDB (the port is stable across sessions) and the exact connection string to
use. Set SC_MONGODB_URI in your .env to that host, keeping the Mongo
credentials from your 1Password vault (the tunnel provides the network path,
not authentication):
SC_MONGODB_URI="mongodb://<user>:<pass>@localhost:<port>/?directConnection=true&tls=false"
directConnection=true is required: without it the driver tries to discover
replica-set members by their in-cluster hostnames (which aren't tunneled) and
fails with a server-selection timeout. tls=false because TLS terminates at the
tunnel. Run lifi-connect list to see every cluster and endpoint.
The tunnel dies when your AWS SSO session expires — re-run awslogin and restart
lifi-connect.
Automatic tunnel handling. The Mongo-touching Safe package scripts —
confirm-safe-tx, propose-safe-tx, add-safe-owners-and-threshold,
unpause-all-diamonds, execute-timelock — run through
script/deploy/safe/with-safe-tunnel.sh, so they ensure the tunnel is up (logging
you into AWS SSO if the session expired, starting the tunnel if needed, then
waiting for the port) before running. So bun confirm-safe-tx just works — no
manual tunnel step.
The tunnel-starting lives only in that one wrapper, and it's visible (it
announces what it's doing and opens a Terminal window running lifi-connect) —
never a silent side effect, and never inside the TS signing logic itself. To
ensure the tunnel without running anything (e.g. to start it once and reuse it):
bun run safe:tunnel # ensure the tunnel, then exit
bun run safe:tunnel bun confirm-safe-tx # ensure, then run (any command)Running an automated agent (Claude Code, Cursor, etc.) against this repo? The agent-specific guidance — what an agent must not do, and how commands differ in a non-interactive shell — is in Setup-agents.md. The steps above are the human path.