Skip to content

Commit 48ed39f

Browse files
committed
add zksync task
1 parent e709f7c commit 48ed39f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+2218
-490
lines changed
Lines changed: 398 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,398 @@
1+
//SPDX-License-Identifier: UNLICENSED
2+
pragma solidity ^0.8.0;
3+
4+
abstract contract Ownable {
5+
event OwnershipTransferred(address indexed user, address indexed newOwner);
6+
7+
error Unauthorized();
8+
error InvalidOwner();
9+
10+
address public owner;
11+
12+
modifier onlyOwner() virtual {
13+
if (msg.sender != owner) revert Unauthorized();
14+
15+
_;
16+
}
17+
18+
constructor(address _owner) {
19+
if (_owner == address(0)) revert InvalidOwner();
20+
21+
owner = _owner;
22+
23+
emit OwnershipTransferred(address(0), _owner);
24+
}
25+
26+
function transferOwnership(address _owner) public virtual onlyOwner {
27+
if (_owner == address(0)) revert InvalidOwner();
28+
29+
owner = _owner;
30+
31+
emit OwnershipTransferred(msg.sender, _owner);
32+
}
33+
34+
function revokeOwnership() public virtual onlyOwner {
35+
owner = address(0);
36+
37+
emit OwnershipTransferred(msg.sender, address(0));
38+
}
39+
}
40+
41+
abstract contract ERC721Receiver {
42+
function onERC721Received(
43+
address,
44+
address,
45+
uint256,
46+
bytes calldata
47+
) external virtual returns (bytes4) {
48+
return ERC721Receiver.onERC721Received.selector;
49+
}
50+
}
51+
52+
/// @notice ERC404
53+
/// A gas-efficient, mixed ERC20 / ERC721 implementation
54+
/// with native liquidity and fractionalization.
55+
///
56+
/// This is an experimental standard designed to integrate
57+
/// with pre-existing ERC20 / ERC721 support as smoothly as
58+
/// possible.
59+
///
60+
/// @dev In order to support full functionality of ERC20 and ERC721
61+
/// supply assumptions are made that slightly constraint usage.
62+
/// Ensure decimals are sufficiently large (standard 18 recommended)
63+
/// as ids are effectively encoded in the lowest range of amounts.
64+
///
65+
/// NFTs are spent on ERC20 functions in a FILO queue, this is by
66+
/// design.
67+
///
68+
abstract contract ERC404 is Ownable {
69+
// Events
70+
event ERC20Transfer(
71+
address indexed from,
72+
address indexed to,
73+
uint256 amount
74+
);
75+
event Approval(
76+
address indexed owner,
77+
address indexed spender,
78+
uint256 amount
79+
);
80+
event Transfer(
81+
address indexed from,
82+
address indexed to,
83+
uint256 indexed id
84+
);
85+
event ERC721Approval(
86+
address indexed owner,
87+
address indexed spender,
88+
uint256 indexed id
89+
);
90+
event ApprovalForAll(
91+
address indexed owner,
92+
address indexed operator,
93+
bool approved
94+
);
95+
96+
// Errors
97+
error NotFound();
98+
error AlreadyExists();
99+
error InvalidRecipient();
100+
error InvalidSender();
101+
error UnsafeRecipient();
102+
103+
// Metadata
104+
/// @dev Token name
105+
string public name;
106+
107+
/// @dev Token symbol
108+
string public symbol;
109+
110+
/// @dev Decimals for fractional representation
111+
uint8 public immutable decimals;
112+
113+
/// @dev Total supply in fractionalized representation
114+
uint256 public immutable totalSupply;
115+
116+
/// @dev Current mint counter, monotonically increasing to ensure accurate ownership
117+
uint256 public minted;
118+
119+
uint256 public ratio;
120+
121+
// Mappings
122+
/// @dev Balance of user in fractional representation
123+
mapping(address => uint256) public balanceOf;
124+
125+
/// @dev Allowance of user in fractional representation
126+
mapping(address => mapping(address => uint256)) public allowance;
127+
128+
/// @dev Approval in native representaion
129+
mapping(uint256 => address) public getApproved;
130+
131+
/// @dev Approval for all in native representation
132+
mapping(address => mapping(address => bool)) public isApprovedForAll;
133+
134+
/// @dev Owner of id in native representation
135+
mapping(uint256 => address) internal _ownerOf;
136+
137+
/// @dev Array of owned ids in native representation
138+
mapping(address => uint256[]) internal _owned;
139+
140+
/// @dev Tracks indices for the _owned mapping
141+
mapping(uint256 => uint256) internal _ownedIndex;
142+
143+
/// @dev Addresses whitelisted from minting / burning for gas savings (pairs, routers, etc)
144+
mapping(address => bool) public whitelist;
145+
146+
// Constructor
147+
constructor(
148+
string memory _name,
149+
string memory _symbol,
150+
uint8 _decimals,
151+
uint256 _totalNativeSupply,
152+
address _owner,
153+
uint256 _ratio
154+
) Ownable(_owner) {
155+
name = _name;
156+
symbol = _symbol;
157+
decimals = _decimals;
158+
ratio = _ratio;
159+
totalSupply = _totalNativeSupply * (10 ** (decimals+_ratio));
160+
}
161+
162+
/// @notice Initialization function to set pairs / etc
163+
/// saving gas by avoiding mint / burn on unnecessary targets
164+
function setWhitelist(address target, bool state) public onlyOwner {
165+
whitelist[target] = state;
166+
}
167+
168+
/// @notice Function to find owner of a given native token
169+
function ownerOf(uint256 id) public view virtual returns (address owner) {
170+
owner = _ownerOf[id];
171+
172+
if (owner == address(0)) {
173+
revert NotFound();
174+
}
175+
}
176+
177+
/// @notice tokenURI must be implemented by child contract
178+
function tokenURI(uint256 id) public view virtual returns (string memory);
179+
180+
/// @notice Function for token approvals
181+
/// @dev This function assumes id / native if amount less than or equal to current max id
182+
function approve(
183+
address spender,
184+
uint256 amountOrId
185+
) public virtual returns (bool) {
186+
if (amountOrId <= minted && amountOrId > 0) {
187+
address owner = _ownerOf[amountOrId];
188+
189+
if (msg.sender != owner && !isApprovedForAll[owner][msg.sender]) {
190+
revert Unauthorized();
191+
}
192+
193+
getApproved[amountOrId] = spender;
194+
195+
emit Approval(owner, spender, amountOrId);
196+
} else {
197+
allowance[msg.sender][spender] = amountOrId;
198+
199+
emit Approval(msg.sender, spender, amountOrId);
200+
}
201+
202+
return true;
203+
}
204+
205+
/// @notice Function native approvals
206+
function setApprovalForAll(address operator, bool approved) public virtual {
207+
isApprovedForAll[msg.sender][operator] = approved;
208+
209+
emit ApprovalForAll(msg.sender, operator, approved);
210+
}
211+
212+
/// @notice Function for mixed transfers
213+
/// @dev This function assumes id / native if amount less than or equal to current max id
214+
function transferFrom(
215+
address from,
216+
address to,
217+
uint256 amountOrId
218+
) public virtual {
219+
if (amountOrId <= minted) {
220+
if (from != _ownerOf[amountOrId]) {
221+
revert InvalidSender();
222+
}
223+
224+
if (to == address(0)) {
225+
revert InvalidRecipient();
226+
}
227+
228+
if (
229+
msg.sender != from &&
230+
!isApprovedForAll[from][msg.sender] &&
231+
msg.sender != getApproved[amountOrId]
232+
) {
233+
revert Unauthorized();
234+
}
235+
236+
balanceOf[from] -= _getUnit();
237+
238+
unchecked {
239+
balanceOf[to] += _getUnit();
240+
}
241+
242+
_ownerOf[amountOrId] = to;
243+
delete getApproved[amountOrId];
244+
245+
// update _owned for sender
246+
uint256 updatedId = _owned[from][_owned[from].length - 1];
247+
_owned[from][_ownedIndex[amountOrId]] = updatedId;
248+
// pop
249+
_owned[from].pop();
250+
// update index for the moved id
251+
_ownedIndex[updatedId] = _ownedIndex[amountOrId];
252+
// push token to to owned
253+
_owned[to].push(amountOrId);
254+
// update index for to owned
255+
_ownedIndex[amountOrId] = _owned[to].length - 1;
256+
257+
emit Transfer(from, to, amountOrId);
258+
emit ERC20Transfer(from, to, _getUnit());
259+
} else {
260+
uint256 allowed = allowance[from][msg.sender];
261+
262+
if (allowed != type(uint256).max)
263+
allowance[from][msg.sender] = allowed - amountOrId;
264+
265+
_transfer(from, to, amountOrId);
266+
}
267+
}
268+
269+
/// @notice Function for fractional transfers
270+
function transfer(
271+
address to,
272+
uint256 amount
273+
) public virtual returns (bool) {
274+
return _transfer(msg.sender, to, amount);
275+
}
276+
277+
/// @notice Function for native transfers with contract support
278+
function safeTransferFrom(
279+
address from,
280+
address to,
281+
uint256 id
282+
) public virtual {
283+
transferFrom(from, to, id);
284+
285+
if (
286+
to.code.length != 0 &&
287+
ERC721Receiver(to).onERC721Received(msg.sender, from, id, "") !=
288+
ERC721Receiver.onERC721Received.selector
289+
) {
290+
revert UnsafeRecipient();
291+
}
292+
}
293+
294+
/// @notice Function for native transfers with contract support and callback data
295+
function safeTransferFrom(
296+
address from,
297+
address to,
298+
uint256 id,
299+
bytes calldata data
300+
) public virtual {
301+
transferFrom(from, to, id);
302+
303+
if (
304+
to.code.length != 0 &&
305+
ERC721Receiver(to).onERC721Received(msg.sender, from, id, data) !=
306+
ERC721Receiver.onERC721Received.selector
307+
) {
308+
revert UnsafeRecipient();
309+
}
310+
}
311+
312+
/// @notice Internal function for fractional transfers
313+
function _transfer(
314+
address from,
315+
address to,
316+
uint256 amount
317+
) internal returns (bool) {
318+
uint256 unit = _getUnit();
319+
uint256 balanceBeforeSender = balanceOf[from];
320+
uint256 balanceBeforeReceiver = balanceOf[to];
321+
322+
balanceOf[from] -= amount;
323+
324+
unchecked {
325+
balanceOf[to] += amount;
326+
}
327+
328+
// Skip burn for certain addresses to save gas
329+
if (!whitelist[from]) {
330+
uint256 tokens_to_burn = (balanceBeforeSender / unit) -
331+
(balanceOf[from] / unit);
332+
for (uint256 i = 0; i < tokens_to_burn; i++) {
333+
_burn(from);
334+
}
335+
}
336+
337+
// Skip minting for certain addresses to save gas
338+
if (!whitelist[to]) {
339+
uint256 tokens_to_mint = (balanceOf[to] / unit) -
340+
(balanceBeforeReceiver / unit);
341+
for (uint256 i = 0; i < tokens_to_mint; i++) {
342+
_mint(to);
343+
}
344+
}
345+
346+
emit ERC20Transfer(from, to, amount);
347+
return true;
348+
}
349+
350+
// Internal utility logic
351+
function _getUnit() internal view returns (uint256) {
352+
return 10 ** (decimals + ratio);
353+
}
354+
355+
function _mint(address to) internal virtual {
356+
if (to == address(0)) {
357+
revert InvalidRecipient();
358+
}
359+
360+
unchecked {
361+
minted++;
362+
}
363+
364+
uint256 id = minted;
365+
366+
if (_ownerOf[id] != address(0)) {
367+
revert AlreadyExists();
368+
}
369+
370+
_ownerOf[id] = to;
371+
_owned[to].push(id);
372+
_ownedIndex[id] = _owned[to].length - 1;
373+
374+
emit Transfer(address(0), to, id);
375+
}
376+
377+
function _burn(address from) internal virtual {
378+
if (from == address(0)) {
379+
revert InvalidSender();
380+
}
381+
382+
uint256 id = _owned[from][_owned[from].length - 1];
383+
_owned[from].pop();
384+
delete _ownedIndex[id];
385+
delete _ownerOf[id];
386+
delete getApproved[id];
387+
388+
emit Transfer(from, address(0), id);
389+
}
390+
391+
function _setNameSymbol(
392+
string memory _name,
393+
string memory _symbol
394+
) internal {
395+
name = _name;
396+
symbol = _symbol;
397+
}
398+
}

0 commit comments

Comments
 (0)