Skip to main content

Restricted Swap

Overview

RestrictedSwap provides a secure means of swapping tokens between known holders of the primary Restricted token and any payment ERC-20 tokens (ie USDC, DAI, etc).

Note: Restricted Swap is not intended between two Restricted tokens. Rather, it is intended as a purchase of Restricted tokens using ERC-20 tokens (AKA payment token or quote token). This is enforced in the Restricted Swap contract itself, by preventing ERC-1404 compatible tokens (checked via ERC-165 interface support) from being used as payment. However, other types of Restricted tokens may not explicitly support the ERC-1404 interface and thus cannot be checked programmatically; therefore, this functionality should be used with caution and payment token types should always be verified first.

Usage

How It Works

As a prerequisite, users of the swap functionality are assumed to have passed any necessary off-chain AML/KYC and deemed valid holders. Contracts are also assumed to have been configured properly with admin-granted roles and transfer restrictions in place.

Configuring a Purchase of Restricted Token

As a holder (Buyer) of an ERC-20 payment token (ie USDC, DAI, etc), one can configure a purchase order for a specified amount of Restricted token with a known party (Seller). Here is an example involving USDC:

  1. Buyer must know in advance specifically how much Restricted token they'd like to receive, how much USDC they are willing to pay, and the Seller on the other end.
  2. Buyer must approve the Swap contract itself to handle their USDC in the purchase amount required.
  3. Buyer then configures an open purchase order by calling configureBuy with the following parameters. A Swap ID is emitted in an event.
    • amount of Restricted token desired
    • address of Seller
    • amount of payment token (USDC) willing to swap
    • address of payment token contract
  4. Seller must approve the Swap contract itself to handle their Restricted token in the sell amount required by the configured swap.
  5. Seller can complete this order by calling completeSwapWithRestrictedToken with the emitted Swap ID.

When transfer restrictions are validated between Buyer and Seller, the swap is completed and assets are actually transferred between transacting parties.

Note: Role checks (e.g., pausing, admin-only operations) are resolved through the pre-deployed AccessControl contract referenced by the Restricted token and ancillary contracts.

Configuring a Sale of Restricted Token

As a holder of the Restricted token, one can also configure a sell order for a specified amount of desired payment ERC-20 token. It is extremely similar to the example above, except terms of the sale must be specified in advance. Here is an example involving USDC:

  1. Seller must know in advance specifically how much Restricted token they'd like to sell, how much USDC they require, and the Buyer on the other end.
  2. Seller must approve the Swap contract itself to handle their Restricted token in the sell amount desired.
  3. Seller then configures an open sell order by calling configureSell with the following parameters. A Swap ID is emitted in an event.
    • amount of Restricted token to sell
    • address of Buyer
    • amount of payment token (USDC) required
    • address of payment token contract
  4. Buyer must approve the Swap contract itself to handle their USDC in the amount specified by the configured swap.
  5. Buyer can complete this order by calling completeSwapWithQuoteToken with the emitted Swap ID.

When transfer restrictions are validated between Buyer and Seller, the swap is completed and assets are actually transferred between transacting parties.

API Reference

Constructor

constructor(address restrictedLockupTokenAddress_, address trustedForwarder_, address accessControl_)

Initializes the RestrictedSwap contract with configuration parameters.

Parameters:

  • restrictedLockupTokenAddress_ (address): The RestrictedLockupToken contract address
  • trustedForwarder_ (address): ERC-2771 trusted forwarder address for meta-transactions
  • accessControl_ (address): AccessControl contract address for role management

Requirements:

  • All addresses must be non-zero
  • Access control address must be valid

Administrative Functions

pause(bool isPaused_)

Pauses or unpauses the contract functionality.

Parameters:

  • isPaused_ (bool): True to pause, false to unpause

Requirements:

  • Caller must have CONTRACT_ADMIN_ROLE or TRANSFER_ADMIN_ROLE

Emits: Paused(account) or Unpaused(account)


Swap Configuration Functions

configureSell(uint256 restrictedTokenAmount, address quoteToken, address quoteTokenSender, uint256 quoteTokenAmount)

Configures a sell order for restricted tokens in exchange for quote tokens.

Parameters:

  • restrictedTokenAmount (uint256): Amount of restricted tokens to sell
  • quoteToken (address): Address of the quote token (ERC-20) to receive
  • quoteTokenSender (address): Address that will provide the quote tokens
  • quoteTokenAmount (uint256): Amount of quote tokens to receive

Requirements:

  • Contract must not be paused
  • Quote token sender must not be zero address
  • Quote token must not be zero address
  • Caller must have sufficient restricted token allowance for this contract
  • Quote token must not support ERC-1404 interface (to prevent restricted token swaps)

Emits: SwapConfigured(swapNumber, restrictedTokenSender, restrictedTokenAmount, quoteToken, quoteTokenSender, quoteTokenAmount)


configureBuy(uint256 restrictedTokenAmount, address restrictedTokenSender, address quoteToken, uint256 quoteTokenAmount)

Configures a buy order for restricted tokens using quote tokens.

Parameters:

  • restrictedTokenAmount (uint256): Amount of restricted tokens to buy
  • restrictedTokenSender (address): Address that will provide the restricted tokens
  • quoteToken (address): Address of the quote token (ERC-20) to pay with
  • quoteTokenAmount (uint256): Amount of quote tokens to pay

Requirements:

  • Contract must not be paused
  • Restricted token sender must not be zero address
  • Quote token must not be zero address
  • Caller must have sufficient quote token balance
  • Caller must have sufficient quote token allowance for this contract
  • Quote token must not support ERC-1404 interface

Emits: SwapConfigured(swapNumber, restrictedTokenSender, restrictedTokenAmount, quoteToken, quoteTokenSender, quoteTokenAmount)


Swap Execution Functions

completeSwapWithQuoteToken(uint256 swapNumber_)

Completes a swap by providing quote tokens (used by quote token sender in sell-configured swaps).

Parameters:

  • swapNumber_ (uint256): The swap number to complete

Requirements:

  • Contract must not be paused
  • Swap must not be canceled or already completed
  • Caller must be the quote token sender for this swap
  • Swap status must be SellConfigured
  • Quote token sender must have sufficient quote token balance
  • Restricted token sender must have sufficient restricted token balance
  • Transfer restrictions must be satisfied between parties

Emits: SwapComplete(swapNumber, restrictedTokenSender, restrictedTokenAmount, quoteTokenSender, quoteToken, quoteTokenAmount)


completeSwapWithRestrictedToken(uint256 swapNumber_)

Completes a swap by providing restricted tokens (used by restricted token sender in buy-configured swaps).

Parameters:

  • swapNumber_ (uint256): The swap number to complete

Requirements:

  • Contract must not be paused
  • Swap must not be canceled or already completed
  • Caller must be the restricted token sender for this swap
  • Swap status must be BuyConfigured
  • Quote token sender must have sufficient quote token balance
  • Restricted token sender must have sufficient restricted token balance
  • Transfer restrictions must be satisfied between parties

Emits: SwapComplete(swapNumber, restrictedTokenSender, restrictedTokenAmount, quoteTokenSender, quoteToken, quoteTokenAmount)


cancelSwap(uint256 swapNumber_)

Cancels a configured swap.

Parameters:

  • swapNumber_ (uint256): The swap number to cancel

Requirements:

  • Swap must not be canceled or already completed
  • Swap must be properly configured
  • For SellConfigured swaps: caller must be the restricted token sender
  • For BuyConfigured swaps: caller must be the quote token sender

Emits: SwapCanceled(sender, swapNumber)


Query Functions

swapNumber() → uint256

Returns the current swap number counter.

Returns:

  • uint256: The current swap number (total swaps configured)

swapStatus(uint256 swapNumber_) → SwapStatus

Returns the status of a specific swap.

Parameters:

  • swapNumber_ (uint256): The swap number to query

Returns:

  • SwapStatus: The status enum (SellConfigured=1, BuyConfigured=2, Complete=3, Canceled=4)

Requirements:

  • Swap record must exist

requiredAllowance(address addr, address token) → uint256

Returns the required allowance for active swaps for a wallet and token.

Parameters:

  • addr (address): Wallet address
  • token (address): Token address

Returns:

  • uint256: Required allowance amount for the configurator

requiredAllowanceCounterparty(address addr, address token) → uint256

Returns the required allowance for active swaps as a counterparty.

Parameters:

  • addr (address): Wallet address
  • token (address): Token address

Returns:

  • uint256: Required allowance amount as counterparty (informational only)

pendingBuys(address addr) → uint256

Returns pending restricted tokens to buy for an address.

Parameters:

  • addr (address): Buyer address

Returns:

  • uint256: Amount of pending restricted tokens to buy

pendingSells(address addr) → uint256

Returns pending restricted tokens to sell for an address.

Parameters:

  • addr (address): Seller address

Returns:

  • uint256: Amount of pending restricted tokens to sell

restrictedLockupToken() → address

Returns the RestrictedLockupToken contract address.

Returns:

  • address: The RestrictedLockupToken contract address

accessControl() → address

Returns the AccessControl contract address.

Returns:

  • address: The AccessControl contract address

INTERFACE_ID() → bytes4

Returns the interface ID for ERC-165 support.

Returns:

  • bytes4: The interface identifier

_swapNumber() → uint256

Returns the internal swap number counter (same as swapNumber()).

Returns:

  • uint256: The current swap number

Inherited Functions

Pausable Functions

paused() → bool

Returns whether the contract is currently paused.

Returns:

  • bool: True if paused, false otherwise

ERC-2771 Functions

isTrustedForwarder(address forwarder) → bool

Checks if an address is a trusted forwarder for meta-transactions.

Parameters:

  • forwarder (address): Address to check

Returns:

  • bool: True if the address is a trusted forwarder

trustedForwarder() → address

Returns the trusted forwarder address.

Returns:

  • address: The trusted forwarder address

ERC-165 Functions

supportsInterface(bytes4 interfaceId) → bool

Checks if the contract supports a specific interface.

Parameters:

  • interfaceId (bytes4): Interface identifier to check

Returns:

  • bool: True if the interface is supported

Events

SwapConfigured(uint256 indexed swapNumber, address indexed restrictedTokenSender, uint256 restrictedTokenAmount, address quoteToken, address indexed quoteTokenSender, uint256 quoteTokenAmount)

Emitted when a new swap is configured.

Parameters:

  • swapNumber (uint256, indexed): Unique swap identifier
  • restrictedTokenSender (address, indexed): Address providing restricted tokens
  • restrictedTokenAmount (uint256): Amount of restricted tokens in the swap
  • quoteToken (address): Quote token contract address
  • quoteTokenSender (address, indexed): Address providing quote tokens
  • quoteTokenAmount (uint256): Amount of quote tokens in the swap

SwapComplete(uint256 indexed swapNumber, address indexed restrictedTokenSender, uint256 restrictedTokenAmount, address indexed quoteTokenSender, address quoteToken, uint256 quoteTokenAmount)

Emitted when a swap is successfully completed.

Parameters:

  • swapNumber (uint256, indexed): Unique swap identifier
  • restrictedTokenSender (address, indexed): Address that provided restricted tokens
  • restrictedTokenAmount (uint256): Amount of restricted tokens swapped
  • quoteTokenSender (address, indexed): Address that provided quote tokens
  • quoteToken (address): Quote token contract address
  • quoteTokenAmount (uint256): Amount of quote tokens swapped

SwapCanceled(address indexed sender, uint256 indexed swapNumber)

Emitted when a swap is canceled.

Parameters:

  • sender (address, indexed): Address that canceled the swap
  • swapNumber (uint256, indexed): Unique swap identifier

Inherited Events

Paused(address account)

Emitted when the contract is paused.

Parameters:

  • account (address): Address that paused the contract

Unpaused(address account)

Emitted when the contract is unpaused.

Parameters:

  • account (address): Address that unpaused the contract

Custom Errors

Configuration Errors

RestrictedSwap_InvalidAccessControl()

Thrown when an invalid access control address is provided during construction.


RestrictedSwap_InvalidQuoteToken()

Thrown when a zero address is provided as the quote token.


RestrictedSwap_InvalidQuoteTokenSender()

Thrown when a zero address is provided as the quote token sender.


RestrictedSwap_InvalidRestrictedTokenSender()

Thrown when a zero address is provided as the restricted token sender.


RestrictedSwap_InvalidRestrictedTokenAmount()

Thrown when zero is provided as the restricted token amount.


RestrictedSwap_InvalidQuoteTokenAmount()

Thrown when zero is provided as the quote token amount.


Validation Errors

RestrictedSwap_DoesNotSupportIERC1404()

Thrown when the quote token supports ERC-1404 interface (restricted tokens not allowed as quote tokens).


RestrictedSwap_InsufficientRestrictedTokenAllowance()

Thrown when the restricted token allowance is insufficient for the swap.


RestrictedSwap_InsufficientQuoteTokenAllowance()

Thrown when the quote token allowance is insufficient for the swap.


RestrictedSwap_InsufficientRestrictedTokenAmount()

Thrown when the restricted token balance is insufficient for the swap.


RestrictedSwap_InsufficientQuoteTokenAmount()

Thrown when the quote token balance is insufficient for the swap.


Swap State Errors

RestrictedSwap_AlreadyCanceled()

Thrown when attempting to operate on a swap that has already been canceled.


RestrictedSwap_AlreadyCompleted()

Thrown when attempting to operate on a swap that has already been completed.


RestrictedSwap_InvalidSwapStatus()

Thrown when the swap status doesn't match the required status for the operation.


RestrictedSwap_InvalidSwapRecord()

Thrown when attempting to query a swap that doesn't exist.


RestrictedSwap_SwapNotConfigured()

Thrown when attempting to cancel a swap that hasn't been properly configured.


Authorization Errors

RestrictedSwap_InvalidTokenSender()

Thrown when the caller is not authorized to complete the swap (not the expected token sender).


RestrictedSwap_InvalidCanceler()

Thrown when the caller is not authorized to cancel the swap.


Transfer Errors

RestrictedSwap_InconsistentQuoteTokenAmount()

Thrown when the actual quote token transfer amount doesn't match the expected amount.


RestrictedSwap_InconsistentRestrictedTokenAmount()

Thrown when the actual restricted token transfer amount doesn't match the expected amount.


Inherited Errors

EasyAccessControl_DoesNotHaveContractOrTransferAdminRole(address addr)

Thrown when an address lacks both CONTRACT_ADMIN_ROLE and TRANSFER_ADMIN_ROLE.

Parameters:

  • addr (address): The unauthorized address

EnforcedPause()

Thrown when an operation is attempted while the contract is paused.


ExpectedPause()

Thrown when attempting to pause an already paused contract or unpause an unpaused contract.


ReentrancyGuardReentrantCall()

Thrown when a reentrant call is detected.


SafeERC20FailedOperation(address token)

Thrown when an ERC-20 operation fails.

Parameters:

  • token (address): The token contract address where the operation failed