Skip to main content

JBSwapTerminal

Git Source

Inherits: JBPermissioned, Ownable, IJBTerminal, IJBPermitTerminal, IJBSwapTerminal, IUniswapV3SwapCallback

The JBSwapTerminal accepts payments in any token. When the JBSwapTerminal is paid, it uses a Uniswap pool to exchange the tokens it received for tokens that another one of its project's terminals can accept. Then, it pays that terminal with the tokens it got from the pool, forwarding the specified beneficiary to receive any tokens or NFTs minted by that payment, as well as payment metadata and other arguments.

To prevent excessive slippage, the user/client can specify a minimum quote and a pool to use in their payment's metadata using the JBMetadataResolver format. If they don't, a quote is calculated for them based on the TWAP oracle for the project's default pool for that token (set by the project's owner).

Notes:

  • metadata-id-used: quoteForSwap and permit2

  • benediction: DEVS BENEDICAT ET PROTEGAT CONTRACTVS MEAM

State Variables

DEFAULT_PROJECT_ID

The ID to store default values in.

uint256 public constant override DEFAULT_PROJECT_ID = 0;

MAX_TWAP_SLIPPAGE_TOLERANCE

Projects cannot specify a TWAP slippage tolerance larger than this constant (out of MAX_SLIPPAGE).

This prevents TWAP slippage tolerances so high that they would result in highly unfavorable trade conditions for the payer unless a quote was specified in the payment metadata.

uint256 public constant override MAX_TWAP_SLIPPAGE_TOLERANCE = 9000;

MIN_TWAP_SLIPPAGE_TOLERANCE

Projects cannot specify a TWAP slippage tolerance smaller than this constant (out of MAX_SLIPPAGE).

This prevents TWAP slippage tolerances so low that the swap always reverts to default behavior unless a quote is specified in the payment metadata.

uint256 public constant override MIN_TWAP_SLIPPAGE_TOLERANCE = 100;

MAX_TWAP_WINDOW

Projects cannot specify a TWAP window longer than this constant.

This serves to avoid excessively long TWAP windows that could lead to outdated pricing information and higher gas costs due to increased computational requirements.

uint256 public constant override MAX_TWAP_WINDOW = 2 days;

MIN_TWAP_WINDOW

Projects cannot specify a TWAP window shorter than this constant.

This serves to avoid extremely short TWAP windows that could be manipulated or subject to high volatility.

uint256 public constant override MIN_TWAP_WINDOW = 2 minutes;

SLIPPAGE_DENOMINATOR

The denominator used when calculating TWAP slippage tolerance values.

uint160 public constant override SLIPPAGE_DENOMINATOR = 10_000;

MIN_DEFAULT_POOL_CARDINALITY

The minimum cardinality for a pool to be configured as a default pool.

The cardinality is automatically increased to this number when added as a default pool.

uint16 public constant override MIN_DEFAULT_POOL_CARDINALITY = 10;

DIRECTORY

The directory of terminals and controllers for PROJECTS.

IJBDirectory public immutable DIRECTORY;

FACTORY

The factory to use for creating new pools

We rely on "a" factory, vanilla uniswap v3 or potential fork

IUniswapV3Factory public immutable FACTORY;

PERMIT2

The permit2 utility.

IPermit2 public immutable PERMIT2;

PROJECTS

Mints ERC-721s that represent project ownership and transfers.

IJBProjects public immutable PROJECTS;

TOKEN_OUT

The token which flows out of this terminal (JBConstants.NATIVE_TOKEN for the chain native token)

address public immutable TOKEN_OUT;

WETH

The ERC-20 wrapper for the native token.

"wETH" is used as a generic term throughout, but any native token wrapper can be used.

IWETH9 public immutable WETH;

_OUT_IS_NATIVE_TOKEN

A flag indicating if the token out is the chain native token (eth on mainnet for instance)

If so, the token out should be unwrapped before being sent to the next terminal

bool internal immutable _OUT_IS_NATIVE_TOKEN;

_accountingContextFor

A mapping which stores accounting contexts to use for a given project ID and token.

Accounting contexts are set up for a project ID and token when the project's owner uses addDefaultPool(...) for that token.

mapping(uint256 projectId => mapping(address token => JBAccountingContext)) internal _accountingContextFor;

_poolFor

A mapping which stores the default pool to use for a given project ID and token.

Default pools are set by the project owner with addDefaultPool(...), the project 0 acts as a wildcard

Default pools are used when a payer doesn't specify a pool in their payment's metadata.

mapping(uint256 projectId => mapping(address tokenIn => IUniswapV3Pool)) internal _poolFor;

_tokensWithAContext

A mapping which stores the tokens that have an accounting context for a given project ID.

This is used to retrieve all the accounting contexts for a project ID.

mapping(uint256 projectId => address[]) internal _tokensWithAContext;

_twapParamsOf

The twap params for each project's pools.

mapping(uint256 projectId => mapping(IUniswapV3Pool pool => uint256 params)) internal _twapParamsOf;

Functions

constructor

constructor(
IJBDirectory directory,
IJBPermissions permissions,
IJBProjects projects,
IPermit2 permit2,
address owner,
IWETH9 weth,
address tokenOut,
IUniswapV3Factory factory
)
JBPermissioned(permissions)
Ownable(owner);

Parameters

NameTypeDescription
directoryIJBDirectoryA contract storing directories of terminals and controllers for each project.
permissionsIJBPermissionsA contract storing permissions.
projectsIJBProjectsA contract which mints ERC-721s that represent project ownership and transfers.
permit2IPermit2A permit2 utility.
owneraddressThe owner of the contract.
wethIWETH9A contract which wraps the native token.
tokenOutaddressThe token which flows out of this terminal (JBConstants.NATIVE_TOKEN for the chain native token)
factoryIUniswapV3FactoryA factory which creates Uniswap V3 pools.

accountingContextForTokenOf

Get the accounting context for the specified project ID and token.

Accounting contexts are set up in addDefaultPool(...).

function accountingContextForTokenOf(
uint256 projectId,
address token
)
external
view
override
returns (JBAccountingContext memory context);

Parameters

NameTypeDescription
projectIduint256The ID of the project to get the accounting context for.
tokenaddressThe address of the token to get the accounting context for.

Returns

NameTypeDescription
contextJBAccountingContextA JBAccountingContext containing the accounting context for the project ID and token.

accountingContextsOf

Return all the accounting contexts for a specified project ID.

This includes both project-specific and generic accounting contexts, with the project-specific contexts taking precedence.

function accountingContextsOf(uint256 projectId)
external
view
override
returns (JBAccountingContext[] memory contexts);

Parameters

NameTypeDescription
projectIduint256The ID of the project to get the accounting contexts for.

Returns

NameTypeDescription
contextsJBAccountingContext[]An array of JBAccountingContext containing the accounting contexts for the project ID.

currentSurplusOf

Empty implementation to satisfy the interface. This terminal has no surplus.

function currentSurplusOf(
uint256 projectId,
JBAccountingContext[] memory accountingContexts,
uint256 decimals,
uint256 currency
)
external
view
returns (uint256);

getPoolFor

Returns the default pool for a given project and token or, if a project has no default pool for the token, the overal default pool for the token

function getPoolFor(uint256 projectId, address tokenIn) external view returns (IUniswapV3Pool pool, bool zeroForOne);

Parameters

NameTypeDescription
projectIduint256The ID of the project to retrieve the default pool for.
tokenInaddressThe address of the token to retrieve the default pool for.

Returns

NameTypeDescription
poolIUniswapV3PoolThe default pool for the token, or the overall default pool for the token if the
zeroForOnebool

supportsInterface

Indicates if this contract adheres to the specified interface.

See IERC165-supportsInterface.

function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool);

Parameters

NameTypeDescription
interfaceIdbytes4The ID of the interface to check for adherance to.

Returns

NameTypeDescription
<none>boolA flag indicating if the provided interface ID is supported.

twapParamsOf

Returns the default twap parameters for a given pool project.

function twapParamsOf(uint256 projectId, IUniswapV3Pool pool) public view returns (uint32, uint160);

Parameters

NameTypeDescription
projectIduint256The ID of the project to retrieve TWAP parameters for.
poolIUniswapV3Pool

Returns

NameTypeDescription
<none>uint32twapWindow The period of time in the past to calculate the TWAP from.
<none>uint160slippageTolerance The maximum allowed slippage tolerance when calculating the TWAP, as a fraction out of SLIPPAGE_DENOMINATOR.

_normalizedTokenOut

Returns the token that flows out of this terminal, wrapped as an ERC-20 if needed.

If the token out is the chain native token (ETH on mainnet), wrapped ETH is returned

function _normalizedTokenOut() internal view returns (address);

Returns

NameTypeDescription
<none>addressThe token that flows out of this terminal.

_pickPoolAndQuote

Picks the pool and quote for the swap.

function _pickPoolAndQuote(
bytes calldata metadata,
uint256 projectId,
address normalizedTokenIn,
uint256 amount,
address normalizedTokenOut
)
internal
view
returns (uint256 minAmountOut, IUniswapV3Pool pool);

Parameters

NameTypeDescription
metadatabytesThe metadata in which quoteForSwap context is provided.
projectIduint256The ID of the project for which the swap is being performed.
normalizedTokenInaddressThe address of the token being swapped, normalized to the wrapped native token.
amountuint256The amount of tokens to swap.
normalizedTokenOutaddressThe address of the token to receive from the swap, normalized to the wrapped native token.

Returns

NameTypeDescription
minAmountOutuint256The minimum amount of tokens to receive from the swap.
poolIUniswapV3PoolThe pool to perform the swap in.

addAccountingContextsFor

Empty implementation to satisfy the interface. Accounting contexts are set in addDefaultPool(...).

function addAccountingContextsFor(
uint256 projectId,
JBAccountingContext[] calldata accountingContexts
)
external
override;

addDefaultPool

Set a project's default pool and accounting context for the specified token. Only the project's owner, an address with ADD_SWAP_TERMINAL_POOL permission from the owner or the terminal owner can call this function.

The pool should have been deployed by the factory associated to this contract. We don't rely on create2 address as this terminal might be used on other chain, where the factory bytecode might differ or the main dex be a fork.

function addDefaultPool(uint256 projectId, address token, IUniswapV3Pool pool) external override;

Parameters

NameTypeDescription
projectIduint256The ID of the project to set the default pool for. The project 0 acts as a catch-all, where non-set pools are defaulted to.
tokenaddressThe address of the token to set the default pool for.
poolIUniswapV3PoolThe Uniswap V3 pool to set as the default for the specified token.

addToBalanceOf

Accepts funds for a given project, swaps them if necessary, and adds them to the project's balance in the specified terminal.

This function handles the token in transfer, potentially swaps the tokens to the desired output token, and then adds the swapped tokens to the project's balance in the specified terminal.

function addToBalanceOf(
uint256 projectId,
address token,
uint256 amount,
bool shouldReturnHeldFees,
string calldata memo,
bytes calldata metadata
)
external
payable
override;

Parameters

NameTypeDescription
projectIduint256The ID of the project for which funds are being accepted and added to its balance.
tokenaddressThe address of the token being paid in.
amountuint256The amount of tokens being paid in.
shouldReturnHeldFeesboolA boolean to indicate whether held fees should be returned.
memostringA memo to pass along to the emitted event.
metadatabytesBytes in JBMetadataResolver's format which can contain additional data for the swap and adding to balance.

addTwapParamsFor

Set the specified project's rules for calculating a quote based on the TWAP. Only the project's owner or an address with MODIFY_TWAP_PARAMS permission from the owner or the terminal owner can call this function.

function addTwapParamsFor(
uint256 projectId,
IUniswapV3Pool pool,
uint256 twapWindow,
uint256 slippageTolerance
)
external
override;

Parameters

NameTypeDescription
projectIduint256The ID of the project to set the TWAP-based quote rules for.
poolIUniswapV3Pool
twapWindowuint256The period of time over which the TWAP is calculated, in seconds.
slippageToleranceuint256The maximum spread allowed between the amount received and the TWAP (as a fraction out of SLIPPAGE_DENOMINATOR).

migrateBalanceOf

Empty implementation to satisfy the interface.

function migrateBalanceOf(
uint256 projectId,
address token,
IJBTerminal to
)
external
override
returns (uint256 balance);

pay

Pay a project by swapping the incoming tokens for tokens that one of the project's other terminals accepts, passing along the funds received from the swap and the specified parameters.

function pay(
uint256 projectId,
address token,
uint256 amount,
address beneficiary,
uint256 minReturnedTokens,
string calldata memo,
bytes calldata metadata
)
external
payable
virtual
override
returns (uint256);

Parameters

NameTypeDescription
projectIduint256The ID of the project being paid.
tokenaddressThe address of the token being paid in.
amountuint256The amount of tokens being paid in, as a fixed point number with the same amount of decimals as the token. If token is the native token, amount is ignored and msg.value is used in its place.
beneficiaryaddressThe beneficiary address to pass along to the other terminal. If the other terminal mints tokens, for example, they will be minted for this address.
minReturnedTokensuint256The minimum number of project tokens expected in return, as a fixed point number with the same number of decimals as the other terminal. This value will be passed along to the other terminal.
memostringA memo to pass along to the emitted event.
metadatabytesBytes in JBMetadataResolver's format which can contain a quote from the user/client. The quote should contain a minimum amount of tokens to receive from the swap and the pool to use. This metadata is also passed to the other terminal's emitted event, as well as its data hook and pay hook if applicable.

Returns

NameTypeDescription
<none>uint256The number of tokens received from the swap, as a fixed point number with the same amount of decimals as that token.

uniswapV3SwapCallback

The Uniswap v3 pool callback where the token transfer is expected to happen.

Only an uniswap v3 pool can call this function

function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external override;

Parameters

NameTypeDescription
amount0Deltaint256The amount of token 0 being used for the swap.
amount1Deltaint256The amount of token 1 being used for the swap.
databytesData passed in by the swap operation.

receive

Fallback to prevent native tokens being sent to this terminal.

Native tokens should only be sent to this terminal when being unwrapped from a swap.

receive() external payable;

_acceptFundsFor

Accepts a token being paid in.

function _acceptFundsFor(address token, uint256 amount, bytes calldata metadata) internal returns (uint256);

Parameters

NameTypeDescription
tokenaddressThe address of the token being paid in.
amountuint256The amount of tokens being paid in.
metadatabytesThe metadata in which permit2 context is provided.

Returns

NameTypeDescription
<none>uint256amount The amount of tokens that have been accepted.

_beforeTransferFor

Logic to be triggered before transferring tokens from this terminal.

function _beforeTransferFor(address to, address token, uint256 amount) internal virtual returns (uint256);

Parameters

NameTypeDescription
toaddressThe address to transfer tokens to.
tokenaddressThe token being transfered.
amountuint256The amount of tokens to transfer, as a fixed point number with the same number of decimals as the token.

Returns

NameTypeDescription
<none>uint256payValue The amount that'll be paid as a msg.value.

_handleTokenTransfersAndSwap

Handles token transfers and swaps for a given project.

This function is responsible for transferring tokens from the sender to this terminal and performing a swap.

function _handleTokenTransfersAndSwap(
uint256 projectId,
address tokenIn,
uint256 amount,
bytes calldata metadata
)
internal
returns (uint256);

Parameters

NameTypeDescription
projectIduint256The ID of the project for which tokens are being transferred and possibly swapped.
tokenInaddressThe address of the token coming to this terminal.
amountuint256
metadatabytesAdditional data to be used in the swap.

Returns

NameTypeDescription
<none>uint256amountToSend The amount of tokens to send after the swap, to the next terminal

_swap

Swaps tokens based on the provided swap configuration.

function _swap(
address tokenIn,
uint256 amountIn,
uint256 minAmountOut,
bool zeroForOne,
uint256 projectId,
IUniswapV3Pool pool
)
internal
returns (uint256 amountOut);

Parameters

NameTypeDescription
tokenInaddressThe address of the token being swapped.
amountInuint256The amount of tokens to swap.
minAmountOutuint256The minimum amount of tokens to receive from the swap.
zeroForOneboolThe order of the token values being passed into the swap.
projectIduint256The ID of the project for which the swap is being performed.
poolIUniswapV3PoolThe pool to perform the swap in.

Returns

NameTypeDescription
amountOutuint256The amount of tokens received from the swap.

_transferFor

Transfers tokens.

function _transferFor(address from, address payable to, address token, uint256 amount) internal virtual;

Parameters

NameTypeDescription
fromaddressThe address to transfer tokens from.
toaddress payableThe address to transfer tokens to.
tokenaddressThe address of the token being transfered.
amountuint256The amount of tokens to transfer, as a fixed point number with the same number of decimals as the token.

Errors

JBSwapTerminal_CallerNotPool

error JBSwapTerminal_CallerNotPool(address caller);

JBSwapTerminal_InvalidTwapSlippageTolerance

error JBSwapTerminal_InvalidTwapSlippageTolerance(
uint256 slippageTolerance, uint256 minSlippageTolerance, uint256 maxSlippageTolerance
);

JBSwapTerminal_InvalidTwapWindow

error JBSwapTerminal_InvalidTwapWindow(uint256 window, uint256 minWindow, uint256 maxWindow);

JBSwapTerminal_SpecifiedSlippageExceeded

error JBSwapTerminal_SpecifiedSlippageExceeded(uint256 amount, uint256 minimum);

JBSwapTerminal_NoDefaultPoolDefined

error JBSwapTerminal_NoDefaultPoolDefined(uint256 projectId, address token);

JBSwapTerminal_NoMsgValueAllowed

error JBSwapTerminal_NoMsgValueAllowed(uint256 value);

JBSwapTerminal_PermitAllowanceNotEnough

error JBSwapTerminal_PermitAllowanceNotEnough(uint256 amount, uint256 allowance);

JBSwapTerminal_TokenNotAccepted

error JBSwapTerminal_TokenNotAccepted(uint256 projectId, address token);

JBSwapTerminal_UnexpectedCall

error JBSwapTerminal_UnexpectedCall(address caller);

JBSwapTerminal_WrongPool

error JBSwapTerminal_WrongPool(address pool, address expectedPool);

JBSwapTerminal_ZeroToken

error JBSwapTerminal_ZeroToken();