diff --git a/contracts/FluiDex.sol b/contracts/FluiDex.sol index 87e87bf..c8f8907 100644 --- a/contracts/FluiDex.sol +++ b/contracts/FluiDex.sol @@ -9,37 +9,18 @@ import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "hardhat/console.sol"; // for debugging -import "./Events.sol"; import "./IFluiDex.sol"; import "./IVerifier.sol"; /** * @title FluiDexDemo */ -contract FluiDexDemo is - AccessControl, - Events, - IFluiDex, - Ownable, - ReentrancyGuard -{ +contract FluiDexDemo is AccessControl, IFluiDex, Ownable, ReentrancyGuard { using SafeERC20 for IERC20; bytes32 public constant PLUGIN_ADMIN_ROLE = keccak256("PLUGIN_ADMIN_ROLE"); - bytes32 public constant TOKEN_ADMIN_ROLE = keccak256("TOKEN_ADMIN_ROLE"); bytes32 public constant DELEGATE_ROLE = keccak256("DELEGATE_ROLE"); - enum BlockState { - Empty, - Committed, - Verified - } - - struct UserInfo { - address ethAddr; - bytes32 bjjPubkey; - } - /// hard limit for ERC20 tokens uint16 constant TOKEN_NUM_LIMIT = 65535; /// hard limit for users @@ -66,10 +47,8 @@ contract FluiDexDemo is GENESIS_ROOT = _genesis_root; verifier = _verifier; _setupRole(DEFAULT_ADMIN_ROLE, msg.sender); - _setRoleAdmin(TOKEN_ADMIN_ROLE, DEFAULT_ADMIN_ROLE); _setRoleAdmin(PLUGIN_ADMIN_ROLE, DEFAULT_ADMIN_ROLE); _setRoleAdmin(DELEGATE_ROLE, DEFAULT_ADMIN_ROLE); - grantRole(TOKEN_ADMIN_ROLE, msg.sender); grantRole(PLUGIN_ADMIN_ROLE, msg.sender); grantRole(DELEGATE_ROLE, msg.sender); } @@ -91,14 +70,13 @@ contract FluiDexDemo is * @param tokenAddr the ERC20 token address * @return the new ERC20 token tokenId */ - function addToken(address origin, address tokenAddr) + function addToken(address tokenAddr) external override nonReentrant onlyRole(DELEGATE_ROLE) returns (uint16) { - require(hasRole(TOKEN_ADMIN_ROLE, origin)); require(tokenAddrToId[tokenAddr] == 0, "token existed"); tokenNum++; require(tokenNum < TOKEN_NUM_LIMIT, "token num limit reached"); @@ -107,40 +85,36 @@ contract FluiDexDemo is tokenIdToAddr[tokenId] = tokenAddr; tokenAddrToId[tokenAddr] = tokenId; - emit NewToken(origin, tokenAddr, tokenId); return tokenId; } /** * @param to the L2 address (bjjPubkey) of the deposit target. */ - function depositETH(address origin, bytes32 to) - external - payable - override - orCreateUser(origin, to) - onlyRole(DELEGATE_ROLE) - { - emit Deposit(ETH_ID, to, msg.value); - } + function depositETH(bytes32 to) + external + payable + override + onlyRole(DELEGATE_ROLE) + {} /** - * @param to the L2 address (bjjPubkey) of the deposit target. * @param amount the deposit amount. */ - function depositERC20( - address origin, - IERC20 token, - bytes32 to, - uint128 amount - ) external override nonReentrant tokenExist(token) orCreateUser(origin, to) { - uint16 tokenId = tokenAddrToId[address(token)]; + function depositERC20(IERC20 token, uint256 amount) + external + override + nonReentrant + tokenExist(token) + onlyRole(DELEGATE_ROLE) + returns (uint16 tokenId, uint256 realAmount) + { + tokenId = tokenAddrToId[address(token)]; uint256 balanceBeforeDeposit = token.balanceOf(address(this)); - token.safeTransferFrom(origin, address(this), amount); + token.safeTransferFrom(msg.sender, address(this), amount); uint256 balanceAfterDeposit = token.balanceOf(address(this)); - uint256 realAmount = balanceAfterDeposit - balanceBeforeDeposit; - emit Deposit(tokenId, to, realAmount); + realAmount = balanceAfterDeposit - balanceBeforeDeposit; } /** @@ -148,23 +122,28 @@ contract FluiDexDemo is * @param ethAddr the L1 address * @param bjjPubkey the L2 address (bjjPubkey) */ - function registerUser(address ethAddr, bytes32 bjjPubkey) public { + function registerUser(address ethAddr, bytes32 bjjPubkey) + external + override + onlyRole(DELEGATE_ROLE) + returns (uint16 userId) + { require(userBjjPubkeyToUserId[bjjPubkey] == 0, "user existed"); userNum++; require(userNum < USER_NUM_LIMIT, "user num limit reached"); - uint16 userId = userNum; + userId = userNum; userIdToUserInfo[userId] = UserInfo({ ethAddr: ethAddr, bjjPubkey: bjjPubkey }); userBjjPubkeyToUserId[bjjPubkey] = userId; - emit RegisterUser(ethAddr, userId, bjjPubkey); } function getBlockStateByBlockId(uint256 _block_id) - public + external view + override returns (BlockState) { return block_states[_block_id]; @@ -230,13 +209,54 @@ contract FluiDexDemo is } /** - * @dev create a user if not exist - * @param bjjPubkey the L2 address (bjjPubkey) + * @param tokenId tokenId + * @return tokenAddr */ - modifier orCreateUser(address origin, bytes32 bjjPubkey) { - if (userBjjPubkeyToUserId[bjjPubkey] == 0) { - registerUser(origin, bjjPubkey); - } - _; + function getTokenAddr(uint16 tokenId) + external + view + override + returns (address) + { + return tokenIdToAddr[tokenId]; + } + + /** + * @param tokenAddr tokenAddr + * @return tokenId + */ + function getTokenId(address tokenAddr) + external + view + override + returns (uint16) + { + return tokenAddrToId[tokenAddr]; + } + + /** + * @param userId userId + * @return UserInfo + */ + function getUserInfo(uint16 userId) + external + view + override + returns (UserInfo memory) + { + return userIdToUserInfo[userId]; + } + + /** + * @param bjjPubkey user's pubkey + * @return userId, returns 0 if not exist + */ + function getUserId(bytes32 bjjPubkey) + external + view + override + returns (uint16) + { + return userBjjPubkeyToUserId[bjjPubkey]; } } diff --git a/contracts/FluiDexDelegate.sol b/contracts/FluiDexDelegate.sol index d664914..955d09e 100644 --- a/contracts/FluiDexDelegate.sol +++ b/contracts/FluiDexDelegate.sol @@ -4,16 +4,31 @@ pragma solidity ^0.8.0; import "@openzeppelin/contracts/access/AccessControl.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "./Events.sol"; import "./IFluiDex.sol"; import "./IFluiDexDelegate.sol"; -contract FluiDexDelegate is AccessControl, IFluiDexDelegate, ReentrancyGuard { +contract FluiDexDelegate is + AccessControl, + Events, + IFluiDexDelegate, + ReentrancyGuard +{ + using SafeERC20 for IERC20; + + bytes32 public constant TOKEN_ADMIN_ROLE = keccak256("TOKEN_ADMIN_ROLE"); + + /// use 0 representing ETH in tokenId + uint16 constant ETH_ID = 0; IFluiDex target; event TargetChange(IFluiDex prev, IFluiDex now); constructor(IFluiDex _target) { _setupRole(DEFAULT_ADMIN_ROLE, msg.sender); + _setRoleAdmin(TOKEN_ADMIN_ROLE, DEFAULT_ADMIN_ROLE); + grantRole(TOKEN_ADMIN_ROLE, msg.sender); target = _target; } @@ -32,21 +47,29 @@ contract FluiDexDelegate is AccessControl, IFluiDexDelegate, ReentrancyGuard { /** * @notice request to add a new ERC20 token * @param tokenAddr the ERC20 token address - * @return the new ERC20 token tokenId + * @return tokenId the new ERC20 token tokenId */ - function addToken(address tokenAddr) - external + function addToken(address tokenAddr) + external override - returns (uint16) + onlyRole(TOKEN_ADMIN_ROLE) + returns (uint16 tokenId) { - return target.addToken(msg.sender, tokenAddr); + tokenId = target.addToken(tokenAddr); + emit NewToken(msg.sender, tokenAddr, tokenId); } /** * @param to the L2 address (bjjPubkey) of the deposit target. */ - function depositETH(bytes32 to) external payable override { - target.depositETH{value: msg.value}(msg.sender, to); + function depositETH(bytes32 to) + external + payable + override + orCreateUser(msg.sender, to) + { + target.depositETH{value: msg.value}(to); + emit Deposit(ETH_ID, to, msg.value); } /** @@ -56,9 +79,19 @@ contract FluiDexDelegate is AccessControl, IFluiDexDelegate, ReentrancyGuard { function depositERC20( IERC20 token, bytes32 to, - uint128 amount + uint256 amount ) external override { - target.depositERC20(msg.sender, token, to, amount); + uint256 balanceBeforeDeposit = token.balanceOf(address(this)); + token.safeTransferFrom(msg.sender, address(this), amount); + uint256 balanceAfterDeposit = token.balanceOf(address(this)); + uint256 realAmount = balanceAfterDeposit - balanceBeforeDeposit; + token.safeIncreaseAllowance(address(target), realAmount); + + (uint16 tokenId, uint256 finalAmount) = target.depositERC20( + token, + realAmount + ); + emit Deposit(tokenId, to, finalAmount); } /** @@ -75,4 +108,16 @@ contract FluiDexDelegate is AccessControl, IFluiDexDelegate, ReentrancyGuard { ) external override returns (bool) { return target.submitBlock(_block_id, _public_inputs, _serialized_proof); } + + /** + * @dev create a user if not exist + * @param bjjPubkey the L2 address (bjjPubkey) + */ + modifier orCreateUser(address origin, bytes32 bjjPubkey) { + if (target.getUserId(bjjPubkey) == 0) { + uint16 userId = target.registerUser(origin, bjjPubkey); + emit RegisterUser(msg.sender, userId, bjjPubkey); + } + _; + } } diff --git a/contracts/IFluiDex.sol b/contracts/IFluiDex.sol index 312ee7a..96f1826 100644 --- a/contracts/IFluiDex.sol +++ b/contracts/IFluiDex.sol @@ -5,28 +5,39 @@ pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface IFluiDex { + enum BlockState { + Empty, + Committed, + Verified + } + + struct UserInfo { + address ethAddr; + bytes32 bjjPubkey; + } + /** * @notice request to add a new ERC20 token * @param tokenAddr the ERC20 token address * @return the new ERC20 token tokenId */ - function addToken(address origin, address tokenAddr) external returns (uint16); + function addToken(address tokenAddr) external returns (uint16); /** * @param to the L2 address (bjjPubkey) of the deposit target. */ - function depositETH(address origin, bytes32 to) external payable; + function depositETH(bytes32 to) external payable; /** - * @param to the L2 address (bjjPubkey) of the deposit target. * @param amount the deposit amount. */ - function depositERC20( - address origin, - IERC20 token, - bytes32 to, - uint128 amount - ) external; + function depositERC20(IERC20 token, uint256 amount) + external + returns (uint16 tokenId, uint256 realAmount); + + function getBlockStateByBlockId(uint256 _block_id) + external + returns (BlockState); /** * @notice request to submit a new l2 block @@ -40,4 +51,37 @@ interface IFluiDex { uint256[] memory _public_inputs, uint256[] memory _serialized_proof ) external returns (bool); + + /** + * @dev this won't verify the pubkey + * @param ethAddr the L1 address + * @param bjjPubkey the L2 address (bjjPubkey) + */ + function registerUser(address ethAddr, bytes32 bjjPubkey) + external + returns (uint16 userId); + + /** + * @param tokenId tokenId + * @return tokenAddr + */ + function getTokenAddr(uint16 tokenId) external view returns (address); + + /** + * @param tokenAddr tokenAddr + * @return tokenId + */ + function getTokenId(address tokenAddr) external view returns (uint16); + + /** + * @param userId userId + * @return UserInfo + */ + function getUserInfo(uint16 userId) external view returns (UserInfo memory); + + /** + * @param bjjPubkey user's pubkey + * @return userId, returns 0 if not exist + */ + function getUserId(bytes32 bjjPubkey) external view returns (uint16); } diff --git a/contracts/IFluiDexDelegate.sol b/contracts/IFluiDexDelegate.sol index a4f62eb..23605f5 100644 --- a/contracts/IFluiDexDelegate.sol +++ b/contracts/IFluiDexDelegate.sol @@ -24,7 +24,7 @@ interface IFluiDexDelegate { function depositERC20( IERC20 token, bytes32 to, - uint128 amount + uint256 amount ) external; /**