{"_id":"@bananapus/swap-terminal-v6","_rev":"2-d26a9305b5f876bf91806c825f1e59d9","name":"@bananapus/swap-terminal-v6","dist-tags":{"latest":"0.0.2"},"versions":{"0.0.1":{"name":"@bananapus/swap-terminal-v6","version":"0.0.1","license":"MIT","_id":"@bananapus/swap-terminal-v6@0.0.1","maintainers":[{"name":"simplemachine","email":"justinpulley92@gmail.com"},{"name":"me.jango","email":"me.jango@protonmail.com"},{"name":"filipviz","email":"f@filip.world"}],"homepage":"https://github.com/Bananapus/nana-swap-terminal-v6#readme","bugs":{"url":"https://github.com/Bananapus/nana-swap-terminal-v6/issues"},"dist":{"shasum":"0bc96a5ec66fc8a46a6f09bc1228ff9ff6498171","tarball":"https://registry.npmjs.org/@bananapus/swap-terminal-v6/-/swap-terminal-v6-0.0.1.tgz","fileCount":60,"integrity":"sha512-MUUZOaMaX67ZtiYUnWfy22abHW1TprEKs2yysudqV5pBO0v6VS80NRz4MaGAcmrGUzl4s/XaaWUZG8BOx9i1qQ==","signatures":[{"sig":"MEUCIHTNLNx9pKV2RuUdKd3kK2x1yfQB3cCrVZRgT9oDAOKUAiEAjSqNIXImPKYD8bZXu3CsrCNaXJGhI6Ypjop4sp3GxTs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":489255},"engines":{"node":">=20.0.0"},"gitHead":"09eb92b43b910c80b45e738d43d5ad79b13c75eb","scripts":{"test":"forge test","coverage":"forge coverage --match-path \"./src/*.sol\" --report lcov --report summary","artifacts":"source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'nana-swap-terminal-v6'","test:fork":"FOUNDRY_PROFILE=CI forge test","postinstall":"cd node_modules/@bananapus/core-v6/test/helpers && sed -i.bak 's|from \"src/|from \"../../src/|g' JBTest.sol && sed -i.bak 's|import \"lib/forge-std/src/Test.sol\"|import \"forge-std/Test.sol\"|' JBTest.sol && rm -f JBTest.sol.bak","deploy:mainnets":"source ./.env && npx sphinx propose ./script/Deploy.s.sol --networks mainnets","deploy:testnets":"source ./.env && npx sphinx propose ./script/Deploy.s.sol --networks testnets"},"_npmUser":{"name":"me.jango","email":"me.jango@protonmail.com"},"repository":{"url":"git+https://github.com/Bananapus/nana-swap-terminal-v6.git","type":"git"},"_npmVersion":"11.11.0","description":"A Juicebox terminal that accepts payments in any token, swaps them via Uniswap V3, and forwards the proceeds to another terminal that accepts the output token.","directories":{},"_nodeVersion":"25.8.0","dependencies":{"@uniswap/permit2":"github:Uniswap/permit2","@uniswap/v3-core":"github:Uniswap/v3-core#0.8","@bananapus/core-v6":"^0.0.1","@uniswap/v3-periphery":"github:Uniswap/v3-periphery#0.8","@openzeppelin/contracts":"^5.2.0","@bananapus/permission-ids-v6":"^0.0.1","@exhausted-pigeon/uniswap-v3-foundry-pool":"^1.0.9","@exhausted-pigeon/uniswap-v3-foundry-quote":"^1.0.0"},"_hasShrinkwrap":false,"devDependencies":{"@sphinx-labs/plugins":"^0.33.1"},"_npmOperationalInternal":{"tmp":"tmp/swap-terminal-v6_0.0.1_1772752693266_0.369754043573683","host":"s3://npm-registry-packages-npm-production"}},"0.0.2":{"name":"@bananapus/swap-terminal-v6","version":"0.0.2","license":"MIT","repository":{"type":"git","url":"git+https://github.com/Bananapus/nana-swap-terminal-v6.git"},"engines":{"node":">=20.0.0"},"scripts":{"test":"forge test","test:fork":"FOUNDRY_PROFILE=CI forge test","coverage":"forge coverage --match-path \"./src/*.sol\" --report lcov --report summary","deploy:mainnets":"source ./.env && npx sphinx propose ./script/Deploy.s.sol --networks mainnets","deploy:testnets":"source ./.env && npx sphinx propose ./script/Deploy.s.sol --networks testnets","artifacts":"source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'nana-swap-terminal-v6'"},"dependencies":{"@bananapus/core-v6":"^0.0.1","@bananapus/permission-ids-v6":"^0.0.1","@exhausted-pigeon/uniswap-v3-foundry-pool":"^1.0.9","@exhausted-pigeon/uniswap-v3-foundry-quote":"^1.0.0","@openzeppelin/contracts":"^5.2.0","@uniswap/permit2":"github:Uniswap/permit2","@uniswap/v3-core":"github:Uniswap/v3-core#0.8","@uniswap/v3-periphery":"github:Uniswap/v3-periphery#0.8"},"devDependencies":{"@sphinx-labs/plugins":"^0.33.1"},"gitHead":"8fccea1484cd54f010dceacb601762aa038cb6ad","_id":"@bananapus/swap-terminal-v6@0.0.2","description":"A Juicebox terminal that accepts payments in any token, swaps them via Uniswap V3, and forwards the proceeds to another terminal that accepts the output token.","bugs":{"url":"https://github.com/Bananapus/nana-swap-terminal-v6/issues"},"homepage":"https://github.com/Bananapus/nana-swap-terminal-v6#readme","_nodeVersion":"25.8.0","_npmVersion":"11.11.0","dist":{"integrity":"sha512-fDx7LLuYJWpxEhRltdCbs4V02sajJUo52xcpHzkzaJ6A8htPYhpVDFCwhlTuAPiJpWH1sgYR/ru0gm9/cmrFCg==","shasum":"2b078e2e50e42ee208e70a8f955427573cd4ef69","tarball":"https://registry.npmjs.org/@bananapus/swap-terminal-v6/-/swap-terminal-v6-0.0.2.tgz","fileCount":60,"unpackedSize":489490,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIG4/LujTOHp6f4f/JwH/su4/a5jIDhwkwTjr4T66vGxRAiBkXTjT8XFaGqpjbYs1nAKleL4rPdv3Bn4t4saSz3eJtQ=="}]},"_npmUser":{"name":"me.jango","email":"me.jango@protonmail.com"},"directories":{},"maintainers":[{"name":"simplemachine","email":"justinpulley92@gmail.com"},{"name":"me.jango","email":"me.jango@protonmail.com"},{"name":"filipviz","email":"f@filip.world"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/swap-terminal-v6_0.0.2_1772818201821_0.2678192537698936"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-05T23:18:13.211Z","modified":"2026-03-06T17:30:02.165Z","0.0.1":"2026-03-05T23:18:13.424Z","0.0.2":"2026-03-06T17:30:02.009Z"},"bugs":{"url":"https://github.com/Bananapus/nana-swap-terminal-v6/issues"},"license":"MIT","homepage":"https://github.com/Bananapus/nana-swap-terminal-v6#readme","repository":{"type":"git","url":"git+https://github.com/Bananapus/nana-swap-terminal-v6.git"},"description":"A Juicebox terminal that accepts payments in any token, swaps them via Uniswap V3, and forwards the proceeds to another terminal that accepts the output token.","maintainers":[{"name":"simplemachine","email":"justinpulley92@gmail.com"},{"name":"me.jango","email":"me.jango@protonmail.com"},{"name":"filipviz","email":"f@filip.world"}],"readme":"# nana-swap-terminal-v5\n\nA Juicebox terminal that accepts payments in any token, swaps them via Uniswap V3, and forwards the proceeds to another terminal that accepts the output token.\n\n_If you're having trouble understanding this contract, take a look at the [core protocol contracts](https://github.com/Bananapus/nana-core) and the [documentation](https://docs.juicebox.money/) first. If you have questions, reach out on [Discord](https://discord.com/invite/ErQYmth4dS)._\n\n## Architecture\n\n| Contract | Description |\n|----------|-------------|\n| `JBSwapTerminal` | Core terminal. Accepts any token via `pay` or `addToBalanceOf`, swaps through a Uniswap V3 pool to a configured `TOKEN_OUT`, then forwards the output to the project's primary terminal. Uses TWAP oracle for automatic slippage protection when the caller doesn't provide a quote. |\n| `JBSwapTerminalRegistry` | A proxy terminal that delegates `pay` and `addToBalanceOf` to a per-project or default `JBSwapTerminal` instance. Allows project owners to choose (and lock) which swap terminal implementation they use. |\n\n## How It Works\n\n1. A payer calls `pay(projectId, token, amount, ...)` with any token.\n2. The terminal accepts the token (supports ERC-20 approvals and Permit2).\n3. If `token != TOKEN_OUT`, it swaps via the project's configured Uniswap V3 pool.\n4. Slippage protection: the caller can pass a minimum output quote in metadata (`quoteForSwap` key), or the terminal calculates one from the pool's TWAP oracle with dynamic slippage tolerance.\n5. The output tokens are forwarded to the project's primary terminal for `TOKEN_OUT` via `terminal.pay(...)` or `terminal.addToBalanceOf(...)`.\n\n```mermaid\nsequenceDiagram\n    participant Frontend client\n    participant JBDirectory\n    participant Uniswap pool\n    participant JBSwapTerminal\n    participant JBMultiTerminal\n    Note left of Frontend client: User attempts to pay project with USDC\n    Frontend client->>JBDirectory: Checks project's primary USDC terminal\n    JBDirectory->>Frontend client: Returns JBSwapTerminal if a USDC pool is available\n    Frontend client->>JBSwapTerminal: Calls pay(...) with USDC payment data (and optional Uniswap quote)\n    JBSwapTerminal->>JBDirectory: Checks project's primary ETH terminal\n    JBDirectory->>JBSwapTerminal: Returns JBMultiTerminal for ETH\n    JBSwapTerminal->>Uniswap pool: Swaps USDC for ETH\n    JBSwapTerminal->>JBMultiTerminal: Pays ETH to JBMultiTerminal\n    Note right of JBMultiTerminal: Mint tokens for original beneficiary\n```\n\n## Install\n\nFor projects using `npm` to manage dependencies (recommended):\n\n```bash\nnpm install @bananapus/swap-terminal\n```\n\nFor projects using `forge` to manage dependencies:\n\n```bash\nforge install Bananapus/nana-swap-terminal\n```\n\nIf you're using `forge`, add `@bananapus/swap-terminal/=lib/nana-swap-terminal/` to `remappings.txt`.\n\n## Develop\n\n`nana-swap-terminal` uses [npm](https://www.npmjs.com/) (version >=20.0.0) for package management and [Foundry](https://github.com/foundry-rs/foundry) for builds and tests.\n\n```bash\nnpm ci && forge install\n```\n\n| Command | Description |\n|---------|-------------|\n| `forge build` | Compile the contracts and write artifacts to `out`. |\n| `forge test` | Run the tests. |\n| `forge fmt` | Lint. |\n| `forge build --sizes` | Get contract sizes. |\n| `forge coverage` | Generate a test coverage report. |\n| `forge clean` | Remove the build artifacts and cache directories. |\n\n### Scripts\n\n| Command | Description |\n|---------|-------------|\n| `npm test` | Run local tests. |\n| `npm run test:fork` | Run fork tests (for use in CI). |\n| `npm run coverage` | Generate an LCOV test coverage report. |\n\n### Configuration\n\nKey `foundry.toml` settings:\n\n- `solc = '0.8.23'`\n- `evm_version = 'paris'` (L2-compatible)\n- `optimizer_runs = 100000000`\n\n## Repository Layout\n\n```\nnana-swap-terminal-v5/\n├── src/\n│   ├── JBSwapTerminal.sol          # Core swap terminal\n│   ├── JBSwapTerminalRegistry.sol  # Per-project terminal routing\n│   └── interfaces/\n│       ├── IJBSwapTerminal.sol     # Swap terminal interface\n│       ├── IJBSwapTerminalRegistry.sol # Registry interface\n│       └── IWETH9.sol              # WETH wrapper interface\n├── script/\n│   └── Deploy.s.sol                # Deployment script\n└── test/\n    ├── Fork/                       # Fork tests\n    └── helper/                     # Test helpers\n```\n\n## Payment Metadata\n\nThe `JBSwapTerminal` accepts encoded `metadata` in its `pay(...)` function. Metadata is decoded using `JBMetadataResolver`:\n\n```solidity\n(bool exists, bytes memory quote) =\n    JBMetadataResolver.getDataFor(JBMetadataResolver.getId(\"quoteForSwap\"), metadata);\n\nif (exists) {\n    (minAmountOut) = abi.decode(quote, (uint256));\n}\n```\n\nIf no quote is provided, the terminal calculates one from the pool's TWAP oracle with a dynamic slippage tolerance based on the estimated price impact of the swap.\n\nThe terminal also supports Permit2 metadata (key: `\"permit2\"`) for gasless token approvals.\n\n## Risks\n\n- The terminal never holds a token balance. After every swap, all output tokens are forwarded and leftover input tokens are returned to the payer.\n- Pool validation uses `FACTORY.getPool()` rather than create2, so the terminal works on chains where Uniswap V3 factory bytecode may differ.\n- TWAP fallback: when no observations exist, the terminal falls back to the pool's current spot tick rather than reverting.\n- `addDefaultPool` calls `pool.increaseObservationCardinalityNext(10)` to proactively set up TWAP history. `_getQuote` also reverts if observations are missing as a safety net.\n","readmeFilename":"README.md"}