Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
8b434d7
feat(margin-sdk): scaffold the @uniswap/margin-sdk package
ccashwell Jul 23, 2026
738b3d7
feat(margin-sdk): add chains, addresses, struct mirrors, and verified…
ccashwell Jul 23, 2026
3310c4b
feat(margin-sdk): add account derivation, market helpers, and sizing …
ccashwell Jul 23, 2026
659f7c2
feat(margin-sdk): add entry-point encoders and the execute-plan builder
ccashwell Jul 23, 2026
d4afa03
feat(margin-sdk): add read layer, package exports, docs, and changeset
ccashwell Jul 23, 2026
bfd12c6
feat(margin-sdk): add end-to-end demo flows against the live mainnet …
ccashwell Jul 23, 2026
2525cb7
feat(margin-sdk): harden encoder and planner inputs
ccashwell Jul 24, 2026
c0b7bd6
test(margin-sdk): cover the read layer and fund-authorization encoders
ccashwell Jul 24, 2026
a9f2c48
fix(margin-sdk): make the published package load under native Node (C…
ccashwell Jul 24, 2026
0be65be
feat(margin-sdk): run the anvil-fork demo suite in CI behind FORK_URL
ccashwell Jul 24, 2026
205a650
feat(margin-sdk): add an ABI drift gate against the v4-periphery source
ccashwell Jul 24, 2026
7797ffd
chore(margin-sdk): keep the initial release a 0.0.x pre-release
ccashwell Jul 24, 2026
5f6fa69
fix(margin-sdk): tolerate per-block interest accrual in a demo assertion
ccashwell Jul 24, 2026
ea15606
feat(margin-sdk): replace hand-written ABIs with forge-generated bind…
ccashwell Jul 24, 2026
7d5d56d
feat(margin-sdk): exercise the browser target in the package check
ccashwell Jul 24, 2026
e3da418
ci(margin-sdk): verify the ABI bindings against the pinned v4-periphe…
ccashwell Jul 24, 2026
efc8670
feat(margin-sdk): add collateral withdrawal and the IMarginAccount es…
Ayoakala Aug 3, 2026
b2519f8
Merge branch 'main' into feat/margin-sdk
Ayoakala Aug 3, 2026
7f1e8d6
Merge branch 'main' into feat/margin-sdk
Ayoakala Aug 4, 2026
1afa9af
feat(margin-sdk): re-pin ABI bindings to latest v4-periphery margin-t…
ccashwell Jul 28, 2026
6c4c05a
feat(margin-sdk): surface the Compound v3 venue and the ZeroAmount re…
ccashwell Jul 28, 2026
0bcc6cd
feat(margin-sdk): adopt the Universal Router per-call position API
ccashwell Aug 11, 2026
970a1f4
test(margin-sdk): route the fork demos through the Universal Router
ccashwell Aug 11, 2026
154cd57
docs(margin-sdk): document the routed API and the redeployed mainnet …
ccashwell Aug 11, 2026
980257d
test(margin-sdk): migrate demo 06's position open to the routed API
ccashwell Aug 11, 2026
aeb3c0a
feat(margin-sdk): size the full-close buffer from measured interest a…
ccashwell Aug 12, 2026
5f33a49
chore(margin-sdk): re-pin bindings to v4-periphery 62a514f
ccashwell Aug 12, 2026
07a9934
feat(margin-sdk): surface resolveAmount on the venue-agnostic adapter…
ccashwell Aug 12, 2026
a4ff3d0
feat(margin-sdk): retarget to the 2026-08-12 mainnet redeploy
ccashwell Aug 12, 2026
fa78f8e
Merge branch 'main' into feat/margin-sdk
Ayoakala Aug 24, 2026
331606a
chore(margin-sdk): re-pin bindings to v4-periphery 3d03ca5 (audit rem…
ccashwell Aug 13, 2026
bab55af
chore: re-pin to the post-audit mainnet redeploy and regenerate ABI b…
ccashwell Aug 27, 2026
b90099a
ci(margin-sdk): run the fork demo suite in its own workflow
Ayoakala Aug 27, 2026
9ac84d9
Merge branch 'main' into feat/margin-sdk
Ayoakala Sep 29, 2026
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
5 changes: 5 additions & 0 deletions .changeset/margin-sdk-initial-release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@uniswap/margin-sdk': patch
---

Initial pre-release of the margin trading SDK for the Uniswap v4 margin periphery (MarginRouter + Morpho Blue / Aave v3 / Aave v4 lending adapters): entry-point encoders and write descriptors (increase/decrease/close/addCollateral/execute/multicall/permit), collateral withdrawal via the `withdrawCollateralPlan` curated `execute` plan plus account-direct encoders for the `IMarginAccount` owner-escape-hatch primitives (withdrawCollateral/supplyCollateral/borrow/repay/sweep), offchain MarginAccount address derivation (Solady CWIA, verified against the live mainnet router), decimal-aware leverage/LTV/health sizing math, a validated `execute`-plan builder over the v4 routing + margin action set, venue-agnostic read descriptors, and the mainnet deployment address registry. Published as 0.0.x deliberately: the contracts (Uniswap/v4-periphery#563) are still in review and governance has not yet been handed to a timelock/multisig — the package graduates to 0.1.0 once the deployment is final.
86 changes: 86 additions & 0 deletions .github/workflows/margin-sdk-abi-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: "Margin SDK ABI Consistency"

# CONSISTENCY GATE — proves the committed forge-generated ABI bindings in sdks/margin-sdk
# (src/generated/abis.ts) actually match a fresh build of the v4-periphery margin contracts at
# the commit they claim to be pinned to. viem encodes tuples positionally, so a contract field
# reorder with stale bindings would produce silently-wrong calldata; if someone edits the pin or
# the bindings and forgets to regenerate (or hand-edits the generated file), this job fails the PR.
#
# It compiles the pinned commit with forge and diffs the regenerated bindings against the
# committed file via `bun run check:abis` (the generator's --check mode). Nothing is written; the
# job only reports pass/fail. Mirrors the liquidity-launcher lock-bytecode gate.
#
# Runs only when the package changes (path filter) — this is not needed on unrelated PRs.

on:
pull_request:
paths:
- "sdks/margin-sdk/**"
workflow_dispatch: {}

permissions:
contents: read

jobs:
check-margin-abis:
name: Verify ABI bindings match the pinned v4-periphery commit
runs-on: ubuntu-latest
steps:
- uses: bullfrogsec/bullfrog@dcde5841b19b7ef693224207a7fdec67fce604db # v0.8.3
with:
egress-policy: audit

- name: ✅ Checkout sdks
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
persist-credentials: false

- name: 💽 Setup Node
uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7
with:
node-version: 24

- name: Setup Bun
uses: oven-sh/setup-bun@ecf28ddc73e819eb6fa29df6b34ef8921c743461 # v2
with:
bun-version: 1.3.10

- name: 📥 Install dependencies
run: bun install --frozen-lockfile
env:
HUSKY: "0"

- name: 🪨 Install Foundry
uses: foundry-rs/foundry-toolchain@82dee4ba654bd2146511f85f0d013af94670c4de # v1
with:
version: nightly

- name: 📌 Read the pinned v4-periphery commit from the bindings header
id: pin
run: |
# Single source of truth: the "Pinned to v4-periphery commit <sha>" line in the
# committed bindings. Keeps the pin human-readable and reviewable in diffs.
sha=$(grep -oE 'Pinned to v4-periphery commit [0-9a-f]{40}' \
sdks/margin-sdk/src/generated/abis.ts | grep -oE '[0-9a-f]{40}')
if [ -z "$sha" ]; then echo "could not read pinned commit"; exit 1; fi
echo "sha=$sha" >> "$GITHUB_OUTPUT"
echo "Pinned v4-periphery commit: $sha"

- name: ⬇️ Clone v4-periphery at the pinned commit (submodules over https)
run: |
# Rewrite any SSH submodule URLs to https BEFORE submodule init so the runner (which
# has no SSH key) can fetch the public repos. The generator script runs
# `git submodule update --init --recursive` itself.
git config --global url."https://github.com/".insteadOf "git@github.com:"
git clone https://github.com/Uniswap/v4-periphery.git "$RUNNER_TEMP/v4-periphery"
git -C "$RUNNER_TEMP/v4-periphery" fetch origin "$PINNED_SHA" || true
git -C "$RUNNER_TEMP/v4-periphery" checkout "$PINNED_SHA"
env:
PINNED_SHA: ${{ steps.pin.outputs.sha }}

- name: 🔎 Check committed bindings against the pinned commit's build
working-directory: sdks/margin-sdk
env:
V4_PERIPHERY_PATH: ${{ runner.temp }}/v4-periphery
V4_PERIPHERY_COMMIT: ${{ steps.pin.outputs.sha }}
run: bun run check:abis
97 changes: 97 additions & 0 deletions .github/workflows/margin-sdk-fork-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
name: "Margin SDK Fork Tests"

# END-TO-END GATE — runs the margin-sdk demo suite (demo/run-all.ts) against an anvil fork of
# mainnet, exercising the SDK against the LIVE deployed margin stack.
#
# The demo flows route their swaps through a Universal Router with already-unlocked V4_SWAP
# support (v4-periphery #491), which the canonical historical mainnet deployments predate — so the
# suite deploys one onto the fork from a v4-periphery build. That build is what this job provides:
# it clones v4-periphery at the commit the committed ABI bindings are pinned to and runs
# `forge build`, exactly like the margin-sdk-abi-check gate, then points the suite at it via
# V4_PERIPHERY_PATH.
#
# This lives outside the shared monorepo test job on purpose: `bun run test` for the package stays
# unit-tests + package check (fast, no foundry, no v4-periphery checkout), and the expensive fork
# suite runs only on PRs that touch the package.
#
# Without a fork RPC (`FORK_URL`) the suite skips cleanly and the job passes — see
# sdks/margin-sdk/scripts/fork-tests.ts.

on:
pull_request:
paths:
- "sdks/margin-sdk/**"
workflow_dispatch: {}

permissions:
contents: read

jobs:
margin-fork-tests:
name: Run the demo suite against an anvil mainnet fork
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: bullfrogsec/bullfrog@dcde5841b19b7ef693224207a7fdec67fce604db # v0.8.3
with:
egress-policy: audit

- name: ✅ Checkout sdks
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
with:
persist-credentials: false

- name: 💽 Setup Node
uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7
with:
node-version: 24

- name: Setup Bun
uses: oven-sh/setup-bun@ecf28ddc73e819eb6fa29df6b34ef8921c743461 # v2
with:
bun-version: 1.3.10

- name: 📥 Install dependencies
run: bun install --frozen-lockfile
env:
HUSKY: "0"

- name: 🪨 Install Foundry
uses: foundry-rs/foundry-toolchain@82dee4ba654bd2146511f85f0d013af94670c4de # v1
with:
version: nightly

- name: 📌 Read the pinned v4-periphery commit from the bindings header
id: pin
run: |
# Same single source of truth as the ABI gate: the "Pinned to v4-periphery commit <sha>"
# line in the committed bindings. The Universal Router the demos deploy onto the fork
# therefore comes from the same build the ABIs are derived from.
sha=$(grep -oE 'Pinned to v4-periphery commit [0-9a-f]{40}' \
sdks/margin-sdk/src/generated/abis.ts | grep -oE '[0-9a-f]{40}')
if [ -z "$sha" ]; then echo "could not read pinned commit"; exit 1; fi
echo "sha=$sha" >> "$GITHUB_OUTPUT"
echo "Pinned v4-periphery commit: $sha"

- name: ⬇️ Clone and build v4-periphery at the pinned commit
run: |
# Rewrite any SSH submodule URLs to https BEFORE submodule init so the runner (which has
# no SSH key) can fetch the public repos.
git config --global url."https://github.com/".insteadOf "git@github.com:"
git clone https://github.com/Uniswap/v4-periphery.git "$RUNNER_TEMP/v4-periphery"
git -C "$RUNNER_TEMP/v4-periphery" fetch origin "$PINNED_SHA" || true
git -C "$RUNNER_TEMP/v4-periphery" checkout "$PINNED_SHA"
git -C "$RUNNER_TEMP/v4-periphery" submodule update --init --recursive
# Default profile — the demo suite starts anvil with --disable-code-size-limit precisely
# because this build's Universal Router runtime exceeds EIP-170.
forge build --root "$RUNNER_TEMP/v4-periphery"
test -f "$RUNNER_TEMP/v4-periphery/foundry-out/UniversalRouter.sol/UniversalRouter.json"
env:
PINNED_SHA: ${{ steps.pin.outputs.sha }}

- name: 🧪 Run the fork demo suite
working-directory: sdks/margin-sdk
env:
V4_PERIPHERY_PATH: ${{ runner.temp }}/v4-periphery
FORK_URL: ${{ secrets.FORK_URL }}
run: bun run test:fork
Loading
Loading