diff --git a/contracts/Events.sol b/contracts/Events.sol index a9edc67..00201ad 100644 --- a/contracts/Events.sol +++ b/contracts/Events.sol @@ -8,7 +8,8 @@ import "./Operations.sol"; interface Events { event NewToken(address tokenAddr, uint16 tokenId); event NewTradingPair(uint16 baseTokenId, uint16 quoteTokenId); - event Deposit(uint16 tokenId, address to, uint256 amount); // emit tokenId or tokenAddr? + event RegisterUser(address ethAddr, uint16 userId, string bjjPubkey); + event Deposit(uint16 tokenId, string to, uint256 amount); // emit tokenId or tokenAddr? event Withdraw(uint16 tokenId, address to, uint256 amount); // emit tokenId or tokenAddr? /// @notice New priority request event. Emitted when a request is placed into mapping diff --git a/contracts/Fluidex.sol b/contracts/Fluidex.sol index aa823ca..faf2191 100644 --- a/contracts/Fluidex.sol +++ b/contracts/Fluidex.sol @@ -14,6 +14,7 @@ import "hardhat/console.sol"; import "./Utils.sol"; import "./Storage.sol"; +import "./UserInfo.sol"; import "./Config.sol"; import "./Events.sol"; @@ -25,11 +26,16 @@ contract Fluidex is ReentrancyGuard, Storage, Config, Events, Ownable { using SafeMath for uint256; uint16 constant TOKEN_NUM_LIMIT = 65535; + uint16 constant USER_NUM_LIMIT = 65535; uint16 public tokenNum; mapping(uint16 => address) public tokenIdToAddr; mapping(address => uint16) public tokenAddrToId; + uint16 public userNum; + mapping(uint16 => UserInfo) public userIdToUserInfo; + mapping(string => uint16) public userBjjPubkeyToUserId; + function initialize() external {} function addToken(address tokenAddr) @@ -58,9 +64,24 @@ contract Fluidex is ReentrancyGuard, Storage, Config, Events, Ownable { emit NewTradingPair(baseTokenId, quoteTokenId); } + // TODO: check signature? + function registerUser(address ethAddr, string memory bjjPubkey) internal { + userNum++; + require(userBjjPubkeyToUserId[bjjPubkey] == 0, "user existed"); + require(userNum < USER_NUM_LIMIT, "user num limit reached"); + + uint16 userId = userNum; + userIdToUserInfo[userId] = UserInfo({ + ethAddr: ethAddr, + bjjPubkey: bjjPubkey + }); + userBjjPubkeyToUserId[bjjPubkey] = userId; + emit RegisterUser(ethAddr, userId, bjjPubkey); + } + // 0 tokenId means native ETH coin // TODO: zkSync uses uint128 for amount - function registerDeposit(uint16 tokenId, address to, uint256 amount) internal { + function registerDeposit(uint16 tokenId, string memory to, uint256 amount) internal { // Priority Queue request Operations.Deposit memory op = Operations.Deposit({ @@ -75,10 +96,15 @@ contract Fluidex is ReentrancyGuard, Storage, Config, Events, Ownable { } /// @param to the L2 address of the deposit target. - // TODO: change to L2 address - function depositETH(address to) external payable { + function depositETH( + string calldata to // L2 bjjPubkey + ) external payable { // You must `approve` the allowance before calling this method - require(to != address(0), "invalid address"); + + if (userBjjPubkeyToUserId[to] == 0) { + registerUser(msg.sender, to); + } + // 0 tokenId means native ETH coin registerDeposit(0, to, msg.value); } @@ -87,13 +113,18 @@ contract Fluidex is ReentrancyGuard, Storage, Config, Events, Ownable { /// @param amount the deposit amount. function depositERC20( IERC20 token, - address to, // TODO: change to L2 address + string calldata to, // L2 bjjPubkey uint128 amount ) external nonReentrant { // You must `approve` the allowance before calling this method - require(to != address(0), "invalid address"); + uint16 tokenId = tokenAddrToId[address(token)]; require(tokenId != 0, "invalid token"); + + if (userBjjPubkeyToUserId[to] == 0) { + registerUser(msg.sender, to); + } + uint256 balanceBeforeDeposit = token.balanceOf(address(this)); token.safeTransferFrom(msg.sender, address(this), amount); uint256 balanceAfterDeposit = token.balanceOf(address(this)); diff --git a/contracts/Operations.sol b/contracts/Operations.sol index 6750300..d360c4d 100644 --- a/contracts/Operations.sol +++ b/contracts/Operations.sol @@ -17,7 +17,8 @@ library Operations { uint32 accountId; uint16 tokenId; uint256 amount; // TODO: zkSync uses uint128 for amount - address owner; + // address owner; + string owner; } /// Serialize deposit pubdata diff --git a/contracts/UserInfo.sol b/contracts/UserInfo.sol new file mode 100644 index 0000000..5ae49a0 --- /dev/null +++ b/contracts/UserInfo.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.6.0 <0.8.0; + +/// @title Fluidex userinfo contract +struct UserInfo { + address ethAddr; + string bjjPubkey; +} diff --git a/test/token.js b/test/token.js index d861a7c..b396aa7 100644 --- a/test/token.js +++ b/test/token.js @@ -1,4 +1,5 @@ const { expect } = require("chai"); +const { randomBytes } = require("@ethersproject/random"); describe("Fluidex", () => { let fluidex; @@ -7,6 +8,7 @@ describe("Fluidex", () => { let senderAddr; let acc2; let acc2addr; + const mockBjj = "randomBytes(32)"; const initialBalance = 1000; const decimal = 2; @@ -50,10 +52,11 @@ describe("Fluidex", () => { await expect( fluidex .connect(acc2) - .depositERC20(erc20Mock.address, acc2addr, depositAmount) + .depositERC20(erc20Mock.address, mockBjj, depositAmount) ) .to.emit(fluidex, "Deposit") - .withArgs(tokenId, acc2addr, depositAmount); + .withArgs(tokenId, mockBjj, depositAmount); + await expect( fluidex.withdrawERC20(erc20Mock.address, acc2addr, withdrawAmount) ) @@ -69,10 +72,10 @@ describe("Fluidex", () => { await expect( fluidex .connect(acc2) - .depositETH(acc2addr, {value: depositAmount}) + .depositETH(mockBjj, {value: depositAmount}) ) .to.emit(fluidex, "Deposit") - .withArgs(0, acc2addr, depositAmount); + .withArgs(0, mockBjj, depositAmount); await expect( fluidex.withdrawETH(acc2addr, withdrawAmount)