Skip to content

Commit

Permalink
refactor: support unlock if callback fails
Browse files Browse the repository at this point in the history
  • Loading branch information
PacificYield committed Dec 23, 2024
1 parent 3fbc515 commit be196b9
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 32 deletions.
27 changes: 17 additions & 10 deletions contracts/token/ERC20/ConfidentialERC20Wrapped.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ 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 { ReentrancyGuardTransient } from "@openzeppelin/contracts/utils/ReentrancyGuardTransient.sol";

import "fhevm/lib/TFHE.sol";
import "fhevm/gateway/GatewayCaller.sol";
Expand All @@ -18,7 +19,12 @@ import { ConfidentialERC20 } from "./ConfidentialERC20.sol";
* 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 ConfidentialERC20Wrapped is ConfidentialERC20, IConfidentialERC20Wrapped, GatewayCaller {
abstract contract ConfidentialERC20Wrapped is
ConfidentialERC20,
IConfidentialERC20Wrapped,
ReentrancyGuardTransient,
GatewayCaller
{
using SafeERC20 for IERC20Metadata;

/// @notice Returned if the maximum decryption delay is higher than 1 day.
Expand Down Expand Up @@ -114,24 +120,25 @@ abstract contract ConfidentialERC20Wrapped is ConfidentialERC20, IConfidentialER
* @param requestId Request id.
* @param canUnwrap Whether it can be unwrapped.
*/
function callbackUnwrap(uint256 requestId, bool canUnwrap) public virtual onlyGateway {
function callbackUnwrap(uint256 requestId, bool canUnwrap) public virtual nonReentrant onlyGateway {
UnwrapRequest memory unwrapRequest = unwrapRequests[requestId];
delete unwrapRequests[requestId];

if (canUnwrap) {
_unsafeBurn(unwrapRequest.account, 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);
try ERC20_TOKEN.transfer(unwrapRequest.account, amountUint256) {
_unsafeBurn(unwrapRequest.account, unwrapRequest.amount);
_totalSupply -= unwrapRequest.amount;
emit Unwrap(unwrapRequest.account, unwrapRequest.amount);
} catch {
emit UnwrapFailTransferFail(unwrapRequest.account, unwrapRequest.amount);
}
} else {
emit UnwrapFail(unwrapRequest.account, unwrapRequest.amount);
emit UnwrapFailNotEnoughBalance(unwrapRequest.account, unwrapRequest.amount);
}

delete unwrapRequests[requestId];
delete isAccountRestricted[unwrapRequest.account];
}

Expand Down
34 changes: 16 additions & 18 deletions contracts/token/ERC20/ConfidentialWETH.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pragma solidity ^0.8.24;

import { ConfidentialERC20 } from "./ConfidentialERC20.sol";
import { ReentrancyGuardTransient } from "@openzeppelin/contracts/utils/ReentrancyGuardTransient.sol";

import "fhevm/lib/TFHE.sol";
import "fhevm/gateway/GatewayCaller.sol";
Expand All @@ -13,7 +14,12 @@ import { IConfidentialERC20Wrapped } from "./IConfidentialERC20Wrapped.sol";
* @notice This contract allows users to wrap/unwrap trustlessly
* ETH (or other native tokens) to ConfidentialERC20 tokens.
*/
abstract contract ConfidentialWETH is ConfidentialERC20, IConfidentialERC20Wrapped, GatewayCaller {
abstract contract ConfidentialWETH is
ConfidentialERC20,
IConfidentialERC20Wrapped,
ReentrancyGuardTransient,
GatewayCaller
{
/// @notice Returned if ETH transfer fails.
error ETHTransferFail();

Expand Down Expand Up @@ -44,13 +50,6 @@ abstract contract ConfidentialWETH is ConfidentialERC20, IConfidentialERC20Wrapp
}
}

/**
* @notice Fallback function calls wrap().
*/
fallback() external payable {
wrap();
}

/**
* @notice Receive function calls wrap().
*/
Expand Down Expand Up @@ -106,30 +105,29 @@ abstract contract ConfidentialWETH is ConfidentialERC20, IConfidentialERC20Wrapp
* @param requestId Request id.
* @param canUnwrap Whether it can be unwrapped.
*/
function callbackUnwrap(uint256 requestId, bool canUnwrap) public virtual onlyGateway {
function callbackUnwrap(uint256 requestId, bool canUnwrap) public virtual nonReentrant onlyGateway {
UnwrapRequest memory unwrapRequest = unwrapRequests[requestId];
delete unwrapRequests[requestId];

if (canUnwrap) {
_unsafeBurn(unwrapRequest.account, unwrapRequest.amount);
_totalSupply -= unwrapRequest.amount;

/// @dev It does a supply adjustment.
uint256 amountUint256 = unwrapRequest.amount * (10 ** (18 - decimals()));

/* solhint-disable avoid-call-value*/
/* solhint-disable avoid-low-level-calls*/
(bool callSuccess, ) = unwrapRequest.account.call{ value: amountUint256 }("");

if (!callSuccess) {
revert ETHTransferFail();
if (callSuccess) {
_unsafeBurn(unwrapRequest.account, unwrapRequest.amount);
_totalSupply -= unwrapRequest.amount;
emit Unwrap(unwrapRequest.account, unwrapRequest.amount);
} else {
emit UnwrapFailTransferFail(unwrapRequest.account, unwrapRequest.amount);
}

emit Unwrap(unwrapRequest.account, unwrapRequest.amount);
} else {
emit UnwrapFail(unwrapRequest.account, unwrapRequest.amount);
emit UnwrapFailNotEnoughBalance(unwrapRequest.account, unwrapRequest.amount);
}

delete unwrapRequests[requestId];
delete isAccountRestricted[unwrapRequest.account];
}

Expand Down
7 changes: 5 additions & 2 deletions contracts/token/ERC20/IConfidentialERC20Wrapped.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ interface IConfidentialERC20Wrapped {
/// @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 if unwrap fails due to lack of funds.
event UnwrapFailNotEnoughBalance(address account, uint64 amount);

/// @notice Emitted if unwrap fails due to fail transfer.
event UnwrapFailTransferFail(address account, uint64 amount);

/// @notice Emitted when token is wrapped.
event Wrap(address indexed to, uint64 amount);
Expand Down
1 change: 0 additions & 1 deletion hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ const config: HardhatUserConfig = {
},
networks: {
hardhat: {
gas: "auto",
accounts: {
count: 10,
mnemonic,
Expand Down
3 changes: 2 additions & 1 deletion test/governance/ConfidentialERC20Votes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,8 @@ describe("ConfidentialERC20Votes", function () {
);
});

it("number of checkpoints is incremented once per block, even when written multiple times in same block", async function () {
// @dev To run this test, it is required to add gas = "auto" in the hardhat config.
it.skip("number of checkpoints is incremented once per block, even when written multiple times in same block", async function () {
await network.provider.send("evm_setAutomine", [false]);
await network.provider.send("evm_setIntervalMining", [0]);

Expand Down

0 comments on commit be196b9

Please sign in to comment.