Skip to content

Commit

Permalink
Merge pull request #68 from zama-ai/updates-encrypted-erc20
Browse files Browse the repository at this point in the history
refactor: updates EncryptedERC20 and other versions inheriting
  • Loading branch information
PacificYield authored Nov 29, 2024
2 parents 13fafe3 + 2238f15 commit 1ed27eb
Show file tree
Hide file tree
Showing 11 changed files with 571 additions and 128 deletions.
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pnpm --no-install prettier:check
pnpm --no-install lint:sol
79 changes: 79 additions & 0 deletions contracts/test/utils/TestEncryptedErrors.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.24;

import "fhevm/lib/TFHE.sol";
import { EncryptedErrors } from "../../utils/EncryptedErrors.sol";
import { MockZamaFHEVMConfig } from "fhevm/config/ZamaFHEVMConfig.sol";

contract TestEncryptedErrors is MockZamaFHEVMConfig, EncryptedErrors {
constructor(uint8 totalNumberErrorCodes_) EncryptedErrors(totalNumberErrorCodes_) {
for (uint8 i; i <= totalNumberErrorCodes_; i++) {
/// @dev It is not possible to access the _errorCodeDefinitions since it is private.
TFHE.allow(TFHE.asEuint8(i), msg.sender);
}
}

function errorChangeIf(
einput encryptedCondition,
einput encryptedErrorCode,
bytes calldata inputProof,
uint8 indexCode
) external returns (euint8 newErrorCode) {
ebool condition = TFHE.asEbool(encryptedCondition, inputProof);
euint8 errorCode = TFHE.asEuint8(encryptedErrorCode, inputProof);
newErrorCode = _errorChangeIf(condition, indexCode, errorCode);
_errorSave(newErrorCode);
TFHE.allow(newErrorCode, msg.sender);
}

function errorChangeIfNot(
einput encryptedCondition,
einput encryptedErrorCode,
bytes calldata inputProof,
uint8 indexCode
) external returns (euint8 newErrorCode) {
ebool condition = TFHE.asEbool(encryptedCondition, inputProof);
euint8 errorCode = TFHE.asEuint8(encryptedErrorCode, inputProof);
newErrorCode = _errorChangeIfNot(condition, indexCode, errorCode);
_errorSave(newErrorCode);
TFHE.allow(newErrorCode, msg.sender);
}

function errorDefineIf(
einput encryptedCondition,
bytes calldata inputProof,
uint8 indexCode
) external returns (euint8 errorCode) {
ebool condition = TFHE.asEbool(encryptedCondition, inputProof);
errorCode = _errorDefineIf(condition, indexCode);
_errorSave(errorCode);
TFHE.allow(errorCode, msg.sender);
}

function errorDefineIfNot(
einput encryptedCondition,
bytes calldata inputProof,
uint8 indexCode
) external returns (euint8 errorCode) {
ebool condition = TFHE.asEbool(encryptedCondition, inputProof);
errorCode = _errorDefineIfNot(condition, indexCode);
_errorSave(errorCode);
TFHE.allow(errorCode, msg.sender);
}

function errorGetCodeDefinition(uint8 indexCodeDefinition) external view returns (euint8 errorCode) {
errorCode = _errorGetCodeDefinition(indexCodeDefinition);
}

function errorGetCodeEmitted(uint256 errorId) external view returns (euint8 errorCode) {
errorCode = _errorGetCodeEmitted(errorId);
}

function errorGetCounter() external view returns (uint256 countErrors) {
countErrors = _errorGetCounter();
}

function errorGetNumCodesDefined() external view returns (uint8 totalNumberErrorCodes) {
totalNumberErrorCodes = _errorGetNumCodesDefined();
}
}
23 changes: 14 additions & 9 deletions contracts/token/ERC20/EncryptedERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
pragma solidity ^0.8.24;

import "fhevm/lib/TFHE.sol";

import { IERC20Errors } from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
import { IEncryptedERC20 } from "./IEncryptedERC20.sol";
import { TFHEErrors } from "../../utils/TFHEErrors.sol";

/**
* @title EncryptedERC20
Expand All @@ -12,10 +15,9 @@ import { IEncryptedERC20 } from "./IEncryptedERC20.sol";
* and setting allowances, but uses encrypted data types.
* The total supply is not encrypted.
*/
abstract contract EncryptedERC20 is IEncryptedERC20 {
abstract contract EncryptedERC20 is IEncryptedERC20, IERC20Errors, TFHEErrors {
/// @notice used as a placehoder in Approval and Transfer events to comply with the official EIP20
uint256 internal constant _PLACEHOLDER = type(uint256).max;

/// @notice Total supply.
uint64 internal _totalSupply;

Expand All @@ -31,11 +33,6 @@ abstract contract EncryptedERC20 is IEncryptedERC20 {
/// @notice A mapping of the form mapping(account => mapping(spender => allowance)).
mapping(address account => mapping(address spender => euint64 allowance)) internal _allowances;

/**
* @notice Error when the `sender` is not allowed to access a value.
*/
error TFHESenderNotAllowed();

/**
* @param name_ Name of the token.
* @param symbol_ Symbol.
Expand Down Expand Up @@ -151,6 +148,14 @@ abstract contract EncryptedERC20 is IEncryptedERC20 {
}

function _approve(address owner, address spender, euint64 amount) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(owner);
}

if (spender == address(0)) {
revert ERC20InvalidSpender(spender);
}

_allowances[owner][spender] = amount;
TFHE.allowThis(amount);
TFHE.allow(amount, owner);
Expand Down Expand Up @@ -184,11 +189,11 @@ abstract contract EncryptedERC20 is IEncryptedERC20 {

function _transferNoEvent(address from, address to, euint64 amount, ebool isTransferable) internal virtual {
if (from == address(0)) {
revert SenderAddressNull();
revert ERC20InvalidSender(from);
}

if (to == address(0)) {
revert ReceiverAddressNull();
revert ERC20InvalidReceiver(to);
}

/// Add to the balance of `to` and subract from the balance of `from`.
Expand Down
10 changes: 0 additions & 10 deletions contracts/token/ERC20/IEncryptedERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,6 @@ interface IEncryptedERC20 {
*/
event Transfer(address indexed from, address indexed to, uint256 errorId);

/**
* @notice Returned when receiver is address(0).
*/
error ReceiverAddressNull();

/**
* @notice Returned when sender is address(0).
*/
error SenderAddressNull();

/**
* @notice Sets the `encryptedAmount` as the allowance of `spender` over the caller's tokens.
*/
Expand Down
39 changes: 23 additions & 16 deletions contracts/token/ERC20/extensions/EncryptedERC20WithErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ abstract contract EncryptedERC20WithErrors is EncryptedERC20, EncryptedErrors {
}

/**
* @param name_ Name of the token.
* @param symbol_ Symbol.
* @param name_ Name of the token.
* @param symbol_ Symbol.
*/
constructor(
string memory name_,
Expand All @@ -43,7 +43,7 @@ abstract contract EncryptedERC20WithErrors is EncryptedERC20, EncryptedErrors {
*/
function transfer(address to, euint64 amount) public virtual override returns (bool) {
_isSenderAllowedForAmount(amount);
/// Check whether the owner has enough tokens.
/// @dev Check whether the owner has enough tokens.
ebool canTransfer = TFHE.le(amount, _balances[msg.sender]);
euint8 errorCode = _errorDefineIfNot(canTransfer, uint8(ErrorCodes.UNSUFFICIENT_BALANCE));
_errorSave(errorCode);
Expand All @@ -53,15 +53,6 @@ abstract contract EncryptedERC20WithErrors is EncryptedERC20, EncryptedErrors {
return true;
}

function getErrorCodeForTransferId(uint256 transferId) public view virtual returns (euint8) {
return _errorGetCodeEmitted(transferId);
}

function _transfer(address from, address to, euint64 amount, ebool isTransferable) internal override {
_transferNoEvent(from, to, amount, isTransferable);
emit Transfer(from, to, _errorGetCounter() - 1);
}

/**
* @notice See {IEncryptedERC20-transferFrom}.
*/
Expand All @@ -73,21 +64,37 @@ abstract contract EncryptedERC20WithErrors is EncryptedERC20, EncryptedErrors {
return true;
}

/**
* @notice Returns the error for a transfer id.
* @param transferId Transfer id. It can read from the `Transfer` event.
* @return errorCode Encrypted error code.
*/
function getErrorCodeForTransferId(uint256 transferId) public view virtual returns (euint8 errorCode) {
errorCode = _errorGetCodeEmitted(transferId);
}

function _transfer(address from, address to, euint64 amount, ebool isTransferable) internal override {
_transferNoEvent(from, to, amount, isTransferable);
/// @dev It was incremented in _saveError.
emit Transfer(from, to, _errorGetCounter() - 1);
}

function _updateAllowance(
address owner,
address spender,
euint64 amount
) internal virtual override returns (ebool isTransferable) {
euint64 currentAllowance = _allowance(owner, spender);
/// Make sure sure the allowance suffices.
/// @dev It checks whether the allowance suffices.
ebool allowedTransfer = TFHE.le(amount, currentAllowance);
euint8 errorCode = _errorDefineIfNot(allowedTransfer, uint8(ErrorCodes.UNSUFFICIENT_APPROVAL));
/// Make sure the owner has enough tokens.
/// @dev It checks that the owner has enough tokens.
ebool canTransfer = TFHE.le(amount, _balances[owner]);
ebool isNotTransferableButIsApproved = TFHE.and(TFHE.not(canTransfer), allowedTransfer);
errorCode = _errorChangeIf(
isNotTransferableButIsApproved, // should indeed check that spender is approved to not leak information
// on balance of `from` to unauthorized spender via calling reencryptTransferError afterwards
isNotTransferableButIsApproved,
/// @dev Should indeed check that spender is approved to not leak information.
/// on balance of `from` to unauthorized spender via calling reencryptTransferError afterwards.
uint8(ErrorCodes.UNSUFFICIENT_BALANCE),
errorCode
);
Expand Down
Loading

0 comments on commit 1ed27eb

Please sign in to comment.