Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 810ee26

Browse files
authoredMay 20, 2020
GNS: cleanup and v1 (#199)
* GNS: cleanup and v1
1 parent 53f6cf6 commit 810ee26

File tree

7 files changed

+203
-306
lines changed

7 files changed

+203
-306
lines changed
 

‎contracts/GNS.sol

Lines changed: 73 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -4,162 +4,105 @@ pragma experimental ABIEncoderV2;
44
import "./Governed.sol";
55

66

7+
/**
8+
* @title GNS
9+
* @dev The Graph Name System contract provides a decentralized namings system for subgraphs
10+
* used in the scope of the Graph Network. It translate subgraph names into subgraphID regarded as
11+
* versions.
12+
*/
713
contract GNS is Governed {
8-
/* Events */
9-
event DomainAdded(bytes32 indexed topLevelDomainHash, address indexed owner, string domainName);
10-
event DomainTransferred(bytes32 indexed domainHash, address indexed newOwner);
11-
event SubgraphCreated(
12-
bytes32 indexed topLevelDomainHash,
13-
bytes32 indexed registeredHash,
14-
string subdomainName,
15-
address indexed owner
16-
);
17-
event SubgraphIDUpdated(bytes32 indexed domainHash, bytes32 indexed subgraphID);
18-
event DomainDeleted(bytes32 indexed domainHash);
19-
event AccountMetadataChanged(address indexed account, bytes32 indexed ipfsHash);
20-
event SubgraphMetadataChanged(bytes32 indexed domainHash, bytes32 indexed ipfsHash);
21-
22-
/* TYPES */
23-
struct Domain {
14+
// -- Types --
15+
16+
enum RecordType { GNS }
17+
18+
struct Record {
2419
address owner;
2520
bytes32 subgraphID;
21+
RecordType nameSystem;
2622
}
2723

28-
/* STATE VARIABLES */
29-
// Storage of a hashed top level domain to owners.
30-
mapping(bytes32 => Domain) public domains;
24+
// -- State --
3125

32-
/* Contract Constructor */
33-
/* @param _governor <address> - Address of the multisig contract as Governor */
34-
constructor(address _governor) public Governed(_governor) {}
26+
mapping(bytes32 => Record) public records;
3527

36-
/* Graph Protocol Functions */
28+
// -- Events --
3729

38-
modifier onlyDomainOwner(bytes32 _domainHash) {
39-
require(msg.sender == domains[_domainHash].owner, "Only domain owner can call.");
40-
_;
41-
}
30+
/**
31+
* @dev Emitted when `owner` publish a `subgraphID` version under subgraph `name`.
32+
* The event also attach `metadataHash` with extra information.
33+
*/
34+
event SubgraphPublished(string name, address owner, bytes32 subgraphID, bytes32 metadataHash);
4235

43-
/*
44-
* @notice Register a domain to an owner.
45-
* @param _domainName <string> - Domain name, which is treated as a username.
36+
/**
37+
* @dev Emitted when subgraph `nameHash` is unpublished by its owner.
4638
*/
47-
function registerDomain(string calldata _domainName) external {
48-
bytes32 hashedName = keccak256(abi.encodePacked(_domainName));
49-
// Require that this domain is not yet owned by anyone.
50-
require(domains[hashedName].owner == address(0), "Domain is already owned.");
51-
domains[hashedName].owner = msg.sender;
52-
emit DomainAdded(hashedName, msg.sender, _domainName);
53-
}
39+
event SubgraphUnpublished(bytes32 nameHash);
5440

55-
/*
56-
* @notice Create a subgraph by registering a subdomain, or registering the top level
57-
* domain as a subgraph.
58-
* @notice To register to the top level domain, pass _subdomainName as a blank string.
59-
* @dev Only the domain owner may do this.
60-
*
61-
* @param _topLevelDomainHash <bytes32> - Hash of the top level domain name.
62-
* @param _subdomainName <string> - Name of the Subdomain. If you were
63-
* registering 'david.thegraph', _subdomainName would be just 'david'.
64-
* @param _ipfsHash <bytes32> - Hash of the subgraph metadata, such as description.
41+
/**
42+
* @dev Emitted when subgraph `nameHash` is transferred to new owner.
6543
*/
66-
function createSubgraph(
67-
bytes32 _topLevelDomainHash,
68-
string calldata _subdomainName,
69-
bytes32 _ipfsHash
70-
) external onlyDomainOwner(_topLevelDomainHash) {
71-
bytes32 domainHash;
72-
bytes32 subdomainHash = keccak256(abi.encodePacked(_subdomainName));
73-
74-
// Subdomain is blank, therefore we are setting the subgraphID of the top level domain
75-
if (subdomainHash == keccak256("")) {
76-
// The domain hash ends up being the top level domain hash.
77-
domainHash = _topLevelDomainHash;
78-
} else {
79-
// The domain hash becomes the subdomain concatenated with the top level domain hash.
80-
domainHash = keccak256(abi.encodePacked(subdomainHash, _topLevelDomainHash));
81-
require(
82-
domains[domainHash].owner == address(0),
83-
"Someone already owns this subdomain."
84-
);
85-
domains[domainHash].owner = msg.sender;
86-
}
87-
88-
// Note - subdomain name and IPFS hash are only emitted through the events.
89-
// Note - if the subdomain is blank, the domain hash ends up being the top level
90-
// domain hash, not the hash of a blank string.
91-
emit SubgraphCreated(_topLevelDomainHash, domainHash, _subdomainName, msg.sender);
92-
emit SubgraphMetadataChanged(domainHash, _ipfsHash);
44+
event SubgraphTransferred(bytes32 nameHash, address from, address to);
45+
46+
modifier onlyRecordOwner(bytes32 _nameHash) {
47+
require(msg.sender == records[_nameHash].owner, "GNS: Only record owner can call");
48+
_;
9349
}
9450

95-
/*
96-
* @notice Update an existing subdomain with a subgraph ID.
97-
* @dev Only the domain owner may do this.
98-
*
99-
* @param _domainHash <bytes32> - Hash of the domain name.
100-
* @param _subgraphID <bytes32> - IPLD subgraph ID of the domain.
51+
/**
52+
* @dev Contract Constructor
53+
* @param _governor Owner address of this contract
10154
*/
102-
function updateDomainSubgraphID(bytes32 _domainHash, bytes32 _subgraphID)
103-
external
104-
onlyDomainOwner(_domainHash)
105-
{
55+
constructor(address _governor) public Governed(_governor) {}
56+
57+
/**
58+
* @dev Publish a version using `subgraphID` under a subgraph name.
59+
* @param _name Name of the subgraph
60+
* @param _subgraphID Subgraph ID to link to the subgraph name
61+
* @param _metadataHash IPFS hash linked to the metadata
62+
*/
63+
function publish(
64+
string calldata _name,
65+
bytes32 _subgraphID,
66+
bytes32 _metadataHash
67+
) external {
68+
address owner = msg.sender;
69+
bytes32 nameHash = keccak256(bytes(_name));
10670
require(
107-
_subgraphID != bytes32(0),
108-
"If you want to reset the subgraphID, call deleteSubdomain."
71+
!isReserved(nameHash) || records[nameHash].owner == owner,
72+
"GNS: Record reserved, only record owner can publish"
10973
);
110-
domains[_domainHash].subgraphID = _subgraphID;
111-
emit SubgraphIDUpdated(_domainHash, _subgraphID);
112-
}
11374

114-
/*
115-
* @notice Remove an existing domain owner and subgraphID
116-
* @dev Only the domain owner may do this.
117-
*
118-
* @param _domainHash <bytes32> - Hash of the domain name.
119-
*/
120-
function deleteSubdomain(bytes32 _domainHash) external onlyDomainOwner(_domainHash) {
121-
delete domains[_domainHash];
122-
emit DomainDeleted(_domainHash);
75+
records[nameHash] = Record(owner, _subgraphID, RecordType.GNS);
76+
emit SubgraphPublished(_name, owner, _subgraphID, _metadataHash);
12377
}
12478

125-
/*
126-
* @notice Transfer ownership of domain by existing domain owner.
127-
* @dev Only the domain owner may do this.
128-
*
129-
* @param _domainHash <bytes32> - Hash of the domain name.
130-
* @param _newOwner <address> - New owner of the domain.
79+
/**
80+
* @dev Unpublish a subgraph name. Can only be done by the owner.
81+
* @param _nameHash Keccak256 hash of the subgraph name
13182
*/
132-
function transferDomainOwnership(bytes32 _domainHash, address _newOwner)
133-
external
134-
onlyDomainOwner(_domainHash)
135-
{
136-
require(_newOwner != address(0), "If you want to reset the owner, call deleteSubdomain.");
137-
domains[_domainHash].owner = _newOwner;
138-
emit DomainTransferred(_domainHash, _newOwner);
83+
function unpublish(bytes32 _nameHash) external onlyRecordOwner(_nameHash) {
84+
delete records[_nameHash];
85+
emit SubgraphUnpublished(_nameHash);
13986
}
14087

141-
/*
142-
* @notice Change or initalize the Account Metadata, which is stored in a schema on IPFS.
143-
* @dev Only the msg.sender can do this.
144-
*
145-
* @param _ipfsHash <bytes32> - Hash of the IPFS file that stores the account metadata.
146-
* @param _account <address> - msg.sender.
88+
/**
89+
* @dev Tranfer the subgraph name to a new owner.
90+
* @param _nameHash Keccak256 hash of the subgraph name
91+
* @param _to Address of the new owner
14792
*/
148-
function changeAccountMetadata(bytes32 _ipfsHash) external {
149-
emit AccountMetadataChanged(msg.sender, _ipfsHash);
93+
function transfer(bytes32 _nameHash, address _to) external onlyRecordOwner(_nameHash) {
94+
require(_to != address(0), "GNS: Cannot transfer to empty address");
95+
require(records[_nameHash].owner != _to, "GNS: Cannot transfer to itself");
96+
records[_nameHash].owner = _to;
97+
emit SubgraphTransferred(_nameHash, msg.sender, _to);
15098
}
15199

152-
/*
153-
* @notice Change or initalize the Account Metadata, which is stored in a schema on IPFS.
154-
* @dev Only the msg.sender can do this.
155-
*
156-
* @param _ipfsHash <bytes32> - Hash of the IPFS file that stores the subgraph metadata.
157-
* @param _domainHash <bytes32> - Hash of the domain name.
100+
/**
101+
* @dev Return whether a subgraph name is registed or not.
102+
* @param _nameHash Keccak256 hash of the subgraph name
103+
* @return Return true if subgraph name exists
158104
*/
159-
function changeSubgraphMetadata(bytes32 _domainHash, bytes32 _ipfsHash)
160-
public
161-
onlyDomainOwner(_domainHash)
162-
{
163-
emit SubgraphMetadataChanged(_domainHash, _ipfsHash);
105+
function isReserved(bytes32 _nameHash) public view returns (bool) {
106+
return records[_nameHash].owner != address(0);
164107
}
165108
}

‎test/curation.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ contract('Curation', ([me, other, governor, curator, staking]) => {
3939
await this.grt.approve(this.curation.address, this.curatorTokens, { from: staking })
4040

4141
// Randomize a subgraphId
42-
this.subgraphId = helpers.randomSubgraphIdHex0x()
42+
this.subgraphId = helpers.randomSubgraphId()
4343
})
4444

4545
describe('configuration', function() {
@@ -146,7 +146,7 @@ contract('Curation', ([me, other, governor, curator, staking]) => {
146146

147147
context('> bonding curve', function() {
148148
beforeEach(function() {
149-
this.subgraphId = helpers.randomSubgraphIdHex0x()
149+
this.subgraphId = helpers.randomSubgraphId()
150150
})
151151

152152
it('convert shares to tokens', async function() {

‎test/disputes.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ contract('Disputes', ([me, other, governor, arbitrator, indexer, fisherman, othe
205205
const receipt = {
206206
requestCID: web3.utils.randomHex(32),
207207
responseCID: web3.utils.randomHex(32),
208-
subgraphID: helpers.randomSubgraphIdHex0x(),
208+
subgraphID: helpers.randomSubgraphId(),
209209
}
210210
this.dispute = await attestation.createDispute(
211211
receipt,

‎test/gns.test.js

Lines changed: 87 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -1,177 +1,123 @@
1+
const { utils } = require('ethers')
12
const { expectEvent, expectRevert } = require('@openzeppelin/test-helpers')
23

3-
// contracts
4-
const GNS = artifacts.require('./GNS.sol')
4+
const deployment = require('./lib/deployment')
55
const helpers = require('./lib/testHelpers')
66

7-
contract('GNS', accounts => {
8-
let deployedGNS
9-
const topLevelDomainName = 'thegraph'
10-
const topLevelDomainHash = web3.utils.soliditySha3(topLevelDomainName)
11-
const subdomainName = 'david'
12-
const hashedSubdomain = web3.utils.soliditySha3(
13-
web3.utils.soliditySha3(subdomainName),
14-
topLevelDomainHash,
15-
) // NOTE: There is a bug with web3.utils.keccak256() when using multiple inputs. soliditySha3() must be used
16-
const subgraphID = helpers.randomSubgraphIdBytes()
17-
const ipfsHash = helpers.randomSubgraphIdBytes()
18-
19-
before(async () => {
20-
// deploy GNS contract
21-
deployedGNS = await GNS.new(
22-
accounts[0], // governor
23-
{ from: accounts[0] },
24-
)
7+
contract('GNS', ([me, other, governor]) => {
8+
beforeEach(async function() {
9+
this.gns = await deployment.deployGNS(governor, { from: me })
10+
this.record = {
11+
name: 'graph',
12+
nameHash: utils.id('graph'),
13+
subgraphID: helpers.randomSubgraphId(),
14+
metadataHash: '0xeb50d096ba95573ae31640e38e4ef64fd02eec174f586624a37ea04e7bd8c751',
15+
}
16+
17+
this.publish = params =>
18+
this.gns.publish(this.record.name, this.record.subgraphID, this.record.metadataHash, params)
19+
this.unpublish = params => this.gns.unpublish(this.record.nameHash, params)
2520
})
2621

27-
it('...should allow a user to register a domain. And then prevent another user from being able to', async () => {
28-
const { logs } = await deployedGNS.registerDomain(topLevelDomainName, {
29-
from: accounts[1],
22+
describe('isReserved()', function() {
23+
it('should return if the name is reserved', async function() {
24+
expect(await this.gns.isReserved(this.record.nameHash)).to.be.eq(false)
25+
await this.publish({ from: me })
26+
expect(await this.gns.isReserved(this.record.nameHash)).to.be.eq(true)
3027
})
31-
const domain = await deployedGNS.domains(topLevelDomainHash)
32-
assert((await domain.owner) === accounts[1], 'Name was not registered properly.')
33-
34-
expectEvent.inLogs(logs, 'DomainAdded', {
35-
topLevelDomainHash: topLevelDomainHash,
36-
owner: accounts[1],
37-
domainName: topLevelDomainName,
38-
})
39-
40-
// Confirm another user cannot register this name
41-
await expectRevert(
42-
deployedGNS.registerDomain(topLevelDomainName, {
43-
from: accounts[3],
44-
}),
45-
'Domain is already owned.',
46-
)
4728
})
4829

49-
it('...should allow a user to create a subgraph only once, and not allow a different user to do so. ', async () => {
50-
const { logs } = await deployedGNS.createSubgraph(topLevelDomainHash, subdomainName, ipfsHash, {
51-
from: accounts[1],
30+
describe('publish()', async function() {
31+
it('should publish a version', async function() {
32+
const { logs } = await this.publish({ from: me })
33+
34+
// State updated
35+
const record = await this.gns.records(this.record.nameHash)
36+
expect(record.owner).to.be.eq(me)
37+
expect(record.subgraphID).to.be.eq(this.record.subgraphID)
38+
39+
// Event emitted
40+
expectEvent.inLogs(logs, 'SubgraphPublished', {
41+
name: this.record.name,
42+
owner: me,
43+
subgraphID: this.record.subgraphID,
44+
metadataHash: this.record.metadataHash,
45+
})
5246
})
53-
const domain = await deployedGNS.domains(hashedSubdomain)
54-
assert((await domain.owner) === accounts[1], 'Subdomain was not created properly.')
5547

56-
expectEvent.inLogs(logs, 'SubgraphCreated', {
57-
topLevelDomainHash: topLevelDomainHash,
58-
registeredHash: hashedSubdomain,
59-
subdomainName: subdomainName,
48+
it('should allow re-publish', async function() {
49+
await this.publish({ from: me })
50+
await this.publish({ from: me })
6051
})
6152

62-
expectEvent.inLogs(logs, 'SubgraphMetadataChanged', {
63-
domainHash: hashedSubdomain,
64-
ipfsHash: web3.utils.bytesToHex(ipfsHash),
53+
it('reject publish if overwritting with different account', async function() {
54+
await this.publish({ from: me })
55+
await expectRevert(
56+
this.publish({ from: other }),
57+
'GNS: Record reserved, only record owner can publish',
58+
)
6559
})
66-
67-
// Check that another user can't create
68-
await expectRevert(
69-
deployedGNS.createSubgraph(topLevelDomainHash, subdomainName, ipfsHash, {
70-
from: accounts[3],
71-
}),
72-
'Only domain owner can call.',
73-
)
74-
75-
// Check that the owner can't call createSubgraph() twice
76-
await expectRevert(
77-
deployedGNS.createSubgraph(topLevelDomainHash, subdomainName, ipfsHash, {
78-
from: accounts[1],
79-
}),
80-
'Someone already owns this subdomain.',
81-
)
8260
})
8361

84-
it('...should allow a user to update a subgraphID, and not allow a different user to do so. ', async () => {
85-
const { logs } = await deployedGNS.updateDomainSubgraphID(hashedSubdomain, subgraphID, {
86-
from: accounts[1],
87-
})
88-
const domain = await deployedGNS.domains(hashedSubdomain)
89-
assert(
90-
(await domain.subgraphID) === web3.utils.bytesToHex(subgraphID),
91-
'Subdomain was not registered properly.',
92-
)
93-
94-
expectEvent.inLogs(logs, 'SubgraphIDUpdated', {
95-
domainHash: hashedSubdomain,
96-
subgraphID: web3.utils.bytesToHex(subgraphID),
97-
})
62+
describe('unpublish()', async function() {
63+
it('should unpublish a name', async function() {
64+
await this.publish({ from: me })
65+
const { logs } = await this.unpublish({ from: me })
9866

99-
// Check that another user can't register
100-
await expectRevert(
101-
deployedGNS.updateDomainSubgraphID(hashedSubdomain, subgraphID, {
102-
from: accounts[3],
103-
}),
104-
'Only domain owner can call.',
105-
)
106-
})
67+
// State updated
68+
const record = await this.gns.records(this.record.nameHash)
69+
expect(record.owner).to.be.eq(helpers.zeroAddress())
10770

108-
it('...should allow subgraph metadata to be updated', async () => {
109-
const { logs } = await deployedGNS.changeSubgraphMetadata(hashedSubdomain, ipfsHash, {
110-
from: accounts[1],
71+
// Event emitted
72+
expectEvent.inLogs(logs, 'SubgraphUnpublished', {
73+
nameHash: this.record.nameHash,
74+
})
11175
})
11276

113-
expectEvent.inLogs(logs, 'SubgraphMetadataChanged', {
114-
domainHash: hashedSubdomain,
115-
ipfsHash: web3.utils.bytesToHex(ipfsHash),
77+
it('reject unpublish if not the owner', async function() {
78+
await expectRevert(this.unpublish({ from: other }), 'GNS: Only record owner can call')
11679
})
117-
118-
// Check that a different owner can't call
119-
await expectRevert(
120-
deployedGNS.changeSubgraphMetadata(ipfsHash, hashedSubdomain, {
121-
from: accounts[3],
122-
}),
123-
'Only domain owner can call.',
124-
)
12580
})
12681

127-
it('...should allow a user to transfer a domain', async () => {
128-
const { logs } = await deployedGNS.transferDomainOwnership(hashedSubdomain, accounts[2], {
129-
from: accounts[1],
130-
})
131-
132-
expectEvent.inLogs(logs, 'DomainTransferred', {
133-
domainHash: hashedSubdomain,
134-
newOwner: accounts[2],
82+
describe('transfer()', function() {
83+
beforeEach(async function() {
84+
await this.publish({ from: me })
13585
})
13686

137-
// Check that a different owner can't call
138-
await expectRevert(
139-
deployedGNS.transferDomainOwnership(hashedSubdomain, accounts[4], {
140-
from: accounts[3],
141-
}),
142-
'Only domain owner can call.',
143-
)
144-
})
87+
it('should transfer to new owner', async function() {
88+
const { logs } = await this.gns.transfer(this.record.nameHash, other, { from: me })
14589

146-
it('...should allow a domain and subgraphID to be deleted', async () => {
147-
await expectRevert(
148-
deployedGNS.deleteSubdomain(hashedSubdomain, { from: accounts[3] }),
149-
'Only domain owner can call.',
150-
)
90+
// State updated
91+
const record = await this.gns.records(this.record.nameHash)
92+
expect(record.owner).to.be.eq(other)
15193

152-
const { logs } = await deployedGNS.deleteSubdomain(hashedSubdomain, {
153-
from: accounts[2],
94+
// Event emitted
95+
expectEvent.inLogs(logs, 'SubgraphTransferred', {
96+
nameHash: this.record.nameHash,
97+
from: me,
98+
to: other,
99+
})
154100
})
155101

156-
expectEvent.inLogs(logs, 'DomainDeleted', {
157-
domainHash: hashedSubdomain,
102+
it('reject transfer if not owner', async function() {
103+
await expectRevert(
104+
this.gns.transfer(this.record.nameHash, other, { from: other }),
105+
'GNS: Only record owner can call',
106+
)
158107
})
159108

160-
const deletedDomain = await deployedGNS.domains(hashedSubdomain)
161-
assert(deletedDomain.subgraphID === helpers.zeroHex(), 'SubgraphID was not deleted')
162-
assert(deletedDomain.owner === helpers.zeroAddress(), 'Owner was not removed')
163-
})
164-
165-
it('...should allow account metadata event to be emitted ', async () => {
166-
const accountIPFSHash = helpers.randomSubgraphIdBytes()
167-
168-
const { logs } = await deployedGNS.changeAccountMetadata(accountIPFSHash, {
169-
from: accounts[2],
109+
it('reject transfer to empty address', async function() {
110+
await expectRevert(
111+
this.gns.transfer(this.record.nameHash, helpers.zeroAddress(), { from: me }),
112+
'GNS: Cannot transfer to empty address',
113+
)
170114
})
171115

172-
expectEvent.inLogs(logs, 'AccountMetadataChanged', {
173-
account: accounts[2],
174-
ipfsHash: web3.utils.bytesToHex(accountIPFSHash),
116+
it('reject transfer to itself', async function() {
117+
await expectRevert(
118+
this.gns.transfer(this.record.nameHash, me, { from: me }),
119+
'GNS: Cannot transfer to itself',
120+
)
175121
})
176122
})
177123
})

‎test/lib/deployment.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
const Curation = artifacts.require('./Curation.sol')
33
const DisputeManager = artifacts.require('./DisputeManager')
44
const EpochManager = artifacts.require('./EpochManager')
5+
const GNS = artifacts.require('./GNS')
56
const GraphToken = artifacts.require('./GraphToken.sol')
67
const ServiceRegisty = artifacts.require('./ServiceRegistry.sol')
78
const Staking = artifacts.require('./Staking.sol')
@@ -40,6 +41,10 @@ function deployEpochManagerContract(owner, params) {
4041
return EpochManager.new(owner, defaults.epochs.lengthInBlocks, params)
4142
}
4243

44+
function deployGNS(owner, params) {
45+
return GNS.new(owner, params)
46+
}
47+
4348
function deployServiceRegistry(owner) {
4449
return ServiceRegisty.new({ from: owner })
4550
}
@@ -56,6 +61,7 @@ module.exports = {
5661
deployCurationContract,
5762
deployDisputeManagerContract,
5863
deployEpochManagerContract,
64+
deployGNS,
5965
deployGRT,
6066
deployServiceRegistry,
6167
deployStakingContract,

‎test/lib/testHelpers.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
const BN = web3.utils.BN
22

33
module.exports = {
4-
randomSubgraphIdHex0x: () => web3.utils.randomHex(32),
5-
randomSubgraphIdHex: hex => hex.substring(2),
6-
randomSubgraphIdBytes: (hex = web3.utils.randomHex(32)) => web3.utils.hexToBytes(hex),
4+
randomSubgraphId: () => web3.utils.randomHex(32),
75
logStake: stakes =>
86
Object.entries(stakes).map(([k, v]) => console.log(k, ':', web3.utils.fromWei(v))),
97
zerobytes: () =>

‎test/staking/general.test.js

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ function weightedAverage(valueA, valueB, periodA, periodB) {
1616
.div(valueA.add(valueB))
1717
}
1818

19+
function toGRT(value) {
20+
return web3.utils.toWei(new BN(value))
21+
}
22+
1923
contract('Staking', ([me, other, governor, indexer, slasher, fisherman]) => {
2024
beforeEach(async function() {
2125
// Deploy epoch contract
@@ -165,13 +169,13 @@ contract('Staking', ([me, other, governor, indexer, slasher, fisherman]) => {
165169

166170
describe('staking', function() {
167171
beforeEach(async function() {
168-
this.subgraphId = helpers.randomSubgraphIdHex0x()
172+
this.subgraphId = helpers.randomSubgraphId()
169173
this.channelPubKey =
170174
'0x0456708870bfd5d8fc956fe33285dcf59b075cd7a25a21ee00834e480d3754bcda180e670145a290bb4bebca8e105ea7776a7b39e16c4df7d4d1083260c6f05d53'
171175
this.channelID = '0x6367E9dD7641e0fF221740b57B8C730031d72530'
172176

173177
// Give some funds to the indexer
174-
this.indexerTokens = web3.utils.toWei(new BN('1000'))
178+
this.indexerTokens = toGRT('1000')
175179
await this.grt.mint(indexer, this.indexerTokens, {
176180
from: governor,
177181
})
@@ -198,7 +202,7 @@ contract('Staking', ([me, other, governor, indexer, slasher, fisherman]) => {
198202

199203
describe('stake()', function() {
200204
it('should stake tokens', async function() {
201-
const indexerStake = web3.utils.toWei(new BN('100'))
205+
const indexerStake = toGRT('100')
202206

203207
// Stake
204208
await this.stake(indexerStake)
@@ -209,7 +213,7 @@ contract('Staking', ([me, other, governor, indexer, slasher, fisherman]) => {
209213

210214
describe('unstake()', function() {
211215
it('reject unstake tokens', async function() {
212-
const tokensToUnstake = web3.utils.toWei(new BN('2'))
216+
const tokensToUnstake = toGRT('2')
213217
await expectRevert(
214218
this.staking.unstake(tokensToUnstake, { from: indexer }),
215219
'Staking: indexer has no stakes',
@@ -219,15 +223,15 @@ contract('Staking', ([me, other, governor, indexer, slasher, fisherman]) => {
219223

220224
describe('allocate()', function() {
221225
it('reject allocate to subgraph', async function() {
222-
const indexerStake = web3.utils.toWei(new BN('100'))
226+
const indexerStake = toGRT('100')
223227
await expectRevert(this.allocate(indexerStake), 'Allocation: indexer has no stakes')
224228
})
225229
})
226230

227231
describe('slash()', function() {
228232
it('reject slash indexer', async function() {
229-
const tokensToSlash = web3.utils.toWei(new BN('10'))
230-
const tokensToReward = web3.utils.toWei(new BN('10'))
233+
const tokensToSlash = toGRT('10')
234+
const tokensToReward = toGRT('10')
231235

232236
await expectRevert(
233237
this.staking.slash(indexer, tokensToSlash, tokensToReward, fisherman, {
@@ -242,7 +246,7 @@ contract('Staking', ([me, other, governor, indexer, slasher, fisherman]) => {
242246
context('> when staked', function() {
243247
beforeEach(async function() {
244248
// Stake
245-
this.indexerStake = web3.utils.toWei(new BN('100'))
249+
this.indexerStake = toGRT('100')
246250
await this.stake(this.indexerStake)
247251
})
248252

@@ -267,7 +271,7 @@ contract('Staking', ([me, other, governor, indexer, slasher, fisherman]) => {
267271

268272
describe('unstake()', function() {
269273
it('should unstake and lock tokens for thawing period', async function() {
270-
const tokensToUnstake = web3.utils.toWei(new BN('2'))
274+
const tokensToUnstake = toGRT('2')
271275
const thawingPeriod = await this.staking.thawingPeriod()
272276
const currentBlock = await time.latestBlock()
273277
const until = currentBlock.add(thawingPeriod).add(new BN(1))
@@ -281,7 +285,7 @@ contract('Staking', ([me, other, governor, indexer, slasher, fisherman]) => {
281285
})
282286

283287
it('should unstake and lock tokens for (weighted avg) thawing period if repeated', async function() {
284-
const tokensToUnstake = web3.utils.toWei(new BN('10'))
288+
const tokensToUnstake = toGRT('10')
285289
const thawingPeriod = await this.staking.thawingPeriod()
286290

287291
// Unstake (1)
@@ -319,7 +323,7 @@ contract('Staking', ([me, other, governor, indexer, slasher, fisherman]) => {
319323
describe('withdraw()', function() {
320324
it('should withdraw if tokens available', async function() {
321325
// Unstake
322-
const tokensToUnstake = web3.utils.toWei(new BN('10'))
326+
const tokensToUnstake = toGRT('10')
323327
const { logs } = await this.staking.unstake(tokensToUnstake, { from: indexer })
324328
const tokensLockedUntil = logs[0].args.until
325329

@@ -392,16 +396,16 @@ contract('Staking', ([me, other, governor, indexer, slasher, fisherman]) => {
392396

393397
it('should slash indexer and give reward to beneficiary slash>reward', async function() {
394398
// Slash indexer
395-
const tokensToSlash = web3.utils.toWei(new BN('100'))
396-
const tokensToReward = web3.utils.toWei(new BN('10'))
399+
const tokensToSlash = toGRT('100')
400+
const tokensToReward = toGRT('10')
397401

398402
await this.shouldSlash(indexer, tokensToSlash, tokensToReward, fisherman)
399403
})
400404

401405
it('should slash indexer and give reward to beneficiary slash=reward', async function() {
402406
// Slash indexer
403-
const tokensToSlash = web3.utils.toWei(new BN('10'))
404-
const tokensToReward = web3.utils.toWei(new BN('10'))
407+
const tokensToSlash = toGRT('10')
408+
const tokensToReward = toGRT('10')
405409

406410
await this.shouldSlash(indexer, tokensToSlash, tokensToReward, fisherman)
407411
})
@@ -411,11 +415,11 @@ contract('Staking', ([me, other, governor, indexer, slasher, fisherman]) => {
411415
const beforeTokensStaked = await this.staking.getIndexerStakeTokens(indexer)
412416

413417
// Unstake partially, these tokens will be locked
414-
const tokensToUnstake = web3.utils.toWei(new BN('10'))
418+
const tokensToUnstake = toGRT('10')
415419
await this.staking.unstake(tokensToUnstake, { from: indexer })
416420

417421
// Allocate indexer stake
418-
const tokensToAllocate = web3.utils.toWei(new BN('70'))
422+
const tokensToAllocate = toGRT('70')
419423
await this.allocate(tokensToAllocate)
420424

421425
// State pre-slashing
@@ -427,8 +431,8 @@ contract('Staking', ([me, other, governor, indexer, slasher, fisherman]) => {
427431
// = Available: 20 (staked - allocated - locked)
428432

429433
// Even if all stake is allocated to subgraphs it should slash the indexer
430-
const tokensToSlash = web3.utils.toWei(new BN('80'))
431-
const tokensToReward = web3.utils.toWei(new BN('0'))
434+
const tokensToSlash = toGRT('80')
435+
const tokensToReward = toGRT('0')
432436
await this.shouldSlash(indexer, tokensToSlash, tokensToReward, fisherman)
433437

434438
// State post-slashing
@@ -453,7 +457,7 @@ contract('Staking', ([me, other, governor, indexer, slasher, fisherman]) => {
453457
const tokensAvailable = stakes.tokensIndexer
454458
.sub(stakes.tokensAllocated)
455459
.sub(stakes.tokensLocked)
456-
expect(tokensAvailable).to.be.bignumber.eq(web3.utils.toWei(new BN('-50')))
460+
expect(tokensAvailable).to.be.bignumber.eq(toGRT('-50'))
457461

458462
await expectRevert(
459463
this.staking.unstake(tokensToUnstake, { from: indexer }),
@@ -462,17 +466,17 @@ contract('Staking', ([me, other, governor, indexer, slasher, fisherman]) => {
462466
})
463467

464468
it('reject to slash indexer if sender is not slasher', async function() {
465-
const tokensToSlash = web3.utils.toWei(new BN('100'))
466-
const tokensToReward = web3.utils.toWei(new BN('10'))
469+
const tokensToSlash = toGRT('100')
470+
const tokensToReward = toGRT('10')
467471
await expectRevert(
468472
this.staking.slash(indexer, tokensToSlash, tokensToReward, me, { from: me }),
469473
'Caller is not a Slasher',
470474
)
471475
})
472476

473477
it('reject to slash indexer if beneficiary is zero address', async function() {
474-
const tokensToSlash = web3.utils.toWei(new BN('100'))
475-
const tokensToReward = web3.utils.toWei(new BN('10'))
478+
const tokensToSlash = toGRT('100')
479+
const tokensToReward = toGRT('10')
476480
await expectRevert(
477481
this.staking.slash(indexer, tokensToSlash, tokensToReward, ZERO_ADDRESS, {
478482
from: slasher,
@@ -482,8 +486,8 @@ contract('Staking', ([me, other, governor, indexer, slasher, fisherman]) => {
482486
})
483487

484488
it('reject to slash indexer if reward is greater than slash amount', async function() {
485-
const tokensToSlash = web3.utils.toWei(new BN('100'))
486-
const tokensToReward = web3.utils.toWei(new BN('200'))
489+
const tokensToSlash = toGRT('100')
490+
const tokensToReward = toGRT('200')
487491
await expectRevert(
488492
this.staking.slash(indexer, tokensToSlash, tokensToReward, fisherman, {
489493
from: slasher,
@@ -516,19 +520,19 @@ contract('Staking', ([me, other, governor, indexer, slasher, fisherman]) => {
516520
})
517521

518522
it('reject allocate zero tokens', async function() {
519-
const zeroTokens = web3.utils.toWei(new BN('0'))
523+
const zeroTokens = toGRT('0')
520524
await expectRevert(this.allocate(zeroTokens), 'Allocation: cannot allocate zero tokens')
521525
})
522526
})
523527

524528
context('> when subgraph allocated', function() {
525529
beforeEach(async function() {
526-
this.tokensAllocated = web3.utils.toWei(new BN('10'))
530+
this.tokensAllocated = toGRT('10')
527531
await this.allocate(this.tokensAllocated)
528532
})
529533

530534
it('reject allocate again if not settled', async function() {
531-
const tokensToAllocate = web3.utils.toWei(new BN('10'))
535+
const tokensToAllocate = toGRT('10')
532536
await expectRevert(
533537
this.allocate(tokensToAllocate),
534538
'Allocation: cannot allocate if already allocated',

0 commit comments

Comments
 (0)
Please sign in to comment.