@lukso/lsp26-contracts 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -0
- package/artifacts/ILSP26FollowerSystem.json +221 -0
- package/artifacts/LSP26FollowerSystem.json +248 -0
- package/contracts/ILSP26FollowerSystem.sol +75 -0
- package/contracts/Imports.sol +7 -0
- package/contracts/LSP26Constants.sol +10 -0
- package/contracts/LSP26Errors.sol +8 -0
- package/contracts/LSP26FollowerSystem.sol +164 -0
- package/contracts/mock/InfiniteLoopURD.sol +24 -0
- package/contracts/mock/ReturnBomb.sol +26 -0
- package/contracts/mock/RevertOnFollow.sol +20 -0
- package/contracts/mock/SelfDestructOnInterfaceCheck.sol +23 -0
- package/dist/index.cjs +12 -0
- package/dist/index.d.cts +7 -0
- package/dist/index.d.mts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.mjs +9 -0
- package/package.json +55 -0
- package/types/index.ts +588 -0
@@ -0,0 +1,164 @@
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
2
|
+
pragma solidity ^0.8.17;
|
3
|
+
|
4
|
+
// interfaces
|
5
|
+
import {ILSP26FollowerSystem} from "./ILSP26FollowerSystem.sol";
|
6
|
+
import {
|
7
|
+
ILSP1UniversalReceiver
|
8
|
+
} from "@lukso/lsp1-contracts/contracts/ILSP1UniversalReceiver.sol";
|
9
|
+
|
10
|
+
// libraries
|
11
|
+
import {
|
12
|
+
EnumerableSet
|
13
|
+
} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
|
14
|
+
import {
|
15
|
+
ERC165Checker
|
16
|
+
} from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
|
17
|
+
|
18
|
+
// constants
|
19
|
+
import {
|
20
|
+
_TYPEID_LSP26_FOLLOW,
|
21
|
+
_TYPEID_LSP26_UNFOLLOW
|
22
|
+
} from "./LSP26Constants.sol";
|
23
|
+
import {
|
24
|
+
_INTERFACEID_LSP1
|
25
|
+
} from "@lukso/lsp1-contracts/contracts/LSP1Constants.sol";
|
26
|
+
|
27
|
+
// errors
|
28
|
+
import {
|
29
|
+
LSP26CannotSelfFollow,
|
30
|
+
LSP26AlreadyFollowing,
|
31
|
+
LSP26NotFollowing
|
32
|
+
} from "./LSP26Errors.sol";
|
33
|
+
|
34
|
+
contract LSP26FollowerSystem is ILSP26FollowerSystem {
|
35
|
+
using EnumerableSet for EnumerableSet.AddressSet;
|
36
|
+
using ERC165Checker for address;
|
37
|
+
|
38
|
+
mapping(address => EnumerableSet.AddressSet) private _followersOf;
|
39
|
+
mapping(address => EnumerableSet.AddressSet) private _followingsOf;
|
40
|
+
|
41
|
+
// @inheritdoc ILSP26FollowerSystem
|
42
|
+
function follow(address addr) public {
|
43
|
+
_follow(addr);
|
44
|
+
}
|
45
|
+
|
46
|
+
// @inheritdoc ILSP26FollowerSystem
|
47
|
+
function followBatch(address[] memory addresses) public {
|
48
|
+
for (uint256 index = 0; index < addresses.length; ++index) {
|
49
|
+
_follow(addresses[index]);
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
// @inheritdoc ILSP26FollowerSystem
|
54
|
+
function unfollow(address addr) public {
|
55
|
+
_unfollow(addr);
|
56
|
+
}
|
57
|
+
|
58
|
+
// @inheritdoc ILSP26FollowerSystem
|
59
|
+
function unfollowBatch(address[] memory addresses) public {
|
60
|
+
for (uint256 index = 0; index < addresses.length; ++index) {
|
61
|
+
_unfollow(addresses[index]);
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
// @inheritdoc ILSP26FollowerSystem
|
66
|
+
function isFollowing(
|
67
|
+
address follower,
|
68
|
+
address addr
|
69
|
+
) public view returns (bool) {
|
70
|
+
return _followingsOf[follower].contains(addr);
|
71
|
+
}
|
72
|
+
|
73
|
+
// @inheritdoc ILSP26FollowerSystem
|
74
|
+
function followerCount(address addr) public view returns (uint256) {
|
75
|
+
return _followersOf[addr].length();
|
76
|
+
}
|
77
|
+
|
78
|
+
// @inheritdoc ILSP26FollowerSystem
|
79
|
+
function followingCount(address addr) public view returns (uint256) {
|
80
|
+
return _followingsOf[addr].length();
|
81
|
+
}
|
82
|
+
|
83
|
+
// @inheritdoc ILSP26FollowerSystem
|
84
|
+
function getFollowsByIndex(
|
85
|
+
address addr,
|
86
|
+
uint256 startIndex,
|
87
|
+
uint256 endIndex
|
88
|
+
) public view returns (address[] memory) {
|
89
|
+
uint256 sliceLength = endIndex - startIndex;
|
90
|
+
|
91
|
+
address[] memory followings = new address[](sliceLength);
|
92
|
+
|
93
|
+
for (uint256 index = 0; index < sliceLength; ++index) {
|
94
|
+
followings[index] = _followingsOf[addr].at(startIndex + index);
|
95
|
+
}
|
96
|
+
|
97
|
+
return followings;
|
98
|
+
}
|
99
|
+
|
100
|
+
// @inheritdoc ILSP26FollowerSystem
|
101
|
+
function getFollowersByIndex(
|
102
|
+
address addr,
|
103
|
+
uint256 startIndex,
|
104
|
+
uint256 endIndex
|
105
|
+
) public view returns (address[] memory) {
|
106
|
+
uint256 sliceLength = endIndex - startIndex;
|
107
|
+
|
108
|
+
address[] memory followers = new address[](sliceLength);
|
109
|
+
|
110
|
+
for (uint256 index = 0; index < sliceLength; ++index) {
|
111
|
+
followers[index] = _followersOf[addr].at(startIndex + index);
|
112
|
+
}
|
113
|
+
|
114
|
+
return followers;
|
115
|
+
}
|
116
|
+
|
117
|
+
function _follow(address addr) internal {
|
118
|
+
if (msg.sender == addr) {
|
119
|
+
revert LSP26CannotSelfFollow();
|
120
|
+
}
|
121
|
+
|
122
|
+
bool isAdded = _followingsOf[msg.sender].add(addr);
|
123
|
+
|
124
|
+
if (!isAdded) {
|
125
|
+
revert LSP26AlreadyFollowing(addr);
|
126
|
+
}
|
127
|
+
|
128
|
+
_followersOf[addr].add(msg.sender);
|
129
|
+
|
130
|
+
emit Follow(msg.sender, addr);
|
131
|
+
|
132
|
+
if (addr.supportsERC165InterfaceUnchecked(_INTERFACEID_LSP1)) {
|
133
|
+
// solhint-disable no-empty-blocks
|
134
|
+
try
|
135
|
+
ILSP1UniversalReceiver(addr).universalReceiver(
|
136
|
+
_TYPEID_LSP26_FOLLOW,
|
137
|
+
abi.encodePacked(msg.sender)
|
138
|
+
)
|
139
|
+
{} catch {}
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
function _unfollow(address addr) internal {
|
144
|
+
bool isRemoved = _followingsOf[msg.sender].remove(addr);
|
145
|
+
|
146
|
+
if (!isRemoved) {
|
147
|
+
revert LSP26NotFollowing(addr);
|
148
|
+
}
|
149
|
+
|
150
|
+
_followersOf[addr].remove(msg.sender);
|
151
|
+
|
152
|
+
emit Unfollow(msg.sender, addr);
|
153
|
+
|
154
|
+
if (addr.supportsERC165InterfaceUnchecked(_INTERFACEID_LSP1)) {
|
155
|
+
// solhint-disable no-empty-blocks
|
156
|
+
try
|
157
|
+
ILSP1UniversalReceiver(addr).universalReceiver(
|
158
|
+
_TYPEID_LSP26_UNFOLLOW,
|
159
|
+
abi.encodePacked(msg.sender)
|
160
|
+
)
|
161
|
+
{} catch {}
|
162
|
+
}
|
163
|
+
}
|
164
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
2
|
+
pragma solidity ^0.8.17;
|
3
|
+
|
4
|
+
// interfaces
|
5
|
+
import {
|
6
|
+
ILSP1UniversalReceiver
|
7
|
+
} from "@lukso/lsp1-contracts/contracts/ILSP1UniversalReceiver.sol";
|
8
|
+
|
9
|
+
contract InfiniteLoopURD is ILSP1UniversalReceiver {
|
10
|
+
uint256 public counter;
|
11
|
+
|
12
|
+
function supportsInterface(bytes4) external pure returns (bool) {
|
13
|
+
return true;
|
14
|
+
}
|
15
|
+
|
16
|
+
function universalReceiver(
|
17
|
+
bytes32,
|
18
|
+
bytes memory
|
19
|
+
) external payable returns (bytes memory) {
|
20
|
+
while (true) {
|
21
|
+
++counter;
|
22
|
+
}
|
23
|
+
}
|
24
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
2
|
+
pragma solidity ^0.8.17;
|
3
|
+
|
4
|
+
// interfaces
|
5
|
+
import {
|
6
|
+
ILSP1UniversalReceiver
|
7
|
+
} from "@lukso/lsp1-contracts/contracts/ILSP1UniversalReceiver.sol";
|
8
|
+
|
9
|
+
contract ReturnBomb is ILSP1UniversalReceiver {
|
10
|
+
uint256 public counter;
|
11
|
+
|
12
|
+
function supportsInterface(bytes4) external pure returns (bool) {
|
13
|
+
return true;
|
14
|
+
}
|
15
|
+
|
16
|
+
function universalReceiver(
|
17
|
+
bytes32,
|
18
|
+
bytes memory
|
19
|
+
) external payable returns (bytes memory) {
|
20
|
+
++counter;
|
21
|
+
// solhint-disable-next-line no-inline-assembly
|
22
|
+
assembly {
|
23
|
+
revert(0, 10000)
|
24
|
+
}
|
25
|
+
}
|
26
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
2
|
+
pragma solidity ^0.8.17;
|
3
|
+
|
4
|
+
// interfaces
|
5
|
+
import {
|
6
|
+
ILSP1UniversalReceiver
|
7
|
+
} from "@lukso/lsp1-contracts/contracts/ILSP1UniversalReceiver.sol";
|
8
|
+
|
9
|
+
contract RevertOnFollow is ILSP1UniversalReceiver {
|
10
|
+
function supportsInterface(bytes4) external pure returns (bool) {
|
11
|
+
return true;
|
12
|
+
}
|
13
|
+
|
14
|
+
function universalReceiver(
|
15
|
+
bytes32,
|
16
|
+
bytes memory
|
17
|
+
) external payable returns (bytes memory) {
|
18
|
+
revert("Block LSP1 notifications");
|
19
|
+
}
|
20
|
+
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
2
|
+
pragma solidity ^0.8.17;
|
3
|
+
|
4
|
+
// interfaces
|
5
|
+
import {
|
6
|
+
ILSP1UniversalReceiver
|
7
|
+
} from "@lukso/lsp1-contracts/contracts/ILSP1UniversalReceiver.sol";
|
8
|
+
|
9
|
+
contract SelfDestructOnInterfaceCheck is ILSP1UniversalReceiver {
|
10
|
+
uint256 public counter;
|
11
|
+
|
12
|
+
function supportsInterface(bytes4) external returns (bool) {
|
13
|
+
selfdestruct(payable(msg.sender));
|
14
|
+
return true;
|
15
|
+
}
|
16
|
+
|
17
|
+
function universalReceiver(
|
18
|
+
bytes32,
|
19
|
+
bytes memory
|
20
|
+
) external payable returns (bytes memory) {
|
21
|
+
return "";
|
22
|
+
}
|
23
|
+
}
|
package/dist/index.cjs
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
const INTERFACE_ID_LSP26 = "0x2b299cea";
|
4
|
+
const LSP26_TYPE_IDS = {
|
5
|
+
// keccak256('LSP26FollowerSystem_FollowNotification')
|
6
|
+
LSP26FollowerSystem_FollowNotification: "0x71e02f9f05bcd5816ec4f3134aa2e5a916669537ec6c77fe66ea595fabc2d51a",
|
7
|
+
// keccak256('LSP26FollowerSystem_UnfollowNotification')
|
8
|
+
LSP26FollowerSystem_UnfollowNotification: "0x9d3c0b4012b69658977b099bdaa51eff0f0460f421fba96d15669506c00d1c4f"
|
9
|
+
};
|
10
|
+
|
11
|
+
exports.INTERFACE_ID_LSP26 = INTERFACE_ID_LSP26;
|
12
|
+
exports.LSP26_TYPE_IDS = LSP26_TYPE_IDS;
|
package/dist/index.d.cts
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
declare const INTERFACE_ID_LSP26 = "0x2b299cea";
|
2
|
+
declare const LSP26_TYPE_IDS: {
|
3
|
+
readonly LSP26FollowerSystem_FollowNotification: "0x71e02f9f05bcd5816ec4f3134aa2e5a916669537ec6c77fe66ea595fabc2d51a";
|
4
|
+
readonly LSP26FollowerSystem_UnfollowNotification: "0x9d3c0b4012b69658977b099bdaa51eff0f0460f421fba96d15669506c00d1c4f";
|
5
|
+
};
|
6
|
+
|
7
|
+
export { INTERFACE_ID_LSP26, LSP26_TYPE_IDS };
|
package/dist/index.d.mts
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
declare const INTERFACE_ID_LSP26 = "0x2b299cea";
|
2
|
+
declare const LSP26_TYPE_IDS: {
|
3
|
+
readonly LSP26FollowerSystem_FollowNotification: "0x71e02f9f05bcd5816ec4f3134aa2e5a916669537ec6c77fe66ea595fabc2d51a";
|
4
|
+
readonly LSP26FollowerSystem_UnfollowNotification: "0x9d3c0b4012b69658977b099bdaa51eff0f0460f421fba96d15669506c00d1c4f";
|
5
|
+
};
|
6
|
+
|
7
|
+
export { INTERFACE_ID_LSP26, LSP26_TYPE_IDS };
|
package/dist/index.d.ts
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
declare const INTERFACE_ID_LSP26 = "0x2b299cea";
|
2
|
+
declare const LSP26_TYPE_IDS: {
|
3
|
+
readonly LSP26FollowerSystem_FollowNotification: "0x71e02f9f05bcd5816ec4f3134aa2e5a916669537ec6c77fe66ea595fabc2d51a";
|
4
|
+
readonly LSP26FollowerSystem_UnfollowNotification: "0x9d3c0b4012b69658977b099bdaa51eff0f0460f421fba96d15669506c00d1c4f";
|
5
|
+
};
|
6
|
+
|
7
|
+
export { INTERFACE_ID_LSP26, LSP26_TYPE_IDS };
|
package/dist/index.mjs
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
const INTERFACE_ID_LSP26 = "0x2b299cea";
|
2
|
+
const LSP26_TYPE_IDS = {
|
3
|
+
// keccak256('LSP26FollowerSystem_FollowNotification')
|
4
|
+
LSP26FollowerSystem_FollowNotification: "0x71e02f9f05bcd5816ec4f3134aa2e5a916669537ec6c77fe66ea595fabc2d51a",
|
5
|
+
// keccak256('LSP26FollowerSystem_UnfollowNotification')
|
6
|
+
LSP26FollowerSystem_UnfollowNotification: "0x9d3c0b4012b69658977b099bdaa51eff0f0460f421fba96d15669506c00d1c4f"
|
7
|
+
};
|
8
|
+
|
9
|
+
export { INTERFACE_ID_LSP26, LSP26_TYPE_IDS };
|
package/package.json
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
{
|
2
|
+
"name": "@lukso/lsp26-contracts",
|
3
|
+
"version": "0.1.2",
|
4
|
+
"description": "Package for the LSP26 Follower System standard",
|
5
|
+
"license": "Apache-2.0",
|
6
|
+
"author": "",
|
7
|
+
"main": "./dist/index.cjs",
|
8
|
+
"module": "./dist/index.mjs",
|
9
|
+
"typings": "./dist/index.d.ts",
|
10
|
+
"exports": {
|
11
|
+
".": {
|
12
|
+
"require": "./dist/index.cjs",
|
13
|
+
"import": "./dist/index.mjs",
|
14
|
+
"types": "./dist/index.d.ts"
|
15
|
+
},
|
16
|
+
"./artifacts/*": "./artifacts/*",
|
17
|
+
"./package.json": "./package.json"
|
18
|
+
},
|
19
|
+
"keywords": [
|
20
|
+
"LUKSO",
|
21
|
+
"LSP",
|
22
|
+
"Blockchain",
|
23
|
+
"Standards",
|
24
|
+
"Smart Contracts",
|
25
|
+
"Ethereum",
|
26
|
+
"EVM",
|
27
|
+
"Solidity"
|
28
|
+
],
|
29
|
+
"files": [
|
30
|
+
"contracts/**/*.sol",
|
31
|
+
"!contracts/Mocks/**/*.sol",
|
32
|
+
"artifacts/*.json",
|
33
|
+
"dist",
|
34
|
+
"types",
|
35
|
+
"!types/factories",
|
36
|
+
"./README.md"
|
37
|
+
],
|
38
|
+
"scripts": {
|
39
|
+
"build": "hardhat compile --show-stack-traces",
|
40
|
+
"build:js": "unbuild",
|
41
|
+
"build:types": "npx wagmi generate",
|
42
|
+
"clean": "hardhat clean && rm -Rf dist/ cache/ node_modules/ .turbo/ types/ typechain/ build/ artifacts/",
|
43
|
+
"format": "prettier --write .",
|
44
|
+
"lint": "eslint . --ext .ts,.js",
|
45
|
+
"lint:solidity": "solhint 'contracts/**/*.sol' && prettier --check 'contracts/**/*.sol'",
|
46
|
+
"test": "hardhat test --no-compile tests/*.test.ts",
|
47
|
+
"test:coverage": "hardhat coverage",
|
48
|
+
"package": "hardhat prepare-package"
|
49
|
+
},
|
50
|
+
"dependencies": {
|
51
|
+
"@openzeppelin/contracts": "^4.9.6",
|
52
|
+
"@lukso/lsp0-contracts": "~0.15.0",
|
53
|
+
"@lukso/lsp1-contracts": "~0.15.0"
|
54
|
+
}
|
55
|
+
}
|