Concrete Swapper
The Concrete Swapper contract allows users to swap their ctAssetTokens against tokens that are in the Concrete Treasury above market rate. There is a built-in reward system which enables Concrete to incentivize users to perform swaps. For the Swapper Contract to perform the of swapping their tokens at a fair price, with a proper incentive and in a permissionless way, it requires an Oracle Integration for the latest price feeds, a Reward Manager for the incentives and a Token Registry that holds information about available tokens for swapping and pertinent information, such as their oracle addresses. We discuss them respectively.
The Swapper Contract
The main feature of this contract is to allow users to provide a ctAssetToken and specify the reward token and then to swap. To that end the contract exposes two functions, one for swapping:
function swapTokensForReward(address ctAssetToken_, address rewardToken_, uint256 ctAssetAmount_);
and another to preview the swap:
function previewSwapTokensForReward(address ctAssetToken, address rewardToken, uint256 ctAssetAmount)
external view returns(uint256 rewardAmount, bool availableForWithdrawal, bool isRewardToken);
Before a user can swap, they need to approve the swapper to spend their ctAssetTokens. When a swap has been performed successfully an event is emitted that indexes the user, and stores the ctAssetToken, the rewardToken and their respective amounts in the logs:
event Swapped(address indexed user, address ctAssetToken, address rewardToken, uint256 ctAssetAmount, uint256 reward);
How does the swap work?
First the swapper calls the vault that issues the chosen ctAssetToken and queries what it is worth in terms of the underlying asset. It then queries an oracle what that asset amount is worth in terms of reward tokens and then adds a given percentage on top as an incentivization. The magnitude of that percentage is handled by the reward manager, which takes information about the tokens, their amount and the user to determine the premium.
How are the external functions protected?
The external functions have guards. First and foremost, the contract inherits the Ownable trait, which designates an owner account. Setting important external contracts, such as the Reward Manager or the Token Registry can exclusively be done by the owner. The swap function is protected by a reentrancy guard avoiding possible exploits with multiple calls of the function in the same transaction.
How can certain tokens be disabled for swapping?
In case that certain tokens should be disabled or re-enabled from being swapped the owner has the possibility to call this function:
function disableTokenForSwap(address token_, bool disableSwap_) public onlyOwner
Oracle Integration
The Swapper contract inherits an Oracle Plug, which gets price quotes from an Oracle. The Oracle Plug makes the following internal functions available for its parent contract, the swapper:
function _convertFromTokenToStable(address token_, uint256 tokenAmount_) internal view returns (uint256)
function _convertFromCtAssetTokenToStable(address ctAssetToken_, uint256 ctAssetAmount_) internal view returns (uint256 stableAmount)
function _convertFromStableToToken(address token_, uint256 stableAmount_) internal view returns (uint256)
function _convertFromStableToCtAssetToken(address ctAssetToken_, uint256 stableAmount_) internal view returns(uint256 ctAssetAmount)
Under the hood it retrieves the pertinent information about the oracle from the Token Registry. This includes the address of the oracle, the number of decimals it uses to quote the price and the string for the price pair.
Note: The only pairs supported are asset/usd pairs, where the asset can also be a stablecoin. Converting from asset to another asset should be done by querying the price feeds for the respective assets. In fact converting the underlying asset of the ctAsset vaults to the reward token and back is done in this two-stage process.
Last updated