-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
5f924c6
commit 77e4f80
Showing
3 changed files
with
168 additions
and
91 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
// SPDX-License-Identifier: BSD-3-Clause-Clear | ||
pragma solidity ^0.8.24; | ||
|
||
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; | ||
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | ||
|
||
import { EncryptedERC20 } from "./EncryptedERC20.sol"; | ||
|
||
import "fhevm/lib/TFHE.sol"; | ||
import "fhevm/gateway/GatewayCaller.sol"; | ||
|
||
/** | ||
* @title EncryptedERC20Wrapped | ||
* @notice This contract allows users to wrap/unwrap trustlessly | ||
* ERC20 tokens to EncryptedERC20 tokens. | ||
* @dev This implementation does not support tokens with rebase functions or | ||
* tokens with a fee on transfer. All ERC20 tokens must have decimals | ||
* inferior or equal to 18 decimals but superior or equal to 6 decimals. | ||
*/ | ||
abstract contract EncryptedERC20Wrapped is EncryptedERC20, GatewayCaller { | ||
using SafeERC20 for IERC20Metadata; | ||
|
||
/// @notice Returns if user cannot transfer or mint. | ||
error CannotTransferOrMint(); | ||
|
||
/// @notice Returned if the amount is greater than 2**64. | ||
error AmountTooHigh(); | ||
|
||
/// @notice Emitted when token is unwrapped. | ||
event Unwrap(address indexed to, uint64 amount); | ||
|
||
/// @notice Emitted if unwrap fails. | ||
event UnwrapFail(address account, uint64 amount); | ||
|
||
/// @notice Emitted when token is wrapped. | ||
event Wrap(address indexed to, uint64 amount); | ||
|
||
/** | ||
* @notice Keeps track of unwrap information. | ||
* @param account Account that initiates the unwrap request. | ||
* @param amount Amount to be unwrapped. | ||
*/ | ||
struct UnwrapRequest { | ||
address account; | ||
uint64 amount; | ||
} | ||
|
||
/// @notice ERC20 token that is wrapped. | ||
IERC20Metadata public immutable ERC20_TOKEN; | ||
|
||
/// @notice Tracks whether the account can move funds. | ||
mapping(address account => bool canMoveFunds) public isAccountRestricted; | ||
|
||
/// @notice Tracks the unwrap request to a unique request id. | ||
mapping(uint256 requestId => UnwrapRequest unwrapRequest) public unwrapRequests; | ||
|
||
/** | ||
* @notice Deposit/withdraw ERC20 tokens using encrypted ERC20 tokens. | ||
* @param erc20_ Address of the ERC20 token to wrap/unwrap. | ||
* @dev The name/symbol are autogenerated. | ||
* For instance, | ||
* "Wrapped Ether" --> "Encrypted Wrapped Ether" | ||
* "WETH" --> "eWETH". | ||
*/ | ||
constructor( | ||
address erc20_ | ||
) | ||
EncryptedERC20( | ||
string(abi.encodePacked("Encrypted ", IERC20Metadata(erc20_).name())), | ||
string(abi.encodePacked("e", IERC20Metadata(erc20_).symbol())) | ||
) | ||
{ | ||
ERC20_TOKEN = IERC20Metadata(erc20_); | ||
} | ||
|
||
/** | ||
* @notice Unwrap EncryptedERC20 tokens to standard ERC20 tokens. | ||
* @param amount Amount to unwrap. | ||
*/ | ||
function unwrap(uint64 amount) public virtual { | ||
_canTransferOrMint(msg.sender); | ||
|
||
/// @dev Once this function is called, it becomes impossible for the sender to move any token. | ||
isAccountRestricted[msg.sender] = true; | ||
ebool canUnwrap = TFHE.le(amount, _balances[msg.sender]); | ||
|
||
uint256[] memory cts = new uint256[](1); | ||
cts[0] = Gateway.toUint256(canUnwrap); | ||
|
||
uint256 requestId = Gateway.requestDecryption( | ||
cts, | ||
this.callbackUnwrap.selector, | ||
0, | ||
block.timestamp + 100, | ||
false | ||
); | ||
|
||
unwrapRequests[requestId] = UnwrapRequest({ account: msg.sender, amount: amount }); | ||
} | ||
|
||
/** | ||
* @notice Wrap ERC20 tokens to an encrypted format. | ||
* @param amount Amount to wrap. | ||
*/ | ||
function wrap(uint256 amount) public virtual { | ||
ERC20_TOKEN.safeTransferFrom(msg.sender, address(this), amount); | ||
|
||
uint256 amountAdjusted = amount / (10 ** (ERC20_TOKEN.decimals() - decimals())); | ||
|
||
if (amountAdjusted > type(uint64).max) { | ||
revert AmountTooHigh(); | ||
} | ||
|
||
uint64 amountUint64 = uint64(amountAdjusted); | ||
|
||
_unsafeMint(msg.sender, TFHE.asEuint64(amountUint64)); | ||
_totalSupply += amountUint64; | ||
|
||
emit Wrap(msg.sender, amountUint64); | ||
} | ||
|
||
/** | ||
* @notice Callback function for the gateway. | ||
* @param requestId Request id. | ||
* @param canUnwrap Whether it can be unwrapped. | ||
*/ | ||
function callbackUnwrap(uint256 requestId, bool canUnwrap) public virtual onlyGateway { | ||
UnwrapRequest memory unwrapRequest = unwrapRequests[requestId]; | ||
delete unwrapRequests[requestId]; | ||
|
||
if (canUnwrap) { | ||
_unsafeBurn(unwrapRequest.account, TFHE.asEuint64(unwrapRequest.amount)); | ||
_totalSupply -= unwrapRequest.amount; | ||
|
||
/// @dev It does a supply adjustment. | ||
uint256 amountUint256 = unwrapRequest.amount * (10 ** (ERC20_TOKEN.decimals() - decimals())); | ||
|
||
ERC20_TOKEN.safeTransfer(unwrapRequest.account, amountUint256); | ||
|
||
emit Unwrap(unwrapRequest.account, unwrapRequest.amount); | ||
} else { | ||
emit UnwrapFail(unwrapRequest.account, unwrapRequest.amount); | ||
} | ||
|
||
delete isAccountRestricted[unwrapRequest.account]; | ||
} | ||
|
||
function _canTransferOrMint(address account) internal virtual { | ||
if (isAccountRestricted[account]) { | ||
revert CannotTransferOrMint(); | ||
} | ||
} | ||
|
||
function _transferNoEvent( | ||
address from, | ||
address to, | ||
euint64 amount, | ||
ebool isTransferable | ||
) internal virtual override { | ||
_canTransferOrMint(from); | ||
super._transferNoEvent(from, to, amount, isTransferable); | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters