Skip to main content

JBSucker

Git Source

Inherits: ERC2771Context, JBPermissioned, Initializable, ERC165, IJBSuckerExtended

An abstract contract for bridging a Juicebox project's tokens and the corresponding funds to and from a remote chain.

Beneficiaries and balances are tracked on two merkle trees: the outbox tree is used to send from the local chain to the remote chain, and the inbox tree is used to receive from the remote chain to the local chain.

Throughout this contract, "terminal token" refers to any token accepted by a project's terminal.

This contract does NOT support tokens that have a fee on regular transfers and rebasing tokens.

State Variables

MESSENGER_BASE_GAS_LIMIT

A reasonable minimum gas limit for a basic cross-chain call. The minimum amount of gas required to call the fromRemote (successfully/safely) on the remote chain.

uint32 public constant override MESSENGER_BASE_GAS_LIMIT = 300_000;

MESSENGER_ERC20_MIN_GAS_LIMIT

A reasonable minimum gas limit used when bridging ERC-20s. The minimum amount of gas required to (successfully/safely) perform a transfer on the remote chain.

uint32 public constant override MESSENGER_ERC20_MIN_GAS_LIMIT = 200_000;

_TREE_DEPTH

The depth of the merkle tree used to store the outbox and inbox.

uint32 constant _TREE_DEPTH = 32;

ADD_TO_BALANCE_MODE

Whether the amountToAddToBalance gets added to the project's balance automatically when claim is called or manually by calling addOutstandingAmountToBalance.

JBAddToBalanceMode public immutable override ADD_TO_BALANCE_MODE;

DEPLOYER

The address of this contract's deployer.

address public immutable override DEPLOYER;

DIRECTORY

The directory of terminals and controllers for projects.

IJBDirectory public immutable override DIRECTORY;

TOKENS

The contract that manages token minting and burning.

IJBTokens public immutable override TOKENS;

_deprecatedAfter

The timestamp after which the sucker is entirely deprecated.

uint256 internal _deprecatedAfter;

_localProjectId

The ID of the project (on the local chain) that this sucker is associated with.

uint256 private _localProjectId;

_executedFor

Tracks whether individual leaves in a given token's merkle tree have been executed (to prevent double-spending).

A leaf is "executed" when the tokens it represents are minted for its beneficiary.

mapping(address token => BitMaps.BitMap) internal _executedFor;

_inboxOf

The inbox merkle tree root for a given token.

mapping(address token => JBInboxTreeRoot root) internal _inboxOf;

_outboxOf

The outbox merkle tree for a given token.

mapping(address token => JBOutboxTree) internal _outboxOf;

_remoteTokenFor

Information about the token on the remote chain that the given token on the local chain is mapped to.

mapping(address token => JBRemoteToken remoteToken) internal _remoteTokenFor;

Functions

constructor

constructor(
IJBDirectory directory,
IJBPermissions permissions,
IJBTokens tokens,
JBAddToBalanceMode addToBalanceMode,
address trusted_forwarder
)
ERC2771Context(trusted_forwarder)
JBPermissioned(permissions);

Parameters

NameTypeDescription
directoryIJBDirectoryA contract storing directories of terminals and controllers for each project.
permissionsIJBPermissionsA contract storing permissions.
tokensIJBTokensA contract that manages token minting and burning.
addToBalanceModeJBAddToBalanceModeThe mode of adding tokens to balance.
trusted_forwarderaddress

amountToAddToBalanceOf

The outstanding amount of tokens to be added to the project's balance by claim or addOutstandingAmountToBalance.

function amountToAddToBalanceOf(address token) public view override returns (uint256);

Parameters

NameTypeDescription
tokenaddressThe local terminal token to get the amount to add to balance for.

inboxOf

The inbox merkle tree root for a given token.

function inboxOf(address token) external view returns (JBInboxTreeRoot memory);

Parameters

NameTypeDescription
tokenaddressThe local terminal token to get the inbox for.

isMapped

Checks whether the specified token is mapped to a remote token.

function isMapped(address token) external view override returns (bool);

Parameters

NameTypeDescription
tokenaddressThe terminal token to check.

Returns

NameTypeDescription
<none>boolA boolean which is true if the token is mapped to a remote token and false if it is not.

outboxOf

Information about the token on the remote chain that the given token on the local chain is mapped to.

function outboxOf(address token) external view returns (JBOutboxTree memory);

Parameters

NameTypeDescription
tokenaddressThe local terminal token to get the remote token for.

peerChainId

Returns the chain on which the peer is located.

function peerChainId() external view virtual returns (uint256);

Returns

NameTypeDescription
<none>uint256chain ID of the peer.

remoteTokenFor

Information about the token on the remote chain that the given token on the local chain is mapped to.

function remoteTokenFor(address token) external view returns (JBRemoteToken memory);

Parameters

NameTypeDescription
tokenaddressThe local terminal token to get the remote token for.

peer

The peer sucker on the remote chain.

function peer() public view virtual returns (address);

projectId

This can be overridden by the inheriting contract to return a different address. This is fully supported by the sucker implementation and all its off-chain infrastructure, This does however break some invariants/assumptions, for revnets it would break the assumption of matching configurations on both chains, for this reason we only support a matching address.

The ID of the project (on the local chain) that this sucker is associated with.

function projectId() public view returns (uint256);

state

Reports the deprecation state of the sucker.

function state() public view override returns (JBSuckerState);

Returns

NameTypeDescription
<none>JBSuckerStatestate The current deprecation state

supportsInterface

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

_balanceOf

Helper to get the addr's balance for a given token.

function _balanceOf(address token, address addr) internal view returns (uint256 balance);

Parameters

NameTypeDescription
tokenaddressThe token to get the balance for.
addraddressThe address to get the token balance of.

Returns

NameTypeDescription
balanceuint256The address' token balance.

_buildTreeHash

Builds a hash as they are stored in the merkle tree.

function _buildTreeHash(
uint256 projectTokenCount,
uint256 terminalTokenAmount,
address beneficiary
)
internal
pure
returns (bytes32);

Parameters

NameTypeDescription
projectTokenCountuint256The number of project tokens being cashed out.
terminalTokenAmountuint256The amount of terminal tokens being reclaimed by the cash out.
beneficiaryaddressThe beneficiary which will receive the project tokens.

_validateTokenMapping

Allow sucker implementations to add/override mapping rules to suite their specific needs.

function _validateTokenMapping(JBTokenMapping calldata map) internal pure virtual;

_msgData

The calldata. Preferred to use over msg.data.

function _msgData() internal view override(ERC2771Context, Context) returns (bytes calldata);

Returns

NameTypeDescription
<none>bytescalldata The msg.data of this call.

_msgSender

The message's sender. Preferred to use over msg.sender.

function _msgSender() internal view override(ERC2771Context, Context) returns (address sender);

Returns

NameTypeDescription
senderaddressThe address which sent this call.

_contextSuffixLength

ERC-2771 specifies the context as being a single address (20 bytes).

function _contextSuffixLength() internal view virtual override(ERC2771Context, Context) returns (uint256);

initialize

Initializes the sucker with the project ID and peer address.

function initialize(uint256 __projectId) public initializer;

Parameters

NameTypeDescription
__projectIduint256The ID of the project (on the local chain) that this sucker is associated with.

addOutstandingAmountToBalance

Adds the reclaimed token balance to the projects terminal. Can only be used if ADD_TO_BALANCE_MODE is MANUAL.

function addOutstandingAmountToBalance(address token) external override;

Parameters

NameTypeDescription
tokenaddressThe address of the terminal token to add to the project's balance.

claim

Performs multiple claims.

function claim(JBClaim[] calldata claims) external override;

Parameters

NameTypeDescription
claimsJBClaim[]A list of claims to perform (including the terminal token, merkle tree leaf, and proof for each claim).

claim

JBClaim project tokens which have been bridged from the remote chain for their beneficiary.

function claim(JBClaim calldata claimData) public override;

Parameters

NameTypeDescription
claimDataJBClaimThe terminal token, merkle tree leaf, and proof for the claim.

fromRemote

Receive a merkle root for a terminal token from the remote project.

This can only be called by the messenger contract on the local chain, with a message from the remote peer.

function fromRemote(JBMessageRoot calldata root) external payable;

Parameters

NameTypeDescription
rootJBMessageRootThe merkle root, token, and amount being received.

mapToken

Map an ERC-20 token on the local chain to an ERC-20 token on the remote chain, allowing that token to be bridged.

function mapToken(JBTokenMapping calldata map) public payable override;

Parameters

NameTypeDescription
mapJBTokenMappingThe local and remote terminal token addresses to map, and minimum amount/gas limits for bridging them.

mapTokens

Map multiple ERC-20 tokens on the local chain to ERC-20 tokens on the remote chain, allowing those tokens to be bridged.

function mapTokens(JBTokenMapping[] calldata maps) external payable override;

Parameters

NameTypeDescription
mapsJBTokenMapping[]A list of local and remote terminal token addresses to map, and minimum amount/gas limits for bridging them.

enableEmergencyHatchFor

Enables the emergency hatch for a list of tokens, allowing users to exit on the chain they deposited on.

For use when a token or a few tokens are no longer compatible with a bridge.

function enableEmergencyHatchFor(address[] calldata tokens) external override;

Parameters

NameTypeDescription
tokensaddress[]The terminal tokens to enable the emergency hatch for.

prepare

Prepare project tokens and the cash out amount backing them to be bridged to the remote chain.

This adds the tokens and funds to the outbox tree for the token. They will be bridged by the next call to toRemote for the same token.

function prepare(
uint256 projectTokenCount,
address beneficiary,
uint256 minTokensReclaimed,
address token
)
external
override;

Parameters

NameTypeDescription
projectTokenCountuint256The number of project tokens to prepare for bridging.
beneficiaryaddressThe address of the recipient of the tokens on the remote chain.
minTokensReclaimeduint256The minimum amount of terminal tokens to cash out for. If the amount cashed out is less than this, the transaction will revert.
tokenaddressThe address of the terminal token to cash out for.

toRemote

Bridge the project tokens, cashed out funds, and beneficiary information for a given token to the remote chain.

This sends the outbox root for the specified token to the remote chain.

function toRemote(address token) external payable override;

Parameters

NameTypeDescription
tokenaddressThe terminal token being bridged.

exitThroughEmergencyHatch

Lets user exit on the chain they deposited in a scenario where the bridge is no longer functional.

function exitThroughEmergencyHatch(JBClaim calldata claimData) external override;

Parameters

NameTypeDescription
claimDataJBClaimThe terminal token, merkle tree leaf, and proof for the claim

setDeprecation

Set or remove the time after which this sucker will be deprecated, once deprecated the sucker will no longer be functional and it will let all users exit.

function setDeprecation(uint40 timestamp) external override;

Parameters

NameTypeDescription
timestampuint40The time after which the sucker will be deprecated. Or 0 to remove the upcoming deprecation.

receive

Used to receive cashed out native tokens.

receive() external payable;

_addToBalance

Adds funds to the projects balance.

function _addToBalance(address token, uint256 amount) internal;

Parameters

NameTypeDescription
tokenaddressThe terminal token to add to the project's balance.
amountuint256The amount of terminal tokens to add to the project's balance.

_handleClaim

The action(s) to perform after a user has succesfully proven their claim.

function _handleClaim(
address terminalToken,
uint256 terminalTokenAmount,
uint256 projectTokenAmount,
address beneficiary
)
internal;

Parameters

NameTypeDescription
terminalTokenaddressThe terminal token being sucked.
terminalTokenAmountuint256The amount of terminal tokens.
projectTokenAmountuint256The amount of project tokens.
beneficiaryaddressThe beneficiary of the project tokens.

_insertIntoTree

Inserts a new leaf into the outbox merkle tree for the specified token.

function _insertIntoTree(
uint256 projectTokenCount,
address token,
uint256 terminalTokenAmount,
address beneficiary
)
internal;

Parameters

NameTypeDescription
projectTokenCountuint256The amount of project tokens being cashed out.
tokenaddressThe terminal token being cashed out for.
terminalTokenAmountuint256The amount of terminal tokens reclaimed by cashing out.
beneficiaryaddressThe beneficiary of the project tokens on the remote chain.

_isRemotePeer

Checks if the sender (_msgSender) is a valid representative of the remote peer.

function _isRemotePeer(address sender) internal virtual returns (bool valid);

Parameters

NameTypeDescription
senderaddressThe message's sender.

_mapToken

Map an ERC-20 token on the local chain to an ERC-20 token on the remote chain, allowing that token to be bridged or disabled.

function _mapToken(JBTokenMapping calldata map, uint256 transportPaymentValue) internal;

Parameters

NameTypeDescription
mapJBTokenMappingThe local and remote terminal token addresses to map, and minimum amount/gas limits for bridging them.
transportPaymentValueuint256The amount of msg.value to send for the token mapping.

_pullBackingAssets

Cash out project tokens for terminal tokens.

function _pullBackingAssets(
IERC20 projectToken,
uint256 count,
address token,
uint256 minTokensReclaimed
)
internal
virtual
returns (uint256 reclaimedAmount);

Parameters

NameTypeDescription
projectTokenIERC20The project token being cashed out.
countuint256The number of project tokens to cash out.
tokenaddressThe terminal token to cash out for.
minTokensReclaimeduint256The minimum amount of terminal tokens to reclaim. If the amount reclaimed is less than this, the transaction will revert.

Returns

NameTypeDescription
reclaimedAmountuint256The amount of terminal tokens reclaimed by the cash out.

_sendRoot

Send the outbox root for the specified token to the remote peer.

The call may have a transportPayment for bridging native tokens. Require it to be 0 if it is not needed. Make sure if a value being paid to the bridge is expected to revert if the given value is 0.

function _sendRoot(uint256 transportPayment, address token, JBRemoteToken memory remoteToken) internal virtual;

Parameters

NameTypeDescription
transportPaymentuint256the amount of msg.value that is going to get paid for sending this message. (usually derived from msg.value)
tokenaddressThe terminal token to bridge the merkle tree of.
remoteTokenJBRemoteTokenThe remote token which the token is mapped to.

_sendRootOverAMB

Performs the logic to send a message to the peer over the AMB.

This is chain/sucker/bridge specific logic.

function _sendRootOverAMB(
uint256 transportPayment,
uint256 index,
address token,
uint256 amount,
JBRemoteToken memory remoteToken,
JBMessageRoot memory message
)
internal
virtual;

Parameters

NameTypeDescription
transportPaymentuint256The amount of msg.value that is going to get paid for sending this message.
indexuint256The index of the most recent message that is part of the root.
tokenaddressThe terminal token being bridged.
amountuint256The amount of terminal tokens being bridged.
remoteTokenJBRemoteTokenThe remote token which the terminal token is mapped to.
messageJBMessageRootThe message/root to send to the remote chain.

_maxMessagingDelay

What is the maximum time it takes for a message to be received on the other side.

Be sure to keep in mind if a message fails having to retry and the time it takes to retry.

function _maxMessagingDelay() internal pure virtual returns (uint40);

Returns

NameTypeDescription
<none>uint40The maximum time it takes for a message to be received on the other side.

_validate

Validates a leaf as being in the inbox merkle tree and registers the leaf as executed (to prevent double-spending).

Reverts if the leaf is invalid.

function _validate(
uint256 projectTokenCount,
address terminalToken,
uint256 terminalTokenAmount,
address beneficiary,
uint256 index,
bytes32[_TREE_DEPTH] calldata leaves
)
internal;

Parameters

NameTypeDescription
projectTokenCountuint256The number of project tokens which were cashed out.
terminalTokenaddressThe terminal token that the project tokens were cashed out for.
terminalTokenAmountuint256The amount of terminal tokens reclaimed by the cash out.
beneficiaryaddressThe beneficiary which will receive the project tokens.
indexuint256The index of the leaf being proved in the terminal token's inbox tree.
leavesbytes32[_TREE_DEPTH]The leaves that prove that the leaf at the index is in the tree (i.e. the merkle branch that the leaf is on).

_validateForEmergencyExit

Validates a leaf as being in the outbox merkle tree and not being send over the amb, and registers the leaf as executed (to prevent double-spending).

Reverts if the leaf is invalid.

function _validateForEmergencyExit(
uint256 projectTokenCount,
address terminalToken,
uint256 terminalTokenAmount,
address beneficiary,
uint256 index,
bytes32[_TREE_DEPTH] calldata leaves
)
internal;

Parameters

NameTypeDescription
projectTokenCountuint256The number of project tokens which were cashed out.
terminalTokenaddressThe terminal token that the project tokens were cashed out for.
terminalTokenAmountuint256The amount of terminal tokens reclaimed by the cash out.
beneficiaryaddressThe beneficiary which will receive the project tokens.
indexuint256The index of the leaf being proved in the terminal token's inbox tree.
leavesbytes32[_TREE_DEPTH]The leaves that prove that the leaf at the index is in the tree (i.e. the merkle branch that the leaf is on).

_validateBranchRoot

Validates a branch root against the expected root.

This is a virtual function to allow a tests to override the behavior, it should never be overwritten otherwise.

function _validateBranchRoot(
bytes32 expectedRoot,
uint256 projectTokenCount,
uint256 terminalTokenAmount,
address beneficiary,
uint256 index,
bytes32[_TREE_DEPTH] calldata leaves
)
internal
virtual;

Errors

JBSucker_BelowMinGas

error JBSucker_BelowMinGas(uint256 minGas, uint256 minGasLimit);

JBSucker_InsufficientBalance

error JBSucker_InsufficientBalance(uint256 amount, uint256 balance);

JBSucker_InvalidNativeRemoteAddress

error JBSucker_InvalidNativeRemoteAddress(address remoteToken);

JBSucker_InvalidProof

error JBSucker_InvalidProof(bytes32 root, bytes32 inboxRoot);

JBSucker_LeafAlreadyExecuted

error JBSucker_LeafAlreadyExecuted(address token, uint256 index);

JBSucker_ManualNotAllowed

error JBSucker_ManualNotAllowed(JBAddToBalanceMode mode);

JBSucker_DeprecationTimestampTooSoon

error JBSucker_DeprecationTimestampTooSoon(uint256 givenTime, uint256 minimumTime);

JBSucker_NoTerminalForToken

error JBSucker_NoTerminalForToken(uint256 projectId, address token);

JBSucker_NotPeer

error JBSucker_NotPeer(address caller);

JBSucker_QueueInsufficientSize

error JBSucker_QueueInsufficientSize(uint256 amount, uint256 minimumAmount);

JBSucker_TokenNotMapped

error JBSucker_TokenNotMapped(address token);

JBSucker_TokenHasInvalidEmergencyHatchState

error JBSucker_TokenHasInvalidEmergencyHatchState(address token);

JBSucker_TokenAlreadyMapped

error JBSucker_TokenAlreadyMapped(address localToken, address mappedTo);

JBSucker_UnexpectedMsgValue

error JBSucker_UnexpectedMsgValue(uint256 value);

JBSucker_ExpectedMsgValue

error JBSucker_ExpectedMsgValue();

JBSucker_InsufficientMsgValue

error JBSucker_InsufficientMsgValue(uint256 received, uint256 expected);

JBSucker_ZeroBeneficiary

error JBSucker_ZeroBeneficiary();

JBSucker_ZeroERC20Token

error JBSucker_ZeroERC20Token();

JBSucker_Deprecated

error JBSucker_Deprecated();