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:
Buyer
must know in advance specifically how much Restricted token they'd like to receive, how much USDC they are willing to pay, and theSeller
on the other end.Buyer
must approve the Swap contract itself to handle their USDC in the purchase amount required.Buyer
then configures an open purchase order by callingconfigureBuy
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
Seller
must approve the Swap contract itself to handle their Restricted token in the sell amount required by the configured swap.Seller
can complete this order by callingcompleteSwapWithRestrictedToken
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:
Seller
must know in advance specifically how much Restricted token they'd like to sell, how much USDC they require, and theBuyer
on the other end.Seller
must approve the Swap contract itself to handle their Restricted token in the sell amount desired.Seller
then configures an open sell order by callingconfigureSell
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
Buyer
must approve the Swap contract itself to handle their USDC in the amount specified by the configured swap.Buyer
can complete this order by callingcompleteSwapWithQuoteToken
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 addresstrustedForwarder_
(address): ERC-2771 trusted forwarder address for meta-transactionsaccessControl_
(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 sellquoteToken
(address): Address of the quote token (ERC-20) to receivequoteTokenSender
(address): Address that will provide the quote tokensquoteTokenAmount
(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 buyrestrictedTokenSender
(address): Address that will provide the restricted tokensquoteToken
(address): Address of the quote token (ERC-20) to pay withquoteTokenAmount
(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 addresstoken
(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 addresstoken
(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 identifierrestrictedTokenSender
(address, indexed): Address providing restricted tokensrestrictedTokenAmount
(uint256): Amount of restricted tokens in the swapquoteToken
(address): Quote token contract addressquoteTokenSender
(address, indexed): Address providing quote tokensquoteTokenAmount
(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 identifierrestrictedTokenSender
(address, indexed): Address that provided restricted tokensrestrictedTokenAmount
(uint256): Amount of restricted tokens swappedquoteTokenSender
(address, indexed): Address that provided quote tokensquoteToken
(address): Quote token contract addressquoteTokenAmount
(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 swapswapNumber
(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