-
Notifications
You must be signed in to change notification settings - Fork 0
/
CheetopiaStaking.sol
174 lines (155 loc) · 6.52 KB
/
CheetopiaStaking.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
//SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import "@openzeppelin/contracts/access/Ownable.sol";
//@GregTheDev
contract CheetopiaStaking is Ownable {
error NotStaked();
error TransferFailed();
address private immutable _CHEETOPIA;
mapping(address => mapping(uint256 => bool)) private _userStakingData;
constructor(address CHEETOPIA_) payable {
_CHEETOPIA = CHEETOPIA_;
}
function stake(uint256 tokenId) external {
assembly {
//Load free memory pointer
let ptr := mload(0x40)
//Get location of this address and token in _userStakingData
mstore(0x0, caller())
mstore(0x20, _userStakingData.slot)
mstore(0x20, keccak256(0x0, 0x40))
mstore(0x00, tokenId)
//Update _userStakingData
sstore(keccak256(0x0, 0x40), 0x1)
//Transfer nft from caller to contract
mstore(0x0, 0x23b872dd) //'transferFrom(address,address,uint256)' selector
mstore(0x20, caller()) //'from'
mstore(0x40, address()) //'to'
mstore(0x60, tokenId) //'tokenId'
//prettier-ignore
//If transfer fails, revert TransferFailed()
if iszero(call(gas(), sload(_CHEETOPIA.slot), 0x0, 0x1C, 0x64, returndatasize(), returndatasize())) {
mstore(0x0, 0x90b8ec18) //'TransferFailed()' selector
revert(0x1c, 0x04)
}
mstore(0x40, ptr) //Restore pointer
mstore(0x60, 0x0) //Restore zero slot
}
}
function stakeBatch(uint256[] calldata tokenIds) external {
assembly {
//Load free memory pointer
let ptr := mload(0x40)
//Cache _userStakingData location for this address.
mstore(0x0, caller())
mstore(0x20, _userStakingData.slot)
mstore(0x20, keccak256(0x0, 0x40))
mstore(ptr, 0x23b872dd) //"transferFrom(address,address,uint256)" selector
mstore(add(ptr, 0x20), caller()) //'from'
mstore(add(ptr, 0x40), address()) //'to'
let selectorOffset := add(ptr, 0x1C)
let idOffset := add(ptr, 0x60)
let n := calldatasize()
//prettier-ignore
for {let i := tokenIds.offset} lt(i, n) {i := add(i, 0x20)} {
let id := calldataload(i)
//Update _userStakingData
mstore(0x0, id)
sstore(keccak256(0x0, 0x40), 0x1)
//Transfer from caller to contract. If transfer fails revert.
mstore(idOffset, id)
//prettier-ignore
if iszero(call(gas(), sload(_CHEETOPIA.slot), 0x0, selectorOffset, 0x64, returndatasize(), returndatasize())) {
mstore(0x0, 0x90b8ec18) //store TransferFailed()
revert(0x1c, 0x04)
}
}
mstore(0x40, ptr) //Restore pointer
mstore(0x60, 0x0) //Restore zero slot
}
}
function unstake(uint256 tokenId) external {
assembly {
//Cache Free memory pointer
let ptr := mload(0x40)
//Cache _userStakingData location for this address.
mstore(0x0, caller())
mstore(0x20, _userStakingData.slot)
mstore(0x20, keccak256(0x0, 0x40))
mstore(0x0, tokenId)
let location := keccak256(0x0, 0x40)
//If not staked revert NotStaked()
if iszero(sload(location)) {
mstore(0x0, 0x039f2e18) //'NotStaked()' selector
revert(0x1c, 0x04)
}
//Update _userStakingData mapping
sstore(location, 0x0)
mstore(0x0, 0x23b872dd) //'transferFrom(address,address,uint256)' selector
mstore(0x20, address()) //'from'
mstore(0x40, caller()) //'to'
mstore(0x60, tokenId) //'tokenId'
//prettier-ignore
//If transfer fails, revert TransferFailed()
if iszero(call(gas(), sload(_CHEETOPIA.slot), 0x0, 0x1C, 0x64, returndatasize(), returndatasize())) {
mstore(0x0, 0x90b8ec18) //'TransferFailed()' selector
revert(0x1c, 0x04)
}
mstore(0x40, ptr) //Restore pointer
mstore(0x60, 0x0) //Restore zero slot
}
}
function unstakeBatch(uint256[] calldata tokenIds) external {
assembly {
//Cache slot in _userStakingData for this address
mstore(0x0, caller())
mstore(0x20, _userStakingData.slot)
mstore(0x20, keccak256(0x0, 0x40))
//Get free memory pointer
let ptr := mload(0x40)
mstore(ptr, 0x23b872dd) //"transferFrom(address,address,uint256)" selector
mstore(add(ptr, 0x20), address()) //'from'
mstore(add(ptr, 0x40), caller()) //'to'
let selectorOffset := add(ptr, 0x1C)
let idOffset := add(ptr, 0x60)
let n := calldatasize()
//prettier-ignore
for {let i := tokenIds.offset} lt(i, n) {i := add(i, 0x20)} {
let id := calldataload(i)
//Store complete location of tokenId in userStakingData.
mstore(0x0, id)
let location := keccak256(0x0, 0x40)
//If not staked revert.
if iszero(sload(location)) {
mstore(0x0, 0x039f2e18) //store NotStaked() selector
revert(0x1c, 0x04)
}
//Update _userStakingData map
sstore(location, 0x0)
mstore(idOffset, id) //'tokenId'
//prettier-ignore
//If Transfer fails revert TransferFailed()
if iszero(call(gas(), sload(_CHEETOPIA.slot), 0x0, selectorOffset, 0x64, returndatasize(), returndatasize())) {
//prettier-ignore
mstore(0x0, 0x90b8ec18) //store TransferFailed() selector
revert(0x1c, 0x04)
}
}
mstore(0x40, ptr) //Restore free memory pointer
mstore(0x60, 0x0) //Restore zero slot
}
}
function setCHEETOPIA(address CHEETOPIA_) external onlyOwner {
_CHEETOPIA = CHEETOPIA_;
}
function CHEETOPIA() external view returns (address) {
return _CHEETOPIA;
}
function userStakingData(address staker, uint256 tokenId)
external
view
returns (bool)
{
return _userStakingData[staker][tokenId];
}
}